-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Enable String, URL and XML-related types for WASI #3051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@swift-ci please test |
Quick q: NSCoding only has a handful of places where it does file access; what's up with removing all that? |
AFAIR NSCoding was tightly coupled with FS access, so it was easier to disable it. We also weren't expecting people to use APIs from Objective-C era on Wasm anyway. I can try and partially re-enable NSCoding w/o FS access if you think that makes more sense. |
e9b0b82
to
553f373
Compare
@swift-ci please test |
@millenomi I've excluded changes related to |
Sources/Foundation/NSURL.swift
Outdated
@@ -528,13 +537,16 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { | |||
components.path = _pathByRemovingDots(pathComponents!) | |||
} | |||
|
|||
#if !os(WASI) | |||
if let filePath = components.path, isFileURL { | |||
return URL(fileURLWithPath: filePath, isDirectory: hasDirectoryPath, relativeTo: baseURL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why we need to disable (fileURL…
entirely rather than disable the paths in (fileURL…
that do actual I/O?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general the whole point of the library is to have a solid amount of compatible API surface; while obviously WASI cannot I/O I don't see why code cannot create file:
-scheme URLs at all.
Sources/Foundation/NSURL.swift
Outdated
@@ -573,6 +586,7 @@ open class NSURL : NSObject, NSSecureCoding, NSCopying { | |||
return CFURLGetTypeID() | |||
} | |||
|
|||
#if !os(WASI) | |||
open func removeAllCachedResourceValues() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, let's remove the implementation and not the API surface wherever possible.
Sources/Foundation/NSString.swift
Outdated
@@ -18,6 +18,7 @@ extension unichar { | |||
} | |||
} | |||
|
|||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is logically part of Bundle
's API surface. If Bundle
is available, this should do fallback, not be removed. Is Bundle
available in your code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have a very limited subset of Bundle
available in the fork. It's basically a shim that adds very basic Bundle.main
and Bundle.path(forResource:ofType:)
implementations, everything else from Bundle
is disabled. I excluded that from this PR as something that goes beyond a simple #if os(WASI)
check.
Let me know if that should be included, or if I should re-enable APIs relying on Bundle
here and tackle the whole Bundle
thing in a separate PR.
In case that would help, the shim looks like this:
open class Bundle {
public let bundlePath: String
public static let main = Bundle(path: "")!
public init?(path: String) {
bundlePath = path
}
// -----------------------------------------------------------------------------------
// MARK: - Path Resource Lookup - Instance
open func path(forResource name: String?, ofType ext: String?) -> String? {
path(forResource: name, ofType: ext, inDirectory: nil)
}
open func path(forResource name: String?, ofType ext: String?, inDirectory subpath: String?) -> String? {
guard let name = name else { return nil }
let subpathOrEmpty: String
if let subpath = subpath {
subpathOrEmpty = "\(subpath)/"
} else {
subpathOrEmpty = ""
}
if let ext = ext {
return "\(bundlePath)/\(subpathOrEmpty)\(name).\(ext)"
} else {
return "\(bundlePath)/\(subpathOrEmpty)\(name)"
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait; if there's no I/O access, how is path(forResource…
useful at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It allows us to utilize Swift package resources https://github.com/apple/swift-evolution/blob/main/proposals/0271-package-manager-resources.md
SwiftPM emits relative paths to package resources that WASI apps in the browser can load, like images, fonts, CSS etc.
Sources/Foundation/NSString.swift
Outdated
@@ -1365,6 +1369,7 @@ extension NSString { | |||
} | |||
} | |||
|
|||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re API surface removal avoidance: this should probably throw at a lower level rather than be removed.
@@ -116,6 +116,7 @@ internal class NSRegularExpressionCheckingResult: NSTextCheckingResult { | |||
super.init() | |||
} | |||
|
|||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've audited NSKeyedArchiver, and it looks like we can salvage basically all the implementation except for the few parts that dealing from reading bytes from a stream; it is not at all bound tightly to file I/O. If NSMutableData
exists, I see little reason to cut it right this second.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And its unarchiving counterpart.
Sources/Foundation/URL.swift
Outdated
@@ -534,6 +535,11 @@ public struct URL : ReferenceConvertible, Equatable { | |||
public init(fileURLWithPath path: String) { | |||
_url = URL._converted(from: NSURL(fileURLWithPath: path.isEmpty ? "." : path)) | |||
} | |||
#else | |||
public init(fileURLWithPath path: String) { | |||
_url = NSURL(string: "file://\(path)")! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's a space or other non-URL-encodable character, this will create an invalid URL. Nope, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this should at least check that path begins with a slash. Otherwise passing in "foo" results in the URL file://foo (i.e. with "foo" in the hostname position and no path)
@@ -85,6 +85,7 @@ open class XMLDocument : XMLNode { | |||
try self.init(data: data, options: mask) | |||
} | |||
|
|||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re API surface removal avoidance: this should probably throw at a lower level rather than be removed.
@@ -48,13 +48,15 @@ private func UTF8STRING(_ bytes: UnsafePointer<UInt8>?) -> String? { | |||
return nil | |||
} | |||
|
|||
#if !os(WASI) | |||
internal func _NSXMLParserCurrentParser() -> _CFXMLInterface? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume CFXML is not available at all on WASI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If so — if XML parsing does not exist at all on WASI — I'd rather not compile FoundationXML
at all for WASI rather than burden it with os(WASI)
checks.
If a type exists, I want it to offer as much of its API surface as it possibly can, and use runtime signaling for inabilities of its host. This allows e.g. packages to be ported without having to work through source compatibility errors. But for secondary-use API like XML, if it isn't supported at all, it's okay to just drop the entire surface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
XML parsing does exist on WASI, we were able to compile libxml without issues. Thanks for the feedback, I'll make the API surface available.
@@ -63,11 +65,13 @@ internal func _NSXMLParserExternalEntityWithURL(_ interface: _CFXMLInterface, ur | |||
if let allowedEntityURLs = parser.allowedExternalEntityURLs { | |||
if let url = URL(string: String(describing: urlStr)) { | |||
a = url | |||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we restore {NS,}URL(fileURL…
, all these fallout consequences should be removed. (This one is in XML, which is dependent on whether the module is compiled for WASI, but this applies in general.)
@@ -993,8 +1031,10 @@ extension NSObject { | |||
func setupXMLParsing() { | |||
_CFSetupXMLInterface() | |||
_CFSetupXMLBridgeIfNeededUsingBlock { | |||
#if !os(WASI) | |||
__CFSwiftXMLParserBridge.CFBridge = CF.originalBridge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does CFXML work at all without access to CF functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've re-enabled this in the updated commit
let chunk = data.withUnsafeMutableBytes { (rawBuffer: UnsafeMutableRawBufferPointer) -> Data in | ||
let ptr = rawBuffer.baseAddress!.advanced(by: range.location) | ||
return Data(bytesNoCopy: ptr, count: range.length, deallocator: .none) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This escapes a scoped inner-pointer. Since Data is self-slicing, can't you just use a regular slice?
internal func parse(from data: Data) -> Bool { | ||
var data = data | ||
var result = true | ||
let buffer = malloc(_chunkSize)!.bindMemory(to: UInt8.self, capacity: _chunkSize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's used in open func parse()
below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean the buffer allocated on this line. It doesn’t seem to be used AFAICT.
47d8ff4
to
f95d19a
Compare
@swift-ci please test |
@millenomi I've cleaned it up, re-enabled as much as possible based on your feedback. There's still API surface relying on |
54fa78b
to
cb3e8c9
Compare
@swift-ci please test |
I feel the usage on Stream subclasses is low enough that I'm less worried. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some nits :)
@@ -53,7 +53,7 @@ open class NSTextCheckingResult: NSObject, NSCopying, NSSecureCoding { | |||
open class var supportsSecureCoding: Bool { | |||
NSRequiresConcreteImplementation() | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to revert the unrelated whitespace changes?
Sources/Foundation/NSURL.swift
Outdated
@@ -841,6 +850,9 @@ extension NSURL { | |||
|
|||
open func appendingPathComponent(_ pathComponent: String) -> URL? { | |||
var result : URL? = appendingPathComponent(pathComponent, isDirectory: false) | |||
|
|||
// File URLs can't be handled on WASI without file system access | |||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#if !os(WASI) | |
#if !os(WASI) |
Sources/Foundation/NSURL.swift
Outdated
@@ -852,6 +864,7 @@ extension NSURL { | |||
} | |||
|
|||
} | |||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#endif | |
#endif |
@@ -74,6 +74,7 @@ open class PropertyListSerialization : NSObject { | |||
} | |||
} | |||
|
|||
#if !os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly outdent here for consistency?
// called to start the event-driven parse. Returns YES in the event of a successful parse, and NO in case of error. | ||
open func parse() -> Bool { | ||
#if os(WASI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here.
21cee4a
to
5809266
Compare
5809266
to
7688233
Compare
@swift-ci please test |
@MaxDesiatov I'm looking today. |
Thanks! I think I've addressed all of the current feedback, happy to update it again or answer any questions if anything else comes up. |
LGTM. |
APIs related to file access (i.e. those depending on
NSCoding
) and multi-threading (those that requireNSRunLoop
) had to be disabled for WASI/Wasm to make this work.