Skip to content

Commit 301dc00

Browse files
Add doc comments for public APIs (Part 1)
1 parent 51331c6 commit 301dc00

File tree

3 files changed

+153
-16
lines changed

3 files changed

+153
-16
lines changed

Diff for: Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift

+62-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
import _CJavaScriptKit
22

3+
/// `JSFunction` represents a function in JavaScript and supports new object instantiation.
4+
/// This type can be callable as a function using `callAsFunction`.
5+
///
6+
/// e.g.
7+
/// ```swift
8+
/// let alert: JSFunction = JSObject.global.alert.function!
9+
/// // Call `JSFunction` as a function
10+
/// alert("Hello, world")
11+
/// ```
12+
///
313
public class JSFunction: JSObject {
14+
15+
/// Call this function with given `arguments` and binding given `this` as context.
16+
/// - Parameters:
17+
/// - this: The value to be passed as the `this` parameter to this function.
18+
/// - arguments: Arguments to be passed to this function.
19+
/// - Returns: The result of this call.
420
@discardableResult
521
public func callAsFunction(this: JSObject? = nil, arguments: [JSValueConvertible]) -> JSValue {
622
let result = arguments.withRawJSValues { rawValues in
@@ -24,19 +40,22 @@ public class JSFunction: JSObject {
2440
return result.jsValue()
2541
}
2642

43+
/// A variadic arguments version of `callAsFunction`.
2744
@discardableResult
2845
public func callAsFunction(this: JSObject? = nil, _ arguments: JSValueConvertible...) -> JSValue {
2946
self(this: this, arguments: arguments)
3047
}
3148

32-
public func new(_ arguments: JSValueConvertible...) -> JSObject {
33-
new(arguments: arguments)
34-
}
35-
36-
// Guaranteed to return an object because either:
37-
// a) the constructor explicitly returns an object, or
38-
// b) the constructor returns nothing, which causes JS to return the `this` value, or
39-
// c) the constructor returns undefined, null or a non-object, in which case JS also returns `this`.
49+
/// Instantiate an object from this function as a constructor.
50+
///
51+
/// Guaranteed to return an object because either:
52+
///
53+
/// - a. the constructor explicitly returns an object, or
54+
/// - b. the constructor returns nothing, which causes JS to return the `this` value, or
55+
/// - c. the constructor returns undefined, null or a non-object, in which case JS also returns `this`.
56+
///
57+
/// - Parameter arguments: Arguments to be passed to this constructor function.
58+
/// - Returns: A new instance of this constructor.
4059
public func new(arguments: [JSValueConvertible]) -> JSObject {
4160
arguments.withRawJSValues { rawValues in
4261
rawValues.withUnsafeBufferPointer { bufferPointer in
@@ -52,6 +71,11 @@ public class JSFunction: JSObject {
5271
}
5372
}
5473

74+
/// A variadic arguments version of `new`.
75+
public func new(_ arguments: JSValueConvertible...) -> JSObject {
76+
new(arguments: arguments)
77+
}
78+
5579
@available(*, unavailable, message: "Please use JSClosure instead")
5680
public static func from(_: @escaping ([JSValue]) -> JSValue) -> JSFunction {
5781
fatalError("unavailable")
@@ -62,33 +86,59 @@ public class JSFunction: JSObject {
6286
}
6387
}
6488

89+
/// `JSClosure` represents a JavaScript function whose body is written in Swift.
90+
/// This type can be passed as a callback handler to JavaScript functions.
91+
/// Note that the lifetime of `JSClosure` should be managed by users manually
92+
/// due to GC boundary between Swift and JavaScript.
93+
/// For further discussion, see also [swiftwasm/JavaScriptKit #33](https://github.com/swiftwasm/JavaScriptKit/pull/33)
94+
///
95+
/// e.g.
96+
/// ```swift
97+
/// let eventListenter = JSClosure { _ in
98+
/// ...
99+
/// return JSValue.undefined
100+
/// }
101+
///
102+
/// button.addEventListener!("click", JSValue.function(eventListenter))
103+
/// ...
104+
/// button.removeEventListener!("click", JSValue.function(eventListenter))
105+
/// eventListenter.release()
106+
/// ```
107+
///
65108
public class JSClosure: JSFunction {
66109
static var sharedFunctions: [JavaScriptHostFuncRef: ([JSValue]) -> JSValue] = [:]
67110

68111
private var hostFuncRef: JavaScriptHostFuncRef = 0
69112

70113
private var isReleased = false
71-
114+
115+
/// Instantiate a new `JSClosure` with given function body.
116+
/// - Parameter body: The body of this function.
72117
public init(_ body: @escaping ([JSValue]) -> JSValue) {
118+
// 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
73119
super.init(id: 0)
74120
let objectId = ObjectIdentifier(self)
75121
let funcRef = JavaScriptHostFuncRef(bitPattern: Int32(objectId.hashValue))
122+
// 2. Retain the given body in static storage by `funcRef`.
76123
Self.sharedFunctions[funcRef] = body
77-
124+
// 3. Create a new JavaScript function which calls the given Swift function.
78125
var objectRef: JavaScriptObjectRef = 0
79126
_create_function(funcRef, &objectRef)
80127

81128
hostFuncRef = funcRef
82129
id = objectRef
83130
}
84-
131+
132+
/// A convenience initializer which assumes that the given body function returns `JSValue.undefined`
85133
convenience public init(_ body: @escaping ([JSValue]) -> ()) {
86134
self.init { (arguments: [JSValue]) -> JSValue in
87135
body(arguments)
88136
return .undefined
89137
}
90138
}
91-
139+
140+
/// Release this function resource.
141+
/// After calling `release`, calling this function from JavaScript will fail.
92142
public func release() {
93143
Self.sharedFunctions[hostFuncRef] = nil
94144
isReleased = true

Diff for: Sources/JavaScriptKit/FundamentalObjects/JSObject.swift

+45-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,36 @@
11
import _CJavaScriptKit
22

3+
/// `JSObject` represents an object in JavaScript and supports dynamic member lookup.
4+
/// Any member access like `object.foo` will dynamically request the JavaScript and Swift
5+
/// runtime bridge library for a member with the specified name in this object.
6+
///
7+
/// And this object supports to call a member method of the object.
8+
///
9+
/// e.g.
10+
/// ```swift
11+
/// let document = JSObject.global.document.object!
12+
/// let divElement = document.createElement!("div")
13+
/// ```
14+
///
15+
/// The lifetime of this object is managed by the JavaScript and Swift runtime bridge library with
16+
/// reference counting system.
317
@dynamicMemberLookup
418
public class JSObject: Equatable {
5-
internal var id: UInt32
6-
init(id: UInt32) {
19+
internal var id: JavaScriptObjectRef
20+
init(id: JavaScriptObjectRef) {
721
self.id = id
822
}
923

24+
/// Returns the `name` member method binding this object as `this` context.
25+
///
26+
/// e.g.
27+
/// ```swift
28+
/// let document = JSObject.global.document.object!
29+
/// let divElement = document.createElement!("div")
30+
/// ```
31+
///
32+
/// - Parameter name: The name of this object's member to access.
33+
/// - Returns: The `name` member method binding this object as `this` context.
1034
@_disfavoredOverload
1135
public subscript(dynamicMember name: String) -> ((JSValueConvertible...) -> JSValue)? {
1236
guard let function = self[name].function else { return nil }
@@ -15,30 +39,49 @@ public class JSObject: Equatable {
1539
}
1640
}
1741

42+
/// A convenience method of `subscript(_ name: String) -> JSValue`
43+
/// to access the member through Dynamic Member Lookup.
1844
public subscript(dynamicMember name: String) -> JSValue {
1945
get { self[name] }
2046
set { self[name] = newValue }
2147
}
2248

49+
/// Access the `name` member dynamically through JavaScript and Swift runtime bridge library.
50+
/// - Parameter name: The name of this object's member to access.
51+
/// - Returns: The value of the `name` member of this object.
2352
public subscript(_ name: String) -> JSValue {
2453
get { getJSValue(this: self, name: name) }
2554
set { setJSValue(this: self, name: name, value: newValue) }
2655
}
2756

57+
/// Access the `index` member dynamically through JavaScript and Swift runtime bridge library.
58+
/// - Parameter index: The index of this object's member to access.
59+
/// - Returns: The value of the `index` member of this object.
2860
public subscript(_ index: Int) -> JSValue {
2961
get { getJSValue(this: self, index: Int32(index)) }
3062
set { setJSValue(this: self, index: Int32(index), value: newValue) }
3163
}
3264

65+
/// Return `true` if this object is an instance of the `constructor`. Return `false`, if not.
66+
/// - Parameter constructor: The constructor function to check.
67+
/// - Returns: The result of `instanceof` in JavaScript environment.
3368
public func isInstanceOf(_ constructor: JSFunction) -> Bool {
3469
_instanceof(id, constructor.id)
3570
}
3671

3772
static let _JS_Predef_Value_Global: JavaScriptObjectRef = 0
73+
74+
/// A `JSObject` of the global scope object.
75+
/// This allows access to the global properties and global names by accessing the `JSObject` returned.
3876
public static let global = JSObject(id: _JS_Predef_Value_Global)
3977

4078
deinit { _release(id) }
4179

80+
/// Returns a Boolean value indicating whether two values points same objects.
81+
///
82+
/// - Parameters:
83+
/// - lhs: A object to compare.
84+
/// - rhs: Another object to compare.
4285
public static func == (lhs: JSObject, rhs: JSObject) -> Bool {
4386
return lhs.id == rhs.id
4487
}

Diff for: Sources/JavaScriptKit/JSValue.swift

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import _CJavaScriptKit
22

3+
/// `JSValue` represents a value in JavaScript.
34
public enum JSValue: Equatable {
45
case boolean(Bool)
56
case string(String)
@@ -9,45 +10,88 @@ public enum JSValue: Equatable {
910
case undefined
1011
case function(JSFunction)
1112

13+
/// Returns the `Bool` value of this JS value if its type is boolean.
14+
/// If not, returns `nil`.
1215
public var boolean: Bool? {
1316
switch self {
1417
case let .boolean(boolean): return boolean
1518
default: return nil
1619
}
1720
}
1821

22+
/// Returns the `String` value of this JS value if the type is string.
23+
/// If not, returns `nil`.
1924
public var string: String? {
2025
switch self {
2126
case let .string(string): return string
2227
default: return nil
2328
}
2429
}
2530

31+
/// Returns the `Double` value of this JS value if the type is number.
32+
/// If not, returns `nil`.
2633
public var number: Double? {
2734
switch self {
2835
case let .number(number): return number
2936
default: return nil
3037
}
3138
}
3239

40+
/// Returns the `JSObject` of this JS value if its type is object.
41+
/// If not, returns `nil`.
3342
public var object: JSObject? {
3443
switch self {
3544
case let .object(object): return object
3645
default: return nil
3746
}
3847
}
3948

40-
public var isNull: Bool { return self == .null }
41-
public var isUndefined: Bool { return self == .undefined }
49+
/// Returns the `JSFunction` of this JS value if its type is function.
50+
/// If not, returns `nil`.
4251
public var function: JSFunction? {
4352
switch self {
4453
case let .function(function): return function
4554
default: return nil
4655
}
4756
}
57+
58+
/// Returns the `true` if this JS value is null.
59+
/// If not, returns `false`.
60+
public var isNull: Bool { return self == .null }
61+
62+
/// Returns the `true` if this JS value is undefined.
63+
/// If not, returns `false`.
64+
public var isUndefined: Bool { return self == .undefined }
65+
4866
}
4967

5068
extension JSValue {
69+
70+
/// Deprecated: Please create `JSClosure` directly and manage its lifetime manually.
71+
///
72+
/// Migrate this usage
73+
///
74+
/// ```swift
75+
/// button.addEventListener!("click", JSValue.function { _ in
76+
/// ...
77+
/// return JSValue.undefined
78+
/// })
79+
/// ```
80+
///
81+
/// into below code.
82+
///
83+
/// ```swift
84+
/// let eventListenter = JSClosure { _ in
85+
/// ...
86+
/// return JSValue.undefined
87+
/// }
88+
///
89+
/// button.addEventListener!("click", JSValue.function(eventListenter))
90+
/// ...
91+
/// button.removeEventListener!("click", JSValue.function(eventListenter))
92+
/// eventListenter.release()
93+
/// ```
94+
@available(*, deprecated, message: "Please create JSClosure directly and manage its lifetime manually.")
5195
public static func function(_ body: @escaping ([JSValue]) -> JSValue) -> JSValue {
5296
.function(JSClosure(body))
5397
}

0 commit comments

Comments
 (0)