From e548967372c0ce946a9ef0d91e02ab0bdcd1b6d8 Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Mon, 4 Mar 2019 11:10:13 -0800 Subject: [PATCH 01/81] Implement NSURLDirectoryEnumerator for Windows --- Foundation/FileManager.swift | 163 ++++++++++++++++++++++++++--------- 1 file changed, 120 insertions(+), 43 deletions(-) diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 16369e63a8a..0750f7dcbf4 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -15,6 +15,21 @@ internal func &(left: UInt32, right: mode_t) -> mode_t { import CoreFoundation +#if os(Windows) +internal func joinPath(prefix: String, suffix: String) -> String { + var pszPath: PWSTR? + _ = prefix.withCString(encodedAs: UTF16.self) { prefix in + _ = suffix.withCString(encodedAs: UTF16.self) { suffix in + PathAllocCombine(prefix, suffix, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &pszPath) + } + } + + let path: String = String(decodingCString: pszPath!, as: UTF16.self) + LocalFree(pszPath) + return path +} +#endif + open class FileManager : NSObject { /* Returns the default singleton instance. @@ -801,21 +816,6 @@ open class FileManager : NSObject { return contents } -#if os(Windows) - private func joinPath(prefix: String, suffix: String) -> String { - var pszPath: PWSTR? - _ = prefix.withCString(encodedAs: UTF16.self) { prefix in - _ = suffix.withCString(encodedAs: UTF16.self) { suffix in - PathAllocCombine(prefix, suffix, ULONG(PATHCCH_ALLOW_LONG_PATHS.rawValue), &pszPath) - } - } - - let path: String = String(decodingCString: pszPath!, as: UTF16.self) - LocalFree(pszPath) - return path - } -#endif - /** Performs a deep enumeration of the specified directory and returns the paths of all of the contained subdirectories. @@ -2377,7 +2377,6 @@ extension FileManager { self.innerEnumerator = ie } - @available(Windows, deprecated, message: "Not Yet Implemented") override func nextObject() -> Any? { let o = innerEnumerator.nextObject() guard let url = o as? URL else { @@ -2385,24 +2384,104 @@ extension FileManager { } #if os(Windows) - NSUnimplemented() + var relativePath = UnsafeMutableBufferPointer.allocate(capacity: Int(MAX_PATH)) + defer { relativePath.deallocate() } + func withURLCString(url: URL, _ f: (UnsafePointer) -> Result?) -> Result? { + return url.withUnsafeFileSystemRepresentation { fsr in + (fsr.flatMap { String(utf8String: $0) })?.withCString(encodedAs: UTF16.self) { f($0) } + } + } + let result = withURLCString(url: baseURL) { pszFrom -> BOOL? in + withURLCString(url: url) { pszTo in + let fromAttrs = GetFileAttributesW(pszFrom) + let toAttrs = GetFileAttributesW(pszTo) + guard fromAttrs != INVALID_FILE_ATTRIBUTES, toAttrs != INVALID_FILE_ATTRIBUTES else { + return FALSE + } + return PathRelativePathToW(relativePath.baseAddress, pszFrom, fromAttrs, pszTo, toAttrs) + } + } + + guard result == TRUE, let (path, _) = String.decodeCString(relativePath.baseAddress, as: UTF16.self) else { + return nil + } #else let path = url.path.replacingOccurrences(of: baseURL.path+"/", with: "") - _currentItemPath = path - return path #endif + _currentItemPath = path + return _currentItemPath } } -#if os(Windows) internal class NSURLDirectoryEnumerator : DirectoryEnumerator { - internal typealias ErrorHandler = /* @escaping */ (URL, Error) -> Bool +#if os(Windows) + var _options : FileManager.DirectoryEnumerationOptions + var _errorHandler : ((URL, Error) -> Bool)? + var _stack: [URL] + var _current: URL? + var _rootDepth : Int - init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: ErrorHandler?) { + init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) { + _options = options + _errorHandler = errorHandler + _stack = [url] + _rootDepth = url.pathComponents.count + } + + override func nextObject() -> Any? { + func contentsOfDir(directory: URL) -> [URL]? { + var ffd: WIN32_FIND_DATAW = WIN32_FIND_DATAW() + guard let dirFSR = directory.withUnsafeFileSystemRepresentation({ $0.flatMap { fsr in String(utf8String: fsr) } }) + else { return nil } + let dirPath = joinPath(prefix: dirFSR, suffix: "*") + let h: HANDLE = dirPath.withCString(encodedAs: UTF16.self) { + FindFirstFileW($0, &ffd) + } + guard h != INVALID_HANDLE_VALUE else { return nil } + defer { FindClose(h) } + + var files: [URL] = [] + repeat { + let fileArr = Array( + UnsafeBufferPointer(start: &ffd.cFileName.0, + count: MemoryLayout.size(ofValue: ffd.cFileName))) + let file = String(decodingCString: fileArr, as: UTF16.self) + if file != "." + && file != ".." + && (!_options.contains(.skipsHiddenFiles) + || (ffd.dwFileAttributes & DWORD(FILE_ATTRIBUTE_HIDDEN) == 0)) { + files.append(URL(fileURLWithPath: joinPath(prefix: dirFSR, suffix: file))) + } + } while(FindNextFileW(h, &ffd) != 0) + return files + } + while let url = _stack.popLast() { + if url.hasDirectoryPath && !_options.contains(.skipsSubdirectoryDescendants) { + guard let dirContents = contentsOfDir(directory: url)?.reversed() else { + if let handler = _errorHandler { + let dirFSR = url.withUnsafeFileSystemRepresentation { $0.flatMap { fsr in String(utf8String: fsr) } } + let keepGoing = handler(URL(fileURLWithPath: dirFSR ?? ""), + _NSErrorWithWindowsError(GetLastError(), reading: true)) + if !keepGoing { return nil } + } + continue + } + _stack.append(contentsOf: dirContents) + } + _current = url + return url + } + return nil + } + + override var level: Int { + return _rootDepth - (_current?.pathComponents.count ?? _rootDepth) + } + + override func skipDescendants() { + _options.insert(.skipsSubdirectoryDescendants) } - } #else - internal class NSURLDirectoryEnumerator : DirectoryEnumerator { var _url : URL var _options : FileManager.DirectoryEnumerationOptions var _errorHandler : ((URL, Error) -> Bool)? @@ -2410,13 +2489,14 @@ extension FileManager { var _current : UnsafeMutablePointer? = nil var _rootError : Error? = nil var _gotRoot : Bool = false - + + // See @escaping comments above. init(url: URL, options: FileManager.DirectoryEnumerationOptions, errorHandler: (/* @escaping */ (URL, Error) -> Bool)?) { _url = url _options = options _errorHandler = errorHandler - + if FileManager.default.fileExists(atPath: _url.path) { let fsRep = FileManager.default.fileSystemRepresentation(withPath: _url.path) let ps = UnsafeMutablePointer?>.allocate(capacity: 2) @@ -2430,15 +2510,15 @@ extension FileManager { _rootError = _NSErrorWithErrno(ENOENT, reading: true, url: url) } } - + deinit { if let stream = _stream { fts_close(stream) } } - - override func nextObject() -> Any? { + + override func nextObject() -> Any? { func match(filename: String, to options: DirectoryEnumerationOptions, isDir: Bool) -> (Bool, Bool) { var showFile = true var skipDescendants = false @@ -2459,10 +2539,10 @@ extension FileManager { if let stream = _stream { - + if !_gotRoot { _gotRoot = true - + // Skip the root. _current = fts_read(stream) } @@ -2504,7 +2584,7 @@ extension FileManager { _current = fts_read(stream) } // TODO: Error handling if fts_read fails. - + } else if let error = _rootError { // Was there an error opening the stream? if let handler = _errorHandler { @@ -2513,24 +2593,21 @@ extension FileManager { } return nil } - - override var directoryAttributes : [FileAttributeKey : Any]? { - return nil - } - - override var fileAttributes: [FileAttributeKey : Any]? { - return nil - } - override var level: Int { return Int(_current?.pointee.fts_level ?? 0) } - + override func skipDescendants() { if let stream = _stream, let current = _current { fts_set(stream, current, FTS_SKIP) } } - } #endif + override var directoryAttributes : [FileAttributeKey : Any]? { + return nil + } + override var fileAttributes: [FileAttributeKey : Any]? { + return nil + } + } } From 8a85f44fc32abea70b2a8bedef1b00520f7501ac Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Tue, 12 Mar 2019 21:02:36 +0000 Subject: [PATCH 02/81] Wrap more filesystem calls with fileSystemRepresentation() - FileManager._removeItem() and FileHandle._openFileDescriptorForURL() now wrap the system calls with _fileSystemRepresentation(withPath:) --- Foundation/FileHandle.swift | 6 ++- Foundation/FileManager.swift | 85 +++++++++++++++++------------------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 304fd72ab80..46e067fae48 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -752,8 +752,10 @@ extension FileHandle { } internal static func _openFileDescriptorForURL(_ url : URL, flags: Int32, reading: Bool) throws -> Int32 { - let path = url.path - let fd = _CFOpenFile(path, flags) + let fd = url.withUnsafeFileSystemRepresentation( { (fsRep) -> Int32 in + guard let fsRep = fsRep else { return -1 } + return _CFOpenFile(fsRep, flags) + }) if fd < 0 { throw _NSErrorWithErrno(errno, reading: reading, url: url) } diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 16369e63a8a..785b5409e54 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -1450,61 +1450,58 @@ open class FileManager : NSObject { #if os(Windows) NSUnimplemented() #else - if rmdir(path) == 0 { - return - } else if errno == ENOTEMPTY { - - let stream = URL(fileURLWithPath: path).withUnsafeFileSystemRepresentation { (fsRep) -> UnsafeMutablePointer? in + try _fileSystemRepresentation(withPath: path, { fsRep in + if rmdir(fsRep) == 0 { + return + } else if errno == ENOTEMPTY { let ps = UnsafeMutablePointer?>.allocate(capacity: 2) ps.initialize(to: UnsafeMutablePointer(mutating: fsRep)) ps.advanced(by: 1).initialize(to: nil) - defer { - ps.deinitialize(count: 2) - ps.deallocate() - } - return fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, nil) - } - - if stream != nil { - defer { - fts_close(stream) - } + let stream = fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, nil) + ps.deinitialize(count: 2) + ps.deallocate() - while let current = fts_read(stream)?.pointee { - let itemPath = string(withFileSystemRepresentation: current.fts_path, length: Int(current.fts_pathlen)) - guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else { - continue + if stream != nil { + defer { + fts_close(stream) } - - do { - switch Int32(current.fts_info) { - case FTS_DEFAULT, FTS_F, FTS_NSOK, FTS_SL, FTS_SLNONE: - if unlink(current.fts_path) == -1 { - throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + + while let current = fts_read(stream)?.pointee { + let itemPath = string(withFileSystemRepresentation: current.fts_path, length: Int(current.fts_pathlen)) + guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else { + continue + } + + do { + switch Int32(current.fts_info) { + case FTS_DEFAULT, FTS_F, FTS_NSOK, FTS_SL, FTS_SLNONE: + if unlink(current.fts_path) == -1 { + throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + } + case FTS_DP: + if rmdir(current.fts_path) == -1 { + throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + } + case FTS_DNR, FTS_ERR, FTS_NS: + throw _NSErrorWithErrno(current.fts_errno, reading: false, path: itemPath) + default: + break } - case FTS_DP: - if rmdir(current.fts_path) == -1 { - throw _NSErrorWithErrno(errno, reading: false, path: itemPath) + } catch { + if !shouldProceedAfterError(error, removingItemAtPath: itemPath, isURL: isURL) { + throw error } - case FTS_DNR, FTS_ERR, FTS_NS: - throw _NSErrorWithErrno(current.fts_errno, reading: false, path: itemPath) - default: - break - } - } catch { - if !shouldProceedAfterError(error, removingItemAtPath: itemPath, isURL: isURL) { - throw error } } + } else { + let _ = _NSErrorWithErrno(ENOTEMPTY, reading: false, path: path) } - } else { - let _ = _NSErrorWithErrno(ENOTEMPTY, reading: false, path: path) + } else if errno != ENOTDIR { + throw _NSErrorWithErrno(errno, reading: false, path: path) + } else if unlink(fsRep) != 0 { + throw _NSErrorWithErrno(errno, reading: false, path: path) } - } else if errno != ENOTDIR { - throw _NSErrorWithErrno(errno, reading: false, path: path) - } else if _fileSystemRepresentation(withPath: path, { unlink($0) != 0 }) { - throw _NSErrorWithErrno(errno, reading: false, path: path) - } + }) #endif } From 5bbcfeafedb3b5e652cc6635f1e2cd2a62552e8f Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Fri, 1 Mar 2019 21:01:26 -0800 Subject: [PATCH 03/81] Use Response Files and Output File Maps in CMake Passing all of the swift source files to the swift compiler causes the command line to go over Window's 32767 character limit command length. Instead, write the source file lists to response files to get around that. Additionally, now use the swift driver to track dependencies instead of manually calling the swiftc frontend --- cmake/modules/SwiftSupport.cmake | 100 +++++++++++++++---------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/cmake/modules/SwiftSupport.cmake b/cmake/modules/SwiftSupport.cmake index ce3449998bf..f8778d3e5ef 100644 --- a/cmake/modules/SwiftSupport.cmake +++ b/cmake/modules/SwiftSupport.cmake @@ -1,6 +1,37 @@ include(CMakeParseArguments) +# Creates an output file map give a target and its list of sources at +# output_path +# +# Usage: +# create_output_file_map(target sources output_path) +function(create_output_file_map target sources output_path) + set(output_list) + set(output_file_map "{\n") + foreach(source ${sources}) + get_filename_component(name ${source} NAME) + + set(obj ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}${CMAKE_C_OUTPUT_EXTENSION}) + set(deps ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.d) + set(swiftdeps ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.swiftdeps) + set(dia ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.dia) + set(output_entry "\"${source}\": {\n\ + \"object\": \"${obj}\",\n\ + \"dependencies\": \"${deps}\",\n\ + \"swift-dependencies\": \"${swiftdeps}\",\n\ + \"diagnostics\": \"${dia}\"\n\ +},\n") + string(APPEND output_file_map ${output_entry}) + endforeach() + set(master_deps ${CMAKE_CURRENT_BINARY_DIR}/${target}.swiftdeps) + string(APPEND output_file_map "\"\": {\n\ + \"swift-dependencies\": \"${master_deps}\"\n\ + }\n") + string(APPEND output_file_map "}\n") + file(WRITE ${output_path} ${output_file_map}) +endfunction() + function(add_swift_target target) set(options LIBRARY;SHARED;STATIC) set(single_value_options MODULE_NAME;MODULE_LINK_NAME;MODULE_PATH;MODULE_CACHE_PATH;OUTPUT;TARGET) @@ -11,6 +42,7 @@ function(add_swift_target target) set(compile_flags ${CMAKE_SWIFT_FLAGS}) set(link_flags) + list(APPEND compile_flags -incremental) if(AST_TARGET) list(APPEND compile_flags -target;${AST_TARGET}) list(APPEND link_flags -target;${AST_TARGET}) @@ -26,6 +58,11 @@ function(add_swift_target target) if(AST_MODULE_CACHE_PATH) list(APPEND compile_flags -module-cache-path;${AST_MODULE_CACHE_PATH}) endif() + if(AST_MODULE_PATH) + get_filename_component(module_location ${AST_MODULE_PATH} PATH) + file(MAKE_DIRECTORY "${module_location}") + list(APPEND compile_flags "-emit-module-path;${AST_MODULE_PATH}") + endif() if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) list(APPEND compile_flags -g) endif() @@ -75,74 +112,37 @@ function(add_swift_target target) endif() set(sources) + set(rsp_text) foreach(source ${AST_SOURCES}) get_filename_component(location ${source} PATH) if(IS_ABSOLUTE ${location}) list(APPEND sources ${source}) + string(APPEND rsp_text "${source} ") else() list(APPEND sources ${CMAKE_CURRENT_SOURCE_DIR}/${source}) + string(APPEND rsp_text "${CMAKE_CURRENT_SOURCE_DIR}/${source} ") endif() endforeach() - set(objs) - set(mods) - set(docs) - set(i 0) - foreach(source ${sources}) - get_filename_component(name ${source} NAME) - - set(obj ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}${CMAKE_C_OUTPUT_EXTENSION}) - set(mod ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.swiftmodule) - set(doc ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${name}.swiftdoc) - - set(all_sources ${sources}) - list(INSERT all_sources ${i} -primary-file) - - add_custom_command(OUTPUT - ${obj} - ${mod} - ${doc} - DEPENDS - ${source} - ${AST_DEPENDS} - COMMAND - ${CMAKE_SWIFT_COMPILER} -frontend ${compile_flags} -emit-module-path ${mod} -emit-module-doc-path ${doc} -o ${obj} -c ${all_sources}) - - list(APPEND objs ${obj}) - list(APPEND mods ${mod}) - list(APPEND docs ${doc}) - - math(EXPR i "${i}+1") - endforeach() - - if(AST_LIBRARY) - get_filename_component(module_directory ${AST_MODULE_PATH} DIRECTORY) - - set(module ${AST_MODULE_PATH}) - set(documentation ${module_directory}/${AST_MODULE_NAME}.swiftdoc) + set(output_map_path ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/output-file-map.json) + create_output_file_map(${target} "${sources}" ${output_map_path}) + list(APPEND compile_flags -output-file-map;${output_map_path}) - add_custom_command(OUTPUT - ${module} - ${documentation} - DEPENDS - ${mods} - ${docs} - ${AST_DEPENDS} - COMMAND - ${CMAKE_SWIFT_COMPILER} -frontend ${compile_flags} -sil-merge-partial-modules -emit-module ${mods} -o ${module} -emit-module-doc-path ${documentation}) - endif() + string(LENGTH sources source_length) + set(rsp_file ${CMAKE_CURRENT_BINARY_DIR}/${target}.dir/${target}.rsp) + file(WRITE ${rsp_file} ${rsp_text}) if(AST_LIBRARY) - set(emit_library -emit-library) + set(emit_library -emit-library) endif() if(NOT AST_LIBRARY OR library_kind STREQUAL SHARED) add_custom_command(OUTPUT ${AST_OUTPUT} DEPENDS - ${objs} + ${sources} ${AST_DEPENDS} COMMAND - ${CMAKE_SWIFT_COMPILER} ${emit_library} ${link_flags} -o ${AST_OUTPUT} ${objs}) + ${CMAKE_SWIFT_COMPILER} ${emit_library} ${compile_flags} ${link_flags} -o ${AST_OUTPUT} @${rsp_file}) add_custom_target(${target} ALL DEPENDS @@ -150,7 +150,7 @@ function(add_swift_target target) ${module} ${documentation}) else() - add_library(${target}-static STATIC ${objs}) + add_library(${target}-static STATIC ${sources}) if(AST_DEPENDS) add_dependencies(${target}-static ${AST_DEPENDS}) endif() From 260828f442caf040cccc9ee5ea8d64738771f776 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Wed, 6 Mar 2019 16:18:41 +0000 Subject: [PATCH 04/81] Fix more compile warnings - Use correct function prototype for functions taking no arguments. - Remove an undeed defer {} wrapper as call occurs at end of block. --- CoreFoundation/String.subproj/CFBurstTrie.h | 2 +- CoreFoundation/URL.subproj/CFURLComponents.h | 12 ++++++------ TestFoundation/TestURLSession.swift | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CoreFoundation/String.subproj/CFBurstTrie.h b/CoreFoundation/String.subproj/CFBurstTrie.h index ec1a28dacb3..fd86e0cc906 100644 --- a/CoreFoundation/String.subproj/CFBurstTrie.h +++ b/CoreFoundation/String.subproj/CFBurstTrie.h @@ -60,7 +60,7 @@ typedef CF_OPTIONS(CFOptionFlags, CFBurstTrieOpts) { typedef void (*CFBurstTrieTraversalCallback)(void* context, const UInt8* key, uint32_t keyLength, uint32_t payload, Boolean *stop); CF_EXPORT -CFBurstTrieRef CFBurstTrieCreate() API_AVAILABLE(macos(10.7), ios(4.2), watchos(2.0), tvos(9.0)); +CFBurstTrieRef CFBurstTrieCreate(void) API_AVAILABLE(macos(10.7), ios(4.2), watchos(2.0), tvos(9.0)); CF_EXPORT CFBurstTrieRef CFBurstTrieCreateWithOptions(CFDictionaryRef options) API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0)); diff --git a/CoreFoundation/URL.subproj/CFURLComponents.h b/CoreFoundation/URL.subproj/CFURLComponents.h index 081ea63d2a9..51e05529a8c 100644 --- a/CoreFoundation/URL.subproj/CFURLComponents.h +++ b/CoreFoundation/URL.subproj/CFURLComponents.h @@ -87,12 +87,12 @@ CF_EXPORT CFStringRef _CFStringCreateByAddingPercentEncodingWithAllowedCharacter CF_EXPORT CFStringRef _Nullable _CFStringCreateByRemovingPercentEncoding(CFAllocatorRef alloc, CFStringRef string); // These return singletons -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLUserAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPasswordAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLHostAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPathAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLQueryAllowedCharacterSet(); -CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLFragmentAllowedCharacterSet(); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLUserAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPasswordAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLHostAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLPathAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLQueryAllowedCharacterSet(void); +CF_EXPORT CFCharacterSetRef _CFURLComponentsGetURLFragmentAllowedCharacterSet(void); // keys for dictionaries returned by _CFURLComponentsCopyQueryItems CF_EXPORT const CFStringRef _kCFURLComponentsNameKey; diff --git a/TestFoundation/TestURLSession.swift b/TestFoundation/TestURLSession.swift index eb4a3ad4717..3f67a1ca1d1 100644 --- a/TestFoundation/TestURLSession.swift +++ b/TestFoundation/TestURLSession.swift @@ -523,7 +523,7 @@ class TestURLSession : LoopbackServerTest { XCTAssertNil(response) XCTAssertNotNil(error) - defer { expect.fulfill() } + expect.fulfill() } task.resume() waitForExpectations(timeout: 12) From 5df0f04797effa895a4dafbd6c739d2d24c7cb57 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 14 Mar 2019 14:43:13 -0700 Subject: [PATCH 05/81] build: use `add_subdirectory` instead of `add_external_project` This switches the CoreFoundation sub-build to use `add_subdirectory`. This has a few **MAJOR** benefits: - CoreFoundation changes get tracked and cause compile/link triggers - CoreFoundation warnings are surfaced in the build - CoreFoundation dependencies are reflected in the right location - Foundation dependencies are reflected in the right location - The overall build is simpler Eventually, it should be possible to further simplify this. Unfortunately, a couple of hacks are needed to make this work. The sub-projects properties must be cached, but they were just passthrough previously, so this is not too terrible. Additionally, the library control know needs to be saved/restored around it. The worst thing here is the need to manually collect the dependency as we do not have a proper library target for the Foundation (Swift) library. When CMake gains proper Swift support, it should be possible to use `add_library` and get a property target that we can use `target_link_libraries` with permitting the dependency tracking to allow us to get that without manual intervention. --- CMakeLists.txt | 97 +++++++++++++---------------------- CoreFoundation/CMakeLists.txt | 14 +++-- 2 files changed, 48 insertions(+), 63 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97fb50ccdee..f542b2f9416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,34 +27,14 @@ include(ExternalProject) string(TOLOWER ${CMAKE_SYSTEM_NAME} swift_os) get_swift_host_arch(swift_arch) -ExternalProject_Add(CoreFoundation - SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/CoreFoundation - CMAKE_COMMAND - ${CMAKE_COMMAND} - CMAKE_ARGS - -DBUILD_SHARED_LIBS=NO - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} - -DCMAKE_INSTALL_PREFIX= - -DCMAKE_INSTALL_LIBDIR=usr/lib - -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} - -DCMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR} - -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} - -DCF_DEPLOYMENT_SWIFT=YES - -DCF_ENABLE_LIBDISPATCH=${FOUNDATION_ENABLE_LIBDISPATCH} - -DCF_PATH_TO_LIBDISPATCH_SOURCE=${FOUNDATION_PATH_TO_LIBDISPATCH_SOURCE} - -DCF_PATH_TO_LIBDISPATCH_BUILD=${FOUNDATION_PATH_TO_LIBDISPATCH_BUILD} - -DICU_LIBRARY=${ICU_LIBRARY} - -DICU_INCLUDE_DIR=${ICU_INCLUDE_DIR} - -DCURL_LIBRARY=${CURL_LIBRARY} - -DCURL_INCLUDE_DIR=${CURL_INCLUDE_DIR} - -DLIBXML2_LIBRARY=${LIBXML2_LIBRARY} - -DLIBXML2_INCLUDE_DIR=${LIBXML2_INCLUDE_DIR} - INSTALL_COMMAND - ${CMAKE_COMMAND} -E env --unset=DESTDIR ${CMAKE_COMMAND} --build . --target install) -ExternalProject_Get_Property(CoreFoundation install_dir) +set(CF_PATH_TO_LIBDISPATCH_SOURCE ${FOUNDATION_PATH_TO_LIBDISPATCH_SOURCE} CACHE PATH "Path to libdispatch source" FORCE) +set(CF_PATH_TO_LIBDISPATCH_BUILD ${FOUNDATION_PATH_TO_LIBDISPATCH_BUILD} CACHE PATH "Path to libdispatch build" FORCE) +set(CF_DEPLOYMENT_SWIFT YES CACHE BOOL "Build for Swift" FORCE) + +set(SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) +set(BUILD_SHARED_LIBS NO) +add_subdirectory(CoreFoundation) +set(BUILD_SHARED_LIBS ${SAVED_BUILD_SHARED_LIBS}) add_library(uuid STATIC @@ -67,13 +47,14 @@ set_target_properties(uuid # the dependency on TargetConditionals.h target_compile_options(uuid PUBLIC - -I${install_dir}/System/Library/Frameworks/CoreFoundation.framework/Headers) + -I${CMAKE_CURRENT_BINARY_DIR}/CoreFoundation.framework/Headers) if(CMAKE_SYSTEM_NAME STREQUAL Windows) target_compile_definitions(uuid PRIVATE _CRT_NONSTDC_NO_WARNINGS _CRT_SECURE_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS) + target_link_libraries(uuid PRIVATE Bcrypt) endif() add_dependencies(uuid CoreFoundation) @@ -107,32 +88,24 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) set(Foundation_RPATH -Xlinker;-rpath;-Xlinker;"\\\$\$ORIGIN") elseif(CMAKE_SYSTEM_NAME STREQUAL Windows) set(deployment_target -DDEPLOYMENT_TARGET_WINDOWS) - # FIXME(compnerd) these are not all CoreFoundation dependencies, some of them - # are Foundation's and others are libcurl's. We should split them up - # accordingly. - set(CoreFoundation_INTERFACE_LIBRARIES - -lAdvAPI32 - -lCrypt32 - -lDbgHelp - -lShell32 - -lOle32 - -lRpcRT4 - -lSecur32 - -lShLwApi - -lUser32 - -lWldap32 - -lWS2_32 - -liphlpapi - -lmincore - -lnormaliz - -lpathcch - -lucrt - -lshell32) # FIXME(SR9138) Silence "locally defined symbol '…' imported in function '…' set(WORKAROUND_SR9138 -Xlinker;-ignore:4217) set(WORKAROUND_SR9995 -Xlinker;-nodefaultlib:libcmt) endif() +# NOTE(compnerd) this is a horrible hack to work around the fact that we do not +# have a proper library target for Foundation which can link against the +# CoreFoundation target. When we gain proper CMake support for Swift, we should +# be able to remove this and just use +# `target_link_libraries(Foundation PRIVATE CoreFoundation)`. +set(CoreFoundation_LIBRARIES $) +get_target_property(CoreFoundation_LINK_LIBRARIES CoreFoundation LINK_LIBRARIES) +foreach(library ${CoreFoundation_LINK_LIBRARIES}) + if(NOT library STREQUAL Threads::Threads) + list(APPEND CoreFoundation_LIBRARIES -l${library}) + endif() +endforeach() + add_swift_library(Foundation MODULE_NAME Foundation @@ -308,21 +281,25 @@ add_swift_library(Foundation CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} -D_DLL LINK_FLAGS - -L${install_dir}/usr/lib - -lCoreFoundation + ${CoreFoundation_LIBRARIES} ${CURL_LIBRARIES} ${ICU_UC_LIBRARY} ${ICU_I18N_LIBRARY} ${LIBXML2_LIBRARIES} ${libdispatch_ldflags} - -L${CMAKE_CURRENT_BINARY_DIR} - -luuid + $ ${Foundation_RPATH} - ${CoreFoundation_INTERFACE_LIBRARIES} ${WORKAROUND_SR9138} ${WORKAROUND_SR9995} + $<$:-lDbgHelp> + $<$:-lOle32> + $<$:-lShLwApi> + $<$:-lShell32> + $<$:-lWS2_32> + $<$:-liphlpapi> + $<$:-lpathcch> SWIFT_FLAGS -DDEPLOYMENT_RUNTIME_SWIFT ${deployment_enable_libdispatch} @@ -352,7 +329,7 @@ add_swift_executable(plutil CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} LINK_FLAGS ${libdispatch_ldflags} -L${CMAKE_CURRENT_BINARY_DIR} @@ -378,7 +355,7 @@ if(ENABLE_TESTING) CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} LINK_FLAGS ${libdispatch_ldflags} -L${CMAKE_CURRENT_BINARY_DIR} @@ -490,7 +467,7 @@ if(ENABLE_TESTING) CFLAGS ${deployment_target} ${deployment_enable_libdispatch} - -F${install_dir}/System/Library/Frameworks + -F${CMAKE_CURRENT_BINARY_DIR} LINK_FLAGS ${libdispatch_ldflags} -L${CMAKE_CURRENT_BINARY_DIR} @@ -576,7 +553,7 @@ else() endif() # TODO(compnerd) install as a Framework as that is how swift actually is built install(DIRECTORY - ${install_dir}/System/Library/Frameworks/CoreFoundation.framework/Headers/ + ${CMAKE_CURRENT_BINARY_DIR}/CoreFoundation.framework/Headers/ DESTINATION lib/swift/CoreFoundation FILES_MATCHING PATTERN "*.h") diff --git a/CoreFoundation/CMakeLists.txt b/CoreFoundation/CMakeLists.txt index 01735dd8e97..0abf9e8c27a 100644 --- a/CoreFoundation/CMakeLists.txt +++ b/CoreFoundation/CMakeLists.txt @@ -373,7 +373,7 @@ target_compile_definitions(CoreFoundation target_include_directories(CoreFoundation PRIVATE - ${CMAKE_SOURCE_DIR}) + ${PROJECT_SOURCE_DIR}) if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) find_package(LibXml2 REQUIRED) target_include_directories(CoreFoundation @@ -403,11 +403,11 @@ endif() if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") target_compile_options(CoreFoundation PRIVATE - $<$:/FI${CMAKE_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) + $<$:/FI${PROJECT_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) else() target_compile_options(CoreFoundation PRIVATE - $<$:-include;${CMAKE_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) + $<$:-include;${PROJECT_SOURCE_DIR}/Base.subproj/CoreFoundation_Prefix.h>) endif() if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") @@ -462,6 +462,14 @@ target_link_libraries(CoreFoundation PRIVATE Threads::Threads ${CMAKE_DL_LIBS}) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + target_link_libraries(CoreFoundation + PRIVATE + AdvAPI32 + Secur32 + User32 + mincore) +endif() if(NOT CMAKE_SYSTEM_NAME STREQUAL Windows AND NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) target_link_libraries(CoreFoundation PRIVATE From e06fcbe9fb2b352077ff2c03326e2d597a68cb01 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 14 Mar 2019 15:01:42 -0700 Subject: [PATCH 06/81] FileHandle: `NSUnsupported` not `NSUnavailable` Windows does not support `SOCKET`s as file descriptors, so mark this function as `NSUnsupported`. --- Foundation/FileHandle.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 304fd72ab80..575d10d7573 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -882,7 +882,7 @@ extension FileHandle { @available(Windows, unavailable, message: "A SOCKET cannot be treated as a fd") open func acceptConnectionInBackgroundAndNotify(forModes modes: [RunLoop.Mode]?) { #if os(Windows) - NSUnavailable() + NSUnsupported() #else let owner = monitor(forReading: true, resumed: false) { (handle, source) in var notification = Notification(name: .NSFileHandleConnectionAccepted, object: handle, userInfo: [:]) From 89bd68b16fb5be404a3c64b84443fca4f7a821be Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 7 Mar 2019 15:38:14 -0800 Subject: [PATCH 07/81] TestFoundation: use Thread.sleep instead of usleep/sleep --- TestFoundation/HTTPServer.swift | 2 +- TestFoundation/TestFileHandle.swift | 4 ++-- TestFoundation/TestOperationQueue.swift | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/TestFoundation/HTTPServer.swift b/TestFoundation/HTTPServer.swift index 310d084cc70..f6dc2916d75 100644 --- a/TestFoundation/HTTPServer.swift +++ b/TestFoundation/HTTPServer.swift @@ -156,7 +156,7 @@ class _TCPSocket { let texts = split(body, count) for item in texts { - sleep(UInt32(sendDelay)) + Thread.sleep(forTimeInterval: sendDelay) var bytes = Array(item.utf8) _ = try attempt("send", valid: isNotNegative, CInt(send(connectionSocket, &bytes, bytes.count, sendFlags))) } diff --git a/TestFoundation/TestFileHandle.swift b/TestFoundation/TestFileHandle.swift index f7d0db3e597..6dec686d1a7 100644 --- a/TestFoundation/TestFileHandle.swift +++ b/TestFoundation/TestFileHandle.swift @@ -318,7 +318,7 @@ class TestFileHandle : XCTestCase { let handle = createFileHandle() handle.readabilityHandler = { _ = $0.offsetInFile } handle.closeFile() - usleep(1000) + Thread.sleep(forTimeInterval: 0.001) } } @@ -327,7 +327,7 @@ class TestFileHandle : XCTestCase { let handle = createFileHandle() handle.readabilityHandler = { _ = try? $0.offset() } try handle.close() - usleep(1000) + Thread.sleep(forTimeInterval: 0.001) } } diff --git a/TestFoundation/TestOperationQueue.swift b/TestFoundation/TestOperationQueue.swift index cb3454cd350..7f67f7276b9 100644 --- a/TestFoundation/TestOperationQueue.swift +++ b/TestFoundation/TestOperationQueue.swift @@ -28,7 +28,7 @@ class TestOperationQueue : XCTestCase { func test_OperationCount() { let queue = OperationQueue() - let op1 = BlockOperation(block: { sleep(2) }) + let op1 = BlockOperation(block: { Thread.sleep(forTimeInterval: 2) }) queue.addOperation(op1) XCTAssertTrue(queue.operationCount == 1) queue.waitUntilAllOperationsAreFinished() @@ -252,7 +252,7 @@ class AsyncOperation: Operation { isExecuting = true queue.async { - sleep(1) + Thread.sleep(forTimeInterval: 1) self.isExecuting = false self.isFinished = true } From 3903677fddd1c5272994f857b8616444f030dbb5 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 7 Mar 2019 15:31:04 -0800 Subject: [PATCH 08/81] XDGTestHelper: disable signal tests on Windows --- TestFoundation/xdgTestHelper/main.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TestFoundation/xdgTestHelper/main.swift b/TestFoundation/xdgTestHelper/main.swift index e9607183c8f..29591e11abe 100644 --- a/TestFoundation/xdgTestHelper/main.swift +++ b/TestFoundation/xdgTestHelper/main.swift @@ -55,6 +55,7 @@ class XDGCheck { } } +#if !os(Windows) // ----- // Used by TestProcess: test_interrupt(), test_suspend_resume() @@ -103,6 +104,7 @@ func signalTest() { } } } +#endif // ----- @@ -190,8 +192,10 @@ case "--nspathfor": test.run() #endif +#if !os(Windows) case "--signal-test": signalTest() +#endif default: fatalError("These arguments are not recognized. Only run this from a unit test.") From 06099cd2c049fe56ac62bc1ab90846615ef1eccf Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 7 Mar 2019 15:34:06 -0800 Subject: [PATCH 09/81] TestFoundation: do not intercept SIGPIPE on Windows Windows does not have a SIGPIPE, do not attempt to intercept it. --- TestFoundation/main.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift index e7494dae92b..b5a4684c66f 100644 --- a/TestFoundation/main.swift +++ b/TestFoundation/main.swift @@ -15,8 +15,10 @@ import Glibc #endif +#if !os(Windows) // ignore SIGPIPE which is sent when writing to closed file descriptors. _ = signal(SIGPIPE, SIG_IGN) +#endif // For the Swift version of the Foundation tests, we must manually list all test cases here. XCTMain([ From ca6eca077ad0487a726b93f5578eb8bc08fef966 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Thu, 14 Mar 2019 21:49:02 +0000 Subject: [PATCH 10/81] DarwinCompatibility: TestStream.seek(to:) is internal API --- TestFoundation/TestStream.swift | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/TestFoundation/TestStream.swift b/TestFoundation/TestStream.swift index c9763e6d366..f703fb45101 100644 --- a/TestFoundation/TestStream.swift +++ b/TestFoundation/TestStream.swift @@ -7,10 +7,12 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if (os(Linux) || os(Android)) - @testable import Foundation -#else - @testable import SwiftFoundation +#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT + #if canImport(SwiftFoundation) + @testable import SwiftFoundation + #else + @testable import Foundation + #endif #endif @@ -33,13 +35,12 @@ private extension Data { class TestStream : XCTestCase { static var allTests: [(String, (TestStream) -> () throws -> Void)] { - return [ + var tests: [(String, (TestStream) -> () throws -> Void)] = [ ("test_InputStreamWithData", test_InputStreamWithData), ("test_InputStreamWithUrl", test_InputStreamWithUrl), ("test_InputStreamWithFile", test_InputStreamWithFile), ("test_InputStreamHasBytesAvailable", test_InputStreamHasBytesAvailable), ("test_InputStreamInvalidPath", test_InputStreamInvalidPath), - ("test_InputStreamSeekToPosition", test_InputStreamSeekToPosition), ("test_outputStreamCreationToFile", test_outputStreamCreationToFile), ("test_outputStreamCreationToBuffer", test_outputStreamCreationToBuffer), ("test_outputStreamCreationWithUrl", test_outputStreamCreationWithUrl), @@ -47,6 +48,11 @@ class TestStream : XCTestCase { ("test_outputStreamHasSpaceAvailable", test_outputStreamHasSpaceAvailable), ("test_ouputStreamWithInvalidPath", test_ouputStreamWithInvalidPath), ] + +#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT + tests.append(("test_InputStreamSeekToPosition", test_InputStreamSeekToPosition)) +#endif + return tests } func test_InputStreamWithData(){ @@ -140,7 +146,8 @@ class TestStream : XCTestCase { fileStream.open() XCTAssertEqual(.error, fileStream.streamStatus) } - + +#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT // Stream.seek(to:) is an internal API method func test_InputStreamSeekToPosition() { let str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras congue laoreet facilisis. Sed porta tristique orci. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel, tristique orci. Maecenas pretium, augue non elementum imperdiet, diam ex vestibulum tortor, non ultrices ante enim iaculis ex. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel, tristique orci. Maecenas pretium, augue non elementum imperdiet, diam ex vestibulum tortor, non ultrices ante enim iaculis ex.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras congue laoreet facilisis. Sed porta tristique orci. Fusce ut nisl dignissim, tempor tortor id, molestie neque. Nam non tincidunt mi. Integer ac diam quis leo aliquam congue et non magna. In porta mauris suscipit erat pulvinar, sed fringilla quam ornare. Nulla vulputate et ligula vitae sollicitudin. Nulla vel vehicula risus. Quisque eu urna ullamcorper, tincidunt ante vitae, aliquet sem. Suspendisse nec turpis placerat, porttitor ex vel." XCTAssert(str.count > 1024) // str.count must be bigger than buffersize inside InputStream.seek func. @@ -184,6 +191,7 @@ class TestStream : XCTestCase { XCTFail() } } +#endif func test_outputStreamCreationToFile() { guard let filePath = createTestFile("TestFileOut.txt", _contents: Data(capacity: 256)) else { From 96cb31e60bc5f965e21f059f0e2451745abd5990 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Thu, 14 Mar 2019 22:39:26 +0000 Subject: [PATCH 11/81] DarwinCompatibility: Disable tests that use new API not yet in Foundation --- TestFoundation/TestFileHandle.swift | 4 ++++ TestFoundation/TestNSCalendar.swift | 2 ++ TestFoundation/TestPipe.swift | 2 ++ TestFoundation/TestScanner.swift | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/TestFoundation/TestFileHandle.swift b/TestFoundation/TestFileHandle.swift index f7d0db3e597..340293bb068 100644 --- a/TestFoundation/TestFileHandle.swift +++ b/TestFoundation/TestFileHandle.swift @@ -7,6 +7,8 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +#if !DARWIN_COMPATIBILITY_TESTS // Disable until Foundation has the new FileHandle API + import Dispatch class TestFileHandle : XCTestCase { @@ -484,3 +486,5 @@ class TestFileHandle : XCTestCase { ] } } + +#endif diff --git a/TestFoundation/TestNSCalendar.swift b/TestFoundation/TestNSCalendar.swift index 9c6a9cea622..69c1eb39327 100644 --- a/TestFoundation/TestNSCalendar.swift +++ b/TestFoundation/TestNSCalendar.swift @@ -751,6 +751,7 @@ class TestNSCalendar: XCTestCase { } func test_rangeOfWeekendStartDate_interval_containingDate() throws { +#if !DARWIN_COMPATIBILITY_TESTS // NSCalender range(ofWeekendContaining:) is experimental try enumerateTestWeekends() { (calendar, interval) in let startDateResult = calendar.range(ofWeekendContaining: interval.start) XCTAssertEqual(startDateResult, interval) @@ -764,6 +765,7 @@ class TestNSCalendar: XCTestCase { let oneSecondBeforeEndResult = calendar.range(ofWeekendContaining: interval.end - 1) XCTAssertEqual(oneSecondBeforeEndResult, interval) } +#endif } func test_enumerateDatesStartingAfterDate_chineseEra_matchYearOne() throws { diff --git a/TestFoundation/TestPipe.swift b/TestFoundation/TestPipe.swift index 7eaff9528af..dbdec9bb60c 100644 --- a/TestFoundation/TestPipe.swift +++ b/TestFoundation/TestPipe.swift @@ -7,6 +7,7 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +#if !DARWIN_COMPATIBILITY_TESTS // Disable until Foundation has the new FileHandle API class TestPipe: XCTestCase { static var allTests: [(String, (TestPipe) -> () throws -> Void)] { @@ -57,3 +58,4 @@ class TestPipe: XCTestCase { XCTAssertEqual(text, convertedData) } } +#endif diff --git a/TestFoundation/TestScanner.swift b/TestFoundation/TestScanner.swift index d418b8164f3..4678569b3f7 100644 --- a/TestFoundation/TestScanner.swift +++ b/TestFoundation/TestScanner.swift @@ -7,6 +7,8 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +#if !DARWIN_COMPATIBILITY_TESTS // Disable until Foundation has the new Scanner API + fileprivate func withScanner(for string: String, invoking block: ((Scanner) throws -> Void)? = nil) rethrows { let scanner = Scanner(string: string) scanner.locale = Locale(identifier: "en_US_POSIX") @@ -481,3 +483,5 @@ class TestScanner : XCTestCase { ] } } + +#endif From 2c271738335b9aa789808dfc177006dea78d10a3 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Thu, 14 Mar 2019 23:00:52 +0000 Subject: [PATCH 12/81] DarwinCompatibility: Update Xcode Project. - Update Swift version to 5.0 and target deployment version to 10.14. - Rename TestNSProgressFraction.swift to TestProgressFraction.swift to reflect name of test class. - Wrap file in NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT as the type is internal and cant be tested by DarwinCompaitibilityTests. - Add TestPropertyListEncoder.swift and TestDateIntervalFormatter.swift to DarwinCompatibilityTests. --- CMakeLists.txt | 4 +- .../project.pbxproj | 32 +++-- .../xcschemes/xdgTestHelper.xcscheme | 112 ++++++++++++++++++ Foundation.xcodeproj/project.pbxproj | 6 +- ...ction.swift => TestProgressFraction.swift} | 19 +-- 5 files changed, 151 insertions(+), 22 deletions(-) create mode 100644 DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme rename TestFoundation/{TestNSProgressFraction.swift => TestProgressFraction.swift} (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97fb50ccdee..7acbe9194bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -424,7 +424,6 @@ if(ENABLE_TESTING) TestFoundation/TestIndexSet.swift TestFoundation/TestISO8601DateFormatter.swift TestFoundation/TestJSONEncoder.swift - TestFoundation/TestPropertyListEncoder.swift TestFoundation/TestJSONSerialization.swift TestFoundation/TestLengthFormatter.swift TestFoundation/TestMassFormatter.swift @@ -449,7 +448,6 @@ if(ENABLE_TESTING) TestFoundation/TestNSNumber.swift TestFoundation/TestNSOrderedSet.swift TestFoundation/TestNSPredicate.swift - TestFoundation/TestNSProgressFraction.swift TestFoundation/TestNSRange.swift TestFoundation/TestNSRegularExpression.swift TestFoundation/TestNSSet.swift @@ -466,6 +464,8 @@ if(ENABLE_TESTING) TestFoundation/TestProcessInfo.swift TestFoundation/TestProcess.swift TestFoundation/TestProgress.swift + TestFoundation/TestProgressFraction.swift + TestFoundation/TestPropertyListEncoder.swift TestFoundation/TestPropertyListSerialization.swift TestFoundation/TestRunLoop.swift TestFoundation/TestScanner.swift diff --git a/DarwinCompatibilityTests.xcodeproj/project.pbxproj b/DarwinCompatibilityTests.xcodeproj/project.pbxproj index fd26c80e57f..8adac6c162b 100644 --- a/DarwinCompatibilityTests.xcodeproj/project.pbxproj +++ b/DarwinCompatibilityTests.xcodeproj/project.pbxproj @@ -11,8 +11,11 @@ B907F36F20BB188800013CBE /* NSString-ISO-8859-1-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B907F36E20BB188800013CBE /* NSString-ISO-8859-1-data.txt */; }; B917D32420B0DB9700728EE0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B917D32320B0DB9700728EE0 /* Foundation.framework */; }; B917D32620B0DE2000728EE0 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = B917D32520B0DE2000728EE0 /* main.swift */; }; + B940492B223B13D000FB4384 /* TestProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940492A223B13D000FB4384 /* TestProgressFraction.swift */; }; B94897772135E7AD00FB930E /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94897762135E7AC00FB930E /* Utilities.swift */; }; B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */; }; + B97E7854222AF973007596B0 /* TestDateIntervalFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B97E7853222AF973007596B0 /* TestDateIntervalFormatter.swift */; }; + B97E7856222AF995007596B0 /* TestPropertyListEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B97E7855222AF995007596B0 /* TestPropertyListEncoder.swift */; }; B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */ = {isa = PBXBuildFile; fileRef = B987C65D2093C8AF0026B50D /* TestImports.swift */; }; B98E33E02136AC120044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */; }; B9C89F361F6BF89C00087AF4 /* TestScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89EE61F6BF88F00087AF4 /* TestScanner.swift */; }; @@ -80,7 +83,6 @@ B9C89F781F6BF89C00087AF4 /* TestNSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F291F6BF89900087AF4 /* TestNSData.swift */; }; B9C89F791F6BF89C00087AF4 /* TestNSRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2A1F6BF89900087AF4 /* TestNSRange.swift */; }; B9C89F7A1F6BF89C00087AF4 /* TestNSGeometry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2B1F6BF89900087AF4 /* TestNSGeometry.swift */; }; - B9C89F7B1F6BF89C00087AF4 /* TestNSProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2C1F6BF89900087AF4 /* TestNSProgressFraction.swift */; }; B9C89F7C1F6BF89C00087AF4 /* TestURLCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2D1F6BF89A00087AF4 /* TestURLCredential.swift */; }; B9C89F7D1F6BF89C00087AF4 /* TestRunLoop.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2E1F6BF89A00087AF4 /* TestRunLoop.swift */; }; B9C89F7E1F6BF89C00087AF4 /* TestThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9C89F2F1F6BF89B00087AF4 /* TestThread.swift */; }; @@ -155,8 +157,11 @@ B917D31C20B0DB8B00728EE0 /* xdgTestHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xdgTestHelper; sourceTree = BUILT_PRODUCTS_DIR; }; B917D32320B0DB9700728EE0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; B917D32520B0DE2000728EE0 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = main.swift; path = TestFoundation/xdgTestHelper/main.swift; sourceTree = ""; }; + B940492A223B13D000FB4384 /* TestProgressFraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestProgressFraction.swift; path = TestFoundation/TestProgressFraction.swift; sourceTree = ""; }; B94897762135E7AC00FB930E /* Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Utilities.swift; path = TestFoundation/Utilities.swift; sourceTree = ""; }; B95788851F6FB9470003EB01 /* TestNSNumberBridging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestNSNumberBridging.swift; path = TestFoundation/TestNSNumberBridging.swift; sourceTree = ""; }; + B97E7853222AF973007596B0 /* TestDateIntervalFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestDateIntervalFormatter.swift; path = TestFoundation/TestDateIntervalFormatter.swift; sourceTree = ""; }; + B97E7855222AF995007596B0 /* TestPropertyListEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestPropertyListEncoder.swift; path = TestFoundation/TestPropertyListEncoder.swift; sourceTree = ""; }; B987C65D2093C8AF0026B50D /* TestImports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestImports.swift; path = TestFoundation/TestImports.swift; sourceTree = ""; }; B98E33DF2136AC120044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TestFileWithZeros.txt; path = TestFoundation/Resources/TestFileWithZeros.txt; sourceTree = ""; }; B9C89ED11F6BF67C00087AF4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; @@ -294,6 +299,9 @@ B9C89EB81F6BF47D00087AF4 = { isa = PBXGroup; children = ( + B940492A223B13D000FB4384 /* TestProgressFraction.swift */, + B97E7855222AF995007596B0 /* TestPropertyListEncoder.swift */, + B97E7853222AF973007596B0 /* TestDateIntervalFormatter.swift */, B9C89FAC1F6DCAE700087AF4 /* Info.plist */, B9C89FAD1F6DCAE800087AF4 /* NSKeyedUnarchiver-ArrayTest.plist */, B9C89FB31F6DCAE900087AF4 /* NSKeyedUnarchiver-ComplexTest.plist */, @@ -558,6 +566,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B940492B223B13D000FB4384 /* TestProgressFraction.swift in Sources */, + B97E7856222AF995007596B0 /* TestPropertyListEncoder.swift in Sources */, + B97E7854222AF973007596B0 /* TestDateIntervalFormatter.swift in Sources */, B94897772135E7AD00FB930E /* Utilities.swift in Sources */, B987C65E2093C8AF0026B50D /* TestImports.swift in Sources */, B95788861F6FB9470003EB01 /* TestNSNumberBridging.swift in Sources */, @@ -632,7 +643,6 @@ B9C89F781F6BF89C00087AF4 /* TestNSData.swift in Sources */, B9C89F791F6BF89C00087AF4 /* TestNSRange.swift in Sources */, B9C89F7A1F6BF89C00087AF4 /* TestNSGeometry.swift in Sources */, - B9C89F7B1F6BF89C00087AF4 /* TestNSProgressFraction.swift in Sources */, B9C89F7C1F6BF89C00087AF4 /* TestURLCredential.swift in Sources */, B9C89F7D1F6BF89C00087AF4 /* TestRunLoop.swift in Sources */, B9C89F7E1F6BF89C00087AF4 /* TestThread.swift in Sources */, @@ -661,10 +671,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CODE_SIGN_STYLE = Automatic; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -675,10 +685,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CODE_SIGN_STYLE = Automatic; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_SWIFT_FLAGS = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -730,7 +740,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -781,7 +791,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -795,10 +805,11 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = DarwinCompatibilityTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; "OTHER_SWIFT_FLAGS[arch=*]" = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_BUNDLE_IDENTIFIER = org.swift.DarwinCompatibilityTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -809,10 +820,11 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = DarwinCompatibilityTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.14; "OTHER_SWIFT_FLAGS[arch=*]" = "-DDEPLOYMENT_RUNTIME_OBJC -DDARWIN_COMPATIBILITY_TESTS"; PRODUCT_BUNDLE_IDENTIFIER = org.swift.DarwinCompatibilityTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme b/DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme new file mode 100644 index 00000000000..192a059c872 --- /dev/null +++ b/DarwinCompatibilityTests.xcodeproj/xcshareddata/xcschemes/xdgTestHelper.xcscheme @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj index f64b9194ff0..c5fd59c6e88 100644 --- a/Foundation.xcodeproj/project.pbxproj +++ b/Foundation.xcodeproj/project.pbxproj @@ -366,6 +366,7 @@ B910957B1EEF237800A71930 /* NSString-UTF16-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B91095791EEF237800A71930 /* NSString-UTF16-BE-data.txt */; }; B933A79E1F3055F700FE6846 /* NSString-UTF32-BE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */; }; B933A79F1F3055F700FE6846 /* NSString-UTF32-LE-data.txt in Resources */ = {isa = PBXBuildFile; fileRef = B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */; }; + B940492D223B146800FB4384 /* TestProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940492C223B146800FB4384 /* TestProgressFraction.swift */; }; B951B5EC1F4E2A2000D8B332 /* TestNSLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */; }; B98E33DD2136AA740044EBE9 /* TestFileWithZeros.txt in Resources */ = {isa = PBXBuildFile; fileRef = B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */; }; B9974B961EDF4A22007F15B8 /* TransferState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9974B8F1EDF4A22007F15B8 /* TransferState.swift */; }; @@ -412,7 +413,6 @@ E1A3726F1C31EBFB0023AF4D /* NSXMLDocumentTestData.xml in Resources */ = {isa = PBXBuildFile; fileRef = E1A3726E1C31EBFB0023AF4D /* NSXMLDocumentTestData.xml */; }; EA01AAEC1DA839C4008F4E07 /* TestProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA01AAEB1DA839C4008F4E07 /* TestProgress.swift */; }; EA0812691DA71C8A00651B70 /* ProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0812681DA71C8A00651B70 /* ProgressFraction.swift */; }; - EA08126B1DA80C3600651B70 /* TestNSProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA08126A1DA80C3600651B70 /* TestNSProgressFraction.swift */; }; EA08126C1DA810BE00651B70 /* ProgressFraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0812681DA71C8A00651B70 /* ProgressFraction.swift */; }; EA418C261D57257D005EAD0D /* NSKeyedArchiverHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA418C251D57257D005EAD0D /* NSKeyedArchiverHelpers.swift */; }; EA54A6FB1DB16D53009E0809 /* TestObjCRuntime.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA54A6FA1DB16D53009E0809 /* TestObjCRuntime.swift */; }; @@ -896,6 +896,7 @@ B91095791EEF237800A71930 /* NSString-UTF16-BE-data.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "NSString-UTF16-BE-data.txt"; sourceTree = ""; }; B933A79C1F3055F600FE6846 /* NSString-UTF32-BE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-BE-data.txt"; sourceTree = ""; }; B933A79D1F3055F600FE6846 /* NSString-UTF32-LE-data.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "NSString-UTF32-LE-data.txt"; sourceTree = ""; }; + B940492C223B146800FB4384 /* TestProgressFraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = TestProgressFraction.swift; path = TestFoundation/TestProgressFraction.swift; sourceTree = ""; }; B951B5EB1F4E2A2000D8B332 /* TestNSLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSLock.swift; sourceTree = ""; }; B98E33DC2136AA740044EBE9 /* TestFileWithZeros.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TestFileWithZeros.txt; sourceTree = ""; }; B9974B8F1EDF4A22007F15B8 /* TransferState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransferState.swift; sourceTree = ""; }; @@ -1119,6 +1120,7 @@ 5B5D88531BBC938800234F36 = { isa = PBXGroup; children = ( + B940492C223B146800FB4384 /* TestProgressFraction.swift */, B167A6641ED7303F0040B09A /* README.md */, 5BDC3F2C1BCC5DB500ED97BB /* Foundation */, EAB57B681BD1A255004AC5C5 /* CoreFoundation */, @@ -2592,6 +2594,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + B940492D223B146800FB4384 /* TestProgressFraction.swift in Sources */, 159884921DCC877700E3314C /* TestHTTPCookieStorage.swift in Sources */, 5FE52C951D147D1C00F7D270 /* TestNSTextCheckingResult.swift in Sources */, 5B13B3451C582D4C00651CE2 /* TestNSString.swift in Sources */, @@ -2618,7 +2621,6 @@ 5B13B3411C582D4C00651CE2 /* TestNSRegularExpression.swift in Sources */, 5B13B3491C582D4C00651CE2 /* TestTimeZone.swift in Sources */, 5B13B34B1C582D4C00651CE2 /* TestNSURLRequest.swift in Sources */, - EA08126B1DA80C3600651B70 /* TestNSProgressFraction.swift in Sources */, 5B13B33E1C582D4C00651CE2 /* TestProcessInfo.swift in Sources */, 5B13B33F1C582D4C00651CE2 /* TestPropertyListSerialization.swift in Sources */, 5B13B32C1C582D4C00651CE2 /* TestDate.swift in Sources */, diff --git a/TestFoundation/TestNSProgressFraction.swift b/TestFoundation/TestProgressFraction.swift similarity index 91% rename from TestFoundation/TestNSProgressFraction.swift rename to TestFoundation/TestProgressFraction.swift index 05a6e3817aa..1b371b570d7 100644 --- a/TestFoundation/TestNSProgressFraction.swift +++ b/TestFoundation/TestProgressFraction.swift @@ -1,16 +1,19 @@ // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2016, 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if !DARWIN_COMPATIBILITY_TESTS + class TestProgressFraction : XCTestCase { - static var allTests: [(String, (TestProgressFraction) -> () throws -> Void)] { - return [ + +#if !NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT // _ProgressFraction is an internal type + static let allTests: [(String, (TestProgressFraction) -> () throws -> Void)] = [] +#else + static let allTests: [(String, (TestProgressFraction) -> () throws -> Void)] = [ ("test_equal", test_equal ), ("test_subtract", test_subtract), ("test_multiply", test_multiply), @@ -21,7 +24,6 @@ class TestProgressFraction : XCTestCase { ("test_fractionFromDouble", test_fractionFromDouble), ("test_unnecessaryOverflow", test_unnecessaryOverflow), ] - } func test_equal() { let f1 = _ProgressFraction(completed: 5, total: 10) @@ -151,6 +153,7 @@ class TestProgressFraction : XCTestCase { let r = f1 + f2 XCTAssertFalse(r.overflowed) } -} #endif +} + From 3d213799a2e4434a2d8ac067a6d80ed58ae2b2db Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Mar 2019 08:39:19 -0700 Subject: [PATCH 13/81] TestFoundation: use `.standardizingPath` for normalization Rather than use `realpath` which is not universally available, use `.standardizingPath` which should normalize the path as `realpath` on all the targets since we are testing `Process` rather than `FileManager`. --- TestFoundation/TestProcess.swift | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index b72c6424986..1ce45ff1a00 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -281,27 +281,8 @@ class TestProcess : XCTestCase { } } - private func realpathOf(path: String) -> String? { - let fm = FileManager.default - let length = Int(PATH_MAX) + 1 - var buf = [Int8](repeating: 0, count: length) - let fsRep = fm.fileSystemRepresentation(withPath: path) -#if !DARWIN_COMPATIBILITY_TESTS - defer { fsRep.deallocate() } -#endif - guard let result = realpath(fsRep, &buf) else { - return nil - } - return fm.string(withFileSystemRepresentation: result, length: strlen(result)) - } - func test_current_working_directory() { - let tmpDir = "/tmp" - - guard let resolvedTmpDir = realpathOf(path: tmpDir) else { - XCTFail("Cant find realpath of /tmp") - return - } + let tmpDir = "/tmp".standardizingPath let fm = FileManager.default let previousWorkingDirectory = fm.currentDirectoryPath @@ -310,7 +291,7 @@ class TestProcess : XCTestCase { do { let (pwd, _) = try runTask([xdgTestHelperURL().path, "--getcwd"], currentDirectoryPath: tmpDir) // Check the sub-process used the correct directory - XCTAssertEqual(pwd.trimmingCharacters(in: .newlines), resolvedTmpDir) + XCTAssertEqual(pwd.trimmingCharacters(in: .newlines), tmpDir) } catch { XCTFail("Test failed: \(error)") } From b84647fa834a8e4435844a0a67c1061b4a3c22a3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Mar 2019 14:25:27 -0700 Subject: [PATCH 14/81] NSURL: Use the platform path style Not all platforms are POSIX. Use the platform URL style when converting the paths. This was possible in CoreFoundation through the `PLATFORM_PATH_STYLE` macro, but, unfortunately, that is unavailable to us in Swift. Emulate that and use it. --- Foundation/NSURL.swift | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Foundation/NSURL.swift b/Foundation/NSURL.swift index a1b8d933b23..d158d9500b7 100644 --- a/Foundation/NSURL.swift +++ b/Foundation/NSURL.swift @@ -15,6 +15,13 @@ internal let kCFURLPOSIXPathStyle = CFURLPathStyle.cfurlposixPathStyle internal let kCFURLWindowsPathStyle = CFURLPathStyle.cfurlWindowsPathStyle #endif +// NOTE: this represents PLATFORM_PATH_STYLE +#if os(Windows) +internal let kCFURLPlatformPathStyle = kCFURLWindowsPathStyle +#else +internal let kCFURLPlatformPathStyle = kCFURLPOSIXPathStyle +#endif + private func _standardizedPath(_ path: String) -> String { if !path.absolutePath { return path._nsObject.standardizingPath @@ -298,9 +305,9 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { let thePath = _standardizedPath(path) if thePath.length > 0 { - _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPOSIXPathStyle, isDir, baseURL?._cfObject) + _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPlatformPathStyle, isDir, baseURL?._cfObject) } else if let baseURL = baseURL { - _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, baseURL.path._cfObject, kCFURLPOSIXPathStyle, baseURL.hasDirectoryPath, nil) + _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, baseURL.path._cfObject, kCFURLPlatformPathStyle, baseURL.hasDirectoryPath, nil) } } @@ -347,7 +354,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { } } super.init() - _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPOSIXPathStyle, isDir.boolValue, nil) + _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPlatformPathStyle, isDir.boolValue, nil) } public convenience init(fileURLWithFileSystemRepresentation path: UnsafePointer, isDirectory isDir: Bool, relativeTo baseURL: URL?) { @@ -517,7 +524,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { open var path: String? { let absURL = CFURLCopyAbsoluteURL(_cfObject) - return CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle)?._swiftObject + return CFURLCopyFileSystemPath(absURL, kCFURLPlatformPathStyle)?._swiftObject } open var fragment: String? { @@ -534,7 +541,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { // The same as path if baseURL is nil open var relativePath: String? { - return CFURLCopyFileSystemPath(_cfObject, kCFURLPOSIXPathStyle)?._swiftObject + return CFURLCopyFileSystemPath(_cfObject, kCFURLPlatformPathStyle)?._swiftObject } /* Determines if a given URL string's path represents a directory (i.e. the path component in the URL string ends with a '/' character). This does not check the resource the URL refers to. From 420ca56db7c91a5380dcf198880f45fddd66e400 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 16 Mar 2019 15:26:19 -0700 Subject: [PATCH 15/81] FileManager: correct boolean logic `CreateDirectoryW` returns `FALSE` on failure, not on success. --- Foundation/FileManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 8ad8c41518a..adf47143d88 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -705,7 +705,7 @@ open class FileManager : NSObject { try path.withCString(encodedAs: UTF16.self) { - if CreateDirectoryW($0, psaAttributes) != FALSE { + if CreateDirectoryW($0, psaAttributes) == FALSE { // FIXME(compnerd) pass along path throw _NSErrorWithWindowsError(GetLastError(), reading: false) } From d99579054bfe43ee3e92cf3e2c4a5357bdc7e0ef Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sat, 16 Mar 2019 16:29:48 +0000 Subject: [PATCH 16/81] NSNumber: Make comparing NaN with number match Darwin --- Foundation/NSNumber.swift | 4 +- TestFoundation/TestNSNumber.swift | 87 ++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/Foundation/NSNumber.swift b/Foundation/NSNumber.swift index 48b8dccbb0a..f6b5bd15d99 100644 --- a/Foundation/NSNumber.swift +++ b/Foundation/NSNumber.swift @@ -945,8 +945,8 @@ open class NSNumber : NSValue { // Apply special handling for NaN as <, >, == always return false // when comparing with NaN if lhs.isNaN && rhs.isNaN { return .orderedSame } - if lhs.isNaN { return .orderedAscending } - if rhs.isNaN { return .orderedDescending } + if lhs.isNaN { return rhs < 0 ? .orderedDescending : .orderedAscending } + if rhs.isNaN { return lhs < 0 ? .orderedAscending : .orderedDescending } if lhs < rhs { return .orderedAscending } if lhs > rhs { return .orderedDescending } diff --git a/TestFoundation/TestNSNumber.swift b/TestFoundation/TestNSNumber.swift index cc1de16c0e0..9fa321f3b04 100644 --- a/TestFoundation/TestNSNumber.swift +++ b/TestFoundation/TestNSNumber.swift @@ -853,6 +853,19 @@ class TestNSNumber : XCTestCase { XCTAssertEqual(NSNumber(value: Float(1)).doubleValue, Double(1)) XCTAssertEqual(NSNumber(value: Float(-37.5)).doubleValue, Double(-37.5)) XCTAssertEqual(NSNumber(value: Float(42.5)).doubleValue, Double(42.5)) + + let nanFloat = NSNumber(value: Float.nan) + XCTAssertTrue(nanFloat.doubleValue.isNaN) + XCTAssertEqual(nanFloat.intValue, Int(bitPattern: 1 << 63)) + XCTAssertEqual(nanFloat.uintValue, UInt(bitPattern: 1 << 63)) + XCTAssertEqual(nanFloat.int8Value, 0) + XCTAssertEqual(nanFloat.uint8Value, 0) + XCTAssertEqual(nanFloat.int16Value, 0) + XCTAssertEqual(nanFloat.uint16Value, 0) + XCTAssertEqual(nanFloat.int32Value, 0) + XCTAssertEqual(nanFloat.uint32Value, 0) + XCTAssertEqual(nanFloat.int64Value, Int64(bitPattern: 1 << 63)) + XCTAssertEqual(nanFloat.uint64Value, UInt64(bitPattern: 1 << 63)) } func test_numberWithDouble() { @@ -885,6 +898,20 @@ class TestNSNumber : XCTestCase { XCTAssertEqual(NSNumber(value: Double(0)).doubleValue, Double(0)) XCTAssertEqual(NSNumber(value: Double(-37.5)).doubleValue, Double(-37.5)) XCTAssertEqual(NSNumber(value: Double(42.1)).doubleValue, Double(42.1)) + + let nanDouble = NSNumber(value: Double.nan) + XCTAssertTrue(nanDouble.floatValue.isNaN) + XCTAssertEqual(nanDouble.intValue, Int(bitPattern: 1 << 63)) + XCTAssertEqual(nanDouble.uintValue, UInt(bitPattern: 1 << 63)) + XCTAssertEqual(nanDouble.int8Value, 0) + XCTAssertEqual(nanDouble.uint8Value, 0) + XCTAssertEqual(nanDouble.int16Value, 0) + XCTAssertEqual(nanDouble.uint16Value, 0) + XCTAssertEqual(nanDouble.int32Value, 0) + XCTAssertEqual(nanDouble.uint32Value, 0) + XCTAssertEqual(nanDouble.int64Value, Int64(bitPattern: 1 << 63)) + XCTAssertEqual(nanDouble.uint64Value, UInt64(bitPattern: 1 << 63)) + } func test_compareNumberWithBool() { @@ -1351,23 +1378,55 @@ class TestNSNumber : XCTestCase { let num = NSNumber(value: Int8.min) XCTAssertFalse(num == NSNumber(value: num.uint64Value)) - let num1 = NSNumber(value: Float.nan) - XCTAssertEqual(num1.compare(num1), .orderedSame) + let zero = NSNumber(value: 0) + let one = NSNumber(value: 1) + let minusOne = NSNumber(value: -1) + let intMin = NSNumber(value: Int.min) + let intMax = NSNumber(value: Int.max) + + let nanFloat = NSNumber(value: Float.nan) + XCTAssertEqual(nanFloat.compare(nanFloat), .orderedSame) + + XCTAssertFalse(nanFloat == zero) + XCTAssertFalse(zero == nanFloat) + XCTAssertEqual(nanFloat.compare(zero), .orderedAscending) + XCTAssertEqual(zero.compare(nanFloat), .orderedDescending) + + XCTAssertEqual(nanFloat.compare(one), .orderedAscending) + XCTAssertEqual(one.compare(nanFloat), .orderedDescending) + + XCTAssertEqual(nanFloat.compare(intMax), .orderedAscending) + XCTAssertEqual(intMax.compare(nanFloat), .orderedDescending) + + XCTAssertEqual(nanFloat.compare(minusOne), .orderedDescending) + XCTAssertEqual(minusOne.compare(nanFloat), .orderedAscending) + + XCTAssertEqual(nanFloat.compare(intMin), .orderedDescending) + XCTAssertEqual(intMin.compare(nanFloat), .orderedAscending) + + + let nanDouble = NSNumber(value: Double.nan) + XCTAssertEqual(nanDouble.compare(nanDouble), .orderedSame) + + XCTAssertFalse(nanDouble == zero) + XCTAssertFalse(zero == nanDouble) + XCTAssertEqual(nanDouble.compare(zero), .orderedAscending) + XCTAssertEqual(zero.compare(nanDouble), .orderedDescending) + + XCTAssertEqual(nanDouble.compare(one), .orderedAscending) + XCTAssertEqual(one.compare(nanDouble), .orderedDescending) + + XCTAssertEqual(nanDouble.compare(intMax), .orderedAscending) + XCTAssertEqual(intMax.compare(nanDouble), .orderedDescending) - let num2 = NSNumber(value: num1.uint8Value) // 0 - XCTAssertFalse(num1 == num2) - XCTAssertFalse(num2 == num1) - XCTAssertEqual(num1.compare(num2), .orderedAscending) - XCTAssertEqual(num2.compare(num1), .orderedDescending) + XCTAssertEqual(nanDouble.compare(minusOne), .orderedDescending) + XCTAssertEqual(minusOne.compare(nanDouble), .orderedAscending) - let num3 = NSNumber(value: Double.nan) - XCTAssertEqual(num3.compare(num3), .orderedSame) + XCTAssertEqual(nanDouble.compare(intMin), .orderedDescending) + XCTAssertEqual(intMin.compare(nanDouble), .orderedAscending) - let num4 = NSNumber(value: num3.intValue) // 0 - XCTAssertFalse(num3 == num2) - XCTAssertFalse(num4 == num3) - XCTAssertEqual(num3.compare(num4), .orderedAscending) - XCTAssertEqual(num4.compare(num3), .orderedDescending) + XCTAssertEqual(nanDouble, nanFloat) + XCTAssertEqual(nanFloat, nanDouble) XCTAssertEqual(NSNumber(value: Double.leastNonzeroMagnitude).compare(NSNumber(value: 0)), .orderedDescending) XCTAssertEqual(NSNumber(value: Double.greatestFiniteMagnitude).compare(NSNumber(value: 0)), .orderedDescending) From a401de4c63dc50e49c4f64b511b929f73f04d3f7 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 16 Mar 2019 19:20:05 -0700 Subject: [PATCH 17/81] CoreFoundation: simplify and fix `CFIterateDirectory` Simplify the Windows implementation for `_CFIterateDirectory`. Additionally, we were passing an absolute path for the full path which is meant to be the prefixed path (with `stuffToPrefix`). For now pass the `fileName` itself. This corrects the `-[NSURL urlForResource:withExtension:]` implementation for Windows. --- CoreFoundation/Base.subproj/CFFileUtilities.c | 90 ++++++++----------- 1 file changed, 36 insertions(+), 54 deletions(-) diff --git a/CoreFoundation/Base.subproj/CFFileUtilities.c b/CoreFoundation/Base.subproj/CFFileUtilities.c index dba76490d12..5db9dd33979 100644 --- a/CoreFoundation/Base.subproj/CFFileUtilities.c +++ b/CoreFoundation/Base.subproj/CFFileUtilities.c @@ -1024,66 +1024,48 @@ CF_PRIVATE void _CFIterateDirectory(CFStringRef directoryPath, Boolean appendSla if (!CFStringGetFileSystemRepresentation(directoryPath, directoryPathBuf, CFMaxPathSize)) return; #if DEPLOYMENT_TARGET_WINDOWS - CFIndex cpathLen = strlen(directoryPathBuf); // Make sure there is room for the additional space we need in the win32 api - if (cpathLen + 2 < CFMaxPathSize) { - WIN32_FIND_DATAW file; - HANDLE handle; - - directoryPathBuf[cpathLen++] = '\\'; - directoryPathBuf[cpathLen++] = '*'; - directoryPathBuf[cpathLen] = '\0'; - - // Convert UTF8 buffer to windows appropriate UTF-16LE - // Get the real length of the string in UTF16 characters - CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, directoryPathBuf, kCFStringEncodingUTF8); - cpathLen = CFStringGetLength(cfStr); - // Allocate a wide buffer to hold the converted string, including space for a NULL terminator - wchar_t *wideBuf = (wchar_t *)malloc((cpathLen + 1) * sizeof(wchar_t)); - // Copy the string into the buffer and terminate - CFStringGetCharacters(cfStr, CFRangeMake(0, cpathLen), (UniChar *)wideBuf); - wideBuf[cpathLen] = 0; - CFRelease(cfStr); - - WCHAR directory[MAX_PATH + 1]; - if (!CFStringGetCString(directoryPath, directory, MAX_PATH, kCFStringEncodingUTF16)) - return; - - handle = FindFirstFileW(wideBuf, (LPWIN32_FIND_DATAW)&file); - if (handle != INVALID_HANDLE_VALUE) { - WCHAR path[MAX_PATH + 1]; - - do { - CFIndex nameLen = wcslen(file.cFileName); - if (file.cFileName[0] == '.' && (nameLen == 1 || (nameLen == 2 && file.cFileName[1] == '.'))) { - continue; - } + if (strlen(directoryPathBuf) > CFMaxPathSize - 2) return; - if (!PathCombineW(path, directory, file.cFileName)) { - continue; - } + strlcat(directoryPathBuf, "\\*", CFMaxPathSize); - CFStringRef filePath = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)path, wcslen(path) * sizeof(WCHAR), kCFStringEncodingUTF16, NO); - if (!filePath) { - continue; - } + UniChar wideBuf[CFMaxPathSize]; - CFStringRef fileName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)file.cFileName, nameLen * sizeof(wchar_t), kCFStringEncodingUTF16, NO); - if (!fileName) { - CFRelease(filePath); - continue; - } + // Convert UTF8 buffer to windows appropriate UTF-16LE + // Get the real length of the string in UTF16 characters + CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, directoryPathBuf, kCFStringEncodingUTF8); - Boolean isDirectory = file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; - Boolean result = fileHandler(fileName, filePath, isDirectory ? DT_DIR : DT_REG); - CFRelease(fileName); - CFRelease(filePath); - if (!result) break; - } while (FindNextFileW(handle, &file)); + // Copy the string into the buffer and terminate + CFStringGetCharacters(cfStr, CFRangeMake(0, CFStringGetLength(cfStr)), wideBuf); + wideBuf[CFStringGetLength(cfStr)] = L'\0'; - FindClose(handle); - } - free(wideBuf); + CFRelease(cfStr); + + WIN32_FIND_DATAW file; + HANDLE handle = FindFirstFileW(wideBuf, &file); + if (handle != INVALID_HANDLE_VALUE) { + do { + CFIndex nameLen = wcslen(file.cFileName); + if (file.cFileName[0] == '.' && (nameLen == 1 || (nameLen == 2 && file.cFileName[1] == '.'))) { + continue; + } + + CFStringRef fileName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, file.cFileName, nameLen); + if (!fileName) { + continue; + } + + assert(!stuffToPrefix && "prefix not yet implemented"); + CFStringRef filePath = CFRetain(fileName); + + Boolean isDirectory = file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + Boolean result = fileHandler(fileName, filePath, isDirectory ? DT_DIR : DT_REG); + CFRelease(fileName); + CFRelease(filePath); + if (!result) break; + } while (FindNextFileW(handle, &file)); + + FindClose(handle); } #else DIR *dirp; From a62b609531c42cc9515a784390e18c2435a575d2 Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Fri, 15 Mar 2019 16:32:28 -0700 Subject: [PATCH 18/81] Implement _lstatFile on Windows Windows doesn't have lstat, but it still can be implemented. This should help remove some of the Windows specific codepaths. --- Foundation/FileManager.swift | 54 ++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index adf47143d88..727a2cdf9fc 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -1804,7 +1804,6 @@ open class FileManager : NSObject { return path1entries.isEmpty } -#if !os(Windows) private func _lstatFile(atPath path: String, withFileSystemRepresentation fsRep: UnsafePointer? = nil) throws -> stat { let _fsRep: UnsafePointer if fsRep == nil { @@ -1818,19 +1817,64 @@ open class FileManager : NSObject { } var statInfo = stat() +#if os(Windows) + let h = path.withCString(encodedAs: UTF16.self) { + CreateFileW(/*lpFileName=*/$0, + /*dwDesiredAccess=*/DWORD(0), + /*dwShareMode=*/DWORD(FILE_SHARE_READ), + /*lpSecurityAttributes=*/nil, + /*dwCreationDisposition=*/DWORD(OPEN_EXISTING), + /*dwFlagsAndAttributes=*/DWORD(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS), + /*hTemplateFile=*/nil) + } + if h == INVALID_HANDLE_VALUE { + throw _NSErrorWithWindowsError(GetLastError(), reading: false) + } + var info: BY_HANDLE_FILE_INFORMATION = BY_HANDLE_FILE_INFORMATION() + GetFileInformationByHandle(h, &info) + // Group id is always 0 on Windows + statInfo.st_gid = 0 + statInfo.st_atime = info.ftLastAccessTime.time_t + statInfo.st_ctime = info.ftCreationTime.time_t + statInfo.st_dev = info.dwVolumeSerialNumber + // inodes have meaning on FAT/HPFS/NTFS + statInfo.st_ino = 0 + statInfo.st_rdev = info.dwVolumeSerialNumber + + let isReparsePoint = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_REPARSE_POINT) != 0 + let isDir = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_DIRECTORY) != 0 + let fileMode = isDir ? _S_IFDIR : _S_IFREG + // On a symlink to a directory, Windows sets both the REPARSE_POINT and + // DIRECTORY attributes. Since Windows doesn't provide S_IFLNK and we + // want unix style "symlinks to directories are not directories + // themselves, we say symlinks are regular files + statInfo.st_mode = UInt16(isReparsePoint ? _S_IFREG : fileMode) + let isReadOnly = info.dwFileAttributes & DWORD(FILE_ATTRIBUTE_READONLY) != 0 + statInfo.st_mode |= UInt16(isReadOnly ? _S_IREAD : (_S_IREAD | _S_IWRITE)) + statInfo.st_mode |= UInt16(_S_IEXEC) + + statInfo.st_mtime = info.ftLastWriteTime.time_t + statInfo.st_nlink = Int16(info.nNumberOfLinks) + if info.nFileSizeHigh != 0 { + throw _NSErrorWithErrno(EOVERFLOW, reading: true, path: path) + } + statInfo.st_size = Int32(info.nFileSizeLow) + // Uid is always 0 on Windows systems + statInfo.st_uid = 0 + CloseHandle(h) +#else guard lstat(_fsRep, &statInfo) == 0 else { throw _NSErrorWithErrno(errno, reading: true, path: path) } +#endif return statInfo } -#endif - @available(Windows, deprecated, message: "Not Yet Implemented") internal func _permissionsOfItem(atPath path: String) throws -> Int { + let fileInfo = try _lstatFile(atPath: path) #if os(Windows) - NSUnimplemented() + return Int(fileInfo.st_mode & ~UInt16(ucrt.S_IFMT)) #else - let fileInfo = try _lstatFile(atPath: path) return Int(fileInfo.st_mode & ~S_IFMT) #endif } From a657f67170f9db92a32d41b2c1a243acedaf3e11 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 7 Mar 2019 16:49:19 -0800 Subject: [PATCH 19/81] TestFoundation: emulate `setenv` using `putenv` on Win32 Windows does not have `setenv`, but it does have a `putenv`, which we can use in its stead. --- TestFoundation/TestProcessInfo.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TestFoundation/TestProcessInfo.swift b/TestFoundation/TestProcessInfo.swift index 447ee8f7dae..8c51fd35cd5 100644 --- a/TestFoundation/TestProcessInfo.swift +++ b/TestFoundation/TestProcessInfo.swift @@ -68,6 +68,13 @@ class TestProcessInfo : XCTestCase { } func test_environment() { +#if os(Windows) + func setenv(_ key: String, _ value: String, _ overwrite: Int) -> Int32 { + assert(overwrite == 1) + return putenv("\(key)=\(value)") + } +#endif + let preset = ProcessInfo.processInfo.environment["test"] setenv("test", "worked", 1) let postset = ProcessInfo.processInfo.environment["test"] From cd93fdc9f3cda2f8f5dc319c9fc4eaeb4f278e6f Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sat, 16 Mar 2019 22:32:46 +0000 Subject: [PATCH 20/81] NumberFormatter: Get properties as attributes - Re-enable TestNumberFormatter.test_en_US_initialValues() --- Foundation/NumberFormatter.swift | 46 ++++++++++-------- TestFoundation/TestNumberFormatter.swift | 62 +++++++++++++++++++++++- 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/Foundation/NumberFormatter.swift b/Foundation/NumberFormatter.swift index c3c67901597..f7fc5e46b3c 100644 --- a/Foundation/NumberFormatter.swift +++ b/Foundation/NumberFormatter.swift @@ -174,6 +174,11 @@ open class NumberFormatter : Formatter { CFNumberFormatterSetProperty(formatter, attributeName, value) } } + + internal func _getFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString) -> String? { + return CFNumberFormatterCopyProperty(formatter, attributeName) as? String + } + // Attributes of a NumberFormatter internal var _numberStyle: Style = .none @@ -320,7 +325,7 @@ open class NumberFormatter : Formatter { internal var _decimalSeparator: String! open var decimalSeparator: String! { get { - return _decimalSeparator + return _decimalSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterDecimalSeparator) } set { _reset() @@ -342,7 +347,7 @@ open class NumberFormatter : Formatter { internal var _currencyDecimalSeparator: String! open var currencyDecimalSeparator: String! { get { - return _currencyDecimalSeparator + return _currencyDecimalSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyDecimalSeparator) } set { _reset() @@ -364,7 +369,7 @@ open class NumberFormatter : Formatter { internal var _groupingSeparator: String! open var groupingSeparator: String! { get { - return _groupingSeparator + return _groupingSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterGroupingSeparator) } set { _reset() @@ -377,7 +382,7 @@ open class NumberFormatter : Formatter { internal var _zeroSymbol: String? open var zeroSymbol: String? { get { - return _zeroSymbol + return _zeroSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterZeroSymbol) } set { _reset() @@ -421,7 +426,7 @@ open class NumberFormatter : Formatter { internal var _notANumberSymbol: String! open var notANumberSymbol: String! { get { - return _notANumberSymbol + return _notANumberSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNaNSymbol) } set { _reset() @@ -489,7 +494,7 @@ open class NumberFormatter : Formatter { internal var _positivePrefix: String! open var positivePrefix: String! { get { - return _positivePrefix + return _positivePrefix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPositivePrefix) } set { _reset() @@ -500,7 +505,7 @@ open class NumberFormatter : Formatter { internal var _positiveSuffix: String! open var positiveSuffix: String! { get { - return _positiveSuffix + return _positiveSuffix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPositiveSuffix) } set { _reset() @@ -511,7 +516,7 @@ open class NumberFormatter : Formatter { internal var _negativePrefix: String! open var negativePrefix: String! { get { - return _negativePrefix + return _negativePrefix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNegativePrefix) } set { _reset() @@ -522,7 +527,7 @@ open class NumberFormatter : Formatter { internal var _negativeSuffix: String! open var negativeSuffix: String! { get { - return _negativeSuffix + return _negativeSuffix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNegativeSuffix) } set { _reset() @@ -533,7 +538,7 @@ open class NumberFormatter : Formatter { internal var _currencyCode: String! open var currencyCode: String! { get { - return _currencyCode + return _currencyCode ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyCode) } set { _reset() @@ -544,7 +549,7 @@ open class NumberFormatter : Formatter { internal var _currencySymbol: String! open var currencySymbol: String! { get { - return _currencySymbol + return _currencySymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencySymbol) } set { _reset() @@ -555,7 +560,7 @@ open class NumberFormatter : Formatter { internal var _internationalCurrencySymbol: String! open var internationalCurrencySymbol: String! { get { - return _internationalCurrencySymbol + return _internationalCurrencySymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterInternationalCurrencySymbol) } set { _reset() @@ -566,7 +571,7 @@ open class NumberFormatter : Formatter { internal var _percentSymbol: String! open var percentSymbol: String! { get { - return _percentSymbol + return _percentSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPercentSymbol) ?? "%" } set { _reset() @@ -577,7 +582,7 @@ open class NumberFormatter : Formatter { internal var _perMillSymbol: String! open var perMillSymbol: String! { get { - return _perMillSymbol + return _perMillSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPerMillSymbol) } set { _reset() @@ -588,7 +593,7 @@ open class NumberFormatter : Formatter { internal var _minusSign: String! open var minusSign: String! { get { - return _minusSign + return _minusSign ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterMinusSign) } set { _reset() @@ -599,7 +604,7 @@ open class NumberFormatter : Formatter { internal var _plusSign: String! open var plusSign: String! { get { - return _plusSign + return _plusSign ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPlusSign) } set { _reset() @@ -610,7 +615,7 @@ open class NumberFormatter : Formatter { internal var _exponentSymbol: String! open var exponentSymbol: String! { get { - return _exponentSymbol + return _exponentSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterExponentSymbol) } set { _reset() @@ -667,7 +672,7 @@ open class NumberFormatter : Formatter { internal var _paddingCharacter: String! open var paddingCharacter: String! { get { - return _paddingCharacter + return _paddingCharacter ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPaddingCharacter) } set { _reset() @@ -782,7 +787,7 @@ open class NumberFormatter : Formatter { internal var _currencyGroupingSeparator: String! open var currencyGroupingSeparator: String! { get { - return _currencyGroupingSeparator + return _currencyGroupingSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyGroupingSeparator) } set { _reset() @@ -889,6 +894,9 @@ open class NumberFormatter : Formatter { internal var _format: String? open var format: String { get { + if _format == nil { + _format = CFNumberFormatterGetFormat(_cfFormatter)._swiftObject + } return _format ?? "#" } set { diff --git a/TestFoundation/TestNumberFormatter.swift b/TestFoundation/TestNumberFormatter.swift index e8435195600..777db9c7dd2 100644 --- a/TestFoundation/TestNumberFormatter.swift +++ b/TestFoundation/TestNumberFormatter.swift @@ -54,7 +54,9 @@ class TestNumberFormatter: XCTestCase { ("test_maximumSignificantDigits", test_maximumSignificantDigits), ("test_stringFor", test_stringFor), ("test_numberFrom", test_numberFrom), - //("test_en_US_initialValues", test_en_US_initialValues) + ("test_en_US_initialValues", test_en_US_initialValues), + ("test_pt_BR_initialValues", test_pt_BR_initialValues), + ("test_changingLocale", test_changingLocale), ] } @@ -652,7 +654,7 @@ class TestNumberFormatter: XCTestCase { numberFormatter.locale = Locale(identifier: "en_US") // TODO: Check if this is true for all versions... - XCTAssertEqual(numberFormatter.format, "#;0;#") + XCTAssertEqual(numberFormatter.format, "#") XCTAssertEqual(numberFormatter.plusSign, "+") XCTAssertEqual(numberFormatter.minusSign, "-") @@ -671,5 +673,61 @@ class TestNumberFormatter: XCTestCase { XCTAssertEqual(numberFormatter.exponentSymbol, "E") XCTAssertEqual(numberFormatter.groupingSeparator, ",") XCTAssertEqual(numberFormatter.paddingCharacter, " ") + XCTAssertEqual(numberFormatter.currencyCode, "USD") + XCTAssertEqual(numberFormatter.currencySymbol, "$") + XCTAssertEqual(numberFormatter.currencyDecimalSeparator, ".") + XCTAssertEqual(numberFormatter.currencyGroupingSeparator, ",") + XCTAssertEqual(numberFormatter.internationalCurrencySymbol, "USD") + XCTAssertNil(numberFormatter.zeroSymbol) + } + + func test_pt_BR_initialValues() { + let numberFormatter = NumberFormatter(); + numberFormatter.locale = Locale(identifier: "pt_BR") + + XCTAssertEqual(numberFormatter.format, "#") + XCTAssertEqual(numberFormatter.plusSign, "+") + XCTAssertEqual(numberFormatter.minusSign, "-") + XCTAssertEqual(numberFormatter.decimalSeparator, ",") + XCTAssertEqual(numberFormatter.groupingSeparator, ".") + XCTAssertEqual(numberFormatter.nilSymbol, "") + XCTAssertEqual(numberFormatter.notANumberSymbol, "NaN") + XCTAssertEqual(numberFormatter.positiveInfinitySymbol, "+∞") + XCTAssertEqual(numberFormatter.negativeInfinitySymbol, "-∞") + XCTAssertEqual(numberFormatter.positivePrefix, "") + XCTAssertEqual(numberFormatter.negativePrefix, "-") + XCTAssertEqual(numberFormatter.positiveSuffix, "") + XCTAssertEqual(numberFormatter.negativeSuffix, "") + XCTAssertEqual(numberFormatter.percentSymbol, "%") + XCTAssertEqual(numberFormatter.perMillSymbol, "‰") + XCTAssertEqual(numberFormatter.exponentSymbol, "E") + XCTAssertEqual(numberFormatter.groupingSeparator, ".") + XCTAssertEqual(numberFormatter.paddingCharacter, " ") + XCTAssertEqual(numberFormatter.currencyCode, "BRL") + XCTAssertEqual(numberFormatter.currencySymbol, "R$") + XCTAssertEqual(numberFormatter.currencyDecimalSeparator, ",") + XCTAssertEqual(numberFormatter.currencyGroupingSeparator, ".") + XCTAssertEqual(numberFormatter.internationalCurrencySymbol, "BRL") + XCTAssertNil(numberFormatter.zeroSymbol) + } + + func test_changingLocale() { + let numberFormatter = NumberFormatter() + numberFormatter.locale = Locale(identifier: "fr_FR") + + XCTAssertEqual(numberFormatter.currencyCode, "EUR") + XCTAssertEqual(numberFormatter.currencySymbol, "€") + numberFormatter.currencySymbol = "E" + XCTAssertEqual(numberFormatter.currencySymbol, "E") + + numberFormatter.locale = Locale(identifier: "fr_FR") + XCTAssertEqual(numberFormatter.currencySymbol, "E") + + numberFormatter.locale = Locale(identifier: "en_GB") + + XCTAssertEqual(numberFormatter.currencyCode, "GBP") + XCTAssertEqual(numberFormatter.currencySymbol, "E") + numberFormatter.currencySymbol = nil + XCTAssertEqual(numberFormatter.currencySymbol, "£") } } From 0a9dc6ecd3f19fa22f6e0818af2f41eee0e1ac87 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sun, 17 Mar 2019 12:58:57 +0000 Subject: [PATCH 21/81] NumberFormatter: Convert internal ivars to private - Also remove Number Formatter Styles constants defined only for macOS. --- Foundation/NumberFormatter.swift | 318 ++++++++++++++----------------- 1 file changed, 145 insertions(+), 173 deletions(-) diff --git a/Foundation/NumberFormatter.swift b/Foundation/NumberFormatter.swift index f7fc5e46b3c..45822d62e35 100644 --- a/Foundation/NumberFormatter.swift +++ b/Foundation/NumberFormatter.swift @@ -1,26 +1,14 @@ // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2016, 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // -// See http://swift.org/LICENSE.txt for license information -// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // import CoreFoundation -#if os(macOS) || os(iOS) -internal let kCFNumberFormatterNoStyle = CFNumberFormatterStyle.noStyle -internal let kCFNumberFormatterDecimalStyle = CFNumberFormatterStyle.decimalStyle -internal let kCFNumberFormatterCurrencyStyle = CFNumberFormatterStyle.currencyStyle -internal let kCFNumberFormatterPercentStyle = CFNumberFormatterStyle.percentStyle -internal let kCFNumberFormatterScientificStyle = CFNumberFormatterStyle.scientificStyle -internal let kCFNumberFormatterSpellOutStyle = CFNumberFormatterStyle.spellOutStyle -internal let kCFNumberFormatterOrdinalStyle = CFNumberFormatterStyle.ordinalStyle -internal let kCFNumberFormatterCurrencyISOCodeStyle = CFNumberFormatterStyle.currencyISOCodeStyle -internal let kCFNumberFormatterCurrencyPluralStyle = CFNumberFormatterStyle.currencyPluralStyle -internal let kCFNumberFormatterCurrencyAccountingStyle = CFNumberFormatterStyle.currencyAccountingStyle -#endif extension NumberFormatter { public enum Style : UInt { @@ -55,7 +43,7 @@ extension NumberFormatter { } open class NumberFormatter : Formatter { - + typealias CFType = CFNumberFormatter private var _currentCfFormatter: CFType? private var _cfFormatter: CFType { @@ -67,7 +55,7 @@ open class NumberFormatter : Formatter { #else let numberStyle = CFNumberFormatterStyle(self.numberStyle.rawValue) #endif - + let obj = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale._cfObject, numberStyle)! _setFormatterAttributes(obj) if let format = _format { @@ -77,28 +65,28 @@ open class NumberFormatter : Formatter { return obj } } - + // this is for NSUnitFormatter - + open var formattingContext: Context = .unknown // default is NSFormattingContextUnknown - + // Report the used range of the string and an NSError, in addition to the usual stuff from Formatter /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative /// - Note: Since this API is under consideration it may be either removed or revised in the near future open func objectValue(_ string: String, range: inout NSRange) throws -> Any? { NSUnimplemented() } - + open override func string(for obj: Any) -> String? { //we need to allow Swift's numeric types here - Int, Double et al. guard let number = __SwiftValue.store(obj) as? NSNumber else { return nil } return string(from: number) } - + // Even though NumberFormatter responds to the usual Formatter methods, // here are some convenience methods which are a little more obvious. open func string(from number: NSNumber) -> String? { return CFNumberFormatterCreateStringWithNumber(kCFAllocatorSystemDefault, _cfFormatter, number._cfObject)._swiftObject } - + open func number(from string: String) -> NSNumber? { var range = CFRange(location: 0, length: string.length) let number = withUnsafeMutablePointer(to: &range) { (rangePointer: UnsafeMutablePointer) -> NSNumber? in @@ -114,18 +102,18 @@ open class NumberFormatter : Formatter { } return number } - + open class func localizedString(from num: NSNumber, number nstyle: Style) -> String { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = nstyle return numberFormatter.string(for: num)! } - - internal func _reset() { + + private func _reset() { _currentCfFormatter = nil } - - internal func _setFormatterAttributes(_ formatter: CFNumberFormatter) { + + private func _setFormatterAttributes(_ formatter: CFNumberFormatter) { _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterCurrencyCode, value: _currencyCode?._cfObject) _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterDecimalSeparator, value: _decimalSeparator?._cfObject) _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterCurrencyDecimalSeparator, value: _currencyDecimalSeparator?._cfObject) @@ -168,25 +156,25 @@ open class NumberFormatter : Formatter { _setFormatterAttribute(formatter, attributeName: kCFNumberFormatterMaxSignificantDigits, value: _maximumSignificantDigits._bridgeToObjectiveC()._cfObject) } } - - internal func _setFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString, value: AnyObject?) { + + private func _setFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString, value: AnyObject?) { if let value = value { CFNumberFormatterSetProperty(formatter, attributeName, value) } } - internal func _getFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString) -> String? { + private func _getFormatterAttribute(_ formatter: CFNumberFormatter, attributeName: CFString) -> String? { return CFNumberFormatterCopyProperty(formatter, attributeName) as? String } - + // Attributes of a NumberFormatter - internal var _numberStyle: Style = .none + private var _numberStyle: Style = .none open var numberStyle: Style { get { return _numberStyle } - + set { switch newValue { case .none, .ordinal, .spellOut: @@ -220,7 +208,7 @@ open class NumberFormatter : Formatter { if _groupingSize == 0 { _groupingSize = 3 } - + case .percent: _usesSignificantDigits = false _usesGroupingSeparator = true @@ -244,8 +232,8 @@ open class NumberFormatter : Formatter { _numberStyle = newValue } } - - internal var _locale: Locale = Locale.current + + private var _locale: Locale = Locale.current /*@NSCopying*/ open var locale: Locale! { get { return _locale @@ -255,8 +243,8 @@ open class NumberFormatter : Formatter { _locale = newValue } } - - internal var _generatesDecimalNumbers: Bool = false + + private var _generatesDecimalNumbers: Bool = false open var generatesDecimalNumbers: Bool { get { return _generatesDecimalNumbers @@ -266,8 +254,8 @@ open class NumberFormatter : Formatter { _generatesDecimalNumbers = newValue } } - - internal var _negativeFormat: String! + + private var _negativeFormat: String! open var negativeFormat: String! { get { return _negativeFormat @@ -277,8 +265,8 @@ open class NumberFormatter : Formatter { _negativeFormat = newValue } } - - internal var _textAttributesForNegativeValues: [String : Any]? + + private var _textAttributesForNegativeValues: [String : Any]? open var textAttributesForNegativeValues: [String : Any]? { get { return _textAttributesForNegativeValues @@ -288,8 +276,8 @@ open class NumberFormatter : Formatter { _textAttributesForNegativeValues = newValue } } - - internal var _positiveFormat: String! + + private var _positiveFormat: String! open var positiveFormat: String! { get { return _positiveFormat @@ -299,8 +287,8 @@ open class NumberFormatter : Formatter { _positiveFormat = newValue } } - - internal var _textAttributesForPositiveValues: [String : Any]? + + private var _textAttributesForPositiveValues: [String : Any]? open var textAttributesForPositiveValues: [String : Any]? { get { return _textAttributesForPositiveValues @@ -310,8 +298,8 @@ open class NumberFormatter : Formatter { _textAttributesForPositiveValues = newValue } } - - internal var _allowsFloats: Bool = true + + private var _allowsFloats: Bool = true open var allowsFloats: Bool { get { return _allowsFloats @@ -322,7 +310,7 @@ open class NumberFormatter : Formatter { } } - internal var _decimalSeparator: String! + private var _decimalSeparator: String! open var decimalSeparator: String! { get { return _decimalSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterDecimalSeparator) @@ -332,8 +320,8 @@ open class NumberFormatter : Formatter { _decimalSeparator = newValue } } - - internal var _alwaysShowsDecimalSeparator: Bool = false + + private var _alwaysShowsDecimalSeparator: Bool = false open var alwaysShowsDecimalSeparator: Bool { get { return _alwaysShowsDecimalSeparator @@ -343,8 +331,8 @@ open class NumberFormatter : Formatter { _alwaysShowsDecimalSeparator = newValue } } - - internal var _currencyDecimalSeparator: String! + + private var _currencyDecimalSeparator: String! open var currencyDecimalSeparator: String! { get { return _currencyDecimalSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyDecimalSeparator) @@ -354,8 +342,8 @@ open class NumberFormatter : Formatter { _currencyDecimalSeparator = newValue } } - - internal var _usesGroupingSeparator: Bool = false + + private var _usesGroupingSeparator: Bool = false open var usesGroupingSeparator: Bool { get { return _usesGroupingSeparator @@ -365,8 +353,8 @@ open class NumberFormatter : Formatter { _usesGroupingSeparator = newValue } } - - internal var _groupingSeparator: String! + + private var _groupingSeparator: String! open var groupingSeparator: String! { get { return _groupingSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterGroupingSeparator) @@ -376,10 +364,8 @@ open class NumberFormatter : Formatter { _groupingSeparator = newValue } } - - // - - internal var _zeroSymbol: String? + + private var _zeroSymbol: String? open var zeroSymbol: String? { get { return _zeroSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterZeroSymbol) @@ -389,8 +375,8 @@ open class NumberFormatter : Formatter { _zeroSymbol = newValue } } - - internal var _textAttributesForZero: [String : Any]? + + private var _textAttributesForZero: [String : Any]? open var textAttributesForZero: [String : Any]? { get { return _textAttributesForZero @@ -400,8 +386,8 @@ open class NumberFormatter : Formatter { _textAttributesForZero = newValue } } - - internal var _nilSymbol: String = "" + + private var _nilSymbol: String = "" open var nilSymbol: String { get { return _nilSymbol @@ -411,8 +397,8 @@ open class NumberFormatter : Formatter { _nilSymbol = newValue } } - - internal var _textAttributesForNil: [String : Any]? + + private var _textAttributesForNil: [String : Any]? open var textAttributesForNil: [String : Any]? { get { return _textAttributesForNil @@ -422,8 +408,8 @@ open class NumberFormatter : Formatter { _textAttributesForNil = newValue } } - - internal var _notANumberSymbol: String! + + private var _notANumberSymbol: String! open var notANumberSymbol: String! { get { return _notANumberSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNaNSymbol) @@ -433,8 +419,8 @@ open class NumberFormatter : Formatter { _notANumberSymbol = newValue } } - - internal var _textAttributesForNotANumber: [String : Any]? + + private var _textAttributesForNotANumber: [String : Any]? open var textAttributesForNotANumber: [String : Any]? { get { return _textAttributesForNotANumber @@ -444,8 +430,8 @@ open class NumberFormatter : Formatter { _textAttributesForNotANumber = newValue } } - - internal var _positiveInfinitySymbol: String = "+∞" + + private var _positiveInfinitySymbol: String = "+∞" open var positiveInfinitySymbol: String { get { return _positiveInfinitySymbol @@ -455,8 +441,8 @@ open class NumberFormatter : Formatter { _positiveInfinitySymbol = newValue } } - - internal var _textAttributesForPositiveInfinity: [String : Any]? + + private var _textAttributesForPositiveInfinity: [String : Any]? open var textAttributesForPositiveInfinity: [String : Any]? { get { return _textAttributesForPositiveInfinity @@ -466,8 +452,8 @@ open class NumberFormatter : Formatter { _textAttributesForPositiveInfinity = newValue } } - - internal var _negativeInfinitySymbol: String = "-∞" + + private var _negativeInfinitySymbol: String = "-∞" open var negativeInfinitySymbol: String { get { return _negativeInfinitySymbol @@ -477,8 +463,8 @@ open class NumberFormatter : Formatter { _negativeInfinitySymbol = newValue } } - - internal var _textAttributesForNegativeInfinity: [String : Any]? + + private var _textAttributesForNegativeInfinity: [String : Any]? open var textAttributesForNegativeInfinity: [String : Any]? { get { return _textAttributesForNegativeInfinity @@ -488,10 +474,8 @@ open class NumberFormatter : Formatter { _textAttributesForNegativeInfinity = newValue } } - - // - - internal var _positivePrefix: String! + + private var _positivePrefix: String! open var positivePrefix: String! { get { return _positivePrefix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPositivePrefix) @@ -501,8 +485,8 @@ open class NumberFormatter : Formatter { _positivePrefix = newValue } } - - internal var _positiveSuffix: String! + + private var _positiveSuffix: String! open var positiveSuffix: String! { get { return _positiveSuffix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPositiveSuffix) @@ -512,8 +496,8 @@ open class NumberFormatter : Formatter { _positiveSuffix = newValue } } - - internal var _negativePrefix: String! + + private var _negativePrefix: String! open var negativePrefix: String! { get { return _negativePrefix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNegativePrefix) @@ -523,8 +507,8 @@ open class NumberFormatter : Formatter { _negativePrefix = newValue } } - - internal var _negativeSuffix: String! + + private var _negativeSuffix: String! open var negativeSuffix: String! { get { return _negativeSuffix ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterNegativeSuffix) @@ -534,8 +518,8 @@ open class NumberFormatter : Formatter { _negativeSuffix = newValue } } - - internal var _currencyCode: String! + + private var _currencyCode: String! open var currencyCode: String! { get { return _currencyCode ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyCode) @@ -545,8 +529,8 @@ open class NumberFormatter : Formatter { _currencyCode = newValue } } - - internal var _currencySymbol: String! + + private var _currencySymbol: String! open var currencySymbol: String! { get { return _currencySymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencySymbol) @@ -556,8 +540,8 @@ open class NumberFormatter : Formatter { _currencySymbol = newValue } } - - internal var _internationalCurrencySymbol: String! + + private var _internationalCurrencySymbol: String! open var internationalCurrencySymbol: String! { get { return _internationalCurrencySymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterInternationalCurrencySymbol) @@ -567,8 +551,8 @@ open class NumberFormatter : Formatter { _internationalCurrencySymbol = newValue } } - - internal var _percentSymbol: String! + + private var _percentSymbol: String! open var percentSymbol: String! { get { return _percentSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPercentSymbol) ?? "%" @@ -578,8 +562,8 @@ open class NumberFormatter : Formatter { _percentSymbol = newValue } } - - internal var _perMillSymbol: String! + + private var _perMillSymbol: String! open var perMillSymbol: String! { get { return _perMillSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPerMillSymbol) @@ -589,8 +573,8 @@ open class NumberFormatter : Formatter { _perMillSymbol = newValue } } - - internal var _minusSign: String! + + private var _minusSign: String! open var minusSign: String! { get { return _minusSign ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterMinusSign) @@ -600,8 +584,8 @@ open class NumberFormatter : Formatter { _minusSign = newValue } } - - internal var _plusSign: String! + + private var _plusSign: String! open var plusSign: String! { get { return _plusSign ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPlusSign) @@ -611,8 +595,8 @@ open class NumberFormatter : Formatter { _plusSign = newValue } } - - internal var _exponentSymbol: String! + + private var _exponentSymbol: String! open var exponentSymbol: String! { get { return _exponentSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterExponentSymbol) @@ -622,10 +606,8 @@ open class NumberFormatter : Formatter { _exponentSymbol = newValue } } - - // - - internal var _groupingSize: Int = 0 + + private var _groupingSize: Int = 0 open var groupingSize: Int { get { return _groupingSize @@ -635,8 +617,8 @@ open class NumberFormatter : Formatter { _groupingSize = newValue } } - - internal var _secondaryGroupingSize: Int = 0 + + private var _secondaryGroupingSize: Int = 0 open var secondaryGroupingSize: Int { get { return _secondaryGroupingSize @@ -646,8 +628,8 @@ open class NumberFormatter : Formatter { _secondaryGroupingSize = newValue } } - - internal var _multiplier: NSNumber? + + private var _multiplier: NSNumber? /*@NSCopying*/ open var multiplier: NSNumber? { get { return _multiplier @@ -657,8 +639,8 @@ open class NumberFormatter : Formatter { _multiplier = newValue } } - - internal var _formatWidth: Int = 0 + + private var _formatWidth: Int = 0 open var formatWidth: Int { get { return _formatWidth @@ -668,8 +650,8 @@ open class NumberFormatter : Formatter { _formatWidth = newValue } } - - internal var _paddingCharacter: String! + + private var _paddingCharacter: String! open var paddingCharacter: String! { get { return _paddingCharacter ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterPaddingCharacter) @@ -679,10 +661,8 @@ open class NumberFormatter : Formatter { _paddingCharacter = newValue } } - - // - - internal var _paddingPosition: PadPosition = .beforePrefix + + private var _paddingPosition: PadPosition = .beforePrefix open var paddingPosition: PadPosition { get { return _paddingPosition @@ -692,8 +672,8 @@ open class NumberFormatter : Formatter { _paddingPosition = newValue } } - - internal var _roundingMode: RoundingMode = .halfEven + + private var _roundingMode: RoundingMode = .halfEven open var roundingMode: RoundingMode { get { return _roundingMode @@ -703,8 +683,8 @@ open class NumberFormatter : Formatter { _roundingMode = newValue } } - - internal var _roundingIncrement: NSNumber! = 0 + + private var _roundingIncrement: NSNumber! = 0 /*@NSCopying*/ open var roundingIncrement: NSNumber! { get { return _roundingIncrement @@ -718,7 +698,7 @@ open class NumberFormatter : Formatter { // Use an optional for _minimumIntegerDigits to track if the value is // set BEFORE the .numberStyle is changed. This allows preserving a setting // of 0. - internal var _minimumIntegerDigits: Int? + private var _minimumIntegerDigits: Int? open var minimumIntegerDigits: Int { get { return _minimumIntegerDigits ?? 0 @@ -728,8 +708,8 @@ open class NumberFormatter : Formatter { _minimumIntegerDigits = newValue } } - - internal var _maximumIntegerDigits: Int = 42 + + private var _maximumIntegerDigits: Int = 42 open var maximumIntegerDigits: Int { get { return _maximumIntegerDigits @@ -739,8 +719,8 @@ open class NumberFormatter : Formatter { _maximumIntegerDigits = newValue } } - - internal var _minimumFractionDigits: Int = 0 + + private var _minimumFractionDigits: Int = 0 open var minimumFractionDigits: Int { get { return _minimumFractionDigits @@ -750,8 +730,8 @@ open class NumberFormatter : Formatter { _minimumFractionDigits = newValue } } - - internal var _maximumFractionDigits: Int = 0 + + private var _maximumFractionDigits: Int = 0 open var maximumFractionDigits: Int { get { return _maximumFractionDigits @@ -761,8 +741,8 @@ open class NumberFormatter : Formatter { _maximumFractionDigits = newValue } } - - internal var _minimum: NSNumber? + + private var _minimum: NSNumber? /*@NSCopying*/ open var minimum: NSNumber? { get { return _minimum @@ -772,8 +752,8 @@ open class NumberFormatter : Formatter { _minimum = newValue } } - - internal var _maximum: NSNumber? + + private var _maximum: NSNumber? /*@NSCopying*/ open var maximum: NSNumber? { get { return _maximum @@ -783,8 +763,8 @@ open class NumberFormatter : Formatter { _maximum = newValue } } - - internal var _currencyGroupingSeparator: String! + + private var _currencyGroupingSeparator: String! open var currencyGroupingSeparator: String! { get { return _currencyGroupingSeparator ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterCurrencyGroupingSeparator) @@ -794,8 +774,8 @@ open class NumberFormatter : Formatter { _currencyGroupingSeparator = newValue } } - - internal var _lenient: Bool = false + + private var _lenient: Bool = false open var isLenient: Bool { get { return _lenient @@ -805,8 +785,8 @@ open class NumberFormatter : Formatter { _lenient = newValue } } - - internal var _usesSignificantDigits: Bool = false + + private var _usesSignificantDigits: Bool = false open var usesSignificantDigits: Bool { get { return _usesSignificantDigits @@ -816,8 +796,8 @@ open class NumberFormatter : Formatter { _usesSignificantDigits = newValue } } - - internal var _minimumSignificantDigits: Int = 1 + + private var _minimumSignificantDigits: Int = 1 open var minimumSignificantDigits: Int { get { return _minimumSignificantDigits @@ -828,8 +808,8 @@ open class NumberFormatter : Formatter { _minimumSignificantDigits = newValue } } - - internal var _maximumSignificantDigits: Int = 6 + + private var _maximumSignificantDigits: Int = 6 open var maximumSignificantDigits: Int { get { return _maximumSignificantDigits @@ -840,8 +820,8 @@ open class NumberFormatter : Formatter { _maximumSignificantDigits = newValue } } - - internal var _partialStringValidationEnabled: Bool = false + + private var _partialStringValidationEnabled: Bool = false open var isPartialStringValidationEnabled: Bool { get { return _partialStringValidationEnabled @@ -851,10 +831,8 @@ open class NumberFormatter : Formatter { _partialStringValidationEnabled = newValue } } - - // - - internal var _hasThousandSeparators: Bool = false + + private var _hasThousandSeparators: Bool = false open var hasThousandSeparators: Bool { get { return _hasThousandSeparators @@ -864,8 +842,8 @@ open class NumberFormatter : Formatter { _hasThousandSeparators = newValue } } - - internal var _thousandSeparator: String! + + private var _thousandSeparator: String! open var thousandSeparator: String! { get { return _thousandSeparator @@ -875,10 +853,8 @@ open class NumberFormatter : Formatter { _thousandSeparator = newValue } } - - // - - internal var _localizesFormat: Bool = true + + private var _localizesFormat: Bool = true open var localizesFormat: Bool { get { return _localizesFormat @@ -888,10 +864,8 @@ open class NumberFormatter : Formatter { _localizesFormat = newValue } } - - // - - internal var _format: String? + + private var _format: String? open var format: String { get { if _format == nil { @@ -904,10 +878,8 @@ open class NumberFormatter : Formatter { _format = newValue } } - - // - - internal var _attributedStringForZero: NSAttributedString = NSAttributedString(string: "0") + + private var _attributedStringForZero: NSAttributedString = NSAttributedString(string: "0") /*@NSCopying*/ open var attributedStringForZero: NSAttributedString { get { return _attributedStringForZero @@ -917,8 +889,8 @@ open class NumberFormatter : Formatter { _attributedStringForZero = newValue } } - - internal var _attributedStringForNil: NSAttributedString = NSAttributedString(string: "") + + private var _attributedStringForNil: NSAttributedString = NSAttributedString(string: "") /*@NSCopying*/ open var attributedStringForNil: NSAttributedString { get { return _attributedStringForNil @@ -928,8 +900,8 @@ open class NumberFormatter : Formatter { _attributedStringForNil = newValue } } - - internal var _attributedStringForNotANumber: NSAttributedString = NSAttributedString(string: "NaN") + + private var _attributedStringForNotANumber: NSAttributedString = NSAttributedString(string: "NaN") /*@NSCopying*/ open var attributedStringForNotANumber: NSAttributedString { get { return _attributedStringForNotANumber @@ -939,8 +911,8 @@ open class NumberFormatter : Formatter { _attributedStringForNotANumber = newValue } } - - internal var _roundingBehavior: NSDecimalNumberHandler = NSDecimalNumberHandler.default + + private var _roundingBehavior: NSDecimalNumberHandler = NSDecimalNumberHandler.default /*@NSCopying*/ open var roundingBehavior: NSDecimalNumberHandler { get { return _roundingBehavior From 5682196e0c464f1e834b98328b0eab6e7135d360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez=20Troiti=C3=B1o?= Date: Wed, 20 Mar 2019 16:05:57 -0700 Subject: [PATCH 22/81] [CoreFoundation] Fix enum name in function body. The enum name was probably autocompleted wrong. There should be no functional change. LLVM 8 seems to raise a new warning here. --- CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c b/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c index 3a7760bb7d7..5f099b2624f 100644 --- a/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c +++ b/CoreFoundation/Locale.subproj/CFDateIntervalFormatter.c @@ -408,7 +408,7 @@ void CFDateIntervalFormatterSetTimeStyle(CFDateIntervalFormatterRef formatter, C _CFDateIntervalFormatterBoundaryStyle _CFDateIntervalFormatterGetBoundaryStyle(CFDateIntervalFormatterRef formatter) { LOCK(); - CFDateIntervalFormatterStyle result = formatter->_boundaryStyle; + _CFDateIntervalFormatterBoundaryStyle result = formatter->_boundaryStyle; UNLOCK(); return result; } From ab7f3b284783c017730d1cd49e3e34ee73ff1584 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Mar 2019 17:34:11 -0700 Subject: [PATCH 23/81] NumberDate: use early return in _CFTimeZoneInit Use an early return to reduce indentation. NFC. --- .../NumberDate.subproj/CFTimeZone.c | 179 +++++++++--------- 1 file changed, 90 insertions(+), 89 deletions(-) diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c index 080b7bec213..933ada4d596 100644 --- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c +++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c @@ -1133,99 +1133,71 @@ Boolean _CFTimeZoneInitWithTimeIntervalFromGMT(CFTimeZoneRef result, CFTimeInter } Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data) { - if (name && __nameStringOK(name)) { - if (data) { - CFTZPeriod *tzp = NULL; - CFIndex cnt = 0; - __CFTimeZoneLockGlobal(); - if (__CFParseTimeZoneData(kCFAllocatorSystemDefault, data, &tzp, &cnt)) { - __CFTimeZoneUnlockGlobal(); - - } else { - __CFTimeZoneUnlockGlobal(); - return false; - } - ((struct __CFTimeZone *)timeZone)->_name = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, name); - ((struct __CFTimeZone *)timeZone)->_data = CFDataCreateCopy(kCFAllocatorSystemDefault, data); - ((struct __CFTimeZone *)timeZone)->_periods = tzp; - ((struct __CFTimeZone *)timeZone)->_periodCnt = cnt; - return true; - } else { - CFStringRef tzName = NULL; - CFDataRef data = NULL; + if (!name || !__nameStringOK(name)) { + return false; + } + + if (data) { + CFTZPeriod *tzp = NULL; + CFIndex cnt = 0; + __CFTimeZoneLockGlobal(); + if (__CFParseTimeZoneData(kCFAllocatorSystemDefault, data, &tzp, &cnt)) { + __CFTimeZoneUnlockGlobal(); - CFIndex len = CFStringGetLength(name); - if (6 == len || 8 == len) { - UniChar buffer[8]; - CFStringGetCharacters(name, CFRangeMake(0, len), buffer); - if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { - if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { - int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); - if (-14 <= hours && hours <= 14) { - CFTimeInterval ti = hours * 3600.0; - if (6 == len) { - return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); - } else { - if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { - int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); - if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { - ti = ti + minutes * 60.0; - return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); - } + } else { + __CFTimeZoneUnlockGlobal(); + return false; + } + ((struct __CFTimeZone *)timeZone)->_name = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, name); + ((struct __CFTimeZone *)timeZone)->_data = CFDataCreateCopy(kCFAllocatorSystemDefault, data); + ((struct __CFTimeZone *)timeZone)->_periods = tzp; + ((struct __CFTimeZone *)timeZone)->_periodCnt = cnt; + return true; + } else { + CFStringRef tzName = NULL; + CFDataRef data = NULL; + + CFIndex len = CFStringGetLength(name); + if (6 == len || 8 == len) { + UniChar buffer[8]; + CFStringGetCharacters(name, CFRangeMake(0, len), buffer); + if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { + if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { + int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); + if (-14 <= hours && hours <= 14) { + CFTimeInterval ti = hours * 3600.0; + if (6 == len) { + return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); + } else { + if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { + int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); + if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { + ti = ti + minutes * 60.0; + return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); } } } } } } - Boolean tryAbbrev = true; - CFURLRef baseURL, tempURL; - void *bytes; - CFIndex length; - Boolean result = false; - - if (!__tzZoneInfo) __InitTZStrings(); - if (!__tzZoneInfo) return NULL; + } + Boolean tryAbbrev = true; + CFURLRef baseURL, tempURL; + void *bytes; + CFIndex length; + Boolean result = false; + + if (!__tzZoneInfo) __InitTZStrings(); + if (!__tzZoneInfo) return NULL; #if TARGET_OS_WIN32 - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); #else - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); #endif - if (tryAbbrev) { - CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); - tzName = CFDictionaryGetValue(abbrevs, name); - if (NULL != tzName) { - tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); - if (NULL != tempURL) { - if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { - data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); - } - CFRelease(tempURL); - } - } - CFRelease(abbrevs); - } - if (NULL == data) { - CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); - CFStringRef mapping = CFDictionaryGetValue(dict, name); - if (mapping) { - name = mapping; - } else if (CFStringHasPrefix(name, __tzZoneInfo)) { - CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); - CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); - mapping = CFDictionaryGetValue(dict, unprefixed); - if (mapping) { - name = mapping; - } - CFRelease(unprefixed); - } - CFRelease(dict); - if (CFEqual(CFSTR(""), name)) { - return false; - } - } - if (NULL == data) { - tzName = name; + if (tryAbbrev) { + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + tzName = CFDictionaryGetValue(abbrevs, name); + if (NULL != tzName) { tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); if (NULL != tempURL) { if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { @@ -1234,15 +1206,44 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data CFRelease(tempURL); } } - CFRelease(baseURL); - if (NULL != data) { - result = _CFTimeZoneInit(timeZone, tzName, data); - CFRelease(data); + CFRelease(abbrevs); + } + if (NULL == data) { + CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); + CFStringRef mapping = CFDictionaryGetValue(dict, name); + if (mapping) { + name = mapping; + } else if (CFStringHasPrefix(name, __tzZoneInfo)) { + CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); + CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); + mapping = CFDictionaryGetValue(dict, unprefixed); + if (mapping) { + name = mapping; + } + CFRelease(unprefixed); + } + CFRelease(dict); + if (CFEqual(CFSTR(""), name)) { + return false; } - return result; } + if (NULL == data) { + tzName = name; + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); + } + CFRelease(tempURL); + } + } + CFRelease(baseURL); + if (NULL != data) { + result = _CFTimeZoneInit(timeZone, tzName, data); + CFRelease(data); + } + return result; } - return false; } CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { From 80da57f1d93f77add71894edc4bd6f441f2686d0 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Mar 2019 17:41:47 -0700 Subject: [PATCH 24/81] NumberDate: extract initialization in `_CFTimeZoneInit` Extract the initializing body of the function into a helper. This helps reduce the overall amount of code in the function, and more importantly makes it easier to improve support for Windows. --- .../NumberDate.subproj/CFTimeZone.c | 178 +++++++++--------- 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c index 933ada4d596..58082348371 100644 --- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c +++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c @@ -1132,103 +1132,77 @@ Boolean _CFTimeZoneInitWithTimeIntervalFromGMT(CFTimeZoneRef result, CFTimeInter return true; } +Boolean _CFTimeZoneInitInternal(CFTimeZoneRef timezone, CFStringRef name, CFDataRef data) { + CFTZPeriod *tzp = NULL; + CFIndex cnt = 0; + Boolean success = false; + + __CFTimeZoneLockGlobal(); + success = __CFParseTimeZoneData(kCFAllocatorSystemDefault, data, &tzp, &cnt); + __CFTimeZoneUnlockGlobal(); + + if (success) { + ((struct __CFTimeZone *)timezone)->_name = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, name); + ((struct __CFTimeZone *)timezone)->_data = CFDataCreateCopy(kCFAllocatorSystemDefault, data); + ((struct __CFTimeZone *)timezone)->_periods = tzp; + ((struct __CFTimeZone *)timezone)->_periodCnt = cnt; + } + + return success; +} + Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data) { if (!name || !__nameStringOK(name)) { return false; } if (data) { - CFTZPeriod *tzp = NULL; - CFIndex cnt = 0; - __CFTimeZoneLockGlobal(); - if (__CFParseTimeZoneData(kCFAllocatorSystemDefault, data, &tzp, &cnt)) { - __CFTimeZoneUnlockGlobal(); - - } else { - __CFTimeZoneUnlockGlobal(); - return false; - } - ((struct __CFTimeZone *)timeZone)->_name = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, name); - ((struct __CFTimeZone *)timeZone)->_data = CFDataCreateCopy(kCFAllocatorSystemDefault, data); - ((struct __CFTimeZone *)timeZone)->_periods = tzp; - ((struct __CFTimeZone *)timeZone)->_periodCnt = cnt; - return true; - } else { - CFStringRef tzName = NULL; - CFDataRef data = NULL; - - CFIndex len = CFStringGetLength(name); - if (6 == len || 8 == len) { - UniChar buffer[8]; - CFStringGetCharacters(name, CFRangeMake(0, len), buffer); - if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { - if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { - int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); - if (-14 <= hours && hours <= 14) { - CFTimeInterval ti = hours * 3600.0; - if (6 == len) { - return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); - } else { - if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { - int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); - if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { - ti = ti + minutes * 60.0; - return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); - } + return _CFTimeZoneInitInternal(timeZone, name, data); + } + + CFIndex len = CFStringGetLength(name); + if (6 == len || 8 == len) { + UniChar buffer[8]; + CFStringGetCharacters(name, CFRangeMake(0, len), buffer); + if ('G' == buffer[0] && 'M' == buffer[1] && 'T' == buffer[2] && ('+' == buffer[3] || '-' == buffer[3])) { + if (('0' <= buffer[4] && buffer[4] <= '9') && ('0' <= buffer[5] && buffer[5] <= '9')) { + int32_t hours = (buffer[4] - '0') * 10 + (buffer[5] - '0'); + if (-14 <= hours && hours <= 14) { + CFTimeInterval ti = hours * 3600.0; + if (6 == len) { + return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); + } else { + if (('0' <= buffer[6] && buffer[6] <= '9') && ('0' <= buffer[7] && buffer[7] <= '9')) { + int32_t minutes = (buffer[6] - '0') * 10 + (buffer[7] - '0'); + if ((-14 == hours && 0 == minutes) || (14 == hours && 0 == minutes) || (0 <= minutes && minutes <= 59)) { + ti = ti + minutes * 60.0; + return _CFTimeZoneInitWithTimeIntervalFromGMT(timeZone, ('-' == buffer[3] ? -1.0 : 1.0) * ti); } } } } } } - Boolean tryAbbrev = true; - CFURLRef baseURL, tempURL; - void *bytes; - CFIndex length; - Boolean result = false; - - if (!__tzZoneInfo) __InitTZStrings(); - if (!__tzZoneInfo) return NULL; + } + + CFStringRef tzName = NULL; + Boolean tryAbbrev = true; + CFURLRef baseURL, tempURL; + void *bytes; + CFIndex length; + Boolean result = false; + + if (!__tzZoneInfo) __InitTZStrings(); + if (!__tzZoneInfo) return NULL; #if TARGET_OS_WIN32 - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); #else - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); #endif - if (tryAbbrev) { - CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); - tzName = CFDictionaryGetValue(abbrevs, name); - if (NULL != tzName) { - tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); - if (NULL != tempURL) { - if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { - data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); - } - CFRelease(tempURL); - } - } - CFRelease(abbrevs); - } - if (NULL == data) { - CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); - CFStringRef mapping = CFDictionaryGetValue(dict, name); - if (mapping) { - name = mapping; - } else if (CFStringHasPrefix(name, __tzZoneInfo)) { - CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); - CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); - mapping = CFDictionaryGetValue(dict, unprefixed); - if (mapping) { - name = mapping; - } - CFRelease(unprefixed); - } - CFRelease(dict); - if (CFEqual(CFSTR(""), name)) { - return false; - } - } - if (NULL == data) { - tzName = name; + if (tryAbbrev) { + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + tzName = CFDictionaryGetValue(abbrevs, name); + if (NULL != tzName) { tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); if (NULL != tempURL) { if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { @@ -1237,13 +1211,43 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data CFRelease(tempURL); } } - CFRelease(baseURL); - if (NULL != data) { - result = _CFTimeZoneInit(timeZone, tzName, data); - CFRelease(data); + CFRelease(abbrevs); + } + if (NULL == data) { + CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); + CFStringRef mapping = CFDictionaryGetValue(dict, name); + if (mapping) { + name = mapping; + } else if (CFStringHasPrefix(name, __tzZoneInfo)) { + CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, CFStringGetLength(name), name); + CFStringDelete(unprefixed, CFRangeMake(0, CFStringGetLength(__tzZoneInfo))); + mapping = CFDictionaryGetValue(dict, unprefixed); + if (mapping) { + name = mapping; + } + CFRelease(unprefixed); + } + CFRelease(dict); + if (CFEqual(CFSTR(""), name)) { + return false; + } + } + if (NULL == data) { + tzName = name; + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); + } + CFRelease(tempURL); } - return result; } + CFRelease(baseURL); + if (NULL != data) { + result = _CFTimeZoneInitInternal(timeZone, tzName, data); + CFRelease(data); + } + return result; } CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { From fd24215fbdc50a29f2c3ffc5ff09a24b985df8a3 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Fri, 15 Mar 2019 17:56:13 -0700 Subject: [PATCH 25/81] NumberDate: inline always `true` condition Remove the unnecessary condition which is always set to true. --- .../NumberDate.subproj/CFTimeZone.c | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c index 58082348371..3804ea3b48d 100644 --- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c +++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c @@ -1186,7 +1186,6 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data } CFStringRef tzName = NULL; - Boolean tryAbbrev = true; CFURLRef baseURL, tempURL; void *bytes; CFIndex length; @@ -1199,20 +1198,20 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data #else baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); #endif - if (tryAbbrev) { - CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); - tzName = CFDictionaryGetValue(abbrevs, name); - if (NULL != tzName) { - tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); - if (NULL != tempURL) { - if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { - data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); - } - CFRelease(tempURL); + + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + tzName = CFDictionaryGetValue(abbrevs, name); + if (NULL != tzName) { + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorSystemDefault, tempURL, &bytes, &length, 0, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, bytes, length, kCFAllocatorSystemDefault); } + CFRelease(tempURL); } - CFRelease(abbrevs); } + CFRelease(abbrevs); + if (NULL == data) { CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); CFStringRef mapping = CFDictionaryGetValue(dict, name); From 2439b2b70d86e34aa0b46cd6360e26e6e81478fd Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 16 Mar 2019 18:06:04 -0700 Subject: [PATCH 26/81] CoreFoundation: extract Windows-Olson mapping Move the plist into a Windows resource file. This cleans up the code path and makes it easier to work with. --- CoreFoundation/CMakeLists.txt | 1 + CoreFoundation/CoreFoundation.rc | 4 + .../NumberDate.subproj/CFTimeZone.c | 208 +++---------- CoreFoundation/WindowsOlsonMapping.plist | 276 ++++++++++++++++++ CoreFoundation/WindowsResources.h | 2 + 5 files changed, 320 insertions(+), 171 deletions(-) create mode 100644 CoreFoundation/CoreFoundation.rc create mode 100644 CoreFoundation/WindowsOlsonMapping.plist create mode 100644 CoreFoundation/WindowsResources.h diff --git a/CoreFoundation/CMakeLists.txt b/CoreFoundation/CMakeLists.txt index 01735dd8e97..827247fda0e 100644 --- a/CoreFoundation/CMakeLists.txt +++ b/CoreFoundation/CMakeLists.txt @@ -205,6 +205,7 @@ add_framework(CoreFoundation URL.subproj/CFURLAccess.h URL.subproj/CFURLComponents.h SOURCES + $<$:CoreFoundation.rc> # Base Base.subproj/CFBase.c Base.subproj/CFFileUtilities.c diff --git a/CoreFoundation/CoreFoundation.rc b/CoreFoundation/CoreFoundation.rc new file mode 100644 index 00000000000..bde9f799a0f --- /dev/null +++ b/CoreFoundation/CoreFoundation.rc @@ -0,0 +1,4 @@ + +#include "WindowsResources.h" + +IDR_WINDOWS_OLSON_MAPPING PLIST "WindowsOlsonMapping.plist" diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c index 3804ea3b48d..e52277da667 100644 --- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c +++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c @@ -34,12 +34,17 @@ #endif #if TARGET_OS_WIN32 #include + +#include "WindowsResources.h" +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include "Windows.h" #endif + #if TARGET_OS_MAC #include #define MACOS_TZDIR1 "/usr/share/zoneinfo/" // 10.12 and earlier #define MACOS_TZDIR2 "/var/db/timezone/zoneinfo/" // 10.13 onwards - #elif TARGET_OS_LINUX || TARGET_OS_BSD #ifndef TZDIR #define TZDIR "/usr/share/zoneinfo/" /* Time zone object file directory */ @@ -463,172 +468,6 @@ CFTypeID CFTimeZoneGetTypeID(void) { } #if TARGET_OS_WIN32 -static const char *__CFTimeZoneWinToOlsonDefaults = -/* Mappings to time zones in Windows Registry are best-guess */ -"" -" " -" " -" " -" Afghanistan Asia/Kabul" -" Afghanistan Standard Time Asia/Kabul" -" Alaskan America/Anchorage" -" Alaskan Standard Time America/Anchorage" -" Arab Asia/Riyadh" -" Arab Standard Time Asia/Riyadh" -" Arabian Asia/Muscat" -" Arabian Standard Time Asia/Muscat" -" Arabic Standard Time Asia/Baghdad" -" Atlantic America/Halifax" -" Atlantic Standard Time America/Halifax" -" AUS Central Australia/Darwin" -" AUS Central Standard Time Australia/Darwin" -" AUS Eastern Australia/Sydney" -" AUS Eastern Standard Time Australia/Sydney" -" Azerbaijan Standard Time Asia/Baku" -" Azores Atlantic/Azores" -" Azores Standard Time Atlantic/Azores" -" Bangkok Asia/Bangkok" -" Bangkok Standard Time Asia/Bangkok" -" Beijing Asia/Shanghai" -" Canada Central America/Regina" -" Canada Central Standard Time America/Regina" -" Cape Verde Standard Time Atlantic/Cape_Verde" -" Caucasus Asia/Yerevan" -" Caucasus Standard Time Asia/Yerevan" -" Cen. Australia Australia/Adelaide" -" Cen. Australia Standard Time Australia/Adelaide" -" Central America/Chicago" -" Central America Standard Time America/Regina" -" Central Asia Asia/Dhaka" -" Central Asia Standard Time Asia/Dhaka" -" Central Brazilian Standard Time America/Manaus" -" Central Europe Europe/Prague" -" Central Europe Standard Time Europe/Prague" -" Central European Europe/Belgrade" -" Central European Standard Time Europe/Belgrade" -" Central Pacific Pacific/Guadalcanal" -" Central Pacific Standard Time Pacific/Guadalcanal" -" Central Standard Time America/Chicago" -" Central Standard Time (Mexico) America/Mexico_City" -" China Asia/Shanghai" -" China Standard Time Asia/Shanghai" -" Dateline GMT-1200" -" Dateline Standard Time GMT-1200" -" E. Africa Africa/Nairobi" -" E. Africa Standard Time Africa/Nairobi" -" E. Australia Australia/Brisbane" -" E. Australia Standard Time Australia/Brisbane" -" E. Europe Europe/Minsk" -" E. Europe Standard Time Europe/Minsk" -" E. South America America/Sao_Paulo" -" E. South America Standard Time America/Sao_Paulo" -" Eastern America/New_York" -" Eastern Standard Time America/New_York" -" Egypt Africa/Cairo" -" Egypt Standard Time Africa/Cairo" -" Ekaterinburg Asia/Yekaterinburg" -" Ekaterinburg Standard Time Asia/Yekaterinburg" -" Fiji Pacific/Fiji" -" Fiji Standard Time Pacific/Fiji" -" FLE Europe/Helsinki" -" FLE Standard Time Europe/Helsinki" -" Georgian Standard Time Asia/Tbilisi" -" GFT Europe/Athens" -" GFT Standard Time Europe/Athens" -" GMT Europe/London" -" GMT Standard Time Europe/London" -" Greenland Standard Time America/Godthab" -" Greenwich GMT" -" Greenwich Standard Time GMT" -" GTB Europe/Athens" -" GTB Standard Time Europe/Athens" -" Hawaiian Pacific/Honolulu" -" Hawaiian Standard Time Pacific/Honolulu" -" India Asia/Calcutta" -" India Standard Time Asia/Calcutta" -" Iran Asia/Tehran" -" Iran Standard Time Asia/Tehran" -" Israel Asia/Jerusalem" -" Israel Standard Time Asia/Jerusalem" -" Jordan Standard Time Asia/Amman" -" Korea Asia/Seoul" -" Korea Standard Time Asia/Seoul" -" Mexico America/Mexico_City" -" Mexico Standard Time America/Mexico_City" -" Mexico Standard Time 2 America/Chihuahua" -" Mid-Atlantic Atlantic/South_Georgia" -" Mid-Atlantic Standard Time Atlantic/South_Georgia" -" Middle East Standard Time Asia/Beirut" -" Mountain America/Denver" -" Mountain Standard Time America/Denver" -" Mountain Standard Time (Mexico) America/Chihuahua" -" Myanmar Standard Time Asia/Rangoon" -" N. Central Asia Standard Time Asia/Novosibirsk" -" Namibia Standard Time Africa/Windhoek" -" Nepal Standard Time Asia/Katmandu" -" New Zealand Pacific/Auckland" -" New Zealand Standard Time Pacific/Auckland" -" Newfoundland America/St_Johns" -" Newfoundland Standard Time America/St_Johns" -" North Asia East Standard Time Asia/Ulaanbaatar" -" North Asia Standard Time Asia/Krasnoyarsk" -" Pacific America/Los_Angeles" -" Pacific SA America/Santiago" -" Pacific SA Standard Time America/Santiago" -" Pacific Standard Time America/Los_Angeles" -" Pacific Standard Time (Mexico) America/Tijuana" -" Prague Bratislava Europe/Prague" -" Romance Europe/Paris" -" Romance Standard Time Europe/Paris" -" Russian Europe/Moscow" -" Russian Standard Time Europe/Moscow" -" SA Eastern America/Buenos_Aires" -" SA Eastern Standard Time America/Buenos_Aires" -" SA Pacific America/Bogota" -" SA Pacific Standard Time America/Bogota" -" SA Western America/Caracas" -" SA Western Standard Time America/Caracas" -" Samoa Pacific/Apia" -" Samoa Standard Time Pacific/Apia" -" Saudi Arabia Asia/Riyadh" -" Saudi Arabia Standard Time Asia/Riyadh" -" SE Asia Standard Time Asia/Bangkok" -" Singapore Asia/Singapore" -" Singapore Standard Time Asia/Singapore" -" South Africa Africa/Harare" -" South Africa Standard Time Africa/Harare" -" Sri Lanka Asia/Colombo" -" Sri Lanka Standard Time Asia/Colombo" -" Sydney Standard Time Australia/Sydney" -" Taipei Asia/Taipei" -" Taipei Standard Time Asia/Taipei" -" Tasmania Australia/Hobart" -" Tasmania Standard Time Australia/Hobart" -" Tasmania Standard Time Australia/Hobart" -" Tokyo Asia/Tokyo" -" Tokyo Standard Time Asia/Tokyo" -" Tonga Standard Time Pacific/Tongatapu" -" US Eastern America/Indianapolis" -" US Eastern Standard Time America/Indianapolis" -" US Mountain America/Phoenix" -" US Mountain Standard Time America/Phoenix" -" Vladivostok Asia/Vladivostok" -" Vladivostok Standard Time Asia/Vladivostok" -" W. Australia Australia/Perth" -" W. Australia Standard Time Australia/Perth" -" W. Central Africa Standard Time Africa/Luanda" -" W. Europe Europe/Berlin" -" W. Europe Standard Time Europe/Berlin" -" Warsaw Europe/Warsaw" -" West Asia Asia/Karachi" -" West Asia Standard Time Asia/Karachi" -" West Pacific Pacific/Guam" -" West Pacific Standard Time Pacific/Guam" -" Western Brazilian Standard Time America/Rio_Branco" -" Yakutsk Asia/Yakutsk" -" " -" "; - CF_INLINE void __CFTimeZoneLockWinToOlson(void) { __CFLock(&__CFTimeZoneWinToOlsonLock); } @@ -637,19 +476,46 @@ CF_INLINE void __CFTimeZoneUnlockWinToOlson(void) { __CFUnlock(&__CFTimeZoneWinToOlsonLock); } +static Boolean CFTimeZoneLoadWindowsOlsonPlist(LPVOID *ppResource, LPDWORD pdwSize) { + HRSRC hResource; + HGLOBAL hMemory; + + hResource = FindResourceA(NULL, MAKEINTRESOURCEA(IDR_WINDOWS_OLSON_MAPPING), "PLIST"); + if (hResource == NULL) { + return FALSE; + } + + hMemory = LoadResource(NULL, hResource); + if (hMemory == NULL) { + return FALSE; + } + + *pdwSize = SizeofResource(NULL, hResource); + *ppResource = LockResource(hMemory); + + return *pdwSize && *ppResource; +} + CFDictionaryRef CFTimeZoneCopyWinToOlsonDictionary(void) { CFDictionaryRef dict; + __CFTimeZoneLockWinToOlson(); if (NULL == __CFTimeZoneWinToOlsonDict) { - CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, (uint8_t *)__CFTimeZoneWinToOlsonDefaults, strlen(__CFTimeZoneWinToOlsonDefaults)); - __CFTimeZoneWinToOlsonDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); - CFRelease(data); + const uint8_t *plist; + DWORD dwSize; + + if (CFTimeZoneLoadWindowsOlsonPlist(&plist, &dwSize)) { + CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, plist, dwSize); + __CFTimeZoneWinToOlsonDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } } if (NULL == __CFTimeZoneWinToOlsonDict) { __CFTimeZoneWinToOlsonDict = CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, NULL, NULL); } - dict = __CFTimeZoneWinToOlsonDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneWinToOlsonDict) : NULL; __CFTimeZoneUnlockWinToOlson(); + + dict = __CFTimeZoneWinToOlsonDict ? (CFDictionaryRef)CFRetain(__CFTimeZoneWinToOlsonDict) : NULL; return dict; } diff --git a/CoreFoundation/WindowsOlsonMapping.plist b/CoreFoundation/WindowsOlsonMapping.plist new file mode 100644 index 00000000000..7af48262deb --- /dev/null +++ b/CoreFoundation/WindowsOlsonMapping.plist @@ -0,0 +1,276 @@ + + + + + Dateline Standard Time + Etc/GMT+12 + UTC-11 + Etc/GMT+11 + Aleutian Standard Time + America/Adak + Hawaiian Standard Time + Pacific/Honolulu + Marquesas Standard Time + Pacific/Marquesas + Alaskan Standard Time + America/Anchorage + UTC-09 + Etc/GMT+9 + Pacific Standard Time (Mexico) + America/Tijuana + UTC-08 + Etc/GMT+8 + Pacific Standard Time + America/Los_Angeles + US Mountain Standard Time + America/Phoenix + Mountain Standard Time (Mexico) + America/Chihuahua + Mountain Standard Time + America/Denver + Central America Standard Time + America/Guatemala + Central Standard Time + America/Chicago + Easter Island Standard Time + Pacific/Easter + Central Standard Time (Mexico) + America/Mexico_City + Canada Central Standard Time + America/Regina + SA Pacific Standard Time + America/Bogota + Eastern Standard Time (Mexico) + America/Cancun + Eastern Standard Time + America/New_York + Haiti Standard Time + America/Port-au-Prince + Cuba Standard Time + America/Havana + US Eastern Standard Time + America/Indianapolis + Paraguay Standard Time + America/Asuncion + Atlantic Standard Time + America/Halifax + Venezuela Standard Time + America/Caracas + Central Brazilian Standard Time + America/Cuiaba + SA Western Standard Time + America/La_Paz + Pacific SA Standard Time + America/Santiago + Turks And Caicos Standard Time + America/Grand_Turk + Newfoundland Standard Time + America/St_Johns + Tocantins Standard Time + America/Araguaina + E. South America Standard Time + America/Sao_Paulo + SA Eastern Standard Time + America/Cayenne + Argentina Standard Time + America/Buenos_Aires + Greenland Standard Time + America/Godthab + Montevideo Standard Time + America/Montevideo + Magallanes Standard Time + America/Punta_Arenas + Saint Pierre Standard Time + America/Miquelon + Bahia Standard Time + America/Bahia + UTC-02 + Etc/GMT+2 + Azores Standard Time + Atlantic/Azores + Cape Verde Standard Time + Atlantic/Cape_Verde + UTC + Etc/GMT + GMT Standard Time + Europe/London + Greenwich Standard Time + Atlantic/Reykjavik + W. Europe Standard Time + Europe/Berlin + Central Europe Standard Time + Europe/Budapest + Romance Standard Time + Europe/Paris + Morocco Standard Time + Africa/Casablanca + Sao Tome Standard Time + Africa/Sao_Tome + Central European Standard Time + Europe/Warsaw + W. Central Africa Standard Time + Africa/Lagos + Jordan Standard Time + Asia/Amman + GTB Standard Time + Europe/Bucharest + Middle East Standard Time + Asia/Beirut + Egypt Standard Time + Africa/Cairo + E. Europe Standard Time + Europe/Chisinau + Syria Standard Time + Asia/Damascus + West Bank Standard Time + Asia/Hebron + South Africa Standard Time + Africa/Johannesburg + FLE Standard Time + Europe/Kiev + Israel Standard Time + Asia/Jerusalem + Kaliningrad Standard Time + Europe/Kaliningrad + Sudan Standard Time + Africa/Khartoum + Libya Standard Time + Africa/Tripoli + Namibia Standard Time + Africa/Windhoek + Arabic Standard Time + Asia/Baghdad + Turkey Standard Time + Europe/Istanbul + Arab Standard Time + Asia/Riyadh + Belarus Standard Time + Europe/Minsk + Russian Standard Time + Europe/Moscow + E. Africa Standard Time + Africa/Nairobi + Iran Standard Time + Asia/Tehran + Arabian Standard Time + Asia/Dubai + Astrakhan Standard Time + Europe/Astrakhan + Azerbaijan Standard Time + Asia/Baku + Russia Time Zone 3 + Europe/Samara + Mauritius Standard Time + Indian/Mauritius + Saratov Standard Time + Europe/Saratov + Georgian Standard Time + Asia/Tbilisi + Caucasus Standard Time + Asia/Yerevan + Afghanistan Standard Time + Asia/Kabul + West Asia Standard Time + Asia/Tashkent + Ekaterinburg Standard Time + Asia/Yekaterinburg + Pakistan Standard Time + Asia/Karachi + India Standard Time + Asia/Calcutta + Sri Lanka Standard Time + Asia/Colombo + Nepal Standard Time + Asia/Katmandu + Central Asia Standard Time + Asia/Almaty + Bangladesh Standard Time + Asia/Dhaka + Omsk Standard Time + Asia/Omsk + Myanmar Standard Time + Asia/Rangoon + SE Asia Standard Time + Asia/Bangkok + Altai Standard Time + Asia/Barnaul + W. Mongolia Standard Time + Asia/Hovd + North Asia Standard Time + Asia/Krasnoyarsk + N. Central Asia Standard Time + Asia/Novosibirsk + Tomsk Standard Time + Asia/Tomsk + China Standard Time + Asia/Shanghai + North Asia East Standard Time + Asia/Irkutsk + Singapore Standard Time + Asia/Singapore + W. Australia Standard Time + Australia/Perth + Taipei Standard Time + Asia/Taipei + Ulaanbaatar Standard Time + Asia/Ulaanbaatar + Aus Central W. Standard Time + Australia/Eucla + Transbaikal Standard Time + Asia/Chita + Tokyo Standard Time + Asia/Tokyo + North Korea Standard Time + Asia/Pyongyang + Korea Standard Time + Asia/Seoul + Yakutsk Standard Time + Asia/Yakutsk + Cen. Australia Standard Time + Australia/Adelaide + AUS Central Standard Time + Australia/Darwin + E. Australia Standard Time + Australia/Brisbane + AUS Eastern Standard Time + Australia/Sydney + West Pacific Standard Time + Pacific/Port_Moresby + Tasmania Standard Time + Australia/Hobart + Vladivostok Standard Time + Asia/Vladivostok + Lord Howe Standard Time + Australia/Lord_Howe + Bougainville Standard Time + Pacific/Bougainville + Russia Time Zone 10 + Asia/Srednekolymsk + Magadan Standard Time + Asia/Magadan + Norfolk Standard Time + Pacific/Norfolk + Sakhalin Standard Time + Asia/Sakhalin + Central Pacific Standard Time + Pacific/Guadalcanal + Russia Time Zone 11 + Asia/Kamchatka + New Zealand Standard Time + Pacific/Auckland + UTC+12 + Etc/GMT-12 + Fiji Standard Time + Pacific/Fiji + Chatham Islands Standard Time + Pacific/Chatham + UTC+13 + Etc/GMT-13 + Tonga Standard Time + Pacific/Tongatapu + Samoa Standard Time + Pacific/Apia + Line Islands Standard Time + Pacific/Kiritimati + + diff --git a/CoreFoundation/WindowsResources.h b/CoreFoundation/WindowsResources.h new file mode 100644 index 00000000000..229cb5a1125 --- /dev/null +++ b/CoreFoundation/WindowsResources.h @@ -0,0 +1,2 @@ + +#define IDR_WINDOWS_OLSON_MAPPING 101 From 793f226bbf4aa96b3726923bf6797edf304a1f68 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 18 Mar 2019 14:23:21 -0700 Subject: [PATCH 27/81] NumberDate: add inverted database for Windows/Olson In order to accept the full Unicode supported Olson names, we need the inverted database for mapping the olson name to the Windows timezone. This plist provides that mapping derived from the Unicode CLDR data. This will permit us to support CFTimeZone creation on Windows. --- CoreFoundation/CoreFoundation.rc | 2 + CoreFoundation/OlsonWindowsMapping.plist | 738 +++++++++++++++++++++++ CoreFoundation/WindowsResources.h | 2 + 3 files changed, 742 insertions(+) create mode 100644 CoreFoundation/OlsonWindowsMapping.plist diff --git a/CoreFoundation/CoreFoundation.rc b/CoreFoundation/CoreFoundation.rc index bde9f799a0f..5419bacca5d 100644 --- a/CoreFoundation/CoreFoundation.rc +++ b/CoreFoundation/CoreFoundation.rc @@ -2,3 +2,5 @@ #include "WindowsResources.h" IDR_WINDOWS_OLSON_MAPPING PLIST "WindowsOlsonMapping.plist" +IDR_OLSON_WINDOWS_MAPPING PLIST "OlsonWindowsMapping.plist" + diff --git a/CoreFoundation/OlsonWindowsMapping.plist b/CoreFoundation/OlsonWindowsMapping.plist new file mode 100644 index 00000000000..871ee7abb9b --- /dev/null +++ b/CoreFoundation/OlsonWindowsMapping.plist @@ -0,0 +1,738 @@ + + + + + Etc/GMT+12 + Dateline Standard Time + Pacific/Pago_Pago + UTC-11 + Pacific/Niue + UTC-11 + Pacific/Midway + UTC-11 + Etc/GMT+11 + UTC-11 + America/Adak + Aleutian Standard Time + Pacific/Rarotonga + Hawaiian Standard Time + Pacific/Tahiti + Hawaiian Standard Time + Pacific/Johnston + Hawaiian Standard Time + Pacific/Honolulu + Hawaiian Standard Time + Etc/GMT+10 + Hawaiian Standard Time + Pacific/Marquesas + Marquesas Standard Time + America/Anchorage + Alaskan Standard Time + Pacific/Gambier + UTC-09 + Etc/GMT+9 + UTC-09 + America/Tijuana + Pacific Standard Time (Mexico) + Pacific/Pitcairn + UTC-08 + Etc/GMT+8 + UTC-08 + America/Vancouver + Pacific Standard Time + America/Los_Angeles + Pacific Standard Time + PST8PDT + Pacific Standard Time + America/Dawson_Creek + US Mountain Standard Time + America/Hermosillo + US Mountain Standard Time + America/Phoenix + US Mountain Standard Time + Etc/GMT+7 + US Mountain Standard Time + America/Chihuahua + Mountain Standard Time (Mexico) + America/Edmonton + Mountain Standard Time + America/Ojinaga + Mountain Standard Time + America/Denver + Mountain Standard Time + MST7MDT + Mountain Standard Time + America/Belize + Central America Standard Time + America/Costa_Rica + Central America Standard Time + Pacific/Galapagos + Central America Standard Time + America/Guatemala + Central America Standard Time + America/Tegucigalpa + Central America Standard Time + America/Managua + Central America Standard Time + America/El_Salvador + Central America Standard Time + Etc/GMT+6 + Central America Standard Time + America/Winnipeg + Central Standard Time + America/Matamoros + Central Standard Time + America/Chicago + Central Standard Time + CST6CDT + Central Standard Time + Pacific/Easter + Easter Island Standard Time + America/Mexico_City + Central Standard Time (Mexico) + America/Regina + Canada Central Standard Time + America/Rio_Branco + SA Pacific Standard Time + America/Coral_Harbour + SA Pacific Standard Time + America/Bogota + SA Pacific Standard Time + America/Guayaquil + SA Pacific Standard Time + America/Jamaica + SA Pacific Standard Time + America/Cayman + SA Pacific Standard Time + America/Panama + SA Pacific Standard Time + America/Lima + SA Pacific Standard Time + Etc/GMT+5 + SA Pacific Standard Time + America/Cancun + Eastern Standard Time (Mexico) + America/Nassau + Eastern Standard Time + America/Toronto + Eastern Standard Time + America/New_York + Eastern Standard Time + EST5EDT + Eastern Standard Time + America/Port-au-Prince + Haiti Standard Time + America/Havana + Cuba Standard Time + America/Indianapolis + US Eastern Standard Time + America/Asuncion + Paraguay Standard Time + Atlantic/Bermuda + Atlantic Standard Time + America/Halifax + Atlantic Standard Time + America/Thule + Atlantic Standard Time + America/Caracas + Venezuela Standard Time + America/Cuiaba + Central Brazilian Standard Time + America/Antigua + SA Western Standard Time + America/Anguilla + SA Western Standard Time + America/Aruba + SA Western Standard Time + America/Barbados + SA Western Standard Time + America/St_Barthelemy + SA Western Standard Time + America/La_Paz + SA Western Standard Time + America/Kralendijk + SA Western Standard Time + America/Manaus + SA Western Standard Time + America/Blanc-Sablon + SA Western Standard Time + America/Curacao + SA Western Standard Time + America/Dominica + SA Western Standard Time + America/Santo_Domingo + SA Western Standard Time + America/Grenada + SA Western Standard Time + America/Guadeloupe + SA Western Standard Time + America/Guyana + SA Western Standard Time + America/St_Kitts + SA Western Standard Time + America/St_Lucia + SA Western Standard Time + America/Marigot + SA Western Standard Time + America/Martinique + SA Western Standard Time + America/Montserrat + SA Western Standard Time + America/Puerto_Rico + SA Western Standard Time + America/Lower_Princes + SA Western Standard Time + America/Port_of_Spain + SA Western Standard Time + America/St_Vincent + SA Western Standard Time + America/Tortola + SA Western Standard Time + America/St_Thomas + SA Western Standard Time + Etc/GMT+4 + SA Western Standard Time + America/Santiago + Pacific SA Standard Time + America/Grand_Turk + Turks And Caicos Standard Time + America/St_Johns + Newfoundland Standard Time + America/Araguaina + Tocantins Standard Time + America/Sao_Paulo + E. South America Standard Time + Antarctica/Rothera + SA Eastern Standard Time + America/Fortaleza + SA Eastern Standard Time + Atlantic/Stanley + SA Eastern Standard Time + America/Cayenne + SA Eastern Standard Time + America/Paramaribo + SA Eastern Standard Time + Etc/GMT+3 + SA Eastern Standard Time + America/Buenos_Aires + Argentina Standard Time + America/Godthab + Greenland Standard Time + America/Montevideo + Montevideo Standard Time + Antarctica/Palmer + Magallanes Standard Time + America/Punta_Arenas + Magallanes Standard Time + America/Miquelon + Saint Pierre Standard Time + America/Bahia + Bahia Standard Time + America/Noronha + UTC-02 + Atlantic/South_Georgia + UTC-02 + Etc/GMT+2 + UTC-02 + America/Scoresbysund + Azores Standard Time + Atlantic/Azores + Azores Standard Time + Atlantic/Cape_Verde + Cape Verde Standard Time + Etc/GMT+1 + Cape Verde Standard Time + America/Danmarkshavn + UTC + Etc/GMT + UTC + Atlantic/Canary + GMT Standard Time + Atlantic/Faeroe + GMT Standard Time + Europe/London + GMT Standard Time + Europe/Guernsey + GMT Standard Time + Europe/Dublin + GMT Standard Time + Europe/Isle_of_Man + GMT Standard Time + Europe/Jersey + GMT Standard Time + Europe/Lisbon + GMT Standard Time + Africa/Ouagadougou + Greenwich Standard Time + Africa/Abidjan + Greenwich Standard Time + Africa/Accra + Greenwich Standard Time + Africa/Banjul + Greenwich Standard Time + Africa/Conakry + Greenwich Standard Time + Africa/Bissau + Greenwich Standard Time + Atlantic/Reykjavik + Greenwich Standard Time + Africa/Monrovia + Greenwich Standard Time + Africa/Bamako + Greenwich Standard Time + Africa/Nouakchott + Greenwich Standard Time + Atlantic/St_Helena + Greenwich Standard Time + Africa/Freetown + Greenwich Standard Time + Africa/Dakar + Greenwich Standard Time + Africa/Lome + Greenwich Standard Time + Europe/Andorra + W. Europe Standard Time + Europe/Vienna + W. Europe Standard Time + Europe/Zurich + W. Europe Standard Time + Europe/Berlin + W. Europe Standard Time + Europe/Gibraltar + W. Europe Standard Time + Europe/Rome + W. Europe Standard Time + Europe/Vaduz + W. Europe Standard Time + Europe/Luxembourg + W. Europe Standard Time + Europe/Monaco + W. Europe Standard Time + Europe/Malta + W. Europe Standard Time + Europe/Amsterdam + W. Europe Standard Time + Europe/Oslo + W. Europe Standard Time + Europe/Stockholm + W. Europe Standard Time + Arctic/Longyearbyen + W. Europe Standard Time + Europe/San_Marino + W. Europe Standard Time + Europe/Vatican + W. Europe Standard Time + Europe/Tirane + Central Europe Standard Time + Europe/Prague + Central Europe Standard Time + Europe/Budapest + Central Europe Standard Time + Europe/Podgorica + Central Europe Standard Time + Europe/Belgrade + Central Europe Standard Time + Europe/Ljubljana + Central Europe Standard Time + Europe/Bratislava + Central Europe Standard Time + Europe/Brussels + Romance Standard Time + Europe/Copenhagen + Romance Standard Time + Europe/Madrid + Romance Standard Time + Europe/Paris + Romance Standard Time + Africa/El_Aaiun + Morocco Standard Time + Africa/Casablanca + Morocco Standard Time + Africa/Sao_Tome + Sao Tome Standard Time + Europe/Sarajevo + Central European Standard Time + Europe/Zagreb + Central European Standard Time + Europe/Skopje + Central European Standard Time + Europe/Warsaw + Central European Standard Time + Africa/Luanda + W. Central Africa Standard Time + Africa/Porto-Novo + W. Central Africa Standard Time + Africa/Kinshasa + W. Central Africa Standard Time + Africa/Bangui + W. Central Africa Standard Time + Africa/Brazzaville + W. Central Africa Standard Time + Africa/Douala + W. Central Africa Standard Time + Africa/Algiers + W. Central Africa Standard Time + Africa/Libreville + W. Central Africa Standard Time + Africa/Malabo + W. Central Africa Standard Time + Africa/Niamey + W. Central Africa Standard Time + Africa/Lagos + W. Central Africa Standard Time + Africa/Ndjamena + W. Central Africa Standard Time + Africa/Tunis + W. Central Africa Standard Time + Etc/GMT-1 + W. Central Africa Standard Time + Asia/Amman + Jordan Standard Time + Asia/Famagusta + GTB Standard Time + Europe/Athens + GTB Standard Time + Europe/Bucharest + GTB Standard Time + Asia/Beirut + Middle East Standard Time + Africa/Cairo + Egypt Standard Time + Europe/Chisinau + E. Europe Standard Time + Asia/Damascus + Syria Standard Time + Asia/Hebron + West Bank Standard Time + Africa/Bujumbura + South Africa Standard Time + Africa/Gaborone + South Africa Standard Time + Africa/Lubumbashi + South Africa Standard Time + Africa/Maseru + South Africa Standard Time + Africa/Blantyre + South Africa Standard Time + Africa/Maputo + South Africa Standard Time + Africa/Kigali + South Africa Standard Time + Africa/Mbabane + South Africa Standard Time + Africa/Johannesburg + South Africa Standard Time + Africa/Lusaka + South Africa Standard Time + Africa/Harare + South Africa Standard Time + Etc/GMT-2 + South Africa Standard Time + Europe/Mariehamn + FLE Standard Time + Europe/Sofia + FLE Standard Time + Europe/Tallinn + FLE Standard Time + Europe/Helsinki + FLE Standard Time + Europe/Vilnius + FLE Standard Time + Europe/Riga + FLE Standard Time + Europe/Kiev + FLE Standard Time + Asia/Jerusalem + Israel Standard Time + Europe/Kaliningrad + Kaliningrad Standard Time + Africa/Khartoum + Sudan Standard Time + Africa/Tripoli + Libya Standard Time + Africa/Windhoek + Namibia Standard Time + Asia/Baghdad + Arabic Standard Time + Europe/Istanbul + Turkey Standard Time + Asia/Bahrain + Arab Standard Time + Asia/Kuwait + Arab Standard Time + Asia/Qatar + Arab Standard Time + Asia/Riyadh + Arab Standard Time + Asia/Aden + Arab Standard Time + Europe/Minsk + Belarus Standard Time + Europe/Moscow + Russian Standard Time + Europe/Simferopol + Russian Standard Time + Antarctica/Syowa + E. Africa Standard Time + Africa/Djibouti + E. Africa Standard Time + Africa/Asmera + E. Africa Standard Time + Africa/Addis_Ababa + E. Africa Standard Time + Africa/Nairobi + E. Africa Standard Time + Indian/Comoro + E. Africa Standard Time + Indian/Antananarivo + E. Africa Standard Time + Africa/Mogadishu + E. Africa Standard Time + Africa/Juba + E. Africa Standard Time + Africa/Dar_es_Salaam + E. Africa Standard Time + Africa/Kampala + E. Africa Standard Time + Indian/Mayotte + E. Africa Standard Time + Etc/GMT-3 + E. Africa Standard Time + Asia/Tehran + Iran Standard Time + Asia/Dubai + Arabian Standard Time + Asia/Muscat + Arabian Standard Time + Etc/GMT-4 + Arabian Standard Time + Europe/Astrakhan + Astrakhan Standard Time + Asia/Baku + Azerbaijan Standard Time + Europe/Samara + Russia Time Zone 3 + Indian/Mauritius + Mauritius Standard Time + Indian/Reunion + Mauritius Standard Time + Indian/Mahe + Mauritius Standard Time + Europe/Saratov + Saratov Standard Time + Asia/Tbilisi + Georgian Standard Time + Asia/Yerevan + Caucasus Standard Time + Asia/Kabul + Afghanistan Standard Time + Antarctica/Mawson + West Asia Standard Time + Asia/Oral + West Asia Standard Time + Indian/Maldives + West Asia Standard Time + Indian/Kerguelen + West Asia Standard Time + Asia/Dushanbe + West Asia Standard Time + Asia/Ashgabat + West Asia Standard Time + Asia/Tashkent + West Asia Standard Time + Etc/GMT-5 + West Asia Standard Time + Asia/Yekaterinburg + Ekaterinburg Standard Time + Asia/Karachi + Pakistan Standard Time + Asia/Calcutta + India Standard Time + Asia/Colombo + Sri Lanka Standard Time + Asia/Katmandu + Nepal Standard Time + Antarctica/Vostok + Central Asia Standard Time + Asia/Urumqi + Central Asia Standard Time + Indian/Chagos + Central Asia Standard Time + Asia/Bishkek + Central Asia Standard Time + Asia/Almaty + Central Asia Standard Time + Etc/GMT-6 + Central Asia Standard Time + Asia/Dhaka + Bangladesh Standard Time + Asia/Thimphu + Bangladesh Standard Time + Asia/Omsk + Omsk Standard Time + Indian/Cocos + Myanmar Standard Time + Asia/Rangoon + Myanmar Standard Time + Antarctica/Davis + SE Asia Standard Time + Indian/Christmas + SE Asia Standard Time + Asia/Jakarta + SE Asia Standard Time + Asia/Phnom_Penh + SE Asia Standard Time + Asia/Vientiane + SE Asia Standard Time + Asia/Bangkok + SE Asia Standard Time + Asia/Saigon + SE Asia Standard Time + Etc/GMT-7 + SE Asia Standard Time + Asia/Barnaul + Altai Standard Time + Asia/Hovd + W. Mongolia Standard Time + Asia/Krasnoyarsk + North Asia Standard Time + Asia/Novosibirsk + N. Central Asia Standard Time + Asia/Tomsk + Tomsk Standard Time + Asia/Shanghai + China Standard Time + Asia/Hong_Kong + China Standard Time + Asia/Macau + China Standard Time + Asia/Irkutsk + North Asia East Standard Time + Asia/Brunei + Singapore Standard Time + Asia/Makassar + Singapore Standard Time + Asia/Kuala_Lumpur + Singapore Standard Time + Asia/Manila + Singapore Standard Time + Asia/Singapore + Singapore Standard Time + Etc/GMT-8 + Singapore Standard Time + Antarctica/Casey + W. Australia Standard Time + Australia/Perth + W. Australia Standard Time + Asia/Taipei + Taipei Standard Time + Asia/Ulaanbaatar + Ulaanbaatar Standard Time + Australia/Eucla + Aus Central W. Standard Time + Asia/Chita + Transbaikal Standard Time + Asia/Jayapura + Tokyo Standard Time + Asia/Tokyo + Tokyo Standard Time + Pacific/Palau + Tokyo Standard Time + Asia/Dili + Tokyo Standard Time + Etc/GMT-9 + Tokyo Standard Time + Asia/Pyongyang + North Korea Standard Time + Asia/Seoul + Korea Standard Time + Asia/Yakutsk + Yakutsk Standard Time + Australia/Adelaide + Cen. Australia Standard Time + Australia/Darwin + AUS Central Standard Time + Australia/Brisbane + E. Australia Standard Time + Australia/Sydney + AUS Eastern Standard Time + Antarctica/DumontDUrville + West Pacific Standard Time + Pacific/Truk + West Pacific Standard Time + Pacific/Guam + West Pacific Standard Time + Pacific/Saipan + West Pacific Standard Time + Pacific/Port_Moresby + West Pacific Standard Time + Etc/GMT-10 + West Pacific Standard Time + Australia/Hobart + Tasmania Standard Time + Asia/Vladivostok + Vladivostok Standard Time + Australia/Lord_Howe + Lord Howe Standard Time + Pacific/Bougainville + Bougainville Standard Time + Asia/Srednekolymsk + Russia Time Zone 10 + Asia/Magadan + Magadan Standard Time + Pacific/Norfolk + Norfolk Standard Time + Asia/Sakhalin + Sakhalin Standard Time + Antarctica/Macquarie + Central Pacific Standard Time + Pacific/Ponape + Central Pacific Standard Time + Pacific/Noumea + Central Pacific Standard Time + Pacific/Guadalcanal + Central Pacific Standard Time + Pacific/Efate + Central Pacific Standard Time + Etc/GMT-11 + Central Pacific Standard Time + Asia/Kamchatka + Russia Time Zone 11 + Antarctica/McMurdo + New Zealand Standard Time + Pacific/Auckland + New Zealand Standard Time + Pacific/Tarawa + UTC+12 + Pacific/Majuro + UTC+12 + Pacific/Nauru + UTC+12 + Pacific/Funafuti + UTC+12 + Pacific/Wake + UTC+12 + Pacific/Wallis + UTC+12 + Etc/GMT-12 + UTC+12 + Pacific/Fiji + Fiji Standard Time + Pacific/Chatham + Chatham Islands Standard Time + Pacific/Enderbury + UTC+13 + Pacific/Fakaofo + UTC+13 + Etc/GMT-13 + UTC+13 + Pacific/Tongatapu + Tonga Standard Time + Pacific/Apia + Samoa Standard Time + Pacific/Kiritimati + Line Islands Standard Time + Etc/GMT-14 + Line Islands Standard Time + + diff --git a/CoreFoundation/WindowsResources.h b/CoreFoundation/WindowsResources.h index 229cb5a1125..f1bd285a978 100644 --- a/CoreFoundation/WindowsResources.h +++ b/CoreFoundation/WindowsResources.h @@ -1,2 +1,4 @@ #define IDR_WINDOWS_OLSON_MAPPING 101 +#define IDR_OLSON_WINDOWS_MAPPING 102 + From 322a30df46b5e5b9c5cca3e9ae427240083a2cfc Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 18 Mar 2019 11:51:15 -0700 Subject: [PATCH 28/81] Tools: add a XSL to create the Windows/Olson mappings Since Windows uses its own names for timezones, the Unicode corsotium provides the canonical mapping for the timezones. Use the Unicode CLDR to generate the mapping plist requires transformation, which this XSLT provides. These XSLs allow the creation of both the Olson -> Windows mapping and the Windows -> Olson mapping. Since the latter only preserves the primary name and not the secondary ones, we split the inverted database. --- CoreFoundation/Tools/OlsonWindowsDatabase.xsl | 51 +++++++++++++++++++ CoreFoundation/Tools/WindowsOlsonDatabase.xsl | 27 ++++++++++ 2 files changed, 78 insertions(+) create mode 100644 CoreFoundation/Tools/OlsonWindowsDatabase.xsl create mode 100644 CoreFoundation/Tools/WindowsOlsonDatabase.xsl diff --git a/CoreFoundation/Tools/OlsonWindowsDatabase.xsl b/CoreFoundation/Tools/OlsonWindowsDatabase.xsl new file mode 100644 index 00000000000..86ab9b87614 --- /dev/null +++ b/CoreFoundation/Tools/OlsonWindowsDatabase.xsl @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + <!DOCTYPE plist SYSTEM "file:///localhost/System/Library/DTDs/PropertyList.dtd"> + + + + + + + + + + + + + + + + + diff --git a/CoreFoundation/Tools/WindowsOlsonDatabase.xsl b/CoreFoundation/Tools/WindowsOlsonDatabase.xsl new file mode 100644 index 00000000000..3966d46cf75 --- /dev/null +++ b/CoreFoundation/Tools/WindowsOlsonDatabase.xsl @@ -0,0 +1,27 @@ + + + + + + + <!DOCTYPE plist SYSTEM "file:///localhost/System/Library/DTDs/PropertyList.dtd"> + + + + + + + + + + + From f3f675e54e361d077ccc011a4c15e986944e49e0 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 18 Mar 2019 17:04:20 -0700 Subject: [PATCH 29/81] NumberDate: read the TimeZone information from the registry This queries the timezone information from the registry on Windows to create and initialize the timezone. This should allow CFTimeZoneCreate to function. Doing this will finally allow Foundation to use timezones on Windows! --- .../NumberDate.subproj/CFTimeZone.c | 164 +++++++++++++----- 1 file changed, 122 insertions(+), 42 deletions(-) diff --git a/CoreFoundation/NumberDate.subproj/CFTimeZone.c b/CoreFoundation/NumberDate.subproj/CFTimeZone.c index e52277da667..5a9973ef485 100644 --- a/CoreFoundation/NumberDate.subproj/CFTimeZone.c +++ b/CoreFoundation/NumberDate.subproj/CFTimeZone.c @@ -70,9 +70,11 @@ struct tzhead { #include +#if !TARGET_OS_WIN32 static CFStringRef __tzZoneInfo = NULL; static char *__tzDir = NULL; static void __InitTZStrings(void); +#endif CONST_STRING_DECL(kCFTimeZoneSystemTimeZoneDidChangeNotification, "kCFTimeZoneSystemTimeZoneDidChangeNotification") @@ -116,6 +118,9 @@ CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { } #if TARGET_OS_WIN32 +#define CF_TIME_ZONES_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" +#define COUNT_OF(array) (sizeof((array)) / sizeof((array)[0])) + /* This function should be used for WIN32 instead of * __CFCopyRecursiveDirectoryList function. * It takes TimeZone names from the registry @@ -124,33 +129,60 @@ CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { static CFMutableArrayRef __CFCopyWindowsTimeZoneList() { CFMutableArrayRef result = NULL; HKEY hkResult; - TCHAR lpName[MAX_PATH+1]; + WCHAR szName[MAX_PATH + 1]; DWORD dwIndex, retCode; - if (RegOpenKey(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones"), &hkResult) != - ERROR_SUCCESS ) + if (RegOpenKeyW(HKEY_LOCAL_MACHINE, CF_TIME_ZONES_KEY, &hkResult) != ERROR_SUCCESS) return NULL; result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); - for (dwIndex=0; (retCode = RegEnumKey(hkResult,dwIndex,lpName,MAX_PATH)) != ERROR_NO_MORE_ITEMS ; dwIndex++) { + for (dwIndex = 0; (retCode = RegEnumKeyW(hkResult, dwIndex, szName, COUNT_OF(szName) - 1)) != ERROR_NO_MORE_ITEMS; dwIndex++) { if (retCode != ERROR_SUCCESS) { RegCloseKey(hkResult); CFRelease(result); return NULL; } else { -#if defined(UNICODE) - CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)lpName, (_tcslen(lpName) * sizeof(UniChar)), kCFStringEncodingUnicode, false); -#else - CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const unsigned char *)lpName, _tcslen(lpName), CFStringGetSystemEncoding(), false); -#endif - CFArrayAppendValue(result, string); - CFRelease(string); + CFStringRef string = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const UInt8 *)szName, (wcslen(szName) * sizeof(WCHAR)), kCFStringEncodingUTF16, false); + CFArrayAppendValue(result, string); + CFRelease(string); } } RegCloseKey(hkResult); return result; } + +static void __CFTimeZoneGetOffset(CFStringRef timezone, int32_t *offset) { + typedef struct _REG_TZI_FORMAT { + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; + } REG_TZI_FORMAT; + + WCHAR szRegKey[COUNT_OF(CF_TIME_ZONES_KEY) + 1 + COUNT_OF(((TIME_ZONE_INFORMATION *)0)->StandardName) + 1]; + REG_TZI_FORMAT tziInfo; + Boolean bResult; + DWORD cbData; + HKEY hKey; + + *offset = 0; + + memset(szRegKey, 0, sizeof(szRegKey)); + wcscpy(szRegKey, CF_TIME_ZONES_KEY); + szRegKey[COUNT_OF(CF_TIME_ZONES_KEY) - 1] = L'\\'; + CFStringGetBytes(timezone, CFRangeMake(0, CFStringGetLength(timezone)), kCFStringEncodingUnicode, FALSE, FALSE, (uint8_t *)&szRegKey[wcslen(CF_TIME_ZONES_KEY) + 1], sizeof(((TIME_ZONE_INFORMATION *)0)->StandardName) + sizeof(WCHAR), NULL); + + if (RegOpenKeyW(HKEY_LOCAL_MACHINE, szRegKey, &hKey) != ERROR_SUCCESS) + return; + + cbData = sizeof(tziInfo); + if (RegQueryValueExW(hKey, L"TZI", NULL, NULL, (LPBYTE)&tziInfo, &cbData) == ERROR_SUCCESS) + *offset = tziInfo.Bias; + + RegCloseKey(hKey); +} #elif TARGET_OS_MAC || TARGET_OS_LINUX || TARGET_OS_BSD static CFMutableArrayRef __CFCopyRecursiveDirectoryList() { CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); @@ -476,21 +508,27 @@ CF_INLINE void __CFTimeZoneUnlockWinToOlson(void) { __CFUnlock(&__CFTimeZoneWinToOlsonLock); } -static Boolean CFTimeZoneLoadWindowsOlsonPlist(LPVOID *ppResource, LPDWORD pdwSize) { +static Boolean CFTimeZoneLoadPlistResource(LPCSTR lpName, LPVOID *ppResource, LPDWORD pdwSize) { HRSRC hResource; HGLOBAL hMemory; + HMODULE hModule; + + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)&CFTimeZoneLoadPlistResource, &hModule)) { + return FALSE; + } - hResource = FindResourceA(NULL, MAKEINTRESOURCEA(IDR_WINDOWS_OLSON_MAPPING), "PLIST"); + hResource = FindResourceA(hModule, lpName, "PLIST"); if (hResource == NULL) { return FALSE; } - hMemory = LoadResource(NULL, hResource); + hMemory = LoadResource(hModule, hResource); if (hMemory == NULL) { return FALSE; } - *pdwSize = SizeofResource(NULL, hResource); + *pdwSize = SizeofResource(hModule, hResource); *ppResource = LockResource(hMemory); return *pdwSize && *ppResource; @@ -504,7 +542,7 @@ CFDictionaryRef CFTimeZoneCopyWinToOlsonDictionary(void) { const uint8_t *plist; DWORD dwSize; - if (CFTimeZoneLoadWindowsOlsonPlist(&plist, &dwSize)) { + if (CFTimeZoneLoadPlistResource(MAKEINTRESOURCEA(IDR_WINDOWS_OLSON_MAPPING), (LPVOID *)&plist, &dwSize)) { CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, plist, dwSize); __CFTimeZoneWinToOlsonDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); CFRelease(data); @@ -519,6 +557,26 @@ CFDictionaryRef CFTimeZoneCopyWinToOlsonDictionary(void) { return dict; } +static CFDictionaryRef CFTimeZoneCopyOlsonToWindowsDictionary(void) { + static CFDictionaryRef dict; + static CFLock_t lock; + + __CFLock(&lock); + if (dict == NULL) { + const uint8_t *plist; + DWORD dwSize; + + if (CFTimeZoneLoadPlistResource(MAKEINTRESOURCEA(IDR_OLSON_WINDOWS_MAPPING), (LPVOID *)&plist, &dwSize)) { + CFDataRef data = CFDataCreate(kCFAllocatorSystemDefault, plist, dwSize); + dict = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + } + __CFUnlock(&lock); + + return dict ? CFRetain(dict) : NULL; +} + void CFTimeZoneSetWinToOlsonDictionary(CFDictionaryRef dict) { __CFGenericValidateType(dict, CFDictionaryGetTypeID()); __CFTimeZoneLockWinToOlson(); @@ -544,23 +602,6 @@ CFTimeZoneRef CFTimeZoneCreateWithWindowsName(CFAllocatorRef allocator, CFString CFRelease(winToOlson); return retval; } - -static void __InitTZStrings(void) { - static CFLock_t __CFTZDirLock = CFLockInit; - __CFLock(&__CFTZDirLock); - // TODO(compnerd) figure out how to initialize __tzZoneInfo - if (!__tzDir && __tzZoneInfo) { - int length = CFStringGetLength(__tzZoneInfo) + sizeof("\\zone.tab") + 1; - __tzDir = malloc(length); // If we don't use ascii, we'll need to malloc more space - if (!__tzDir || !CFStringGetCString(__tzZoneInfo, __tzDir, length, kCFStringEncodingASCII)) { - free(__tzDir); - } else { - strcat(__tzDir, "\\zone.tab"); - } - } - __CFUnlock(&__CFTZDirLock); -} - #elif TARGET_OS_MAC static void __InitTZStrings(void) { static dispatch_once_t initOnce = 0; @@ -1056,14 +1097,33 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data void *bytes; CFIndex length; Boolean result = false; - - if (!__tzZoneInfo) __InitTZStrings(); - if (!__tzZoneInfo) return NULL; + #if TARGET_OS_WIN32 - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + + tzName = CFDictionaryGetValue(abbrevs, name); + if (tzName == NULL) { + CFDictionaryRef olson = CFTimeZoneCopyOlsonToWindowsDictionary(); + tzName = CFDictionaryGetValue(olson, name); + CFRelease(olson); + } + + CFRelease(abbrevs); + + if (tzName) { + int32_t offset; + __CFTimeZoneGetOffset(tzName, &offset); + CFRelease(tzName); + // TODO(compnerd) handle DST + __CFTimeZoneInitFixed(timeZone, offset, name, 0); + return TRUE; + } + + return FALSE; #else + if (!__tzZoneInfo) __InitTZStrings(); + if (!__tzZoneInfo) return NULL; baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); -#endif CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); tzName = CFDictionaryGetValue(abbrevs, name); @@ -1113,6 +1173,7 @@ Boolean _CFTimeZoneInit(CFTimeZoneRef timeZone, CFStringRef name, CFDataRef data CFRelease(data); } return result; +#endif } CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { @@ -1254,13 +1315,31 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam void *bytes; CFIndex length; - if (!__tzZoneInfo) __InitTZStrings(); - if (!__tzZoneInfo) return NULL; #if TARGET_OS_WIN32 - baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLWindowsPathStyle, true); + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + + tzName = CFDictionaryGetValue(abbrevs, name); + if (tzName == NULL) { + CFDictionaryRef olson = CFTimeZoneCopyOlsonToWindowsDictionary(); + tzName = CFDictionaryGetValue(olson, name); + CFRelease(olson); + } + + CFRelease(abbrevs); + + if (tzName) { + int32_t offset; + __CFTimeZoneGetOffset(tzName, &offset); + CFRelease(tzName); + // TODO(compnerd) handle DST + result = __CFTimeZoneCreateFixed(allocator, offset, name, 0); + } + + return result; #else + if (!__tzZoneInfo) __InitTZStrings(); + if (!__tzZoneInfo) return NULL; baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, __tzZoneInfo, kCFURLPOSIXPathStyle, true); -#endif if (tryAbbrev) { CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); tzName = CFDictionaryGetValue(abbrevs, name); @@ -1317,6 +1396,7 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam CFRelease(data); } return result; +#endif } CFStringRef CFTimeZoneGetName(CFTimeZoneRef tz) { From 43f91de91c46797f186012d877a8f72bcb4add61 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 19 Mar 2019 09:17:11 -0700 Subject: [PATCH 30/81] build: add dependency on Windows/Olson mapping plists These are source files as far as the CoreFoundation build is concerned. Add a dependency on the files. --- CoreFoundation/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CoreFoundation/CMakeLists.txt b/CoreFoundation/CMakeLists.txt index 827247fda0e..5755a9c2b12 100644 --- a/CoreFoundation/CMakeLists.txt +++ b/CoreFoundation/CMakeLists.txt @@ -316,6 +316,15 @@ add_framework(CoreFoundation URL.subproj/CFURLComponents.c URL.subproj/CFURLComponents_URIParser.c URL.subproj/CFURLSessionInterface.c) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + # NOTE(compnerd) the WindowsOlsonMapping.plist and OlsonWindowsMapping.plist + # are embedded Windows resources that we use to map the Windows timezone to + # the Olson name. Ensure that we rebuild on regeneration of the files. + add_custom_target(CoreFoundationWindowsTimeZonesPLists DEPENDS + WindowsOlsonMapping.plist + OlsonWindowsMapping.plist) + add_dependencies(CoreFoundation CoreFoundationWindowsTimeZonesPLists) +endif() if(CMAKE_SYSTEM_NAME STREQUAL Linux OR CMAKE_SYSTEM_NAME STREQUAL Android) target_compile_definitions(CoreFoundation PRIVATE From aa281d1205a44642adf210c1b4180ed029856043 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 19 Mar 2019 09:18:28 -0700 Subject: [PATCH 31/81] build: don't add C flags to RC files This was adding a spurious `-I` to the compile flags which resulted in the output being placed incorrectly. Simply elide the flags for RC targets. --- CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake b/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake index 58d8c41291a..53f6a1ee7e3 100644 --- a/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake +++ b/CoreFoundation/cmake/modules/CoreFoundationAddFramework.cmake @@ -63,7 +63,7 @@ function(add_framework NAME) endif() target_compile_options(${NAME} PRIVATE - -I;${CMAKE_BINARY_DIR}/${NAME}.framework/PrivateHeaders) + $<$,$>:-I;${CMAKE_BINARY_DIR}/${NAME}.framework/PrivateHeaders>) add_dependencies(${NAME} ${NAME}_POPULATE_HEADERS) if(AF_FRAMEWORK_DIRECTORY) From d6f351f03a64d9ce0bdf3a049b9c41a6673ce999 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 20 Mar 2019 19:21:53 -0700 Subject: [PATCH 32/81] Foundation: initialize CFSocket on startup Windows requires the networking subsystem (WinSock) to be initialized. However, the user expects to be able to use the networking APIs in Foundation without performing any one-time initialization. Furthermore, due to the macro usage in the initialization path, it is easier to perform the initialization in C than in Swift. CoreFoundation has a SPI for this (`__CFSocketInitializeWinSock`). --- CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h | 4 ++++ Foundation/NSSwiftRuntime.swift | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h index 88ebf4cd96d..7960d691b92 100644 --- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h +++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h @@ -566,6 +566,10 @@ _stat_with_btime(const char *filename, struct stat *buffer, struct timespec *bti #warning "Enabling statx" #endif +#if DEPLOYMENT_TARGET_WINDOWS +CF_EXPORT void __CFSocketInitializeWinSock(void); +#endif + _CF_EXPORT_SCOPE_END #endif /* __COREFOUNDATION_FORSWIFTFOUNDATIONONLY__ */ diff --git a/Foundation/NSSwiftRuntime.swift b/Foundation/NSSwiftRuntime.swift index f33b28ca276..718eff326a4 100644 --- a/Foundation/NSSwiftRuntime.swift +++ b/Foundation/NSSwiftRuntime.swift @@ -150,7 +150,10 @@ internal func __CFSwiftGetBaseClass() -> UnsafeRawPointer { @usableFromInline @_cdecl("__CFInitializeSwift") internal func __CFInitializeSwift() { - +#if os(Windows) + __CFInitializeWinSock() +#endif + _CFRuntimeBridgeTypeToClass(CFStringGetTypeID(), unsafeBitCast(_NSCFString.self, to: UnsafeRawPointer.self)) _CFRuntimeBridgeTypeToClass(CFArrayGetTypeID(), unsafeBitCast(_NSCFArray.self, to: UnsafeRawPointer.self)) _CFRuntimeBridgeTypeToClass(CFDictionaryGetTypeID(), unsafeBitCast(_NSCFDictionary.self, to: UnsafeRawPointer.self)) From 91c0b80aef7aba28d89f49796d1fcd35b77daa26 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Fri, 22 Mar 2019 13:25:31 +0000 Subject: [PATCH 33/81] TestNSNumber: Disable testNSNumberToBool() that breaks on Darwin. - This still works on Linux so disable it conditionally. --- TestFoundation/TestNSNumberBridging.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TestFoundation/TestNSNumberBridging.swift b/TestFoundation/TestNSNumberBridging.swift index ade7bca089a..aace790cfc6 100644 --- a/TestFoundation/TestNSNumberBridging.swift +++ b/TestFoundation/TestNSNumberBridging.swift @@ -626,6 +626,11 @@ class TestNSNumberBridging : XCTestCase { } func testNSNumberToBool() { + // FIXME: When running swift-corelibs-foundation on Darwin, these tests all give the warning: + // 'Conditional cast from 'NSNumber' to 'Bool' always succeeds' and never return nil so these tests fail. + // These tests work on Darwin using normal Foundation (when testing with DarwinCompatibilityTests) + // and also work on Linux, so just disable them on Darwin/swift-corelibs-foundation. +#if !canImport(SwiftFoundation) let b0 = NSNumber(value: 0) as? Bool XCTAssertNotNil(b0) XCTAssertEqual(b0, false) @@ -664,6 +669,7 @@ class TestNSNumberBridging : XCTestCase { XCTAssertNil(NSNumber(value: Int64.max) as? Bool) XCTAssertNil(NSNumber(value: Int.min) as? Bool) XCTAssertNil(NSNumber(value: Int.max) as? Bool) +#endif } } From 96b00e7a78c170e063a109d151595fb5491baa2c Mon Sep 17 00:00:00 2001 From: Luigi Date: Sat, 23 Mar 2019 12:23:17 +0900 Subject: [PATCH 34/81] Refactor if statements in URL.swift I refactored some of the if statements in URL.swift to be guard instead. This made the code shorter and easier to read. --- Foundation/URL.swift | 72 ++++++++++---------------------------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/Foundation/URL.swift b/Foundation/URL.swift index 1ee3396e60e..94e377fb346 100644 --- a/Foundation/URL.swift +++ b/Foundation/URL.swift @@ -433,26 +433,16 @@ public struct URL : ReferenceConvertible, Equatable { /// /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). public init?(string: String) { - guard !string.isEmpty else { return nil } - - if let inner = NSURL(string: string) { - _url = URL._converted(from: inner) - } else { - return nil - } + guard !string.isEmpty, let inner = NSURL(string: string) else { return nil } + _url = URL._converted(from: inner) } /// Initialize with string, relative to another URL. /// /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). public init?(string: String, relativeTo url: URL?) { - guard !string.isEmpty else { return nil } - - if let inner = NSURL(string: string, relativeTo: url) { - _url = URL._converted(from: inner) - } else { - return nil - } + guard !string.isEmpty, let inner = NSURL(string: string, relativeTo: url) else { return nil } + _url = URL._converted(from: inner) } /// Initializes a newly created file URL referencing the local file or directory at path, relative to a base URL. @@ -750,15 +740,8 @@ public struct URL : ReferenceConvertible, Equatable { /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. public func deletingLastPathComponent() -> URL { // This is a slight behavior change from NSURL, but better than returning "http://www.example.com../". - if path.isEmpty { - return self - } - - if let result = _url.deletingLastPathComponent { - return result - } else { - return self - } + guard !path.isEmpty, let result = _url.deletingLastPathComponent else { return self } + return result } /// Returns a URL constructed by appending the given path extension to self. @@ -768,30 +751,16 @@ public struct URL : ReferenceConvertible, Equatable { /// Certain special characters (for example, Unicode Right-To-Left marks) cannot be used as path extensions. If any of those are contained in `pathExtension`, the function will return the URL unchanged. /// - parameter pathExtension: The extension to append. public func appendingPathExtension(_ pathExtension: String) -> URL { - if path.isEmpty { - return self - } - - if let result = _url.appendingPathExtension(pathExtension) { - return result - } else { - return self - } + guard !path.isEmpty, let result = _url.appendingPathExtension(pathExtension) else { return self } + return result } /// Returns a URL constructed by removing any path extension. /// /// If the URL has an empty path (e.g., `http://www.example.com`), then this function will return the URL unchanged. public func deletingPathExtension() -> URL { - if path.isEmpty { - return self - } - - if let result = _url.deletingPathExtension { - return result - } else { - return self - } + guard !path.isEmpty, let result = _url.deletingPathExtension else { return self } + return result } /// Appends a path component to the URL. @@ -839,11 +808,8 @@ public struct URL : ReferenceConvertible, Equatable { /// Returns a `URL` with any instances of ".." or "." removed from its path. public var standardized : URL { // The NSURL API can only return nil in case of file reference URL, which we should not be - if let result = _url.standardized { - return result - } else { - return self - } + guard let result = _url.standardized else { return self } + return result } /// Standardizes the path of a file URL. @@ -858,11 +824,8 @@ public struct URL : ReferenceConvertible, Equatable { /// If the `isFileURL` is false, this method returns `self`. public var standardizedFileURL : URL { // NSURL should not return nil here unless this is a file reference URL, which should be impossible - if let result = _url.standardizingPath { - return result - } else { - return self - } + guard let result = _url.standardizingPath else { return self } + return result } /// Resolves any symlinks in the path of a file URL. @@ -870,11 +833,8 @@ public struct URL : ReferenceConvertible, Equatable { /// If the `isFileURL` is false, this method returns `self`. public func resolvingSymlinksInPath() -> URL { // NSURL should not return nil here unless this is a file reference URL, which should be impossible - if let result = _url.resolvingSymlinksInPath { - return result - } else { - return self - } + guard let result = _url.resolvingSymlinksInPath else { return self } + return result } /// Resolves any symlinks in the path of a file URL. From cd71e72d7f7e8ef13c53f102693264786ab602d0 Mon Sep 17 00:00:00 2001 From: kirei Date: Sat, 23 Mar 2019 12:24:24 +0900 Subject: [PATCH 35/81] Extend URLResponse tests --- Docs/Status.md | 2 +- TestFoundation/TestURLResponse.swift | 54 ++++++++++++++++++---------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Docs/Status.md b/Docs/Status.md index 5a18734cff3..ec34da41804 100644 --- a/Docs/Status.md +++ b/Docs/Status.md @@ -56,7 +56,7 @@ There is no _Complete_ status for test coverage because there are always additio | `URLProtocolClient` | Unimplemented | None | | | `NSURLRequest` | Complete | Incomplete | | | `NSMutableURLRequest` | Complete | Incomplete | | - | `URLResponse` | Complete | Incomplete | | + | `URLResponse` | Complete | Substantial | | | `NSHTTPURLResponse` | Complete | Substantial | | | `NSURL` | Mostly Complete | Substantial | Resource values remain unimplemented | | `NSURLQueryItem` | Complete | N/A | | diff --git a/TestFoundation/TestURLResponse.swift b/TestFoundation/TestURLResponse.swift index f2797ba4577..dbe6c852940 100644 --- a/TestFoundation/TestURLResponse.swift +++ b/TestFoundation/TestURLResponse.swift @@ -13,9 +13,12 @@ class TestURLResponse : XCTestCase { ("test_URL", test_URL), ("test_MIMEType_1", test_MIMEType_1), ("test_MIMEType_2", test_MIMEType_2), - ("test_ExpectedContentLength", test_ExpectedContentLength), - ("test_TextEncodingName", test_TextEncodingName), - ("test_suggestedFilename", test_suggestedFilename), + ("test_MIMEType_notAvailable", test_MIMEType_notAvailable), + ("test_ExpectedContentLength_positive", test_ExpectedContentLength_positive), + ("test_ExpectedContentLength_negative", test_ExpectedContentLength_negative), + ("test_TextEncodingName_positive", test_TextEncodingName_positive), + ("test_TextEncodingName_negative", test_TextEncodingName_negative), + ("test_suggestedFilename_1", test_suggestedFilename_1), ("test_suggestedFilename_2", test_suggestedFilename_2), ("test_suggestedFilename_3", test_suggestedFilename_3), ("test_copywithzone", test_copyWithZone), @@ -23,6 +26,8 @@ class TestURLResponse : XCTestCase { ] } + let testURL = URL(string: "test")! + func test_URL() { let url = URL(string: "a/test/path")! let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) @@ -31,36 +36,46 @@ class TestURLResponse : XCTestCase { func test_MIMEType_1() { let mimetype = "text/plain" - let res = URLResponse(url: URL(string: "test")!, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + let res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the passed in mimetype") } func test_MIMEType_2() { let mimetype = "APPlication/wordperFECT" - let res = URLResponse(url: URL(string: "test")!, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + let res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the other mimetype") } - - func test_ExpectedContentLength() { - let zeroContentLength = 0 - let positiveContentLength = 100 - let url = URL(string: "test")! - let res1 = URLResponse(url: url, mimeType: "text/plain", expectedContentLength: zeroContentLength, textEncodingName: nil) - XCTAssertEqual(res1.expectedContentLength, Int64(zeroContentLength), "should be Int65 of the zero length") - let res2 = URLResponse(url: url, mimeType: "text/plain", expectedContentLength: positiveContentLength, textEncodingName: nil) - XCTAssertEqual(res2.expectedContentLength, Int64(positiveContentLength), "should be Int64 of the positive content length") + + func test_MIMEType_notAvailable() { + let mimetype: String? = nil + let res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + XCTAssertEqual(res.mimeType, mimetype, "should be the other mimetype") + } + + func test_ExpectedContentLength_positive() { + let contentLength = 100 + let res1 = URLResponse(url: testURL, mimeType: "text/plain", expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res1.expectedContentLength, Int64(contentLength), "should be positive Int64 content length") } - func test_TextEncodingName() { + func test_ExpectedContentLength_negative() { + let contentLength = -1 + let res2 = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res2.expectedContentLength, Int64(contentLength), "should be invalid (-1) Int64 content length") + } + + func test_TextEncodingName_positive() { let encoding = "utf8" - let url = URL(string: "test")! - let res1 = URLResponse(url: url, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) + let res1 = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) XCTAssertEqual(res1.textEncodingName, encoding, "should be the utf8 encoding") - let res2 = URLResponse(url: url, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) + } + + func test_TextEncodingName_negative() { + let res2 = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) XCTAssertNil(res2.textEncodingName) } - func test_suggestedFilename() { + func test_suggestedFilename_1() { let url = URL(string: "a/test/name.extension")! let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.suggestedFilename, "name.extension") @@ -77,6 +92,7 @@ class TestURLResponse : XCTestCase { let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.suggestedFilename, "Unknown") } + func test_copyWithZone() { let url = URL(string: "a/test/path")! let res = URLResponse(url: url, mimeType: "txt", expectedContentLength: 0, textEncodingName: nil) From 97cc84c5dff8737ad2d1204f24bcffa312731560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A6=8ATomoya=20Hirano?= Date: Sat, 23 Mar 2019 11:28:01 +0900 Subject: [PATCH 36/81] Implement NSArray's customMirror --- Foundation/NSArray.swift | 4 +++- TestFoundation/TestNSArray.swift | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Foundation/NSArray.swift b/Foundation/NSArray.swift index 56eefb83942..d5b004eafa5 100644 --- a/Foundation/NSArray.swift +++ b/Foundation/NSArray.swift @@ -952,7 +952,9 @@ extension NSArray : ExpressibleByArrayLiteral { } extension NSArray : CustomReflectable { - public var customMirror: Mirror { NSUnimplemented() } + public var customMirror: Mirror { + return Mirror(reflecting: Array(self)) + } } extension NSArray : _StructTypeBridgeable { diff --git a/TestFoundation/TestNSArray.swift b/TestFoundation/TestNSArray.swift index a02975eb743..62dd4604768 100644 --- a/TestFoundation/TestNSArray.swift +++ b/TestFoundation/TestNSArray.swift @@ -45,6 +45,7 @@ class TestNSArray : XCTestCase { ("test_replaceObjectsAtIndexesWithObjects", test_replaceObjectsAtIndexesWithObjects), ("test_pathsMatchingExtensions", test_pathsMatchingExtensions), ("test_arrayUsedAsCFArrayInvokesArrayMethods", test_arrayUsedAsCFArrayInvokesArrayMethods), + ("test_customMirror", test_customMirror), ] } @@ -796,6 +797,22 @@ class TestNSArray : XCTestCase { let match5 = paths.pathsMatchingExtensions(["..txt"]) XCTAssertEqual(match5, []) } + + func test_customMirror() { + let inputArray = ["this", "is", "a", "test", "of", "copy", "with", "strings"] + let array = NSArray(array: inputArray) + let arrayMirror = array.customMirror + dump(arrayMirror.children) + + XCTAssertEqual(array[0] as! String, arrayMirror.descendant(0) as! String) + XCTAssertEqual(array[1] as! String, arrayMirror.descendant(1) as! String) + XCTAssertEqual(array[2] as! String, arrayMirror.descendant(2) as! String) + XCTAssertEqual(array[3] as! String, arrayMirror.descendant(3) as! String) + XCTAssertEqual(array[4] as! String, arrayMirror.descendant(4) as! String) + XCTAssertEqual(array[5] as! String, arrayMirror.descendant(5) as! String) + XCTAssertEqual(array[6] as! String, arrayMirror.descendant(6) as! String) + XCTAssertEqual(array[7] as! String, arrayMirror.descendant(7) as! String) + } func test_arrayUsedAsCFArrayInvokesArrayMethods() { let number = 789 as NSNumber From 6bcde8229edbd90e7e9950f5fee35c1c9ec1597c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A6=8ATomoya=20Hirano?= Date: Sat, 23 Mar 2019 11:35:40 +0900 Subject: [PATCH 37/81] fix sample array for testing --- TestFoundation/TestNSArray.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TestFoundation/TestNSArray.swift b/TestFoundation/TestNSArray.swift index 62dd4604768..3fde328805a 100644 --- a/TestFoundation/TestNSArray.swift +++ b/TestFoundation/TestNSArray.swift @@ -799,7 +799,7 @@ class TestNSArray : XCTestCase { } func test_customMirror() { - let inputArray = ["this", "is", "a", "test", "of", "copy", "with", "strings"] + let inputArray = ["this", "is", "a", "test", "of", "custom", "mirror"] let array = NSArray(array: inputArray) let arrayMirror = array.customMirror dump(arrayMirror.children) @@ -811,7 +811,6 @@ class TestNSArray : XCTestCase { XCTAssertEqual(array[4] as! String, arrayMirror.descendant(4) as! String) XCTAssertEqual(array[5] as! String, arrayMirror.descendant(5) as! String) XCTAssertEqual(array[6] as! String, arrayMirror.descendant(6) as! String) - XCTAssertEqual(array[7] as! String, arrayMirror.descendant(7) as! String) } func test_arrayUsedAsCFArrayInvokesArrayMethods() { From 0dd93825d0cae975971468841f789ab4e892106c Mon Sep 17 00:00:00 2001 From: kohhashi Date: Sat, 23 Mar 2019 15:19:49 +0900 Subject: [PATCH 38/81] Implement Host.isEqual and Test --- Foundation/Host.swift | 2 +- TestFoundation/TestHost.swift | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Foundation/Host.swift b/Foundation/Host.swift index f7e68cbdc31..9dfd02ba5c9 100644 --- a/Foundation/Host.swift +++ b/Foundation/Host.swift @@ -75,7 +75,7 @@ open class Host: NSObject { } open func isEqual(to aHost: Host) -> Bool { - return false + return aHost._addresses == _addresses && aHost._info == _info && aHost._names == _names && aHost._resolved == _resolved && aHost._type == _type } internal func _resolveCurrent() { diff --git a/TestFoundation/TestHost.swift b/TestFoundation/TestHost.swift index 6cdb0903635..58dc1a146fa 100644 --- a/TestFoundation/TestHost.swift +++ b/TestFoundation/TestHost.swift @@ -12,6 +12,8 @@ class TestHost: XCTestCase { static var allTests: [(String, (TestHost) -> () throws -> Void)] { return [ ("test_addressesDoNotGrow", test_addressesDoNotGrow), + ("test_isEqual_positive", test_isEqual_positive), + ("test_isEqual_negative", test_isEqual_negative) ] } @@ -32,5 +34,17 @@ class TestHost: XCTestCase { let swiftAddressesSecond = swift.addresses XCTAssertEqual(swiftAddressesSecond.count, swiftAddressesFirst.count) } + + func test_isEqual_positive() { + let host0 = Host(address: "8.8.8.8") + let host1 = Host(address: "8.8.8.8") + XCTAssertTrue(host0.isEqual(to: host1)) + } + + func test_isEqual_negative() { + let host0 = Host(address: "8.8.8.8") + let host1 = Host(address: "8.8.8.9") + XCTAssertFalse(host0.isEqual(to: host1)) + } } From c14ab252bfcb58d20248b383b6f77cb0041cfa65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A6=8ATomoya=20Hirano?= Date: Sat, 23 Mar 2019 15:50:41 +0900 Subject: [PATCH 39/81] using _storage --- Foundation/NSArray.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/NSArray.swift b/Foundation/NSArray.swift index d5b004eafa5..e2d4f4d3bc8 100644 --- a/Foundation/NSArray.swift +++ b/Foundation/NSArray.swift @@ -953,7 +953,7 @@ extension NSArray : ExpressibleByArrayLiteral { extension NSArray : CustomReflectable { public var customMirror: Mirror { - return Mirror(reflecting: Array(self)) + return Mirror(reflecting: _storage) } } From 678a14d4a69d2645daf055c904dfea1ecca64528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A6=8ATomoya=20Hirano?= Date: Sat, 23 Mar 2019 15:51:22 +0900 Subject: [PATCH 40/81] remove debug print --- TestFoundation/TestNSArray.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/TestFoundation/TestNSArray.swift b/TestFoundation/TestNSArray.swift index 3fde328805a..738df182643 100644 --- a/TestFoundation/TestNSArray.swift +++ b/TestFoundation/TestNSArray.swift @@ -802,7 +802,6 @@ class TestNSArray : XCTestCase { let inputArray = ["this", "is", "a", "test", "of", "custom", "mirror"] let array = NSArray(array: inputArray) let arrayMirror = array.customMirror - dump(arrayMirror.children) XCTAssertEqual(array[0] as! String, arrayMirror.descendant(0) as! String) XCTAssertEqual(array[1] as! String, arrayMirror.descendant(1) as! String) From 0856854bfcd061d940a5be43394b5cfe0eed13a7 Mon Sep 17 00:00:00 2001 From: Yuki Yoshioka Date: Sat, 23 Mar 2019 14:58:55 +0900 Subject: [PATCH 41/81] [Foundation] Implement `NSSet#description` Because `NSSet#description` was not implemented. --- Docs/Status.md | 2 +- Foundation/NSSet.swift | 43 +++++++++++++++++++++++++++++++--- TestFoundation/TestNSSet.swift | 23 +++++++++++++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Docs/Status.md b/Docs/Status.md index 5a18734cff3..09520852e04 100644 --- a/Docs/Status.md +++ b/Docs/Status.md @@ -166,7 +166,7 @@ There is no _Complete_ status for test coverage because there are always additio | `NSDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers, `descriptionInStringsFileFormat`, `sharedKeySet(forKeys:)`, and reading/writing to files/URLs remain unimplemented | | `NSMutableDictionary` | Mostly Complete | Incomplete | `descriptionInStringsFileFormat`, `sharedKeySet(forKeys:)`, and reading/writing to files/URLs remain unimplemented | | `NSCFDictionary` | N/A | N/A | For internal use only | - | `NSSet` | Mostly Complete | Incomplete | `description(withLocale:)` and `customMirror` remain unimplemented | + | `NSSet` | Mostly Complete | Incomplete | `customMirror` remain unimplemented | | `NSMutableSet` | Mostly Complete | Incomplete | `init?(coder:)` remains unimplemented | | `NSCountedSet` | Mostly Complete | Incomplete | `init?(coder:)` remains unimplemented | | `NSCFSet` | N/A | N/A | For internal use only | diff --git a/Foundation/NSSet.swift b/Foundation/NSSet.swift index a64f62d7038..0c51e1799b2 100644 --- a/Foundation/NSSet.swift +++ b/Foundation/NSSet.swift @@ -107,9 +107,46 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi return true } - open func description(withLocale locale: Locale?) -> String { - // NSUnimplemented() - return description + override open var description: String { + return description(withLocale: nil) + } + + open func description(withLocale locale: Locale?) -> String { + return description(withLocale: locale, indent: 0) + } + + private func description(withLocale locale: Locale?, indent level: Int) -> String { + var descriptions = [String]() + + for obj in self._storage { + if let string = obj as? String { + descriptions.append(string) + } else if let array = obj as? [Any] { + descriptions.append(NSArray(array: array).description(withLocale: locale, indent: level + 1)) + } else if let dict = obj as? [AnyHashable : Any] { + descriptions.append(dict._bridgeToObjectiveC().description(withLocale: locale, indent: level + 1)) + } else if let set = obj as? Set { + descriptions.append(set._bridgeToObjectiveC().description(withLocale: locale, indent: level + 1)) + } else { + descriptions.append("\(obj)") + } + } + var indent = "" + for _ in 0..)) } + + func test_description() { + let array = NSArray(array: ["array_element1", "array_element2"]) + let dictionary = NSDictionary(dictionary: ["key1": "value1", "key2": "value2"]) + let innerSet = NSSet(array: [4444, 5555]) + let set: NSSet = NSSet(array: [array, dictionary, innerSet, 1111, 2222, 3333]) + + let description = set.description + + XCTAssertTrue(String(description.prefix(2)) == "{(") + XCTAssertTrue(String(description.suffix(2)) == ")}") + XCTAssertTrue(description.contains(" (\n array_element1,\n array_element2\n )")) + XCTAssertTrue(description.contains(" key1 = value1")) + XCTAssertTrue(description.contains(" key2 = value2")) + XCTAssertTrue(description.contains(" 4444")) + XCTAssertTrue(description.contains(" 5555")) + XCTAssertTrue(description.contains(" 1111")) + XCTAssertTrue(description.contains(" 2222")) + XCTAssertTrue(description.contains(" 3333")) + } } From 231c8be1d55e688ee6b893df2b2c339ee7aaa7c9 Mon Sep 17 00:00:00 2001 From: Yuki Yoshioka Date: Sat, 23 Mar 2019 19:33:30 +0900 Subject: [PATCH 42/81] Remove variable named `cnt` For following review https://github.com/apple/swift-corelibs-foundation/pull/2037#discussion_r268389699 --- Foundation/NSSet.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Foundation/NSSet.swift b/Foundation/NSSet.swift index 0c51e1799b2..86368c2a634 100644 --- a/Foundation/NSSet.swift +++ b/Foundation/NSSet.swift @@ -136,10 +136,9 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi indent += " " } var result = indent + "{(\n" - let cnt = count - for idx in 0.. Date: Sat, 23 Mar 2019 22:21:00 +0900 Subject: [PATCH 43/81] Fix Host.isEqual --- Foundation/Host.swift | 3 ++- TestFoundation/TestHost.swift | 12 ++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Foundation/Host.swift b/Foundation/Host.swift index 9dfd02ba5c9..9a66ab3f79e 100644 --- a/Foundation/Host.swift +++ b/Foundation/Host.swift @@ -75,7 +75,8 @@ open class Host: NSObject { } open func isEqual(to aHost: Host) -> Bool { - return aHost._addresses == _addresses && aHost._info == _info && aHost._names == _names && aHost._resolved == _resolved && aHost._type == _type + if self === aHost { return true } + return _addresses.firstIndex { aHost._addresses.contains($0) } != nil } internal func _resolveCurrent() { diff --git a/TestFoundation/TestHost.swift b/TestFoundation/TestHost.swift index 58dc1a146fa..3e831352b79 100644 --- a/TestFoundation/TestHost.swift +++ b/TestFoundation/TestHost.swift @@ -12,8 +12,7 @@ class TestHost: XCTestCase { static var allTests: [(String, (TestHost) -> () throws -> Void)] { return [ ("test_addressesDoNotGrow", test_addressesDoNotGrow), - ("test_isEqual_positive", test_isEqual_positive), - ("test_isEqual_negative", test_isEqual_negative) + ("test_isEqual", test_isEqual) ] } @@ -35,16 +34,13 @@ class TestHost: XCTestCase { XCTAssertEqual(swiftAddressesSecond.count, swiftAddressesFirst.count) } - func test_isEqual_positive() { + func test_isEqual() { let host0 = Host(address: "8.8.8.8") let host1 = Host(address: "8.8.8.8") XCTAssertTrue(host0.isEqual(to: host1)) - } - func test_isEqual_negative() { - let host0 = Host(address: "8.8.8.8") - let host1 = Host(address: "8.8.8.9") - XCTAssertFalse(host0.isEqual(to: host1)) + let host2 = Host(address: "8.8.8.9") + XCTAssertFalse(host0.isEqual(to: host2)) } } From 21549ff34a9cc0137ee48d842bcc28cbb675deb5 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sat, 23 Mar 2019 17:08:28 +0000 Subject: [PATCH 44/81] _BodyFileSource: Fix use after free in init() - init(fileURL:workQueue:dataAvailableHandler:) was using the closure argument for the buffer pointer outside of the closure, after .deallocate() was called. --- Foundation/URLSession/BodySource.swift | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Foundation/URLSession/BodySource.swift b/Foundation/URLSession/BodySource.swift index f5d3a0a7cd4..577db95223e 100644 --- a/Foundation/URLSession/BodySource.swift +++ b/Foundation/URLSession/BodySource.swift @@ -135,11 +135,12 @@ extension _BodyDataSource : _BodySource { /// have to be thread safe. internal final class _BodyFileSource { fileprivate let fileURL: URL - fileprivate let channel: DispatchIO - fileprivate let workQueue: DispatchQueue + fileprivate let channel: DispatchIO + fileprivate let workQueue: DispatchQueue fileprivate let dataAvailableHandler: () -> Void fileprivate var hasActiveReadHandler = false fileprivate var availableChunk: _Chunk = .empty + /// Create a new data source backed by a file. /// /// - Parameter fileURL: the file to read from @@ -156,13 +157,13 @@ internal final class _BodyFileSource { self.fileURL = fileURL self.workQueue = workQueue self.dataAvailableHandler = dataAvailableHandler - var fileSystemRepresentation: UnsafePointer! = nil - fileURL.withUnsafeFileSystemRepresentation { - fileSystemRepresentation = $0 - } - guard let channel = DispatchIO(type: .stream, path: fileSystemRepresentation, - oflag: O_RDONLY, mode: 0, queue: workQueue, - cleanupHandler: {_ in }) else { + + guard let channel = fileURL.withUnsafeFileSystemRepresentation({ + // DisptachIO (dispatch_io_create_with_path) makes a copy of the path + DispatchIO(type: .stream, path: $0!, + oflag: O_RDONLY, mode: 0, queue: workQueue, + cleanupHandler: {_ in }) + }) else { fatalError("Cant create DispatchIO channel") } self.channel = channel From d293de171f5698b80875097bf33a4365679a6255 Mon Sep 17 00:00:00 2001 From: Yuki Yoshioka Date: Sun, 24 Mar 2019 02:11:38 +0900 Subject: [PATCH 45/81] Fix build error of NSSet#description's test on Linux --- TestFoundation/TestNSSet.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TestFoundation/TestNSSet.swift b/TestFoundation/TestNSSet.swift index 112d61f5e96..eb04ded579a 100644 --- a/TestFoundation/TestNSSet.swift +++ b/TestFoundation/TestNSSet.swift @@ -245,10 +245,10 @@ class TestNSSet : XCTestCase { let innerSet = NSSet(array: [4444, 5555]) let set: NSSet = NSSet(array: [array, dictionary, innerSet, 1111, 2222, 3333]) - let description = set.description - - XCTAssertTrue(String(description.prefix(2)) == "{(") - XCTAssertTrue(String(description.suffix(2)) == ")}") + let description = NSString(string: set.description) + + XCTAssertTrue(String(description.substring(to: 2)) == "{(") + XCTAssertTrue(String(description.substring(from: description.length - 2)) == ")}") XCTAssertTrue(description.contains(" (\n array_element1,\n array_element2\n )")) XCTAssertTrue(description.contains(" key1 = value1")) XCTAssertTrue(description.contains(" key2 = value2")) From 1ab846bddf14b95ebe655e2a6c93bb17cf8e8826 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Sat, 23 Mar 2019 19:02:51 +0000 Subject: [PATCH 46/81] FileHandle: Increase testing of .readabilityHandler - TestProcess: Replace use of DispatchSource.makeReadSource() with FileHandle.readabilityHandler to increase test coverage. --- TestFoundation/TestProcess.swift | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index 1ce45ff1a00..c5233ccd5f4 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -517,8 +517,7 @@ class _SignalHelperRunner { let semaphore = DispatchSemaphore(value: 0) private let outputPipe = Pipe() - private let sQueue = DispatchQueue(label: "io queue") - private let source: DispatchSourceRead + private let sQueue = DispatchQueue(label: "signal queue") private var gotReady = false private var bytesIn = Data() @@ -534,17 +533,16 @@ class _SignalHelperRunner { process.arguments = ["--signal-test"] process.standardOutput = outputPipe.fileHandleForWriting - source = DispatchSource.makeReadSource(fileDescriptor: outputPipe.fileHandleForReading.fileDescriptor, queue: sQueue) - let workItem = DispatchWorkItem(block: { [weak self] in + outputPipe.fileHandleForReading.readabilityHandler = { [weak self] fh in if let strongSelf = self { let newLine = UInt8(ascii: "\n") - strongSelf.bytesIn.append(strongSelf.outputPipe.fileHandleForReading.availableData) + strongSelf.bytesIn.append(fh.availableData) if strongSelf.bytesIn.isEmpty { return } // Split the incoming data into lines. - while let index = strongSelf.bytesIn.index(of: newLine) { + while let index = strongSelf.bytesIn.firstIndex(of: newLine) { if index >= strongSelf.bytesIn.startIndex { // dont include the newline when converting to string let line = String(data: strongSelf.bytesIn[strongSelf.bytesIn.startIndex.. Date: Sun, 24 Mar 2019 09:34:38 +0900 Subject: [PATCH 47/81] Fix build error of NSSet#description's test on Linux Remove `String` using --- TestFoundation/TestNSSet.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TestFoundation/TestNSSet.swift b/TestFoundation/TestNSSet.swift index eb04ded579a..47e64d3980e 100644 --- a/TestFoundation/TestNSSet.swift +++ b/TestFoundation/TestNSSet.swift @@ -247,8 +247,8 @@ class TestNSSet : XCTestCase { let description = NSString(string: set.description) - XCTAssertTrue(String(description.substring(to: 2)) == "{(") - XCTAssertTrue(String(description.substring(from: description.length - 2)) == ")}") + XCTAssertTrue(description.substring(to: 2).isEqual(to: "{(")) + XCTAssertTrue(description.substring(from: description.length - 2).isEqual(to: ")}")) XCTAssertTrue(description.contains(" (\n array_element1,\n array_element2\n )")) XCTAssertTrue(description.contains(" key1 = value1")) XCTAssertTrue(description.contains(" key2 = value2")) From 49f50aa2d7686219484661d8601472fc3974fcc7 Mon Sep 17 00:00:00 2001 From: kirei Date: Sun, 24 Mar 2019 13:38:43 +0900 Subject: [PATCH 48/81] Add URLResponse.test_ExpectedContentLength_zero() back in --- TestFoundation/TestURLResponse.swift | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/TestFoundation/TestURLResponse.swift b/TestFoundation/TestURLResponse.swift index dbe6c852940..99f16e17420 100644 --- a/TestFoundation/TestURLResponse.swift +++ b/TestFoundation/TestURLResponse.swift @@ -15,6 +15,7 @@ class TestURLResponse : XCTestCase { ("test_MIMEType_2", test_MIMEType_2), ("test_MIMEType_notAvailable", test_MIMEType_notAvailable), ("test_ExpectedContentLength_positive", test_ExpectedContentLength_positive), + ("test_ExpectedContentLength_zero", test_ExpectedContentLength_zero), ("test_ExpectedContentLength_negative", test_ExpectedContentLength_negative), ("test_TextEncodingName_positive", test_TextEncodingName_positive), ("test_TextEncodingName_negative", test_TextEncodingName_negative), @@ -54,14 +55,20 @@ class TestURLResponse : XCTestCase { func test_ExpectedContentLength_positive() { let contentLength = 100 - let res1 = URLResponse(url: testURL, mimeType: "text/plain", expectedContentLength: contentLength, textEncodingName: nil) - XCTAssertEqual(res1.expectedContentLength, Int64(contentLength), "should be positive Int64 content length") + let res = URLResponse(url: testURL, mimeType: "text/plain", expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be positive Int64 content length") + } + + func test_ExpectedContentLength_zero() { + let contentLength = 0 + let res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be zero Int64 content length") } func test_ExpectedContentLength_negative() { let contentLength = -1 - let res2 = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) - XCTAssertEqual(res2.expectedContentLength, Int64(contentLength), "should be invalid (-1) Int64 content length") + let res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be invalid (-1) Int64 content length") } func test_TextEncodingName_positive() { From 587d44f9b456d88af889a7f816df94dee53e5cb7 Mon Sep 17 00:00:00 2001 From: Yury Vovk Date: Sun, 24 Mar 2019 20:25:15 +0300 Subject: [PATCH 49/81] NSPathUtilities: fixed 'guard body must not fall through' error --- Foundation/NSPathUtilities.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/NSPathUtilities.swift b/Foundation/NSPathUtilities.swift index 067b0f8177d..6f7a513507e 100755 --- a/Foundation/NSPathUtilities.swift +++ b/Foundation/NSPathUtilities.swift @@ -14,7 +14,7 @@ public func NSTemporaryDirectory() -> String { let cchLength: DWORD = GetTempPathW(0, nil) var wszPath: [WCHAR] = Array(repeating: 0, count: Int(cchLength + 1)) guard GetTempPathW(DWORD(wszPath.count), &wszPath) <= cchLength else { - precondition(false, "GetTempPathW mutation race") + preconditionFailure("GetTempPathW mutation race") } return String(decodingCString: wszPath, as: UTF16.self) #else From 8e2f23264a61f4c1cf2fa63aa92b746d93cb6e26 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Mon, 25 Mar 2019 10:13:10 +0000 Subject: [PATCH 50/81] TestProcess: Improve reading from a pipe with a sub-process - TestProcess.runTask() uses a pipe to read the stdout of a subprocess during tests however sometimes the data read from the pipe is truncated causing issues with the testing of the response. - Use .readabilityHandler to read the data whilst the sub-process is running and then drain the pipe after the process has terminated. --- TestFoundation/TestProcess.swift | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift index c5233ccd5f4..0850ffafc4b 100644 --- a/TestFoundation/TestProcess.swift +++ b/TestFoundation/TestProcess.swift @@ -609,19 +609,39 @@ internal func runTask(_ arguments: [String], environment: [String: String]? = ni let stderrPipe = Pipe() process.standardOutput = stdoutPipe process.standardError = stderrPipe + + var stdoutData = Data() + stdoutPipe.fileHandleForReading.readabilityHandler = { fh in + stdoutData.append(fh.availableData) + } + + var stderrData = Data() + stderrPipe.fileHandleForReading.readabilityHandler = { fh in + stderrData.append(fh.availableData) + } + try process.run() process.waitUntilExit() + stdoutPipe.fileHandleForReading.readabilityHandler = nil + stderrPipe.fileHandleForReading.readabilityHandler = nil + + // Drain any data remaining in the pipes + if let d = try stdoutPipe.fileHandleForReading.readToEnd() { + stdoutData.append(d) + } + + if let d = try stderrPipe.fileHandleForReading.readToEnd() { + stderrData.append(d) + } guard process.terminationStatus == 0 else { throw Error.TerminationStatus(process.terminationStatus) } - let stdoutData = stdoutPipe.fileHandleForReading.availableData guard let stdout = String(data: stdoutData, encoding: .utf8) else { throw Error.UnicodeDecodingError(stdoutData) } - let stderrData = stderrPipe.fileHandleForReading.availableData guard let stderr = String(data: stderrData, encoding: .utf8) else { throw Error.UnicodeDecodingError(stderrData) } From 1a30b4d259b0de68d24feb2994816b250a3a0805 Mon Sep 17 00:00:00 2001 From: Kazuki Ohara Date: Tue, 26 Mar 2019 00:21:48 +0900 Subject: [PATCH 51/81] Fix plutil installation failure on Windows plutil is not installed on Windows becuase ".exe" extension is missing in CMakeLists.txt. --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97fb50ccdee..f0582d7e809 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,7 +584,12 @@ install(FILES CoreFoundation/Base.subproj/module.map DESTINATION lib/swift/CoreFoundation) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + set(plutil_executable plutil.exe) +else() + set(plutil_executable plutil) +endif() install(PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/plutil + ${CMAKE_CURRENT_BINARY_DIR}/${plutil_executable} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) From 7b12cb19c6f94bf7cf6da1aa91346768f7e406b2 Mon Sep 17 00:00:00 2001 From: saiHemak Date: Mon, 25 Mar 2019 21:37:25 +0530 Subject: [PATCH 52/81] [SR-9887] HTTPCookie on Linux does not accept Substring values (#2022) --- Foundation/HTTPCookie.swift | 13 +++++++++---- TestFoundation/TestHTTPCookie.swift | 10 ++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Foundation/HTTPCookie.swift b/Foundation/HTTPCookie.swift index 0e9beff8c1e..4f7547e0d89 100644 --- a/Foundation/HTTPCookie.swift +++ b/Foundation/HTTPCookie.swift @@ -255,10 +255,16 @@ open class HTTPCookie : NSObject { /// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative /// - Note: Since this API is under consideration it may be either removed or revised in the near future public init?(properties: [HTTPCookiePropertyKey : Any]) { + func stringValue(_ strVal: Any?) -> String? { + if let subStr = strVal as? Substring { + return String(subStr) + } + return strVal as? String + } guard - let path = properties[.path] as? String, - let name = properties[.name] as? String, - let value = properties[.value] as? String + let path = stringValue(properties[.path]), + let name = stringValue(properties[.name]), + let value = stringValue(properties[.value]) else { return nil } @@ -659,4 +665,3 @@ fileprivate extension String { return self.replacingOccurrences(of: "&comma", with: ",") } } - diff --git a/TestFoundation/TestHTTPCookie.swift b/TestFoundation/TestHTTPCookie.swift index aee767f6fdf..035e8fe679d 100644 --- a/TestFoundation/TestHTTPCookie.swift +++ b/TestFoundation/TestHTTPCookie.swift @@ -19,6 +19,7 @@ class TestHTTPCookie: XCTestCase { ("test_cookiesWithResponseHeaderNoDomain", test_cookiesWithResponseHeaderNoDomain), ("test_cookiesWithResponseHeaderNoPathNoDomain", test_cookiesWithResponseHeaderNoPathNoDomain), ("test_cookieExpiresDateFormats", test_cookieExpiresDateFormats), + ("test_httpCookieWithSubstring", test_httpCookieWithSubstring), ] } @@ -191,4 +192,13 @@ class TestHTTPCookie: XCTestCase { XCTAssertEqual(cookie.path, "/") } } + + func test_httpCookieWithSubstring() { + let cookie = HTTPCookie(properties: [.domain: ".", .path: "/", .name: "davesy".dropLast(), .value: "Jonesy".dropLast()]) + if let cookie = cookie { + XCTAssertEqual(cookie.name, "daves") + } else { + XCTFail("Unable to create cookie with substring") + } + } } From a257b03cb6c9140fb663934ba78b02ad8ddfd5bb Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Fri, 22 Mar 2019 14:52:41 -0700 Subject: [PATCH 53/81] Implement Missing Command Line Quoting on Windows --- Foundation/Process.swift | 65 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/Foundation/Process.swift b/Foundation/Process.swift index 8b8aa868b4d..da8906ef514 100644 --- a/Foundation/Process.swift +++ b/Foundation/Process.swift @@ -102,6 +102,67 @@ private func processIsEqual(_ a : UnsafeRawPointer?, _ b : UnsafeRawPointer?) -> return true } +#if os(Windows) + +private func quoteWindowsCommandLine(_ commandLine: [String]) -> String { + func quoteWindowsCommandArg(arg: String) -> String { + // Windows escaping, adapted from Daniel Colascione's "Everyone quotes + // command line arguments the wrong way" - Microsoft Developer Blog + if !arg.contains(where: {" \t\n\"".contains($0)}) { + return arg + } + + // To escape the command line, we surround the argument with quotes. However + // the complication comes due to how the Windows command line parser treats + // backslashes (\) and quotes (") + // + // - \ is normally treated as a literal backslash + // - e.g. foo\bar\baz => foo\bar\baz + // - However, the sequence \" is treated as a literal " + // - e.g. foo\"bar => foo"bar + // + // But then what if we are given a path that ends with a \? Surrounding + // foo\bar\ with " would be "foo\bar\" which would be an unterminated string + + // since it ends on a literal quote. To allow this case the parser treats: + // + // - \\" as \ followed by the " metachar + // - \\\" as \ followed by a literal " + // - In general: + // - 2n \ followed by " => n \ followed by the " metachar + // - 2n+1 \ followed by " => n \ followed by a literal " + var quoted = "\"" + var unquoted = arg.unicodeScalars + + while !unquoted.isEmpty { + guard let firstNonBackslash = unquoted.firstIndex(where: { $0 != "\\" }) else { + // String ends with a backslash e.g. foo\bar\, escape all the backslashes + // then add the metachar " below + let backslashCount = unquoted.count + quoted.append(String(repeating: "\\", count: backslashCount * 2)) + break + } + let backslashCount = unquoted.distance(from: unquoted.startIndex, to: firstNonBackslash) + if (unquoted[firstNonBackslash] == "\"") { + // This is a string of \ followed by a " e.g. foo\"bar. Escape the + // backslashes and the quote + quoted.append(String(repeating: "\\", count: backslashCount * 2 + 1)) + quoted.append(String(unquoted[firstNonBackslash])) + } else { + // These are just literal backslashes + quoted.append(String(repeating: "\\", count: backslashCount)) + quoted.append(String(unquoted[firstNonBackslash])) + } + // Drop the backslashes and the following character + unquoted.removeFirst(backslashCount + 1) + } + quoted.append("\"") + return quoted + } + return commandLine.map(quoteWindowsCommandArg).joined(separator: " ") +} +#endif + open class Process: NSObject { private static func setup() { struct Once { @@ -318,8 +379,6 @@ open class Process: NSObject { } #if os(Windows) - // TODO(compnerd) quote the commandline correctly - // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ var command: [String] = [launchPath] if let arguments = self.arguments { command.append(contentsOf: arguments) @@ -412,7 +471,7 @@ open class Process: NSObject { CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0) CFRunLoopAddSource(managerThreadRunLoop?._cfRunLoop, source, kCFRunLoopDefaultMode) - try command.joined(separator: " ").withCString(encodedAs: UTF16.self) { wszCommandLine in + try quoteWindowsCommandLine(command).withCString(encodedAs: UTF16.self) { wszCommandLine in try currentDirectoryURL.path.withCString(encodedAs: UTF16.self) { wszCurrentDirectory in try szEnvironment.withCString(encodedAs: UTF16.self) { wszEnvironment in if CreateProcessW(nil, UnsafeMutablePointer(mutating: wszCommandLine), From 1bc15d1544f11d5fc94d65a9d29dab8dd3bb39bd Mon Sep 17 00:00:00 2001 From: Kazuki Ohara Date: Tue, 26 Mar 2019 03:00:33 +0900 Subject: [PATCH 54/81] Use ${CMAKE_EXECUTALBE_SUFFIX} for extension of plutil on Windows --- CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0582d7e809..a9d9048bc0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,12 +584,7 @@ install(FILES CoreFoundation/Base.subproj/module.map DESTINATION lib/swift/CoreFoundation) -if(CMAKE_SYSTEM_NAME STREQUAL Windows) - set(plutil_executable plutil.exe) -else() - set(plutil_executable plutil) -endif() install(PROGRAMS - ${CMAKE_CURRENT_BINARY_DIR}/${plutil_executable} + ${CMAKE_CURRENT_BINARY_DIR}/plutil${CMAKE_EXECUTABLE_SUFFIX} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) From e1b3d7b089284887b609f01e08579833b21c9f77 Mon Sep 17 00:00:00 2001 From: Karthik Date: Thu, 21 Mar 2019 11:44:14 -0700 Subject: [PATCH 55/81] [SR-9979] FileHandle class used to implement FileManager._compareFiles --- Foundation/FileManager.swift | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 727a2cdf9fc..a7d6e5608a1 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -1720,39 +1720,31 @@ open class FileManager : NSObject { #if os(Windows) NSUnimplemented() #else - let fd1 = open(file1Rep, O_RDONLY) - guard fd1 >= 0 else { + guard let file1 = FileHandle(forReadingAtPath: String(cString: file1Rep)) else { return false } - defer { close(fd1) } - - let fd2 = open(file2Rep, O_RDONLY) - guard fd2 >= 0 else { + + defer { try? file1.close() } + + guard let file2 = FileHandle(forReadingAtPath: String(cString: file2Rep)) else { return false } - defer { close(fd2) } - - let buffer1 = UnsafeMutablePointer.allocate(capacity: bufSize) - let buffer2 = UnsafeMutablePointer.allocate(capacity: bufSize) - defer { - buffer1.deallocate() - buffer2.deallocate() - } - + + defer { try? file2.close() } + var bytesLeft = size while bytesLeft > 0 { let bytesToRead = Int(min(Int64(bufSize), bytesLeft)) - guard read(fd1, buffer1, bytesToRead) == bytesToRead else { - return false - } - guard read(fd2, buffer2, bytesToRead) == bytesToRead else { - return false - } - guard memcmp(buffer1, buffer2, bytesToRead) == 0 else { + let file1Data = file1.readData(ofLength: bytesToRead) + let file2Data = file2.readData(ofLength: bytesToRead) + + guard file1Data.count == bytesToRead, file2Data.count == bytesToRead, file1Data == file2Data else { return false } + bytesLeft -= Int64(bytesToRead) } + return true #endif } From e327e32a94df4798538ca67133f69dd399ef1102 Mon Sep 17 00:00:00 2001 From: Karthik Date: Fri, 22 Mar 2019 12:12:43 -0700 Subject: [PATCH 56/81] Internal methods introduced to reduce the usage of FileHandle.readData() in order to avoid calling fstat, alloc and dealloc while reading a file * New internal init method introduced to init with fileSystemRepresentation UnsafeMutablePointer * New internal method _readBytes() introduced for continuous iteration of reading a file without allocating and deallocating buffer memory. --- Foundation/FileHandle.swift | 17 +++++++++++++++++ Foundation/FileManager.swift | 28 ++++++++++++++++------------ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 575d10d7573..94dbd4145ee 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -342,6 +342,14 @@ open class FileHandle : NSObject, NSSecureCoding { } #endif } + + internal func _readBytes(into buffer: UnsafeMutablePointer, length: Int) throws -> Int { + let amtRead = _read(_fd, buffer, length) + if amtRead < 0 { + throw _NSErrorWithErrno(errno, reading: true) + } + return amtRead + } internal func _writeBytes(buf: UnsafeRawPointer, length: Int) throws { #if os(Windows) @@ -425,6 +433,15 @@ open class FileHandle : NSObject, NSSecureCoding { } } #endif + + internal init?(fileSystemRepresentation: UnsafePointer, flags: Int32, createMode: Int) { + _fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode)) + _closeOnDealloc = true + super.init() + if _fd < 0 { + return nil + } + } deinit { // .close() tries to wait after operations in flight on the handle queue, if one exists, and then close. It does so by sending .sync { … } work to it. diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index a7d6e5608a1..77893b12cb6 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -1720,25 +1720,29 @@ open class FileManager : NSObject { #if os(Windows) NSUnimplemented() #else - guard let file1 = FileHandle(forReadingAtPath: String(cString: file1Rep)) else { - return false - } - - defer { try? file1.close() } + guard let file1 = FileHandle(fileSystemRepresentation: file1Rep, flags: O_RDONLY, createMode: 0) else { return false } + guard let file2 = FileHandle(fileSystemRepresentation: file2Rep, flags: O_RDONLY, createMode: 0) else { return false } - guard let file2 = FileHandle(forReadingAtPath: String(cString: file2Rep)) else { - return false + var buffer1 = UnsafeMutablePointer.allocate(capacity: bufSize) + var buffer2 = UnsafeMutablePointer.allocate(capacity: bufSize) + defer { + buffer1.deallocate() + buffer2.deallocate() } - defer { try? file2.close() } - var bytesLeft = size while bytesLeft > 0 { let bytesToRead = Int(min(Int64(bufSize), bytesLeft)) - let file1Data = file1.readData(ofLength: bytesToRead) - let file2Data = file2.readData(ofLength: bytesToRead) - guard file1Data.count == bytesToRead, file2Data.count == bytesToRead, file1Data == file2Data else { + guard let file1BytesRead = try? file1._readBytes(into: buffer1, length: bytesToRead), file1BytesRead == bytesToRead else { + return false + } + + guard let file2BytesRead = try? file2._readBytes(into: buffer2, length: bytesToRead), file2BytesRead == bytesToRead else { + return false + } + + guard memcmp(buffer1, buffer2, bytesToRead) == 0 else { return false } From 690d2669a6594f7aa723ab9c39e7fb464064008c Mon Sep 17 00:00:00 2001 From: Karthik Date: Fri, 22 Mar 2019 14:41:59 -0700 Subject: [PATCH 57/81] Windows platform compatibility added for the internal method init?(fileSystemRepresentation:flags:createMode:). --- Foundation/FileHandle.swift | 18 ++++++++++-------- Foundation/FileManager.swift | 4 ---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 94dbd4145ee..79397566308 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -433,14 +433,16 @@ open class FileHandle : NSObject, NSSecureCoding { } } #endif - - internal init?(fileSystemRepresentation: UnsafePointer, flags: Int32, createMode: Int) { - _fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode)) - _closeOnDealloc = true - super.init() - if _fd < 0 { - return nil - } + + internal convenience init?(fileSystemRepresentation: UnsafePointer, flags: Int32, createMode: Int) { + let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode)) + guard fd > 0 else { return nil } + +#if os(Windows) + self.init(handle: HANDLE(bitPattern: _get_osfhandle(fd))!, closeOnDealloc: true) +#else + self.init(fileDescriptor: fd, closeOnDealloc: true) +#endif } deinit { diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 77893b12cb6..aee6f9c4901 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -1737,18 +1737,14 @@ open class FileManager : NSObject { guard let file1BytesRead = try? file1._readBytes(into: buffer1, length: bytesToRead), file1BytesRead == bytesToRead else { return false } - guard let file2BytesRead = try? file2._readBytes(into: buffer2, length: bytesToRead), file2BytesRead == bytesToRead else { return false } - guard memcmp(buffer1, buffer2, bytesToRead) == 0 else { return false } - bytesLeft -= Int64(bytesToRead) } - return true #endif } From 5e020b8ea8d6dfef7a09bcd916e89d58650254d8 Mon Sep 17 00:00:00 2001 From: Karthik Date: Fri, 22 Mar 2019 15:41:05 -0700 Subject: [PATCH 58/81] Windows platform compatibility added for internal method _readBytes(into:lenght) in FileHandle --- Foundation/FileHandle.swift | 13 +++++++++---- Foundation/FileManager.swift | 1 - 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 79397566308..1f406cd1427 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -344,11 +344,20 @@ open class FileHandle : NSObject, NSSecureCoding { } internal func _readBytes(into buffer: UnsafeMutablePointer, length: Int) throws -> Int { +#if os(Windows) + var BytesRead: DWORD = 0 + let BytesToRead: DWORD = DWORD(length) + if ReadFile(_handle, buffer, BytesToRead, &BytesRead, nil) == FALSE { + throw _NSErrorWithWindowsError(GetLastError(), reading: true) + } + return Int(BytesRead) +#else let amtRead = _read(_fd, buffer, length) if amtRead < 0 { throw _NSErrorWithErrno(errno, reading: true) } return amtRead +#endif } internal func _writeBytes(buf: UnsafeRawPointer, length: Int) throws { @@ -438,11 +447,7 @@ open class FileHandle : NSObject, NSSecureCoding { let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode)) guard fd > 0 else { return nil } -#if os(Windows) - self.init(handle: HANDLE(bitPattern: _get_osfhandle(fd))!, closeOnDealloc: true) -#else self.init(fileDescriptor: fd, closeOnDealloc: true) -#endif } deinit { diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index aee6f9c4901..156bee18c21 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -1729,7 +1729,6 @@ open class FileManager : NSObject { buffer1.deallocate() buffer2.deallocate() } - var bytesLeft = size while bytesLeft > 0 { let bytesToRead = Int(min(Int64(bufSize), bytesLeft)) From e33eb72696809f087bb89b95f3ac81df31559876 Mon Sep 17 00:00:00 2001 From: Karthik Date: Mon, 25 Mar 2019 14:54:17 -0700 Subject: [PATCH 59/81] FileSystemRepresentation converted to UTF16 before opening file in Windows platform. --- Foundation/FileHandle.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 1f406cd1427..6db52a923f9 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -444,9 +444,15 @@ open class FileHandle : NSObject, NSSecureCoding { #endif internal convenience init?(fileSystemRepresentation: UnsafePointer, flags: Int32, createMode: Int) { +#if os(Windows) + var fd: Int = -1 + if let path = String(cString: fileSystemRepresentation).cString(using: .utf16) { + fd = _CFOpenFileWithMode(path, flags, mode_t(createMode)) + } +#else let fd = _CFOpenFileWithMode(fileSystemRepresentation, flags, mode_t(createMode)) +#endif guard fd > 0 else { return nil } - self.init(fileDescriptor: fd, closeOnDealloc: true) } From 76e39090504f6838171de38eb32d9ac093e2d296 Mon Sep 17 00:00:00 2001 From: kirei Date: Tue, 26 Mar 2019 23:45:01 +0900 Subject: [PATCH 60/81] Reduce multiple small tests into one --- TestFoundation/TestURLResponse.swift | 71 +++++++++++----------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/TestFoundation/TestURLResponse.swift b/TestFoundation/TestURLResponse.swift index 99f16e17420..69648b2c1d0 100644 --- a/TestFoundation/TestURLResponse.swift +++ b/TestFoundation/TestURLResponse.swift @@ -11,14 +11,9 @@ class TestURLResponse : XCTestCase { static var allTests: [(String, (TestURLResponse) -> () throws -> Void)] { return [ ("test_URL", test_URL), - ("test_MIMEType_1", test_MIMEType_1), - ("test_MIMEType_2", test_MIMEType_2), - ("test_MIMEType_notAvailable", test_MIMEType_notAvailable), - ("test_ExpectedContentLength_positive", test_ExpectedContentLength_positive), - ("test_ExpectedContentLength_zero", test_ExpectedContentLength_zero), - ("test_ExpectedContentLength_negative", test_ExpectedContentLength_negative), - ("test_TextEncodingName_positive", test_TextEncodingName_positive), - ("test_TextEncodingName_negative", test_TextEncodingName_negative), + ("test_MIMEType", test_MIMEType), + ("test_ExpectedContentLength", test_ExpectedContentLength), + ("test_TextEncodingName", test_TextEncodingName), ("test_suggestedFilename_1", test_suggestedFilename_1), ("test_suggestedFilename_2", test_suggestedFilename_2), ("test_suggestedFilename_3", test_suggestedFilename_3), @@ -35,51 +30,41 @@ class TestURLResponse : XCTestCase { XCTAssertEqual(res.url, url, "should be the expected url") } - func test_MIMEType_1() { - let mimetype = "text/plain" - let res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + func test_MIMEType() { + var mimetype: String? = "text/plain" + var res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the passed in mimetype") - } - - func test_MIMEType_2() { - let mimetype = "APPlication/wordperFECT" - let res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + + mimetype = "APPlication/wordperFECT" + res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the other mimetype") - } - - func test_MIMEType_notAvailable() { - let mimetype: String? = nil - let res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) + + mimetype = nil + res = URLResponse(url: testURL, mimeType: mimetype, expectedContentLength: 0, textEncodingName: nil) XCTAssertEqual(res.mimeType, mimetype, "should be the other mimetype") } - func test_ExpectedContentLength_positive() { - let contentLength = 100 - let res = URLResponse(url: testURL, mimeType: "text/plain", expectedContentLength: contentLength, textEncodingName: nil) + func test_ExpectedContentLength() { + var contentLength = 100 + var res = URLResponse(url: testURL, mimeType: "text/plain", expectedContentLength: contentLength, textEncodingName: nil) XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be positive Int64 content length") - } - - func test_ExpectedContentLength_zero() { - let contentLength = 0 - let res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + + contentLength = 0 + res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be zero Int64 content length") - } - - func test_ExpectedContentLength_negative() { - let contentLength = -1 - let res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) + + contentLength = -1 + res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: contentLength, textEncodingName: nil) XCTAssertEqual(res.expectedContentLength, Int64(contentLength), "should be invalid (-1) Int64 content length") } - func test_TextEncodingName_positive() { - let encoding = "utf8" - let res1 = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) - XCTAssertEqual(res1.textEncodingName, encoding, "should be the utf8 encoding") - } - - func test_TextEncodingName_negative() { - let res2 = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) - XCTAssertNil(res2.textEncodingName) + func test_TextEncodingName() { + var encoding = "utf8" + var res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) + XCTAssertEqual(res.textEncodingName, encoding, "should be the utf8 encoding") + + let res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) + XCTAssertNil(res.textEncodingName) } func test_suggestedFilename_1() { From 97893d091522a6b8a70855f611dfba4bec2f1691 Mon Sep 17 00:00:00 2001 From: Kazuki Ohara Date: Mon, 25 Mar 2019 02:10:39 +0900 Subject: [PATCH 61/81] Use RtlGetVersion to get OS version on Windows --- Foundation/ProcessInfo.swift | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Foundation/ProcessInfo.swift b/Foundation/ProcessInfo.swift index a379df9d77f..71e9eaf2a8d 100644 --- a/Foundation/ProcessInfo.swift +++ b/Foundation/ProcessInfo.swift @@ -104,12 +104,26 @@ open class ProcessInfo: NSObject { } versionString = productVersion._swiftObject #elseif os(Windows) - var siVersionInfo: OSVERSIONINFOW = OSVERSIONINFOW() - siVersionInfo.dwOSVersionInfoSize = DWORD(MemoryLayout.size) - if GetVersionExW(&siVersionInfo) == FALSE { - return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) + guard let ntdll = ("ntdll.dll".withCString(encodedAs: UTF16.self) { + LoadLibraryExW($0, nil, DWORD(LOAD_LIBRARY_SEARCH_SYSTEM32)) + }) else { + return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) + } + defer { FreeLibrary(ntdll) } + typealias RTLGetVersionTy = @convention(c) (UnsafeMutablePointer) -> NTSTATUS + guard let pfnRTLGetVersion = unsafeBitCast(GetProcAddress(ntdll, "RtlGetVersion"), to: Optional.self) else { + return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) + } + var osVersionInfo = RTL_OSVERSIONINFOW() + osVersionInfo.dwOSVersionInfoSize = DWORD(MemoryLayout.size) + guard pfnRTLGetVersion(&osVersionInfo) == 0 else { + return OperatingSystemVersion(majorVersion: fallbackMajor, minorVersion: fallbackMinor, patchVersion: fallbackPatch) } - return OperatingSystemVersion(majorVersion: Int(siVersionInfo.dwMajorVersion), minorVersion: Int(siVersionInfo.dwMinorVersion), patchVersion: Int(siVersionInfo.dwBuildNumber)) + return OperatingSystemVersion( + majorVersion: Int(osVersionInfo.dwMajorVersion), + minorVersion: Int(osVersionInfo.dwMinorVersion), + patchVersion: Int(osVersionInfo.dwBuildNumber) + ) #else var utsNameBuffer = utsname() guard uname(&utsNameBuffer) == 0 else { From e4e9bf4c0c1fdde9450207f6a175913626c12106 Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Wed, 27 Mar 2019 14:06:44 -0700 Subject: [PATCH 62/81] Fix FileHandle Compilation on Windows fileDescriptor should be an Int32, not an Int --- Foundation/FileHandle.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index 6db52a923f9..aa60a9b014e 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -445,7 +445,7 @@ open class FileHandle : NSObject, NSSecureCoding { internal convenience init?(fileSystemRepresentation: UnsafePointer, flags: Int32, createMode: Int) { #if os(Windows) - var fd: Int = -1 + var fd: Int32 = -1 if let path = String(cString: fileSystemRepresentation).cString(using: .utf16) { fd = _CFOpenFileWithMode(path, flags, mode_t(createMode)) } From 5cace9dae6802dcc6cb1b4638812fa799d31e5ed Mon Sep 17 00:00:00 2001 From: Matis Schotte Date: Thu, 28 Mar 2019 12:55:05 +0900 Subject: [PATCH 63/81] Fix redundant declaration compile error --- TestFoundation/TestURLResponse.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TestFoundation/TestURLResponse.swift b/TestFoundation/TestURLResponse.swift index 69648b2c1d0..84a81f5c0cd 100644 --- a/TestFoundation/TestURLResponse.swift +++ b/TestFoundation/TestURLResponse.swift @@ -59,11 +59,11 @@ class TestURLResponse : XCTestCase { } func test_TextEncodingName() { - var encoding = "utf8" + let encoding = "utf8" var res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: encoding) XCTAssertEqual(res.textEncodingName, encoding, "should be the utf8 encoding") - let res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) + res = URLResponse(url: testURL, mimeType: nil, expectedContentLength: 0, textEncodingName: nil) XCTAssertNil(res.textEncodingName) } From 71ca3b6850da8ffc6255c0cae8a3debd8e32a867 Mon Sep 17 00:00:00 2001 From: kohhashi Date: Fri, 29 Mar 2019 01:31:09 +0900 Subject: [PATCH 64/81] Fix Host.isEqual --- Foundation/Host.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/Host.swift b/Foundation/Host.swift index 9a66ab3f79e..a39cbbb77be 100644 --- a/Foundation/Host.swift +++ b/Foundation/Host.swift @@ -76,7 +76,7 @@ open class Host: NSObject { open func isEqual(to aHost: Host) -> Bool { if self === aHost { return true } - return _addresses.firstIndex { aHost._addresses.contains($0) } != nil + return addresses.firstIndex { aHost.addresses.contains($0) } != nil } internal func _resolveCurrent() { From 1ea949b800d9f5fbd77602ff45231bc136e71b5b Mon Sep 17 00:00:00 2001 From: kohhashi Date: Fri, 29 Mar 2019 01:31:52 +0900 Subject: [PATCH 65/81] Fix TestHost.test_isEqual --- TestFoundation/TestHost.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TestFoundation/TestHost.swift b/TestFoundation/TestHost.swift index 3e831352b79..0fdc79a6e36 100644 --- a/TestFoundation/TestHost.swift +++ b/TestFoundation/TestHost.swift @@ -41,6 +41,13 @@ class TestHost: XCTestCase { let host2 = Host(address: "8.8.8.9") XCTAssertFalse(host0.isEqual(to: host2)) + + let swift0 = Host(name: "swift.org") + let swift1 = Host(name: "swift.org") + XCTAssertTrue(swift0.isEqual(to: swift1)) + + let google = Host(name: "google.com") + XCTAssertFalse(swift0.isEqual(to: google)) } } From 3f46775e0219e17757675cd521635e15a8a32a66 Mon Sep 17 00:00:00 2001 From: Kazuki Ohara Date: Sat, 30 Mar 2019 22:15:57 +0900 Subject: [PATCH 66/81] Fix build falure by calling unexisting function __CFInitializeWinSock --- Foundation/NSSwiftRuntime.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Foundation/NSSwiftRuntime.swift b/Foundation/NSSwiftRuntime.swift index 718eff326a4..fe3af3a3be5 100644 --- a/Foundation/NSSwiftRuntime.swift +++ b/Foundation/NSSwiftRuntime.swift @@ -151,7 +151,7 @@ internal func __CFSwiftGetBaseClass() -> UnsafeRawPointer { @_cdecl("__CFInitializeSwift") internal func __CFInitializeSwift() { #if os(Windows) - __CFInitializeWinSock() + __CFSocketInitializeWinSock() #endif _CFRuntimeBridgeTypeToClass(CFStringGetTypeID(), unsafeBitCast(_NSCFString.self, to: UnsafeRawPointer.self)) From 099fa7d0e93eed9f262a450a798e6eadd7c69ba4 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Mon, 1 Apr 2019 09:47:10 -0700 Subject: [PATCH 67/81] Disable TestXMLDocument.test_xpath which crashes sometimes in CI Test Case 'TestXMLDocument.test_xpath' started at 2019-03-29 17:27:41.139 0% tests passed, 1 tests failed out of 1 Total Test time (real) = 23.90 sec The following tests FAILED: 1 - TestFoundation (SEGFAULT) https://bugs.swift.org/browse/SR-10098 --- TestFoundation/TestXMLDocument.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TestFoundation/TestXMLDocument.swift b/TestFoundation/TestXMLDocument.swift index 53ed6f7fbad..d7437ed4f6e 100644 --- a/TestFoundation/TestXMLDocument.swift +++ b/TestFoundation/TestXMLDocument.swift @@ -13,7 +13,8 @@ class TestXMLDocument : LoopbackServerTest { return [ ("test_basicCreation", test_basicCreation), ("test_nextPreviousNode", test_nextPreviousNode), - ("test_xpath", test_xpath), + // Disabled because of https://bugs.swift.org/browse/SR-10098 + // ("test_xpath", test_xpath), ("test_elementCreation", test_elementCreation), ("test_elementChildren", test_elementChildren), ("test_stringValue", test_stringValue), From 1ae74c0fdea9df242868b967ee3ac936e54d50ec Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Mon, 1 Apr 2019 19:13:18 +0100 Subject: [PATCH 68/81] SR-10240: Dont try to write an empty Data() if it has a nil baseAddress. --- Foundation/FileHandle.swift | 4 +++- TestFoundation/TestPipe.swift | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Foundation/FileHandle.swift b/Foundation/FileHandle.swift index d07f5a2f0bf..e9df8ece03e 100644 --- a/Foundation/FileHandle.swift +++ b/Foundation/FileHandle.swift @@ -507,7 +507,9 @@ open class FileHandle : NSObject, NSSecureCoding { for region in data.regions { try region.withUnsafeBytes { (bytes) in - try _writeBytes(buf: UnsafeRawPointer(bytes.baseAddress!), length: bytes.count) + if let baseAddress = bytes.baseAddress, bytes.count > 0 { + try _writeBytes(buf: UnsafeRawPointer(baseAddress), length: bytes.count) + } } } } diff --git a/TestFoundation/TestPipe.swift b/TestFoundation/TestPipe.swift index dbdec9bb60c..0c285b581dd 100644 --- a/TestFoundation/TestPipe.swift +++ b/TestFoundation/TestPipe.swift @@ -43,7 +43,10 @@ class TestPipe: XCTestCase { // First write some data into the pipe let stringAsData = try text.data(using: .utf8).unwrapped() try aPipe.fileHandleForWriting.write(contentsOf: stringAsData) - + + // SR-10240 - Check empty Data() can be written without crashing + aPipe.fileHandleForWriting.write(Data()) + // Then read it out again let data = try aPipe.fileHandleForReading.read(upToCount: stringAsData.count).unwrapped() From 0ea3e4d7dce6bf4f56ec2ff1e616c70493655f83 Mon Sep 17 00:00:00 2001 From: Gwen Mittertreiner Date: Fri, 15 Mar 2019 16:59:27 -0700 Subject: [PATCH 69/81] Windows: Implement attributesOfItem with _lstat --- Foundation/FileManager.swift | 42 ++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/Foundation/FileManager.swift b/Foundation/FileManager.swift index 727a2cdf9fc..2dfcdec0b23 100644 --- a/Foundation/FileManager.swift +++ b/Foundation/FileManager.swift @@ -871,14 +871,6 @@ open class FileManager : NSObject { open func attributesOfItem(atPath path: String) throws -> [FileAttributeKey : Any] { var result: [FileAttributeKey:Any] = [:] -#if os(Windows) - let faAttributes: WIN32_FILE_ATTRIBUTE_DATA = try windowsFileAttributes(atPath: path) - - result[.size] = NSNumber(value: (faAttributes.nFileSizeHigh << 32) | faAttributes.nFileSizeLow) - result[.modificationDate] = Date(timeIntervalSinceReferenceDate: TimeInterval(faAttributes.ftLastWriteTime)) - // FIXME(compnerd) what about .posixPermissions, .referenceCount, .systemNumber, .systemFileNumber, .ownerAccountName, .groupOwnerAccountName, .type, .immuatable, .appendOnly, .ownerAccountID, .groupOwnerAccountID -#else - #if os(Linux) let (s, creationDate) = try _statxFile(atPath: path) result[.creationDate] = creationDate @@ -892,29 +884,36 @@ open class FileManager : NSObject { let ti = (TimeInterval(s.st_mtimespec.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtimespec.tv_nsec)) #elseif os(Android) let ti = (TimeInterval(s.st_mtime) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtime_nsec)) +#elseif os(Windows) + let ti = (TimeInterval(s.st_mtime) - kCFAbsoluteTimeIntervalSince1970) #else let ti = (TimeInterval(s.st_mtim.tv_sec) - kCFAbsoluteTimeIntervalSince1970) + (1.0e-9 * TimeInterval(s.st_mtim.tv_nsec)) #endif result[.modificationDate] = Date(timeIntervalSinceReferenceDate: ti) - - result[.posixPermissions] = NSNumber(value: UInt64(s.st_mode & ~S_IFMT)) + + result[.posixPermissions] = NSNumber(value: _filePermissionsMask(mode: UInt32(s.st_mode))) result[.referenceCount] = NSNumber(value: UInt64(s.st_nlink)) result[.systemNumber] = NSNumber(value: UInt64(s.st_dev)) result[.systemFileNumber] = NSNumber(value: UInt64(s.st_ino)) - + +#if os(Windows) + result[.deviceIdentifier] = NSNumber(value: UInt64(s.st_rdev)) + let type = FileAttributeType(attributes: try windowsFileAttributes(atPath: path)) +#else if let pwd = getpwuid(s.st_uid), pwd.pointee.pw_name != nil { let name = String(cString: pwd.pointee.pw_name) result[.ownerAccountName] = name } - + if let grd = getgrgid(s.st_gid), grd.pointee.gr_name != nil { let name = String(cString: grd.pointee.gr_name) result[.groupOwnerAccountName] = name } let type = FileAttributeType(statMode: s.st_mode) +#endif result[.type] = type - + if type == .typeBlockSpecial || type == .typeCharacterSpecial { result[.deviceIdentifier] = NSNumber(value: UInt64(s.st_rdev)) } @@ -929,11 +928,10 @@ open class FileManager : NSObject { #endif result[.ownerAccountID] = NSNumber(value: UInt64(s.st_uid)) result[.groupOwnerAccountID] = NSNumber(value: UInt64(s.st_gid)) -#endif return result } - + /* attributesOfFileSystemForPath:error: returns an NSDictionary of key/value pairs containing the attributes of the filesystem containing the provided path. If this method returns 'nil', an NSError will be returned by reference in the 'error' parameter. This method does not traverse a terminal symlink. This method replaces fileSystemAttributesAtPath:. @@ -1870,15 +1868,21 @@ open class FileManager : NSObject { return statInfo } - internal func _permissionsOfItem(atPath path: String) throws -> Int { - let fileInfo = try _lstatFile(atPath: path) + internal func _filePermissionsMask(mode : UInt32) -> Int { #if os(Windows) - return Int(fileInfo.st_mode & ~UInt16(ucrt.S_IFMT)) + return Int(mode & ~UInt32(ucrt.S_IFMT)) +#elseif canImport(Darwin) + return Int(mode & ~UInt32(S_IFMT)) #else - return Int(fileInfo.st_mode & ~S_IFMT) + return Int(mode & ~S_IFMT) #endif } + internal func _permissionsOfItem(atPath path: String) throws -> Int { + let fileInfo = try _lstatFile(atPath: path) + return _filePermissionsMask(mode: UInt32(fileInfo.st_mode)) + } + #if os(Linux) // statx() is only supported by Linux kernels >= 4.11.0 From a6226abf50fe547a558ead11f7ff84d77e2bb87b Mon Sep 17 00:00:00 2001 From: saiHemak Date: Wed, 3 Apr 2019 23:53:28 +0530 Subject: [PATCH 70/81] [SR-10281] URLSession crash while handling basic authentication --- Foundation/URLSession/URLSessionTask.swift | 2 +- TestFoundation/HTTPServer.swift | 10 +++++++++- TestFoundation/TestURLSession.swift | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Foundation/URLSession/URLSessionTask.swift b/Foundation/URLSession/URLSessionTask.swift index 1c1c68ac107..f6f5fc128b8 100644 --- a/Foundation/URLSession/URLSessionTask.swift +++ b/Foundation/URLSession/URLSessionTask.swift @@ -574,11 +574,11 @@ extension _ProtocolClient : URLProtocolClient { sender: `protocol` as! _HTTPURLProtocol) task.previousFailureCount += 1 urlProtocol(`protocol`, didReceive: authenticationChallenge) - return } else { let urlError = URLError(_nsError: NSError(domain: NSURLErrorDomain, code: NSURLErrorUserAuthenticationRequired, userInfo: nil)) urlProtocol(`protocol`, didFailWithError: urlError) } + return } switch session.behaviour(for: task) { case .taskDelegate(let delegate): diff --git a/TestFoundation/HTTPServer.swift b/TestFoundation/HTTPServer.swift index f6dc2916d75..d1b77e2a150 100644 --- a/TestFoundation/HTTPServer.swift +++ b/TestFoundation/HTTPServer.swift @@ -335,7 +335,13 @@ class _HTTPServer { try self.socket.writeRawData(responseData) } - + func respondWithUnauthorizedHeader() throws{ + let responseData = ("HTTP/1.1 401 UNAUTHORIZED \r\n" + + "Content-Length: 0\r\n" + + "Connection: keep-Alive\r\n" + + "\r\n").data(using: .utf8)! + try self.socket.writeRawData(responseData) + } } struct _HTTPRequest { @@ -453,6 +459,8 @@ public class TestURLSessionServer { } else if req.uri.hasPrefix("/auth") { httpServer.willReadAgain = true try httpServer.respondWithAuthResponse(uri: req.uri, firstRead: true) + } else if req.uri.hasPrefix("/unauthorized") { + try httpServer.respondWithUnauthorizedHeader() } else { try httpServer.respond(with: process(request: req), startDelay: self.startDelay, sendDelay: self.sendDelay, bodyChunks: self.bodyChunks) } diff --git a/TestFoundation/TestURLSession.swift b/TestFoundation/TestURLSession.swift index 3f67a1ca1d1..c31a907f746 100644 --- a/TestFoundation/TestURLSession.swift +++ b/TestFoundation/TestURLSession.swift @@ -47,6 +47,7 @@ class TestURLSession : LoopbackServerTest { ("test_basicAuthRequest", test_basicAuthRequest), ("test_redirectionWithSetCookies", test_redirectionWithSetCookies), ("test_postWithEmptyBody", test_postWithEmptyBody), + ("test_basicAuthWithUnauthorizedHeader", test_basicAuthWithUnauthorizedHeader), ] } @@ -758,6 +759,20 @@ class TestURLSession : LoopbackServerTest { task.resume() waitForExpectations(timeout: 30) } + + func test_basicAuthWithUnauthorizedHeader() { + let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/unauthorized" + let url = URL(string: urlString)! + let expect = expectation(description: "GET \(urlString): with a completion handler") + var expectedResult = "unknown" + let session = URLSession(configuration: URLSessionConfiguration.default) + let task = session.dataTask(with: url) { _, _, error in + defer { expect.fulfill() } + XCTAssertNotNil(error) + } + task.resume() + waitForExpectations(timeout: 12, handler: nil) + } } class SharedDelegate: NSObject { From 0dceb78417510c66d60fbf9407e0324c8392b889 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Fri, 5 Apr 2019 02:20:09 +0100 Subject: [PATCH 71/81] SR-10245: NumberFormatter: Implement .positiveFormat and .negativeFormat - Ensure .format returns the full format string including .zeroSymbol. --- Foundation/NumberFormatter.swift | 104 +++++++++---- TestFoundation/TestNumberFormatter.swift | 189 ++++++++++++++++++++++- 2 files changed, 260 insertions(+), 33 deletions(-) diff --git a/Foundation/NumberFormatter.swift b/Foundation/NumberFormatter.swift index 45822d62e35..08fe3bc2ac4 100644 --- a/Foundation/NumberFormatter.swift +++ b/Foundation/NumberFormatter.swift @@ -58,7 +58,12 @@ open class NumberFormatter : Formatter { let obj = CFNumberFormatterCreate(kCFAllocatorSystemDefault, locale._cfObject, numberStyle)! _setFormatterAttributes(obj) - if let format = _format { + if _positiveFormat != nil || _negativeFormat != nil { + var format = _positiveFormat ?? "#" + if let negative = _negativeFormat { + format.append(";") + format.append(negative) + } CFNumberFormatterSetFormat(obj, format._cfObject) } _currentCfFormatter = obj @@ -255,17 +260,6 @@ open class NumberFormatter : Formatter { } } - private var _negativeFormat: String! - open var negativeFormat: String! { - get { - return _negativeFormat - } - set { - _reset() - _negativeFormat = newValue - } - } - private var _textAttributesForNegativeValues: [String : Any]? open var textAttributesForNegativeValues: [String : Any]? { get { @@ -277,17 +271,6 @@ open class NumberFormatter : Formatter { } } - private var _positiveFormat: String! - open var positiveFormat: String! { - get { - return _positiveFormat - } - set { - _reset() - _positiveFormat = newValue - } - } - private var _textAttributesForPositiveValues: [String : Any]? open var textAttributesForPositiveValues: [String : Any]? { get { @@ -368,7 +351,7 @@ open class NumberFormatter : Formatter { private var _zeroSymbol: String? open var zeroSymbol: String? { get { - return _zeroSymbol ?? _getFormatterAttribute(_cfFormatter, attributeName: kCFNumberFormatterZeroSymbol) + return _zeroSymbol } set { _reset() @@ -865,17 +848,80 @@ open class NumberFormatter : Formatter { } } - private var _format: String? + private func getFormatterComponents() -> (String, String) { + let format = CFNumberFormatterGetFormat(_cfFormatter)._swiftObject + let components = format.components(separatedBy: ";") + let positive = _positiveFormat ?? components.first ?? "#" + let negative = _negativeFormat ?? components.last ?? "#" + return (positive, negative) + } + + private func getZeroFormat() -> String { + return string(from: 0) ?? "0" + } + open var format: String { get { - if _format == nil { - _format = CFNumberFormatterGetFormat(_cfFormatter)._swiftObject + let (p, n) = getFormatterComponents() + let z = _zeroSymbol ?? getZeroFormat() + return "\(p);\(z);\(n)" + } + set { + // Special case empty string + if newValue == "" { + _positiveFormat = "" + _negativeFormat = "-" + _zeroSymbol = "0" + _reset() + } else { + let components = newValue.components(separatedBy: ";") + let count = components.count + guard count <= 3 else { return } + _reset() + + _positiveFormat = components.first ?? "" + if count == 1 { + _negativeFormat = "-\(_positiveFormat ?? "")" + } + else if count == 2 { + _negativeFormat = components[1] + _zeroSymbol = getZeroFormat() + } + else if count == 3 { + _zeroSymbol = components[1] + _negativeFormat = components[2] + } + + if _negativeFormat == nil { + _negativeFormat = getFormatterComponents().1 + } + + if _zeroSymbol == nil { + _zeroSymbol = getZeroFormat() + } } - return _format ?? "#" + } + } + + private var _positiveFormat: String! + open var positiveFormat: String! { + get { + return getFormatterComponents().0 + } + set { + _reset() + _positiveFormat = newValue + } + } + + private var _negativeFormat: String! + open var negativeFormat: String! { + get { + return getFormatterComponents().1 } set { _reset() - _format = newValue + _negativeFormat = newValue } } diff --git a/TestFoundation/TestNumberFormatter.swift b/TestFoundation/TestNumberFormatter.swift index 777db9c7dd2..0fa48c52ec9 100644 --- a/TestFoundation/TestNumberFormatter.swift +++ b/TestFoundation/TestNumberFormatter.swift @@ -57,14 +57,25 @@ class TestNumberFormatter: XCTestCase { ("test_en_US_initialValues", test_en_US_initialValues), ("test_pt_BR_initialValues", test_pt_BR_initialValues), ("test_changingLocale", test_changingLocale), + ("test_settingFormat", test_settingFormat), + ("test_usingFormat", test_usingFormat), ] } func test_currencyCode() { let numberFormatter = NumberFormatter() + numberFormatter.locale = Locale(identifier: "en_GB") numberFormatter.numberStyle = .currency + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;£0.00;¤#,##0.00") + XCTAssertEqual(numberFormatter.string(from: 1.1), "£1.10") + XCTAssertEqual(numberFormatter.string(from: 0), "£0.00") + XCTAssertEqual(numberFormatter.string(from: -1.1), "-£1.10") + numberFormatter.currencyCode = "T" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;T0.00;¤#,##0.00") numberFormatter.currencyDecimalSeparator = "_" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;T0_00;¤#,##0.00") + let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "T42_00") } @@ -72,9 +83,11 @@ class TestNumberFormatter: XCTestCase { func test_decimalSeparator() { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .decimal + XCTAssertEqual(numberFormatter.format, "#,##0.###;0;#,##0.###") let separator = "-" numberFormatter.decimalSeparator = separator + XCTAssertEqual(numberFormatter.format, "#,##0.###;0;#,##0.###") XCTAssertEqual(numberFormatter.decimalSeparator, separator) let formattedString = numberFormatter.string(from: 42.42) @@ -86,6 +99,7 @@ class TestNumberFormatter: XCTestCase { numberFormatter.numberStyle = .currency numberFormatter.currencyDecimalSeparator = "-" numberFormatter.currencyCode = "T" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;T0-00;¤#,##0.00") let formattedString = numberFormatter.string(from: 42.42) XCTAssertEqual(formattedString, "T42-42") } @@ -94,6 +108,7 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() numberFormatter.decimalSeparator = "-" numberFormatter.alwaysShowsDecimalSeparator = true + XCTAssertEqual(numberFormatter.format, "#.;0-;#.") let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "42-") } @@ -103,18 +118,23 @@ class TestNumberFormatter: XCTestCase { XCTAssertEqual(decFormatter1.groupingSize, 0) decFormatter1.numberStyle = .decimal XCTAssertEqual(decFormatter1.groupingSize, 3) + XCTAssertEqual(decFormatter1.format, "#,##0.###;0;#,##0.###") let decFormatter2 = NumberFormatter() XCTAssertEqual(decFormatter2.groupingSize, 0) decFormatter2.groupingSize = 1 decFormatter2.numberStyle = .decimal XCTAssertEqual(decFormatter2.groupingSize, 1) + XCTAssertEqual(decFormatter2.format, "#,0.###;0;#,0.###") let numberFormatter = NumberFormatter() numberFormatter.usesGroupingSeparator = true numberFormatter.groupingSeparator = "_" XCTAssertEqual(numberFormatter.groupingSize, 0) + XCTAssertEqual(numberFormatter.format, "#;0;#") numberFormatter.groupingSize = 3 + XCTAssertEqual(numberFormatter.format, "#,###;0;#,###") + let formattedString = numberFormatter.string(from: 42_000) XCTAssertEqual(formattedString, "42_000") } @@ -123,6 +143,8 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .percent numberFormatter.percentSymbol = "💯" + XCTAssertEqual(numberFormatter.format, "#,##0%;0💯;#,##0%") + let formattedString = numberFormatter.string(from: 0.42) XCTAssertEqual(formattedString, "42💯") } @@ -130,6 +152,8 @@ class TestNumberFormatter: XCTestCase { func test_zeroSymbol() { let numberFormatter = NumberFormatter() numberFormatter.zeroSymbol = "⚽️" + XCTAssertEqual(numberFormatter.format, "#;⚽️;#") + let formattedString = numberFormatter.string(from: 0) XCTAssertEqual(formattedString, "⚽️") } @@ -138,6 +162,7 @@ class TestNumberFormatter: XCTestCase { func test_notANumberSymbol() { let numberFormatter = NumberFormatter() numberFormatter.notANumberSymbol = "👽" + XCTAssertEqual(numberFormatter.format, "#;0;#") let number: Double = -42 let numberObject = NSNumber(value: sqrt(number)) let formattedString = numberFormatter.string(from: numberObject) @@ -147,6 +172,7 @@ class TestNumberFormatter: XCTestCase { func test_positiveInfinitySymbol() { let numberFormatter = NumberFormatter() numberFormatter.positiveInfinitySymbol = "🚀" + XCTAssertEqual(numberFormatter.format, "#;0;#") let numberObject = NSNumber(value: Double(42.0) / Double(0)) let formattedString = numberFormatter.string(from: numberObject) @@ -156,6 +182,7 @@ class TestNumberFormatter: XCTestCase { func test_minusSignSymbol() { let numberFormatter = NumberFormatter() numberFormatter.minusSign = "👎" + XCTAssertEqual(numberFormatter.format, "#;0;#") let formattedString = numberFormatter.string(from: -42) XCTAssertEqual(formattedString, "👎42") } @@ -165,8 +192,12 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() let format = "#E+0" numberFormatter.format = format - XCTAssertEqual(numberFormatter.format, format) - + XCTAssertEqual(numberFormatter.positiveFormat, "#E+0") + XCTAssertEqual(numberFormatter.zeroSymbol, "0E+0") + XCTAssertEqual(numberFormatter.negativeFormat, "-#E+0") + XCTAssertEqual(numberFormatter.format, "#E+0;0E+0;-#E+0") + XCTAssertEqual(numberFormatter.string(from: 0), "0E+0") + XCTAssertEqual(numberFormatter.plusSign, "+") let sign = "👍" numberFormatter.plusSign = sign XCTAssertEqual(numberFormatter.plusSign, sign) @@ -191,6 +222,7 @@ class TestNumberFormatter: XCTestCase { numberFormatter.numberStyle = .currency numberFormatter.currencySymbol = "🍯" numberFormatter.currencyDecimalSeparator = "_" + XCTAssertEqual(numberFormatter.format, "¤#,##0.00;🍯0_00;¤#,##0.00") let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "🍯42_00") } @@ -199,6 +231,7 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter() numberFormatter.numberStyle = .scientific numberFormatter.exponentSymbol = "⬆️" + XCTAssertEqual(numberFormatter.format, "#E0;0⬆️0;#E0") let formattedString = numberFormatter.string(from: 42) XCTAssertEqual(formattedString, "4.2⬆️1") } @@ -209,17 +242,20 @@ class TestNumberFormatter: XCTestCase { numberFormatter1.minimumIntegerDigits = 3 numberFormatter1.numberStyle = .decimal XCTAssertEqual(numberFormatter1.minimumIntegerDigits, 3) + XCTAssertEqual(numberFormatter1.format, "#,000.###;000;#,000.###") let numberFormatter = NumberFormatter() XCTAssertEqual(numberFormatter.minimumIntegerDigits, 0) numberFormatter.numberStyle = .decimal XCTAssertEqual(numberFormatter.minimumIntegerDigits, 1) numberFormatter.minimumIntegerDigits = 3 + XCTAssertEqual(numberFormatter.format, "#,000.###;000;#,000.###") var formattedString = numberFormatter.string(from: 0) XCTAssertEqual(formattedString, "000") numberFormatter.numberStyle = .decimal XCTAssertEqual(numberFormatter.minimumIntegerDigits, 3) + XCTAssertEqual(numberFormatter.format, "#,000.###;000;#,000.###") formattedString = numberFormatter.string(from: 0.1) XCTAssertEqual(formattedString, "000.1") } @@ -227,19 +263,24 @@ class TestNumberFormatter: XCTestCase { func test_currencyMinimumIntegerDigits() { // If .minimumIntegerDigits is set to 0 before .numberStyle change, preserve the value let formatter = NumberFormatter() + formatter.locale = Locale(identifier: "en_GB") XCTAssertEqual(formatter.minimumIntegerDigits, 0) formatter.minimumIntegerDigits = 0 formatter.numberStyle = .currency + XCTAssertEqual(formatter.format, "¤#,###.00;£.00;¤#,###.00") XCTAssertEqual(formatter.minimumIntegerDigits, 0) formatter.locale = Locale(identifier: "en_US") + XCTAssertEqual(formatter.format, "¤#,###.00;$.00;¤#,###.00") XCTAssertEqual(formatter.string(from: 0), "$.00") XCTAssertEqual(formatter.string(from: 1.23), "$1.23") XCTAssertEqual(formatter.string(from: 123.4), "$123.40") // If .minimumIntegerDigits is not set before .numberStyle change, update the value let formatter2 = NumberFormatter() + formatter2.locale = Locale(identifier: "en_GB") XCTAssertEqual(formatter2.minimumIntegerDigits, 0) formatter2.numberStyle = .currency + XCTAssertEqual(formatter2.format, "¤#,##0.00;£0.00;¤#,##0.00") XCTAssertEqual(formatter2.minimumIntegerDigits, 1) formatter2.locale = Locale(identifier: "en_US") XCTAssertEqual(formatter2.string(from: 0.001), "$0.00") @@ -654,7 +695,7 @@ class TestNumberFormatter: XCTestCase { numberFormatter.locale = Locale(identifier: "en_US") // TODO: Check if this is true for all versions... - XCTAssertEqual(numberFormatter.format, "#") + XCTAssertEqual(numberFormatter.format, "#;0;#") XCTAssertEqual(numberFormatter.plusSign, "+") XCTAssertEqual(numberFormatter.minusSign, "-") @@ -685,7 +726,7 @@ class TestNumberFormatter: XCTestCase { let numberFormatter = NumberFormatter(); numberFormatter.locale = Locale(identifier: "pt_BR") - XCTAssertEqual(numberFormatter.format, "#") + XCTAssertEqual(numberFormatter.format, "#;0;#") XCTAssertEqual(numberFormatter.plusSign, "+") XCTAssertEqual(numberFormatter.minusSign, "-") XCTAssertEqual(numberFormatter.decimalSeparator, ",") @@ -730,4 +771,144 @@ class TestNumberFormatter: XCTestCase { numberFormatter.currencySymbol = nil XCTAssertEqual(numberFormatter.currencySymbol, "£") } + + func test_settingFormat() { + let formatter = NumberFormatter() + + XCTAssertEqual(formatter.format, "#;0;#") + XCTAssertEqual(formatter.positiveFormat, "#") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.positiveFormat = "#" + XCTAssertEqual(formatter.format, "#;0;#") + XCTAssertEqual(formatter.positiveFormat, "#") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.positiveFormat = "##.##" + XCTAssertEqual(formatter.format, "##.##;0;#0.##") + XCTAssertEqual(formatter.positiveFormat, "##.##") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#0.##") + + formatter.positiveFormat = "##;##" + XCTAssertEqual(formatter.format, "##;##;0;#") + XCTAssertEqual(formatter.positiveFormat, "##;##") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.positiveFormat = "+#.#########" + XCTAssertEqual(formatter.format, "+#.#########;+0;+#0.#########") + XCTAssertEqual(formatter.positiveFormat, "+#.#########") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "+#0.#########") + + formatter.negativeFormat = "-#.#########" + XCTAssertEqual(formatter.format, "+#.#########;+0;-#.#########") + XCTAssertEqual(formatter.positiveFormat, "+#.#########") + XCTAssertEqual(formatter.zeroSymbol, nil) + XCTAssertEqual(formatter.negativeFormat, "-#.#########") + + formatter.format = "+++#;000;---#.##" + XCTAssertEqual(formatter.format, "+++#;000;---#.##") + XCTAssertEqual(formatter.positiveFormat, "+++#") + XCTAssertEqual(formatter.zeroSymbol, "000") + XCTAssertEqual(formatter.negativeFormat, "---#.##") + + formatter.positiveFormat = nil + XCTAssertEqual(formatter.positiveFormat, "#") + XCTAssertEqual(formatter.format, "#;000;---#.##") + + formatter.zeroSymbol = "00" + formatter.positiveFormat = "+++#.#" + XCTAssertEqual(formatter.format, "+++#.#;00;---#.##") + XCTAssertEqual(formatter.positiveFormat, "+++#.#") + XCTAssertEqual(formatter.zeroSymbol, "00") + XCTAssertEqual(formatter.negativeFormat, "---#.##") + + formatter.negativeFormat = "---#.#" + XCTAssertEqual(formatter.format, "+++#.#;00;---#.#") + XCTAssertEqual(formatter.positiveFormat, "+++#.#") + XCTAssertEqual(formatter.zeroSymbol, "00") + XCTAssertEqual(formatter.negativeFormat, "---#.#") + + // Test setting only first 2 parts + formatter.format = "+##.##;0.00" + XCTAssertEqual(formatter.format, "+##.##;00;0.00") + XCTAssertEqual(formatter.positiveFormat, "+##.##") + XCTAssertEqual(formatter.zeroSymbol, "00") + XCTAssertEqual(formatter.negativeFormat, "0.00") + + formatter.format = "+##.##;+0;0.00" + XCTAssertEqual(formatter.format, "+##.##;+0;0.00") + XCTAssertEqual(formatter.positiveFormat, "+##.##") + XCTAssertEqual(formatter.zeroSymbol, "+0") + XCTAssertEqual(formatter.negativeFormat, "0.00") + + formatter.format = "#;0;#" + formatter.positiveFormat = "1" + XCTAssertEqual(formatter.format, "1;0;#") + XCTAssertEqual(formatter.positiveFormat, "1") + XCTAssertEqual(formatter.zeroSymbol, "0") + XCTAssertEqual(formatter.negativeFormat, "#") + + formatter.format = "1" + XCTAssertEqual(formatter.format, "1;0;-1") + XCTAssertEqual(formatter.positiveFormat, "1") + XCTAssertEqual(formatter.zeroSymbol, "0") + XCTAssertEqual(formatter.negativeFormat, "-1") + + formatter.format = "1;2;3" + XCTAssertEqual(formatter.format, "1;2;3") + XCTAssertEqual(formatter.positiveFormat, "1") + XCTAssertEqual(formatter.zeroSymbol, "2") + XCTAssertEqual(formatter.negativeFormat, "3") + + formatter.format = "" + XCTAssertEqual(formatter.format, ";0;-") + XCTAssertEqual(formatter.positiveFormat, "") + XCTAssertEqual(formatter.zeroSymbol, "0") + XCTAssertEqual(formatter.negativeFormat, "-") + } + + func test_usingFormat() { + var formatter = NumberFormatter() + + formatter.format = "+++#.#;00;-+-#.#" + XCTAssertEqual(formatter.string(from: 1), "+++1") + XCTAssertEqual(formatter.string(from: Int.max as NSNumber), "+++9223372036854775807") + XCTAssertEqual(formatter.string(from: 0), "00") + XCTAssertEqual(formatter.string(from: -1), "-+-1") + XCTAssertEqual(formatter.string(from: Int.min as NSNumber), "-+-9223372036854775808") + + + formatter.format = "+#.##;0.00;-#.##" + XCTAssertEqual(formatter.string(from: 0.5), "+0.5") + XCTAssertEqual(formatter.string(from: 0), "0.00") + XCTAssertEqual(formatter.string(from: -0.2), "-0.2") + + formatter.positiveFormat = "#.##" + formatter.negativeFormat = "-#.##" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.14") + XCTAssertEqual(formatter.string(from: NSNumber(value: -Double.pi)), "-3.14") + + formatter = NumberFormatter() + formatter.negativeFormat = "--#.##" + XCTAssertEqual(formatter.string(from: NSNumber(value: -Double.pi)), "--3") + formatter.positiveFormat = "#.###" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.142") + + formatter.positiveFormat = "#.####" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.1416") + + formatter.positiveFormat = "#.#####" + XCTAssertEqual(formatter.string(from: NSNumber(value: Double.pi)), "3.14159") + + formatter = NumberFormatter() + formatter.negativeFormat = "#.#########" + formatter.positiveFormat = "#.#########" + XCTAssertEqual(formatter.string(from: NSNumber(value: 0.5)), "0.5") + XCTAssertEqual(formatter.string(from: NSNumber(value: -0.5)), "-0.5") + } } From 0e449d0ed5c5e844c0a98e060177e29ac9e95720 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Fri, 5 Apr 2019 18:43:50 +0100 Subject: [PATCH 72/81] Status.md: Update the implementation status of various types - Also misc formatting tidyups. --- Docs/Status.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Docs/Status.md b/Docs/Status.md index ea05fd98828..d0f2e31d62c 100644 --- a/Docs/Status.md +++ b/Docs/Status.md @@ -56,7 +56,7 @@ There is no _Complete_ status for test coverage because there are always additio | `URLProtocolClient` | Unimplemented | None | | | `NSURLRequest` | Complete | Incomplete | | | `NSMutableURLRequest` | Complete | Incomplete | | - | `URLResponse` | Complete | Substantial | | + | `URLResponse` | Complete | Substantial | | | `NSHTTPURLResponse` | Complete | Substantial | | | `NSURL` | Mostly Complete | Substantial | Resource values remain unimplemented | | `NSURLQueryItem` | Complete | N/A | | @@ -76,13 +76,13 @@ There is no _Complete_ status for test coverage because there are always additio | `HTTPMessage` | N/A | N/A | For internal use only | | `libcurlHelpers` | N/A | N/A | For internal use only | | `MultiHandle` | N/A | N/A | For internal use only | - | `URLSession` | Mostly Complete | Incomplete | `shared`, invalidation, resetting, flushing, getting tasks, and others remain unimplemented | + | `URLSession` | Mostly Complete | Incomplete | invalidation, resetting, flushing, getting tasks, and others remain unimplemented | | `URLSessionConfiguration` | Mostly Complete | Incomplete | `ephemeral` and `background(withIdentifier:)` remain unimplemented | | `URLSessionDelegate` | Complete | N/A | | | `URLSessionTask` | Mostly Complete | Incomplete | `cancel()`, `createTransferState(url:)` with streams, and others remain unimplemented | | `URLSessionDataTask` | Complete | Incomplete | | | `URLSessionUploadTask` | Complete | None | | - | `URLSessionDownloadTask` | Incomplete | Incomplete | | + | `URLSessionDownloadTask` | Incomplete | Incomplete | | | `URLSessionStreamTask` | Unimplemented | None | | | `TaskRegistry` | N/A | N/A | For internal use only | | `TransferState` | N/A | N/A | For internal use only | @@ -94,10 +94,10 @@ There is no _Complete_ status for test coverage because there are always additio | Entity Name | Status | Test Coverage | Notes | |---------------------------------|-----------------|---------------|-------------------------------------------------------------------------------------------| - | `DateComponentFormatter` | Unimplemented | None | | - | `DateIntervalFormatter` | Unimplemented | None | | - | `EnergyFormatter` | Unimplemented | None | | - | `ISO8601DateFormatter` | Unimplemented | None | | + | `DateComponentsFormatter` | Unimplemented | None | | + | `DateIntervalFormatter` | Mostly Complete | Incomplete | `NSCoding` remains to be implemented | + | `EnergyFormatter` | Complete | Substantial | | + | `ISO8601DateFormatter` | Mostly Complete | Substantial | `NSCoding` remains to be implemented | | `LengthFormatter` | Complete | Substantial | | | `MassFormatter` | Complete | Substantial | Needs localization | | `NumberFormatter` | Mostly Complete | Substantial | `objectValue(_:range:)` remains unimplemented | @@ -159,18 +159,18 @@ There is no _Complete_ status for test coverage because there are always additio | `NSIndexSet` | Mostly Complete | Incomplete | `NSCoding` remains to be implemented | | `NSMutableIndexSet` | Mostly Complete | Incomplete | `NSCoding` remains to be implemented | | `IndexSet` | Complete | Incomplete | | - | `NSIndexPath` | Mostly Complete | None | `NSCoding`, `NSCopying`, `getIndexes(_:)` remain unimplemented | + | `NSIndexPath` | Mostly Complete | None | `NSCoding` remains to be implemented | | `IndexPath` | Complete | Incomplete | | - | `NSArray` | Mostly Complete | Substantial | Reading/writing to files/URLs, concurrent `enumerateObjects(at:options:using:)`, and `sortedArray(from:options:usingComparator:)` with options remain unimplemented | + | `NSArray` | Mostly Complete | Substantial | concurrent `enumerateObjects(at:options:using:)`, and `sortedArray(from:options:usingComparator:)` with options remain unimplemented | | `NSMutableArray` | Mostly Complete | Substantial | `exchangeObject(at:withObjectAt:)` and `replaceObjects(in:withObjectsFromArray:)` remain unimplemented for types other than `NSMutableArray` | - | `NSDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers, `descriptionInStringsFileFormat`, `sharedKeySet(forKeys:)`, and reading/writing to files/URLs remain unimplemented | - | `NSMutableDictionary` | Mostly Complete | Incomplete | `descriptionInStringsFileFormat`, `sharedKeySet(forKeys:)`, and reading/writing to files/URLs remain unimplemented | + | `NSDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers and `sharedKeySet(forKeys:)` | + | `NSMutableDictionary` | Mostly Complete | Incomplete | `NSCoding` with non-keyed-coding archivers and`sharedKeySet(forKeys:)` | | `NSCFDictionary` | N/A | N/A | For internal use only | - | `NSSet` | Mostly Complete | Incomplete | `customMirror` remain unimplemented | + | `NSSet` | Mostly Complete | Incomplete | `NSCoding` and `customMirror` remain unimplemented | | `NSMutableSet` | Mostly Complete | Incomplete | `init?(coder:)` remains unimplemented | | `NSCountedSet` | Mostly Complete | Incomplete | `init?(coder:)` remains unimplemented | | `NSCFSet` | N/A | N/A | For internal use only | - | `NSCache` | Complete | Incomplete | | + | `NSCache` | Complete | Incomplete | `NSCoding` and `customMirror` remain unimplemented | | `NSSortDescriptor` | Unimplemented | None | | * **RunLoop**: Timers, streams and run loops. @@ -200,7 +200,7 @@ There is no _Complete_ status for test coverage because there are always additio |-----------------------------|-----------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `NSRegularExpression` | Complete | Substantial | | | `Scanner` | Mostly Complete | Incomplete | `localizedScannerWithString(_:)` remains unimplemented | - | `NSTextCheckingResult` | Mostly Complete | Incomplete | `NSCoding`, `NSCopying`, `resultType`, and `range(at:)` remain unimplemented | + | `NSTextCheckingResult` | Mostly Complete | Incomplete | `NSCoding`, `resultType`, and `range(at:)` remain unimplemented | | `NSAttributedString` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented | | `NSMutableAttributedString` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented | | `NSCharacterSet` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented | @@ -219,7 +219,7 @@ There is no _Complete_ status for test coverage because there are always additio |-----------------------------------|-----------------|---------------|-------------------------------------------------------------------------------| | `NSRange` | Complete | Incomplete | | | `Decimal` | Complete | Substantial | | - | `NSDecimalNumber` | Mostly Complete | Substantial | | + | `NSDecimalNumber` | Complete | Substantial | | | `NSDecimalNumberHandler` | Complete | None | | | `CGPoint` | Complete | Substantial | | | `CGSize` | Complete | Substantial | | @@ -273,10 +273,10 @@ There is no _Complete_ status for test coverage because there are always additio | Entity Name | Status | Test Coverage | Notes | |------------------|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------------| - | `FileHandle` | Mostly Complete | Incomplete | `NSCoding`, and background operations remain unimplemented | + | `FileHandle` | Mostly Complete | Incomplete | `NSCoding` remain unimplemented | | `Pipe` | Complete | Incomplete | | - | `FileManager` | Incomplete | Incomplete | URL searches, relationship lookups, item copying, cross-device moving, recursive linking, and others remain unimplemented | - | `Process` | Mostly Complete | Substantial | `interrupt()`, `terminate()`, `suspend()`, and `resume()` remain unimplemented | + | `FileManager` | Incomplete | Incomplete | relationship lookups, `displayName(atPath:)`, `componentsToDisplay(forPath:)`, and replacing items remain unimplemented | + | `Process` | Complete | Substantial | | | `Bundle` | Mostly Complete | Incomplete | `allBundles`, `init(for:)`, `unload()`, `classNamed()`, and `principalClass` remain unimplemented | | `ProcessInfo` | Complete | Substantial | | | `Thread` | Complete | Incomplete | | @@ -292,7 +292,7 @@ There is no _Complete_ status for test coverage because there are always additio | Entity Name | Status | Test Coverage | Notes | |--------------------|-----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------| - | `NSCalendar` | Complete | None | `autoupdatingCurrent`, and `enumerateDates` remain unimplemented | + | `NSCalendar` | Complete | None | `autoupdatingCurrent` remains unimplemented | | `NSDateComponents` | Complete | None | | | `Calendar` | Complete | Incomplete | | | `DateComponents` | Complete | Incomplete | | From 17bc4adb68ffd46f4ec3e83cdbcf3e8eab4113e3 Mon Sep 17 00:00:00 2001 From: YOCKOW Date: Mon, 8 Apr 2019 09:41:31 +0900 Subject: [PATCH 73/81] XMLParser: Add a test for SR-10157. --- TestFoundation/TestXMLParser.swift | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/TestFoundation/TestXMLParser.swift b/TestFoundation/TestXMLParser.swift index 9c0516d8e05..1ccd1c852f3 100644 --- a/TestFoundation/TestXMLParser.swift +++ b/TestFoundation/TestXMLParser.swift @@ -66,6 +66,7 @@ class TestXMLParser : XCTestCase { ("test_withDataEncodings", test_withDataEncodings), ("test_withDataOptions", test_withDataOptions), ("test_sr9758_abortParsing", test_sr9758_abortParsing), + ("test_sr10157_swappedElementNames", test_sr10157_swappedElementNames), ] } @@ -154,4 +155,27 @@ class TestXMLParser : XCTestCase { XCTAssertNotNil(parser.parserError) } + func test_sr10157_swappedElementNames() { + class ElementNameChecker: NSObject, XMLParserDelegate { + let name: String + init(_ name: String) { self.name = name } + func parser(_ parser: XMLParser, + didStartElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?, + attributes attributeDict: [String: String] = [:]) { + XCTAssertEqual(self.name, elementName) + } + func check() { + let elementString = "<\(self.name) />" + let parser = XMLParser(data: elementString.data(using: .utf8)!) + parser.delegate = self + XCTAssertTrue(parser.parse()) + } + } + + ElementNameChecker("noPrefix").check() + ElementNameChecker("myPrefix:myLocalName").check() + } + } From 89ca8af3d3ca915731d238bacd255b2b75aabe01 Mon Sep 17 00:00:00 2001 From: YOCKOW Date: Mon, 8 Apr 2019 10:39:42 +0900 Subject: [PATCH 74/81] XMLParser: Modify the test for SR-10157. The parts of the element name should not be swapped also in `didEndElement`. --- TestFoundation/TestXMLParser.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TestFoundation/TestXMLParser.swift b/TestFoundation/TestXMLParser.swift index 1ccd1c852f3..ba2df228385 100644 --- a/TestFoundation/TestXMLParser.swift +++ b/TestFoundation/TestXMLParser.swift @@ -166,6 +166,12 @@ class TestXMLParser : XCTestCase { attributes attributeDict: [String: String] = [:]) { XCTAssertEqual(self.name, elementName) } + func parser(_ parser: XMLParser, + didEndElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?) { + XCTAssertEqual(self.name, elementName) + } func check() { let elementString = "<\(self.name) />" let parser = XMLParser(data: elementString.data(using: .utf8)!) From 64904c722c297dcf85b8c2375b047676b6e2643f Mon Sep 17 00:00:00 2001 From: YOCKOW Date: Mon, 8 Apr 2019 11:22:31 +0900 Subject: [PATCH 75/81] XMLParser: Don't swap the parts of the element name. --- Foundation/XMLParser.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Foundation/XMLParser.swift b/Foundation/XMLParser.swift index c2a17d4ebca..e17b3a083bc 100644 --- a/Foundation/XMLParser.swift +++ b/Foundation/XMLParser.swift @@ -296,13 +296,14 @@ internal func _NSXMLParserStartElementNs(_ ctx: _CFXMLInterface, localname: Unsa var qualifiedName: String? = nil if parser.shouldProcessNamespaces { namespaceURI = UTF8STRING(URI) ?? "" - qualifiedName = elementName if let prefix = UTF8STRING(prefix) { - qualifiedName = elementName + ":" + prefix + qualifiedName = prefix + ":" + elementName + } else { + qualifiedName = elementName } } else if let prefix = UTF8STRING(prefix) { - elementName = elementName + ":" + prefix + elementName = prefix + ":" + elementName } parser.delegate?.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qualifiedName, attributes: attrDict) @@ -316,13 +317,14 @@ internal func _NSXMLParserEndElementNs(_ ctx: _CFXMLInterface , localname: Unsaf var qualifiedName: String? = nil if parser.shouldProcessNamespaces { namespaceURI = UTF8STRING(URI) ?? "" - qualifiedName = elementName if let prefix = UTF8STRING(prefix) { - qualifiedName = elementName + ":" + prefix + qualifiedName = prefix + ":" + elementName + } else { + qualifiedName = elementName } } else if let prefix = UTF8STRING(prefix) { - elementName = elementName + ":" + prefix + elementName = prefix + ":" + elementName } parser.delegate?.parser(parser, didEndElement: elementName, namespaceURI: namespaceURI, qualifiedName: qualifiedName) From c8bd73cd034608907f57ce9ac78a9ac2c234d94c Mon Sep 17 00:00:00 2001 From: YOCKOW Date: Mon, 8 Apr 2019 11:43:40 +0900 Subject: [PATCH 76/81] XMLParser: Confirm that the parts of QName is also not swapped. --- TestFoundation/TestXMLParser.swift | 32 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/TestFoundation/TestXMLParser.swift b/TestFoundation/TestXMLParser.swift index ba2df228385..d7214f9f340 100644 --- a/TestFoundation/TestXMLParser.swift +++ b/TestFoundation/TestXMLParser.swift @@ -160,22 +160,38 @@ class TestXMLParser : XCTestCase { let name: String init(_ name: String) { self.name = name } func parser(_ parser: XMLParser, - didStartElement elementName: String, - namespaceURI: String?, - qualifiedName qName: String?, - attributes attributeDict: [String: String] = [:]) { - XCTAssertEqual(self.name, elementName) + didStartElement elementName: String, + namespaceURI: String?, + qualifiedName qName: String?, + attributes attributeDict: [String: String] = [:]) + { + if parser.shouldProcessNamespaces { + XCTAssertEqual(self.name, qName) + } else { + XCTAssertEqual(self.name, elementName) + } } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, - qualifiedName qName: String?) { - XCTAssertEqual(self.name, elementName) + qualifiedName qName: String?) + { + if parser.shouldProcessNamespaces { + XCTAssertEqual(self.name, qName) + } else { + XCTAssertEqual(self.name, elementName) + } } func check() { let elementString = "<\(self.name) />" - let parser = XMLParser(data: elementString.data(using: .utf8)!) + var parser = XMLParser(data: elementString.data(using: .utf8)!) + parser.delegate = self + XCTAssertTrue(parser.parse()) + + // Confirm that the parts of QName is also not swapped. + parser = XMLParser(data: elementString.data(using: .utf8)!) parser.delegate = self + parser.shouldProcessNamespaces = true XCTAssertTrue(parser.parse()) } } From d8f09b6234a94838cd0c5b917d4cc136f6dc9004 Mon Sep 17 00:00:00 2001 From: Simon Evans Date: Mon, 8 Apr 2019 09:46:35 +0100 Subject: [PATCH 77/81] SR-9439: Calendar.dateComponetns: Enable .nanoseconds - Nanoseconds were originally disable in #1658 to fix SR-7011, however the CF/NSCalendar rewrite in #1755 fixed them to work correctly so they can now be re-enabled. --- Foundation/NSCalendar.swift | 9 +++------ TestFoundation/TestCalendar.swift | 33 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Foundation/NSCalendar.swift b/Foundation/NSCalendar.swift index 47bb95ffafc..5d7ba0cab0b 100644 --- a/Foundation/NSCalendar.swift +++ b/Foundation/NSCalendar.swift @@ -643,9 +643,9 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding { open func components(_ unitFlags: Unit, from startingDate: Date, to resultDate: Date, options opts: Options = []) -> DateComponents { let validUnitFlags: NSCalendar.Unit = [ - .era, .year, .month, .day, .hour, .minute, .second, .weekOfYear, .weekOfMonth, .yearForWeekOfYear, .weekday, .weekdayOrdinal ] + .era, .year, .month, .day, .hour, .minute, .second, .nanosecond, .weekOfYear, .weekOfMonth, .yearForWeekOfYear, .weekday, .weekdayOrdinal ] - let invalidUnitFlags: NSCalendar.Unit = [ .quarter, .nanosecond, .timeZone, .calendar] + let invalidUnitFlags: NSCalendar.Unit = [ .quarter, .timeZone, .calendar] // Mask off the unsupported fields let newUnitFlags = Unit(rawValue: unitFlags.rawValue & validUnitFlags.rawValue) @@ -665,10 +665,7 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding { let emptyUnitFlags = Unit(rawValue: unitFlags.rawValue & invalidUnitFlags.rawValue) var components = _components(newUnitFlags, vector: ints, addIsLeapMonth: false) - // nanosecond and quarter always get set to zero if requested in the output - if emptyUnitFlags.contains(.nanosecond) { - components.nanosecond = 0 - } + // quarter always gets set to zero if requested in the output if emptyUnitFlags.contains(.quarter) { components.quarter = 0 } diff --git a/TestFoundation/TestCalendar.swift b/TestFoundation/TestCalendar.swift index 1a848cfa8d3..ca2d0d47eed 100644 --- a/TestFoundation/TestCalendar.swift +++ b/TestFoundation/TestCalendar.swift @@ -215,6 +215,7 @@ class TestNSDateComponents: XCTestCase { ("test_hash", test_hash), ("test_copyNSDateComponents", test_copyNSDateComponents), ("test_dateDifferenceComponents", test_dateDifferenceComponents), + ("test_nanoseconds", test_nanoseconds), ] } @@ -429,4 +430,36 @@ class TestNSDateComponents: XCTestCase { XCTAssertNil(diff9.calendar) XCTAssertNil(diff9.timeZone) } + + func test_nanoseconds() throws { + // 1971-06-21 00:00:00 + let date1 = Date(timeIntervalSince1970: 46310400) + + // 1971-06-21 00:00:00.00123 + let date2 = Date(timeIntervalSince1970: 46310400.00123) + + // 1971-06-24 00:16:40:00123 + let date3 = Date(timeIntervalSince1970: 46570600.45678) + + var calendar = Calendar.current + calendar.timeZone = try TimeZone(abbreviation: "UTC").unwrapped() + + let diff1 = calendar.dateComponents([.nanosecond], from: date1, to: date2) + XCTAssertEqual(diff1.nanosecond, 1230003) + + let diff2 = calendar.dateComponents([.nanosecond], from: date1, to: date2) + XCTAssertEqual(diff2.nanosecond, 1230003) + + let diff3 = calendar.dateComponents([.day, .minute, .second, .nanosecond], from: date2, to: date3) + XCTAssertEqual(diff3.day, 3) + XCTAssertEqual(diff3.minute, 16) + XCTAssertEqual(diff3.second, 40) + XCTAssertEqual(diff3.nanosecond, 455549949) + + let diff4 = calendar.dateComponents([.day, .minute, .second, .nanosecond], from: date3, to: date2) + XCTAssertEqual(diff4.day, -3) + XCTAssertEqual(diff4.minute, -16) + XCTAssertEqual(diff4.second, -40) + XCTAssertEqual(diff4.nanosecond, -455549950) + } } From 90d5520062760ddb91e7a86a67c2e22e6babc74c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 12 Mar 2019 10:26:34 -0700 Subject: [PATCH 78/81] TestFoundation: give preference to SwiftFoundation, SwiftXCTest Alter the importing to use `canImport` and prefer the Swift prefixed variants of the modules. This avoids us from maintaining an ever growing list of OSes, particularly when the Swift variants are less widely available. --- TestFoundation/TestDateIntervalFormatter.swift | 6 +++--- TestFoundation/TestFileManager.swift | 6 +++--- TestFoundation/TestImports.swift | 8 ++++---- TestFoundation/TestStream.swift | 10 ++++------ TestFoundation/xdgTestHelper/main.swift | 6 +++--- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/TestFoundation/TestDateIntervalFormatter.swift b/TestFoundation/TestDateIntervalFormatter.swift index 9f1e0e3d980..571b6d8a8e8 100644 --- a/TestFoundation/TestDateIntervalFormatter.swift +++ b/TestFoundation/TestDateIntervalFormatter.swift @@ -8,10 +8,10 @@ // #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - #if (os(Linux) || os(Android)) - @testable import Foundation - #else + #if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC @testable import SwiftFoundation + #else + @testable import Foundation #endif #endif diff --git a/TestFoundation/TestFileManager.swift b/TestFoundation/TestFileManager.swift index 2c24c63b6ba..433000a682b 100644 --- a/TestFoundation/TestFileManager.swift +++ b/TestFoundation/TestFileManager.swift @@ -8,10 +8,10 @@ // #if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - #if (os(Linux) || os(Android)) - @testable import Foundation - #else + #if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC @testable import SwiftFoundation + #else + @testable import Foundation #endif #endif diff --git a/TestFoundation/TestImports.swift b/TestFoundation/TestImports.swift index 60b48b6cf3e..7df40f90de4 100644 --- a/TestFoundation/TestImports.swift +++ b/TestFoundation/TestImports.swift @@ -9,10 +9,10 @@ // Centralized conditional imports for all test sources -#if DEPLOYMENT_RUNTIME_OBJC || os(Linux) || os(Android) -@_exported import Foundation -@_exported import XCTest -#else +#if !DEPLOYMENT_RUNTIME_OBJC && canImport(SwiftFoundation) && canImport(SwiftXCTest) @_exported import SwiftFoundation @_exported import SwiftXCTest +#else +@_exported import Foundation +@_exported import XCTest #endif diff --git a/TestFoundation/TestStream.swift b/TestFoundation/TestStream.swift index f703fb45101..6f8216a9a6f 100644 --- a/TestFoundation/TestStream.swift +++ b/TestFoundation/TestStream.swift @@ -7,12 +7,10 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if NS_FOUNDATION_ALLOWS_TESTABLE_IMPORT - #if canImport(SwiftFoundation) - @testable import SwiftFoundation - #else - @testable import Foundation - #endif +#if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC + @testable import SwiftFoundation +#else + @testable import Foundation #endif diff --git a/TestFoundation/xdgTestHelper/main.swift b/TestFoundation/xdgTestHelper/main.swift index 29591e11abe..dd8dbe98ece 100644 --- a/TestFoundation/xdgTestHelper/main.swift +++ b/TestFoundation/xdgTestHelper/main.swift @@ -7,10 +7,10 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // -#if DEPLOYMENT_RUNTIME_OBJC || os(Linux) || os(Android) -import Foundation -#else +#if canImport(SwiftFoundation) && !DEPLOYMENT_RUNTIME_OBJC import SwiftFoundation +#else +import Foundation #endif enum HelperCheckStatus : Int32 { From 66224ee4f8d1b887231a351d4f37a2f805a578f8 Mon Sep 17 00:00:00 2001 From: Lily Vulcano Date: Mon, 8 Apr 2019 13:57:36 -0700 Subject: [PATCH 79/81] Parity: Bundle.classNamed(_:) and Bundle.principalClass. .classNamed(_:) can now be used, though with a limitation noted in the comments. .principalClass is now implemented in terms of .classNamed(_:) --- Foundation/Bundle.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Foundation/Bundle.swift b/Foundation/Bundle.swift index 0a2601edded..95c1cb9fdc8 100644 --- a/Foundation/Bundle.swift +++ b/Foundation/Bundle.swift @@ -292,8 +292,18 @@ open class Bundle: NSObject { } } - open func classNamed(_ className: String) -> AnyClass? { NSUnimplemented() } - open var principalClass: AnyClass? { NSUnimplemented() } + open func classNamed(_ className: String) -> AnyClass? { + // FIXME: This will return a class that may not be associated with the receiver. https://bugs.swift.org/browse/SR-10347. + guard isLoaded || load() else { return nil } + return NSClassFromString(className) + } + + open var principalClass: AnyClass? { + // NB: Cross-platform Swift doesn't have a notion of 'the first class in the ObjC segment' that ObjC platforms have. For swift-corelibs-foundation, if a bundle doesn't have a principal class named, the principal class is nil. + guard let name = infoDictionary?["NSPrincipalClass"] as? String else { return nil } + return classNamed(name) + } + open var preferredLocalizations: [String] { return Bundle.preferredLocalizations(from: localizations) } From 8c24a188496f044fa14d08f35a8a76160e58e7d4 Mon Sep 17 00:00:00 2001 From: Karthik Date: Fri, 8 Mar 2019 13:16:10 -0800 Subject: [PATCH 80/81] URLCache: init method and first time sqlite database setup implemented * init method implemented * URLCache.shared singleton object created with 4MB of memory space and 20MB of disk space * Directory and database file Cache.db file created under local directory * Sqlite Tables and Indices created in database * Unit tests added for URLCache to verify directory, file, tables and indices --- Foundation.xcodeproj/project.pbxproj | 5 + Foundation/URLCache.swift | 167 ++++++++++++++++++++++++++- TestFoundation/TestURLCache.swift | 91 +++++++++++++++ TestFoundation/main.swift | 1 + 4 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 TestFoundation/TestURLCache.swift diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj index c5fd59c6e88..63764470a58 100644 --- a/Foundation.xcodeproj/project.pbxproj +++ b/Foundation.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 159884921DCC877700E3314C /* TestHTTPCookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 159884911DCC877700E3314C /* TestHTTPCookieStorage.swift */; }; 15F10CDC218909BF00D88114 /* TestNSCalendar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F10CDB218909BF00D88114 /* TestNSCalendar.swift */; }; 231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestDecimal.swift */; }; + 25EB1806223334D30053EE59 /* TestURLCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25EB1805223334D30053EE59 /* TestURLCache.swift */; }; 294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; }; 2EBE67A51C77BF0E006583D5 /* TestDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */; }; 3E55A2331F52463B00082000 /* TestUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E55A2321F52463B00082000 /* TestUnit.swift */; }; @@ -574,6 +575,7 @@ 15F10CDB218909BF00D88114 /* TestNSCalendar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNSCalendar.swift; sourceTree = ""; }; 22B9C1E01C165D7A00DECFF9 /* TestDate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDate.swift; sourceTree = ""; }; 231503DA1D8AEE5D0061694D /* TestDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDecimal.swift; sourceTree = ""; }; + 25EB1805223334D30053EE59 /* TestURLCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestURLCache.swift; sourceTree = ""; }; 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = ""; }; 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDateFormatter.swift; sourceTree = ""; }; 3E55A2321F52463B00082000 /* TestUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUnit.swift; sourceTree = ""; }; @@ -1658,6 +1660,7 @@ 5B6F17961C48631C00935030 /* TestUtils.swift */, 03B6F5831F15F339004F25AF /* TestURLProtocol.swift */, 3E55A2321F52463B00082000 /* TestUnit.swift */, + 25EB1805223334D30053EE59 /* TestURLCache.swift */, ); name = Tests; sourceTree = ""; @@ -2242,6 +2245,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2624,6 +2628,7 @@ 5B13B33E1C582D4C00651CE2 /* TestProcessInfo.swift in Sources */, 5B13B33F1C582D4C00651CE2 /* TestPropertyListSerialization.swift in Sources */, 5B13B32C1C582D4C00651CE2 /* TestDate.swift in Sources */, + 25EB1806223334D30053EE59 /* TestURLCache.swift in Sources */, C7DE1FCC21EEE67200174F35 /* TestUUID.swift in Sources */, 231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */, 7900433C1CACD33E00ECCBF1 /* TestNSPredicate.swift in Sources */, diff --git a/Foundation/URLCache.swift b/Foundation/URLCache.swift index 561f31cd31c..16e67a40e89 100644 --- a/Foundation/URLCache.swift +++ b/Foundation/URLCache.swift @@ -7,6 +7,7 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // +import SQLite3 /*! @enum URLCache.StoragePolicy @@ -123,6 +124,26 @@ open class CachedURLResponse : NSObject, NSSecureCoding, NSCopying { open class URLCache : NSObject { + private static let sharedSyncQ = DispatchQueue(label: "org.swift.URLCache.sharedSyncQ") + + private static var sharedCache: URLCache? { + willSet { + URLCache.sharedCache?.syncQ.sync { + URLCache.sharedCache?._databaseClient?.close() + URLCache.sharedCache?.flushDatabase() + } + } + didSet { + URLCache.sharedCache?.syncQ.sync { + URLCache.sharedCache?.setupCacheDatabaseIfNotExist() + } + } + } + + private let syncQ = DispatchQueue(label: "org.swift.URLCache.syncQ") + private let _baseDiskPath: String? + private var _databaseClient: _CacheSQLiteClient? + /*! @method sharedURLCache @abstract Returns the shared URLCache instance. @@ -142,10 +163,22 @@ open class URLCache : NSObject { */ open class var shared: URLCache { get { - NSUnimplemented() + return sharedSyncQ.sync { + if let cache = sharedCache { + return cache + } else { + let fourMegaByte = 4 * 1024 * 1024 + let twentyMegaByte = 20 * 1024 * 1024 + let cacheDirectoryPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.path ?? "\(NSHomeDirectory())/Library/Caches/" + let path = "\(cacheDirectoryPath)\(Bundle.main.bundleIdentifier ?? UUID().uuidString)" + let cache = URLCache(memoryCapacity: fourMegaByte, diskCapacity: twentyMegaByte, diskPath: path) + sharedCache = cache + return cache + } + } } set { - NSUnimplemented() + sharedSyncQ.sync { sharedCache = newValue } } } @@ -162,7 +195,13 @@ open class URLCache : NSObject { @result an initialized URLCache, with the given capacity, backed by disk. */ - public init(memoryCapacity: Int, diskCapacity: Int, diskPath path: String?) { NSUnimplemented() } + public init(memoryCapacity: Int, diskCapacity: Int, diskPath path: String?) { + self.memoryCapacity = memoryCapacity + self.diskCapacity = diskCapacity + self._baseDiskPath = path + + super.init() + } /*! @method cachedResponseForRequest: @@ -244,6 +283,18 @@ open class URLCache : NSObject { @result the current usage of the on-disk cache of the receiver. */ open var currentDiskUsage: Int { NSUnimplemented() } + + private func flushDatabase() { + guard let path = _baseDiskPath else { return } + + do { + let dbPath = path.appending("/Cache.db") + try FileManager.default.removeItem(atPath: dbPath) + } catch { + fatalError("Unable to flush database for URLCache: \(error.localizedDescription)") + } + } + } extension URLCache { @@ -251,3 +302,113 @@ extension URLCache { public func getCachedResponse(for dataTask: URLSessionDataTask, completionHandler: (CachedURLResponse?) -> Void) { NSUnimplemented() } public func removeCachedResponse(for dataTask: URLSessionDataTask) { NSUnimplemented() } } + +extension URLCache { + + private func setupCacheDatabaseIfNotExist() { + guard let path = _baseDiskPath else { return } + + if !FileManager.default.fileExists(atPath: path) { + do { + try FileManager.default.createDirectory(atPath: path, withIntermediateDirectories: true) + } catch { + fatalError("Unable to create directories for URLCache: \(error.localizedDescription)") + } + } + + // Close the currently opened database connection(if any), before creating/replacing the db file + _databaseClient?.close() + + let dbPath = path.appending("/Cache.db") + if !FileManager.default.createFile(atPath: dbPath, contents: nil, attributes: nil) { + fatalError("Unable to setup database for URLCache") + } + + _databaseClient = _CacheSQLiteClient(databasePath: dbPath) + if _databaseClient == nil { + _databaseClient?.close() + flushDatabase() + fatalError("Unable to setup database for URLCache") + } + + if !createTables() { + _databaseClient?.close() + flushDatabase() + fatalError("Unable to setup database for URLCache: Tables not created") + } + + if !createIndicesForTables() { + _databaseClient?.close() + flushDatabase() + fatalError("Unable to setup database for URLCache: Indices not created for tables") + } + } + + private func createTables() -> Bool { + guard _databaseClient != nil else { + fatalError("Cannot create table before database setup") + } + + let tableSQLs = [ + "CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY, version INTEGER, hash_value VARCHAR, storage_policy INTEGER, request_key VARCHAR, time_stamp DATETIME, partition VARCHAR)", + "CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, isDataOnFS INTEGER, receiver_data BLOB)", + "CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB)", + "CREATE TABLE cfurl_cache_schema_version(schema_version INTEGER)" + ] + + for sql in tableSQLs { + if let isSuccess = _databaseClient?.execute(sql: sql), !isSuccess { + return false + } + } + + return true + } + + private func createIndicesForTables() -> Bool { + guard _databaseClient != nil else { + fatalError("Cannot create table before database setup") + } + + let indicesSQLs = [ + "CREATE INDEX proto_props_index ON cfurl_cache_blob_data(entry_ID)", + "CREATE INDEX receiver_data_index ON cfurl_cache_receiver_data(entry_ID)", + "CREATE INDEX request_key_index ON cfurl_cache_response(request_key)", + "CREATE INDEX time_stamp_index ON cfurl_cache_response(time_stamp)" + ] + + for sql in indicesSQLs { + if let isSuccess = _databaseClient?.execute(sql: sql), !isSuccess { + return false + } + } + + return true + } + +} + +fileprivate struct _CacheSQLiteClient { + + private var database: OpaquePointer? + + init?(databasePath: String) { + if sqlite3_open_v2(databasePath, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil) != SQLITE_OK { + return nil + } + } + + func execute(sql: String) -> Bool { + guard let db = database else { return false } + + return sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK + } + + mutating func close() { + guard let db = database else { return } + + sqlite3_close_v2(db) + database = nil + } + +} diff --git a/TestFoundation/TestURLCache.swift b/TestFoundation/TestURLCache.swift new file mode 100644 index 00000000000..b290969ec6e --- /dev/null +++ b/TestFoundation/TestURLCache.swift @@ -0,0 +1,91 @@ +// +// TestURLCache.swift +// TestFoundation +// +// Created by Karthikkeyan Bala Sundaram on 3/8/19. +// Copyright © 2019 Apple. All rights reserved. +// + +import SQLite3 + +class TestURLCache: XCTestCase { + + static var allTests: [(String, (TestURLCache) -> () throws -> Void)] { + return [ + ("test_cacheFileAndDirectorySetup", test_cacheFileAndDirectorySetup), + ("test_cacheDatabaseTables", test_cacheDatabaseTables), + ("test_cacheDatabaseIndices", test_cacheDatabaseIndices), + ] + } + + private var cacheDirectoryPath: String { + if let path = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.path { + return "\(path)/org.swift.TestFoundation" + } else { + return "\(NSHomeDirectory())/Library/Caches/org.swift.TestFoundation" + } + } + + private var cacheDatabasePath: String { + return "\(cacheDirectoryPath)/Cache.db" + } + + func test_cacheFileAndDirectorySetup() { + let _ = URLCache.shared + + XCTAssertTrue(FileManager.default.fileExists(atPath: cacheDirectoryPath)) + XCTAssertTrue(FileManager.default.fileExists(atPath: cacheDatabasePath)) + } + + func test_cacheDatabaseTables() { + let _ = URLCache.shared + + var db: OpaquePointer? = nil + let openDBResult = sqlite3_open_v2(cacheDatabasePath, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil) + XCTAssertTrue(openDBResult == SQLITE_OK, "Unable to open database") + + var statement: OpaquePointer? = nil + let prepareResult = sqlite3_prepare_v2(db!, "select tbl_name from sqlite_master where type='table'", -1, &statement, nil) + XCTAssertTrue(prepareResult == SQLITE_OK, "Unable to prepare list tables statement") + + var tables = ["cfurl_cache_response": false, "cfurl_cache_receiver_data": false, "cfurl_cache_blob_data": false, "cfurl_cache_schema_version": false] + while sqlite3_step(statement!) == SQLITE_ROW { + let tableName = String(cString: sqlite3_column_text(statement!, 0)) + tables[tableName] = true + } + + let tablesNotExist = tables.filter({ !$0.value }) + if tablesNotExist.count == tables.count { + XCTFail("No tables created for URLCache") + } + + XCTAssertTrue(tablesNotExist.count == 0, "Table(s) not created: \(tablesNotExist.map({ $0.key }).joined(separator: ", "))") + sqlite3_close_v2(db!) + } + + func test_cacheDatabaseIndices() { + let _ = URLCache.shared + + var db: OpaquePointer? = nil + let openDBResult = sqlite3_open_v2(cacheDatabasePath, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil) + XCTAssertTrue(openDBResult == SQLITE_OK, "Unable to open database") + + var statement: OpaquePointer? = nil + let prepareResult = sqlite3_prepare_v2(db!, "select name from sqlite_master where type='index'", -1, &statement, nil) + XCTAssertTrue(prepareResult == SQLITE_OK, "Unable to prepare list tables statement") + + var indices = ["proto_props_index": false, "receiver_data_index": false, "request_key_index": false, "time_stamp_index": false] + while sqlite3_step(statement!) == SQLITE_ROW { + let name = String(cString: sqlite3_column_text(statement!, 0)) + indices[name] = true + } + + let indicesNotExist = indices.filter({ !$0.value }) + if indicesNotExist.count == indices.count { + XCTFail("No index created for URLCache") + } + + XCTAssertTrue(indicesNotExist.count == 0, "Indices not created: \(indicesNotExist.map({ $0.key }).joined(separator: ", "))") + } + +} diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift index b5a4684c66f..74c6c553ae2 100644 --- a/TestFoundation/main.swift +++ b/TestFoundation/main.swift @@ -80,6 +80,7 @@ XCTMain([ testCase(TestTimer.allTests), testCase(TestTimeZone.allTests), testCase(TestURL.allTests), + testCase(TestURLCache.allTests), testCase(TestURLComponents.allTests), testCase(TestURLCredential.allTests), testCase(TestURLProtectionSpace.allTests), From be57ae3cadbffdf29e8ea62a79a0491763c499df Mon Sep 17 00:00:00 2001 From: Karthik Date: Wed, 13 Mar 2019 15:47:03 -0700 Subject: [PATCH 81/81] Database connection closed at the end of test_cacheDatabaseIndices unit test --- TestFoundation/TestURLCache.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/TestFoundation/TestURLCache.swift b/TestFoundation/TestURLCache.swift index b290969ec6e..dcfe10fb245 100644 --- a/TestFoundation/TestURLCache.swift +++ b/TestFoundation/TestURLCache.swift @@ -86,6 +86,7 @@ class TestURLCache: XCTestCase { } XCTAssertTrue(indicesNotExist.count == 0, "Indices not created: \(indicesNotExist.map({ $0.key }).joined(separator: ", "))") + sqlite3_close_v2(db!) } }