Skip to content

Commit 5ec4e76

Browse files
Merge pull request #28670 from ravikandhadai/oslog-ns-object-extension
[os log][stdlib/private] Add extensions for logging NSObjects.
2 parents 7309868 + f705720 commit 5ec4e76

File tree

6 files changed

+336
-89
lines changed

6 files changed

+336
-89
lines changed

stdlib/private/OSLog/CMakeLists.txt

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ add_swift_target_library(swiftOSLogPrototype
66
OSLogMessage.swift
77
OSLogIntegerTypes.swift
88
OSLogStringTypes.swift
9+
OSLogNSObjectType.swift
910

10-
SWIFT_MODULE_DEPENDS_IOS Darwin os
11-
SWIFT_MODULE_DEPENDS_OSX Darwin os
12-
SWIFT_MODULE_DEPENDS_TVOS Darwin os
13-
SWIFT_MODULE_DEPENDS_WATCHOS Darwin os
11+
SWIFT_MODULE_DEPENDS_IOS Darwin os ObjectiveC
12+
SWIFT_MODULE_DEPENDS_OSX Darwin os ObjectiveC
13+
SWIFT_MODULE_DEPENDS_TVOS Darwin os ObjectiveC
14+
SWIFT_MODULE_DEPENDS_WATCHOS Darwin os ObjectiveC
1415
TARGET_SDKS ALL_APPLE_PLATFORMS
1516
SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
1617
INSTALL_IN_COMPONENT never_install

stdlib/private/OSLog/OSLog.swift

+12-9
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public struct Logger {
4141
@_transparent
4242
@_optimize(none)
4343
public func log(level: OSLogType = .default, _ message: OSLogMessage) {
44-
osLog(logObject, level, message)
44+
osLog(log: logObject, level: level, message)
4545
}
4646

4747
// TODO: define overloads for logging at specific levels: debug, info, notice,
@@ -52,12 +52,11 @@ public struct Logger {
5252
/// extract the format string, serialize the arguments to a byte buffer,
5353
/// and pass them to the OS logging system.
5454
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
55-
@usableFromInline
5655
@_transparent
5756
@_optimize(none)
58-
internal func osLog(
59-
_ logObject: OSLog,
60-
_ logLevel: OSLogType,
57+
public func osLog(
58+
log logObject: OSLog,
59+
level logLevel: OSLogType,
6160
_ message: OSLogMessage
6261
) {
6362
// Compute static constants first so that they can be folded by
@@ -94,9 +93,12 @@ internal func osLog(
9493
bufferMemory,
9594
uint32bufferSize)
9695

97-
// The following operation extends the lifetime of stringStorageObjects
98-
// and also of the objects stored in it till this point.
99-
stringStorageObjects.removeAll()
96+
// The following operation extends the lifetime of argumentClosures,
97+
// stringStorageObjects, and also of the objects stored in them till this
98+
// point. This is necessary because __os_log_impl is passed internal pointers
99+
// to the objects/strings stored in these arrays.
100+
_fixLifetime(argumentClosures)
101+
_fixLifetime(stringStorageObjects)
100102
bufferMemory.deallocate()
101103
}
102104

@@ -136,6 +138,7 @@ func _checkFormatStringAndBuffer(
136138
formatString,
137139
UnsafeBufferPointer(start: UnsafePointer(bufferMemory), count: bufferSize))
138140

139-
stringStorageObjects.removeAll()
141+
_fixLifetime(argumentClosures)
142+
_fixLifetime(stringStorageObjects)
140143
bufferMemory.deallocate()
141144
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//===----------------- OSLogNSObjectType.swift ----------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// This file defines extensions for interpolating NSObject into a OSLogMesage.
14+
// It defines `appendInterpolation` function for NSObject type. It also defines
15+
// extensions for generating an os_log format string for NSObjects (using the
16+
// format specifier %@) and for serializing NSObject into the argument buffer
17+
// passed to os_log ABIs.
18+
//
19+
// The `appendInterpolation` function defined in this file accept formatting
20+
// and privacy options along with the interpolated expression as shown below:
21+
//
22+
// "\(x, privacy: .public\)"
23+
import ObjectiveC
24+
25+
extension OSLogInterpolation {
26+
27+
/// Define interpolation for expressions of type NSObject.
28+
/// - Parameters:
29+
/// - argumentObject: the interpolated expression of type NSObject, which is autoclosured.
30+
/// - privacy: a privacy qualifier which is either private or public. Default is private.
31+
/// TODO: create a specifier to denote auto-inferred privacy level and make it default.
32+
@_semantics("constant_evaluable")
33+
@inlinable
34+
@_optimize(none)
35+
public mutating func appendInterpolation(
36+
_ argumentObject: @autoclosure @escaping () -> NSObject,
37+
privacy: Privacy = .private
38+
) {
39+
guard argumentCount < maxOSLogArgumentCount else { return }
40+
41+
let isPrivateArgument = isPrivate(privacy)
42+
formatString += getNSObjectFormatSpecifier(isPrivateArgument)
43+
addNSObjectHeaders(isPrivateArgument)
44+
45+
arguments.append(argumentObject)
46+
argumentCount += 1
47+
}
48+
49+
/// Update preamble and append argument headers based on the parameters of
50+
/// the interpolation.
51+
@_semantics("constant_evaluable")
52+
@inlinable
53+
@_optimize(none)
54+
internal mutating func addNSObjectHeaders(_ isPrivate: Bool) {
55+
// Append argument header.
56+
let header = getArgumentHeader(isPrivate: isPrivate, type: .object)
57+
arguments.append(header)
58+
59+
// Append number of bytes needed to serialize the argument.
60+
let byteCount = pointerSizeInBytes()
61+
arguments.append(UInt8(byteCount))
62+
63+
// Increment total byte size by the number of bytes needed for this
64+
// argument, which is the sum of the byte size of the argument and
65+
// two bytes needed for the headers.
66+
totalBytesForSerializingArguments += byteCount + 2
67+
68+
preamble = getUpdatedPreamble(isPrivate: isPrivate, isScalar: false)
69+
}
70+
71+
/// Construct an os_log format specifier from the given parameters.
72+
/// This function must be constant evaluable and all its arguments
73+
/// must be known at compile time.
74+
@inlinable
75+
@_semantics("constant_evaluable")
76+
@_effects(readonly)
77+
@_optimize(none)
78+
internal func getNSObjectFormatSpecifier(_ isPrivate: Bool) -> String {
79+
// TODO: create a specifier to denote auto-inferred privacy.
80+
return isPrivate ? "%{private}@" : "%{public}@"
81+
}
82+
}
83+
84+
extension OSLogArguments {
85+
/// Append an (autoclosured) interpolated expression of type NSObject, passed to
86+
/// `OSLogMessage.appendInterpolation`, to the array of closures tracked
87+
/// by this instance.
88+
@_semantics("constant_evaluable")
89+
@inlinable
90+
@_optimize(none)
91+
internal mutating func append(_ value: @escaping () -> NSObject) {
92+
argumentClosures.append({ (position, _) in
93+
serialize(value(), at: &position)
94+
})
95+
}
96+
}
97+
98+
/// Serialize an NSObject pointer at the buffer location pointed by
99+
/// `bufferPosition`.
100+
@inlinable
101+
@_alwaysEmitIntoClient
102+
@inline(__always)
103+
internal func serialize(
104+
_ object: NSObject,
105+
at bufferPosition: inout ByteBufferPointer
106+
) {
107+
let byteCount = pointerSizeInBytes();
108+
let dest =
109+
UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount)
110+
// Get the address of this NSObject as an UnsafeRawPointer.
111+
let objectAddress = Unmanaged.passUnretained(object).toOpaque()
112+
// Copy the address into the destination buffer. Note that the input NSObject
113+
// is an interpolated expression and is guaranteed to be alive until the
114+
// os_log ABI call is completed by the implementation. Therefore, passing
115+
// this address to the os_log ABI is safe.
116+
withUnsafeBytes(of: objectAddress) { dest.copyMemory(from: $0) }
117+
bufferPosition += byteCount
118+
}

stdlib/private/OSLog/OSLogStringTypes.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ extension OSLogInterpolation {
6060
arguments.append(header)
6161

6262
// Append number of bytes needed to serialize the argument.
63-
let byteCount = sizeForEncoding()
63+
let byteCount = pointerSizeInBytes()
6464
arguments.append(UInt8(byteCount))
6565

6666
// Increment total byte size by the number of bytes needed for this
@@ -105,7 +105,7 @@ extension OSLogArguments {
105105
@_semantics("constant_evaluable")
106106
@inlinable
107107
@_optimize(none)
108-
internal func sizeForEncoding() -> Int {
108+
internal func pointerSizeInBytes() -> Int {
109109
return Int.bitWidth &>> logBitsPerByte
110110
}
111111

@@ -129,7 +129,7 @@ internal func serialize(
129129
storageObjects.append(storage)
130130
}
131131

132-
let byteCount = sizeForEncoding()
132+
let byteCount = pointerSizeInBytes()
133133
let dest =
134134
UnsafeMutableRawBufferPointer(start: bufferPosition, count: byteCount)
135135
withUnsafeBytes(of: bytePointer) { dest.copyMemory(from: $0) }

test/SILOptimizer/OSLogPrototypeCompileTest.swift

+50
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// the size of the byte buffer etc. are literals after the mandatory pipeline.
99

1010
import OSLogPrototype
11+
import Foundation
1112

1213
if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
1314

@@ -410,5 +411,54 @@ if #available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *) {
410411
// CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
411412
// CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6
412413
}
414+
415+
// CHECK-LABEL: @$s25OSLogPrototypeCompileTest25testNSObjectInterpolationL_1hy0aB06LoggerV_tF
416+
func testNSObjectInterpolation(h: Logger) {
417+
let nsArray: NSArray = [0, 1, 2]
418+
let nsDictionary: NSDictionary = [1 : ""]
419+
h.log("""
420+
NSArray: \(nsArray, privacy: .public) \
421+
NSDictionary: \(nsDictionary, privacy: .private)
422+
""")
423+
// Check if there is a call to _os_log_impl with a literal format string.
424+
// CHECK-DAG is used here as it is easier to perform the checks backwards
425+
// from uses to the definitions.
426+
427+
// CHECK-DAG: [[OS_LOG_IMPL:%[0-9]+]] = function_ref @_os_log_impl : $@convention(c)
428+
// CHECK-DAG: apply [[OS_LOG_IMPL]]({{%.*}}, {{%.*}}, {{%.*}}, [[CHARPTR:%[0-9]+]], {{%.*}}, {{%.*}})
429+
// CHECK-DAG: [[CHARPTR]] = struct $UnsafePointer<Int8> ([[LIT:%[0-9]+]] : $Builtin.RawPointer)
430+
// CHECK-DAG: [[LIT]] = string_literal utf8 "NSArray: %{public}@ NSDictionary: %{private}@"
431+
432+
// Check if the size of the argument buffer is a constant.
433+
434+
// CHECK-DAG: [[ALLOCATE:%[0-9]+]] = function_ref @$sSp8allocate8capacitySpyxGSi_tFZ
435+
// CHECK-DAG: apply [[ALLOCATE]]<UInt8>([[BUFFERSIZE:%[0-9]+]], {{%.*}})
436+
// CHECK-DAG: [[BUFFERSIZE]] = struct $Int ([[BUFFERSIZELIT:%[0-9]+]]
437+
// CHECK-64-DAG: [[BUFFERSIZELIT]] = integer_literal $Builtin.Int64, 22
438+
// CHECK-32-DAG: [[BUFFERSIZELIT]] = integer_literal $Builtin.Int32, 14
439+
440+
// Check whether the header bytes: premable and argument count are constants.
441+
442+
// CHECK-DAG: [[SERIALIZE:%[0-9]+]] = function_ref @$s14OSLogPrototype9serialize_2atys5UInt8V_SpyAEGztF
443+
// CHECK-DAG: apply [[SERIALIZE]]([[PREAMBLE:%[0-9]+]], {{%.*}})
444+
// CHECK-DAG: [[PREAMBLE]] = struct $UInt8 ([[PREAMBLELIT:%[0-9]+]] : $Builtin.Int8)
445+
// CHECK-DAG: [[PREAMBLELIT]] = integer_literal $Builtin.Int8, 3
446+
447+
// CHECK-DAG: [[SERIALIZE:%[0-9]+]] = function_ref @$s14OSLogPrototype9serialize_2atys5UInt8V_SpyAEGztF
448+
// CHECK-DAG: apply [[SERIALIZE]]([[ARGCOUNT:%[0-9]+]], {{%.*}})
449+
// CHECK-DAG: [[ARGCOUNT]] = struct $UInt8 ([[ARGCOUNTLIT:%[0-9]+]] : $Builtin.Int8)
450+
// CHECK-DAG: [[ARGCOUNTLIT]] = integer_literal $Builtin.Int8, 2
451+
452+
// Check whether argument array is folded. We need not check the contents of
453+
// the array which is checked by a different test suite.
454+
455+
// CHECK-DAG: [[FOREACH:%[0-9]+]] = function_ref @$sSTsE7forEachyyy7ElementQzKXEKF
456+
// CHECK-DAG: try_apply [[FOREACH]]<Array<(inout UnsafeMutablePointer<UInt8>, inout Array<AnyObject>) -> ()>>({{%.*}}, [[ARGSARRAYADDR:%[0-9]+]])
457+
// CHECK-DAG: store [[ARGSARRAY:%[0-9]+]] to [[ARGSARRAYADDR]]
458+
// CHECK-DAG: [[ARGSARRAY]] = tuple_extract [[ARRAYINITRES:%[0-9]+]] : $(Array<(inout UnsafeMutablePointer<UInt8>, inout Array<AnyObject>) -> ()>, Builtin.RawPointer), 0
459+
// CHECK-DAG: [[ARRAYINITRES]] = apply [[ARRAYINIT:%[0-9]+]]<(inout UnsafeMutablePointer<UInt8>, inout Array<AnyObject>) -> ()>([[ARRAYSIZE:%[0-9]+]])
460+
// CHECK-DAG: [[ARRAYINIT]] = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF
461+
// CHECK-DAG: [[ARRAYSIZE]] = integer_literal $Builtin.Word, 6
462+
}
413463
}
414464

0 commit comments

Comments
 (0)