forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOSLogMessage.swift
358 lines (322 loc) · 12 KB
/
OSLogMessage.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
//===----------------- OSLogMessage.swift ---------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
// This file contains data structures and helper functions that are used by
// the new OS log APIs.
import ObjectiveC
/// Maximum number of arguments i.e., interpolated expressions that can
/// be used in the string interpolations passed to the log APIs.
/// This limit is imposed by the logging system.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
public var maxOSLogArgumentCount: UInt8 { return 48 }
// Note that this is marked transparent instead of @inline(__always) as it is
// used in optimize(none) functions.
@_transparent
@_alwaysEmitIntoClient
internal var logBitsPerByte: Int { return 3 }
/// Represents a string interpolation passed to the log APIs.
///
/// This type converts (through its methods) the given string interpolation into
/// a C-style format string and a sequence of arguments.
///
/// - Warning: Do not explicitly refer to this type. It will be implicitly created
/// by the compiler when you pass a string interpolation to the log APIs.
@frozen
public struct OSLogInterpolation : StringInterpolationProtocol {
/// A format string constructed from the given string interpolation to be
/// passed to the os_log ABI.
@usableFromInline
internal var formatString: String
/// A representation of a sequence of arguments that must be serialized
/// to a byte buffer and passed to the os_log ABI. Each argument, which is
/// an (autoclosured) expressions that is interpolated, is prepended with a
/// two byte header. The first header byte consists of a four bit flag and
/// a four bit type. The second header byte has the size of the argument in
/// bytes. This is schematically illustrated below.
/// ----------------------------
/// | 4-bit type | 4-bit flag |
/// ----------------------------
/// | 1st argument size in bytes|
/// ----------------------------
/// | 1st argument bytes |
/// ----------------------------
/// | 4-bit type | 4-bit flag |
/// -----------------------------
/// | 2nd argument size in bytes|
/// ----------------------------
/// | 2nd argument bytes |
/// ----------------------------
/// ...
@usableFromInline
internal var arguments: OSLogArguments
/// The possible values for the argument type, as defined by the os_log ABI,
/// which occupies four most significant bits of the first byte of the
/// argument header. The rawValue of this enum must be constant evaluable.
/// (Note that an auto-generated rawValue is not constant evaluable because
/// it cannot be annotated so.)
@usableFromInline
internal enum ArgumentType {
case scalar, count, string, pointer, object, mask
@inlinable
internal var rawValue: UInt8 {
switch self {
case .scalar:
return 0
case .count:
return 1
case .string:
return 2
case .pointer:
return 3
case .mask:
return 7
default: //.object
return 4
}
}
}
/// The first summary byte in the byte buffer passed to the os_log ABI that
/// summarizes the privacy and nature of the arguments.
@usableFromInline
internal var preamble: UInt8
/// Denotes the bit that indicates whether there is private argument.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal var privateBitMask: UInt8 { 0x1 }
/// Denotes the bit that indicates whether there is non-scalar argument:
/// String, NSObject or Pointer.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal var nonScalarBitMask: UInt8 { 0x2 }
/// The second summary byte that denotes the number of arguments, which is
/// also the number of interpolated expressions. This will be determined
/// on the fly in order to support concatenation and interpolation of
/// instances of `OSLogMessage`.
@usableFromInline
internal var argumentCount: UInt8
/// Sum total of all the bytes (including header bytes) needed for
/// serializing the arguments.
@usableFromInline
internal var totalBytesForSerializingArguments: Int
/// The number of arguments that are Strings. This count is used to create
/// auxiliary storage meant for extending the lifetime of the string arguments
/// until the log call completes.
@usableFromInline
internal var stringArgumentCount: Int
/// The number of arguments that are NSObjects. This count is used to create
/// auxiliary storage meant for extending the lifetime of the NSObject
/// arguments until the log call completes.
@usableFromInline
internal var objectArgumentCount: Int
// Some methods defined below are marked @_optimize(none) to prevent inlining
// of string internals (such as String._StringGuts) which will interfere with
// constant evaluation and folding. Note that these methods will be inlined,
// constant evaluated/folded and optimized in the context of a caller.
@_semantics("oslog.interpolation.init")
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
public init(literalCapacity: Int, interpolationCount: Int) {
// Since the format string and the arguments array are fully constructed
// at compile time, the parameters are ignored.
formatString = ""
arguments = OSLogArguments()
preamble = 0
argumentCount = 0
totalBytesForSerializingArguments = 0
stringArgumentCount = 0
objectArgumentCount = 0
}
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
public mutating func appendLiteral(_ literal: String) {
formatString += literal.percentEscapedString
}
/// `appendInterpolation` conformances will be added by extensions to this type.
/// Compute a byte-sized argument header consisting of flag and type.
/// Flag and type take up the least and most significant four bits
/// of the header byte, respectively.
/// This function should be constant evaluable.
@inlinable
@_semantics("constant_evaluable")
@_effects(readonly)
@_optimize(none)
internal func getArgumentHeader(
privacy: OSLogPrivacy,
type: ArgumentType
) -> UInt8 {
return (type.rawValue &<< 4) | privacy.argumentFlag
}
/// Compute the new preamble based whether the current argument is private
/// or not. This function must be constant evaluable.
@inlinable
@_semantics("constant_evaluable")
@_effects(readonly)
@_optimize(none)
internal func getUpdatedPreamble(
privacy: OSLogPrivacy,
isScalar: Bool
) -> UInt8 {
var preamble = self.preamble
if privacy.isAtleastPrivate {
preamble |= privateBitMask
}
if !isScalar || privacy.hasMask {
preamble |= nonScalarBitMask
}
return preamble
}
}
extension String {
/// Replace all percents "%" in the string by "%%" so that the string can be
/// interpreted as a C format string. This function is constant evaluable
/// and its semantics is modeled within the evaluator.
@inlinable
internal var percentEscapedString: String {
@_semantics("string.escapePercent.get")
@_effects(readonly)
@_optimize(none)
get {
return self
.split(separator: "%", omittingEmptySubsequences: false)
.joined(separator: "%%")
}
}
}
/// Represents a message passed to the log APIs. This type should be created
/// from a string interpolation or a string literal.
///
/// Do not explicitly refer to this type. It will be implicitly created
/// by the compiler when you pass a string interpolation to the log APIs.
@frozen
public struct OSLogMessage :
ExpressibleByStringInterpolation, ExpressibleByStringLiteral
{
public let interpolation: OSLogInterpolation
@inlinable
@_optimize(none)
@_semantics("oslog.message.init_interpolation")
@_semantics("constant_evaluable")
public init(stringInterpolation: OSLogInterpolation) {
self.interpolation = stringInterpolation
}
@inlinable
@_optimize(none)
@_semantics("oslog.message.init_stringliteral")
@_semantics("constant_evaluable")
public init(stringLiteral value: String) {
var s = OSLogInterpolation(literalCapacity: 1, interpolationCount: 0)
s.appendLiteral(value)
self.interpolation = s
}
/// The byte size of the buffer that will be passed to the logging system.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
public var bufferSize: Int {
// The two additional bytes is for the preamble and argument count.
return interpolation.totalBytesForSerializingArguments + 2
}
}
@usableFromInline
internal typealias ByteBufferPointer = UnsafeMutablePointer<UInt8>
@usableFromInline
internal typealias ObjectStorage<T> = UnsafeMutablePointer<T>?
@usableFromInline
internal typealias ArgumentClosures =
[(inout ByteBufferPointer,
inout ObjectStorage<NSObject>,
inout ObjectStorage<Any>) -> ()]
/// A representation of a sequence of arguments and headers (of possibly
/// different types) that have to be serialized to a byte buffer. The arguments
/// are captured within closures and stored in an array. The closures accept an
/// instance of `OSLogByteBufferBuilder`, and when invoked, serialize the
/// argument using the passed `OSLogByteBufferBuilder` instance.
@frozen
@usableFromInline
internal struct OSLogArguments {
/// An array of closures that captures arguments of possibly different types.
/// Each closure accepts a pointer into a byte buffer and serializes the
/// captured arguments at the pointed location. The closures also accept an
/// array of AnyObject to store references to auxiliary storage created during
/// serialization.
@usableFromInline
internal var argumentClosures: ArgumentClosures
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal init() {
argumentClosures = []
}
/// Append a byte-sized header, constructed by
/// `OSLogMessage.appendInterpolation`, to the tracked array of closures.
@_semantics("constant_evaluable")
@inlinable
@_optimize(none)
internal mutating func append(_ header: UInt8) {
argumentClosures.append({ (position, _, _) in
serialize(header, at: &position)
})
}
/// `append` for other types must be implemented by extensions.
}
/// Serialize a UInt8 value at the buffer location pointed to by `bufferPosition`,
/// and increment the `bufferPosition` with the byte size of the serialized value.
@_alwaysEmitIntoClient
@inline(__always)
internal func serialize(
_ value: UInt8,
at bufferPosition: inout ByteBufferPointer)
{
bufferPosition[0] = value
bufferPosition += 1
}
// The following code defines helper functions for creating and maintaining
// a buffer for holding a fixed number for instances of a type T. Such buffers
// are used to hold onto NSObjects and Strings that are interpolated in the log
// message until the end of the log call.
@_alwaysEmitIntoClient
@inline(__always)
internal func createStorage<T>(
capacity: Int,
type: T.Type
) -> ObjectStorage<T> {
return
capacity == 0 ?
nil :
UnsafeMutablePointer<T>.allocate(capacity: capacity)
}
@_alwaysEmitIntoClient
@inline(__always)
internal func initializeAndAdvance<T>(
_ storageOpt: inout ObjectStorage<T>,
to value: T
) {
// This if statement should get optimized away.
if let storage = storageOpt {
storage.initialize(to: value)
storageOpt = storage.advanced(by: 1)
}
}
@_alwaysEmitIntoClient
@inline(__always)
internal func destroyStorage<T>(_ storageOpt: ObjectStorage<T>, count: Int) {
// This if statement should get optimized away.
if let storage = storageOpt {
storage.deinitialize(count: count)
storage.deallocate()
}
}