Skip to content

Commit 074d939

Browse files
authored
Merge pull request #10538 from itaiferber/4.0-foundation-shared-encoders
[4.0] Allow classes to share an Encoder with superclass
2 parents b1911dd + 62ce425 commit 074d939

File tree

4 files changed

+248
-133
lines changed

4 files changed

+248
-133
lines changed

stdlib/public/SDK/Foundation/JSONEncoder.swift

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ fileprivate class _JSONEncoder : Encoder {
190190
/// Returns whether a new element can be encoded at this coding path.
191191
///
192192
/// `true` if an element has not yet been encoded at this coding path; `false` otherwise.
193-
var canEncodeNewElement: Bool {
193+
var canEncodeNewValue: Bool {
194194
// Every time a new value gets encoded, the key it's encoded for is pushed onto the coding path (even if it's a nil key from an unkeyed container).
195195
// At the same time, every time a container is requested, a new value gets pushed onto the storage stack.
196196
// If there are more values on the storage stack than on the coding path, it means the value is requesting more than one container, which violates the precondition.
@@ -200,39 +200,43 @@ fileprivate class _JSONEncoder : Encoder {
200200
return self.storage.count == self.codingPath.count
201201
}
202202

203-
/// Asserts that a new container can be requested at this coding path.
204-
/// `preconditionFailure()`s if one cannot be requested.
205-
func assertCanRequestNewContainer() {
206-
guard self.canEncodeNewElement else {
207-
let previousContainerType: String
208-
if self.storage.containers.last is NSDictionary {
209-
previousContainerType = "keyed"
210-
} else if self.storage.containers.last is NSArray {
211-
previousContainerType = "unkeyed"
212-
} else {
213-
previousContainerType = "single value"
203+
// MARK: - Encoder Methods
204+
func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
205+
// If an existing keyed container was already requested, return that one.
206+
let topContainer: NSMutableDictionary
207+
if self.canEncodeNewValue {
208+
// We haven't yet pushed a container at this level; do so here.
209+
topContainer = self.storage.pushKeyedContainer()
210+
} else {
211+
guard let container = self.storage.containers.last as? NSMutableDictionary else {
212+
preconditionFailure("Attempt to push new keyed encoding container when already previously encoded at this path.")
214213
}
215214

216-
preconditionFailure("Attempt to encode with new container when already encoded with \(previousContainerType) container.")
215+
topContainer = container
217216
}
218-
}
219217

220-
// MARK: - Encoder Methods
221-
func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
222-
assertCanRequestNewContainer()
223-
let topContainer = self.storage.pushKeyedContainer()
224218
let container = _JSONKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
225219
return KeyedEncodingContainer(container)
226220
}
227221

228222
func unkeyedContainer() -> UnkeyedEncodingContainer {
229-
assertCanRequestNewContainer()
230-
let topContainer = self.storage.pushUnkeyedContainer()
223+
// If an existing unkeyed container was already requested, return that one.
224+
let topContainer: NSMutableArray
225+
if self.canEncodeNewValue {
226+
// We haven't yet pushed a container at this level; do so here.
227+
topContainer = self.storage.pushUnkeyedContainer()
228+
} else {
229+
guard let container = self.storage.containers.last as? NSMutableArray else {
230+
preconditionFailure("Attempt to push new unkeyed encoding container when already previously encoded at this path.")
231+
}
232+
233+
topContainer = container
234+
}
235+
231236
return _JSONUnkeyedEncodingContainer(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
232237
}
233238

234239
func singleValueContainer() -> SingleValueEncodingContainer {
235-
assertCanRequestNewContainer()
236240
return self
237241
}
238242
}
@@ -474,106 +478,89 @@ fileprivate struct _JSONUnkeyedEncodingContainer : UnkeyedEncodingContainer {
474478
}
475479

476480
extension _JSONEncoder : SingleValueEncodingContainer {
477-
// MARK: - Utility Methods
478-
479-
/// Asserts that a single value can be encoded at the current coding path (i.e. that one has not already been encoded through this container).
480-
/// `preconditionFailure()`s if one cannot be encoded.
481-
///
482-
/// This is similar to assertCanRequestNewContainer above.
483-
func assertCanEncodeSingleValue() {
484-
guard self.canEncodeNewElement else {
485-
let previousContainerType: String
486-
if self.storage.containers.last is NSDictionary {
487-
previousContainerType = "keyed"
488-
} else if self.storage.containers.last is NSArray {
489-
previousContainerType = "unkeyed"
490-
} else {
491-
preconditionFailure("Attempt to encode multiple values in a single value container.")
492-
}
481+
// MARK: - SingleValueEncodingContainer Methods
493482

494-
preconditionFailure("Attempt to encode with new container when already encoded with \(previousContainerType) container.")
495-
}
483+
func assertCanEncodeNewValue() {
484+
precondition(self.canEncodeNewValue, "Attempt to encode value through single value container when previously value already encoded.")
496485
}
497486

498-
// MARK: - SingleValueEncodingContainer Methods
499-
500487
func encodeNil() throws {
501-
assertCanEncodeSingleValue()
488+
assertCanEncodeNewValue()
502489
self.storage.push(container: NSNull())
503490
}
504491

505492
func encode(_ value: Bool) throws {
506-
assertCanEncodeSingleValue()
493+
assertCanEncodeNewValue()
507494
self.storage.push(container: box(value))
508495
}
509496

510497
func encode(_ value: Int) throws {
511-
assertCanEncodeSingleValue()
498+
assertCanEncodeNewValue()
512499
self.storage.push(container: box(value))
513500
}
514501

515502
func encode(_ value: Int8) throws {
516-
assertCanEncodeSingleValue()
503+
assertCanEncodeNewValue()
517504
self.storage.push(container: box(value))
518505
}
519506

520507
func encode(_ value: Int16) throws {
521-
assertCanEncodeSingleValue()
508+
assertCanEncodeNewValue()
522509
self.storage.push(container: box(value))
523510
}
524511

525512
func encode(_ value: Int32) throws {
526-
assertCanEncodeSingleValue()
513+
assertCanEncodeNewValue()
527514
self.storage.push(container: box(value))
528515
}
529516

530517
func encode(_ value: Int64) throws {
531-
assertCanEncodeSingleValue()
518+
assertCanEncodeNewValue()
532519
self.storage.push(container: box(value))
533520
}
534521

535522
func encode(_ value: UInt) throws {
536-
assertCanEncodeSingleValue()
523+
assertCanEncodeNewValue()
537524
self.storage.push(container: box(value))
538525
}
539526

540527
func encode(_ value: UInt8) throws {
541-
assertCanEncodeSingleValue()
528+
assertCanEncodeNewValue()
542529
self.storage.push(container: box(value))
543530
}
544531

545532
func encode(_ value: UInt16) throws {
546-
assertCanEncodeSingleValue()
533+
assertCanEncodeNewValue()
547534
self.storage.push(container: box(value))
548535
}
549536

550537
func encode(_ value: UInt32) throws {
551-
assertCanEncodeSingleValue()
538+
assertCanEncodeNewValue()
552539
self.storage.push(container: box(value))
553540
}
554541

555542
func encode(_ value: UInt64) throws {
556-
assertCanEncodeSingleValue()
543+
assertCanEncodeNewValue()
557544
self.storage.push(container: box(value))
558545
}
559546

560547
func encode(_ value: String) throws {
561-
assertCanEncodeSingleValue()
548+
assertCanEncodeNewValue()
562549
self.storage.push(container: box(value))
563550
}
564551

565552
func encode(_ value: Float) throws {
566-
assertCanEncodeSingleValue()
553+
assertCanEncodeNewValue()
567554
try self.storage.push(container: box(value))
568555
}
569556

570557
func encode(_ value: Double) throws {
571-
assertCanEncodeSingleValue()
558+
assertCanEncodeNewValue()
572559
try self.storage.push(container: box(value))
573560
}
574561

575562
func encode<T : Encodable>(_ value: T) throws {
576-
assertCanEncodeSingleValue()
563+
assertCanEncodeNewValue()
577564
try self.storage.push(container: box(value))
578565
}
579566
}
@@ -763,7 +750,7 @@ fileprivate class _JSONReferencingEncoder : _JSONEncoder {
763750

764751
// MARK: - Coding Path Operations
765752

766-
override var canEncodeNewElement: Bool {
753+
override var canEncodeNewValue: Bool {
767754
// With a regular encoder, the storage and coding path grow together.
768755
// A referencing encoder, however, inherits its parents coding path, as well as the key it was created for.
769756
// We have to take this into account.

0 commit comments

Comments
 (0)