Skip to content
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

Support async/await and require Swift 5.5 #137

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattribute
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Sources/_CJavaScriptEventLoop/swift/* linguist-vendored
Sources/_CJavaScriptEventLoop/llvm/* linguist-vendored
5 changes: 2 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ jobs:
matrix:
os: [macos-10.15, macos-11, ubuntu-18.04, ubuntu-20.04]
toolchain:
- wasm-5.3.1-RELEASE
- wasm-5.4.0-RELEASE
- wasm-5.5-SNAPSHOT-2021-09-01-a
- wasm-DEVELOPMENT-SNAPSHOT-2021-09-29-a
- wasm-5.5-SNAPSHOT-2021-09-26-a
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
wasm-5.3.0-RELEASE
wasm-5.5-SNAPSHOT-2021-09-26-a
12 changes: 10 additions & 2 deletions Example/JavaScriptKitExample/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.2
// swift-tools-version:5.5

import PackageDescription

Expand All @@ -10,5 +10,13 @@ let package = Package(
),
],
dependencies: [.package(name: "JavaScriptKit", path: "../../")],
targets: [.target(name: "JavaScriptKitExample", dependencies: ["JavaScriptKit"])]
targets: [
.target(
name: "JavaScriptKitExample",
dependencies: [
.product(name: "JavaScriptKit", package: "JavaScriptKit"),
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
]
),
]
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import JavaScriptKit
import JavaScriptEventLoop

JavaScriptEventLoop.install()

let alert = JSObject.global.alert.function!
let document = JSObject.global.document
Expand All @@ -15,3 +18,15 @@ let listener = JSClosure { _ in
buttonElement.onclick = .object(listener)

_ = document.body.appendChild(buttonElement)

let fetch = JSObject.global.fetch.function!.async

func printZen() async {
let result = await try! fetch("https://api.github.com/zen").object!
let text = await try! result.asyncing.text!()
print(text)
}

JavaScriptEventLoop.runAsync {
await printZen()
}
2 changes: 2 additions & 0 deletions IntegrationTests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ benchmark: benchmark_setup run_benchmark
.PHONY: test
test: build_rt dist/PrimaryTests.wasm
node bin/primary-tests.js
concurrency_test: build_rt dist/ConcurrencyTests.wasm
node bin/concurrency-tests.js
8 changes: 7 additions & 1 deletion IntegrationTests/TestSuites/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.2
// swift-tools-version:5.5

import PackageDescription

Expand All @@ -15,6 +15,12 @@ let package = Package(
dependencies: [.package(name: "JavaScriptKit", path: "../../")],
targets: [
.target(name: "PrimaryTests", dependencies: ["JavaScriptKit"]),
.target(
name: "ConcurrencyTests",
dependencies: [
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
]
),
.target(name: "BenchmarkTests", dependencies: ["JavaScriptKit"]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import JavaScriptKit

var printTestNames = false
// Uncomment the next line to print the name of each test suite before running it.
// This will make it easier to debug any errors that occur on the JS side.
//printTestNames = true

func test(_ name: String, testBlock: () throws -> Void) throws {
if printTestNames { print(name) }
do {
try testBlock()
} catch {
print("Error in \(name)")
print(error)
throw error
}
}

func asyncTest(_ name: String, testBlock: () async throws -> Void) async throws -> Void {
if printTestNames { print(name) }
do {
await try testBlock()
} catch {
print("Error in \(name)")
print(error)
throw error
}
}

struct MessageError: Error {
let message: String
let file: StaticString
let line: UInt
let column: UInt
init(_ message: String, file: StaticString, line: UInt, column: UInt) {
self.message = message
self.file = file
self.line = line
self.column = column
}
}

func expectEqual<T: Equatable>(
_ lhs: T, _ rhs: T,
file: StaticString = #file, line: UInt = #line, column: UInt = #column
) throws {
if lhs != rhs {
throw MessageError("Expect to be equal \"\(lhs)\" and \"\(rhs)\"", file: file, line: line, column: column)
}
}

func expectCast<T, U>(
_ value: T, to type: U.Type = U.self,
file: StaticString = #file, line: UInt = #line, column: UInt = #column
) throws -> U {
guard let value = value as? U else {
throw MessageError("Expect \"\(value)\" to be \(U.self)", file: file, line: line, column: column)
}
return value
}

func expectObject(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSObject {
switch value {
case let .object(ref): return ref
default:
throw MessageError("Type of \(value) should be \"object\"", file: file, line: line, column: column)
}
}

func expectArray(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSArray {
guard let array = value.array else {
throw MessageError("Type of \(value) should be \"object\"", file: file, line: line, column: column)
}
return array
}

func expectFunction(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSFunction {
switch value {
case let .function(ref): return ref
default:
throw MessageError("Type of \(value) should be \"function\"", file: file, line: line, column: column)
}
}

func expectBoolean(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Bool {
switch value {
case let .boolean(bool): return bool
default:
throw MessageError("Type of \(value) should be \"boolean\"", file: file, line: line, column: column)
}
}

func expectNumber(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Double {
switch value {
case let .number(number): return number
default:
throw MessageError("Type of \(value) should be \"number\"", file: file, line: line, column: column)
}
}

func expectString(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> String {
switch value {
case let .string(string): return String(string)
default:
throw MessageError("Type of \(value) should be \"string\"", file: file, line: line, column: column)
}
}

func expectAsyncThrow<T>(_ body: @autoclosure () async throws -> T, file: StaticString = #file, line: UInt = #line, column: UInt = #column) async throws -> Error {
do {
_ = await try body()
} catch {
return error
}
throw MessageError("Expect to throw an exception", file: file, line: line, column: column)
}

func expectNotNil<T>(_ value: T?, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws {
switch value {
case .some: return
case .none:
throw MessageError("Expect a non-nil value", file: file, line: line, column: column)
}
}
40 changes: 40 additions & 0 deletions IntegrationTests/TestSuites/Sources/ConcurrencyTests/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import JavaScriptEventLoop
import JavaScriptKit

JavaScriptEventLoop.install()

try JavaScriptEventLoop.runAsync {
struct E: Error, Equatable {
let value: Int
}

await try asyncTest("Task.runDetached value") {
let handle = Task.runDetached { 1 }
await try expectEqual(handle.get(), 1)
}

await try asyncTest("Task.runDetached throws") {
let handle = Task.runDetached {
throw E(value: 2)
}
let error = await try expectAsyncThrow(await handle.get())
let e = try expectCast(error, to: E.self)
try expectEqual(e, E(value: 2))
}

await try asyncTest("await resolved Promise") {
let p = JSPromise(resolver: { resolve in
resolve(.success(1))
})
await try expectEqual(p.await(), 1)
}

await try asyncTest("await rejected Promise") {
let p = JSPromise(resolver: { resolve in
resolve(.failure(.number(3)))
})
let error = await try expectAsyncThrow(await p.await())
let jsValue = try expectCast(error, to: JSValue.self)
try expectEqual(jsValue, 3)
}
}
15 changes: 15 additions & 0 deletions IntegrationTests/bin/concurrency-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { startWasiTask } = require("../lib");

global.fetch = require('node-fetch');
global.sleep = function () {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}

startWasiTask("./dist/ConcurrencyTests.wasm").catch((err) => {
console.log(err);
process.exit(1);
});
28 changes: 26 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,41 @@
// swift-tools-version:5.2
// swift-tools-version:5.3

import PackageDescription

let package = Package(
name: "JavaScriptKit",
products: [
.library(name: "JavaScriptKit", targets: ["JavaScriptKit"]),
.library(name: "JavaScriptEventLoop", targets: ["JavaScriptEventLoop"]),
],
targets: [
.target(
name: "JavaScriptKit",
dependencies: ["_CJavaScriptKit"]
),
.target(
name: "JavaScriptEventLoop",
dependencies: ["JavaScriptKit", "_CJavaScriptEventLoop"],
swiftSettings: [
.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"]),
]
),
.target(name: "_CJavaScriptKit"),
]
.target(
name: "_CJavaScriptEventLoop",
dependencies: ["_CJavaScriptKit"],
exclude: [
"README", "LICENSE-llvm", "LICENSE-swift", "scripts",
"include/swift/ABI/MetadataKind.def",
"include/swift/ABI/ValueWitness.def",
"include/swift/AST/ReferenceStorage.def",
"include/swift/Demangling/DemangleNodes.def",
"include/swift/Demangling/ValueWitnessMangling.def",
],
linkerSettings: [
.linkedLibrary("swift_Concurrency", .when(platforms: [.wasi])),
]
),
],
cxxLanguageStandard: .cxx14
)
Loading