Skip to content
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
7 changes: 7 additions & 0 deletions stdlib/public/SwiftShims/RuntimeShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ int _swift_stdlib_putc_stderr(int C);
SWIFT_RUNTIME_STDLIB_API
__swift_size_t _swift_stdlib_getHardwareConcurrency(void);

SWIFT_RUNTIME_STDLIB_INTERNAL
char *_swift_stdlib_demangle(const char *mangledName,
__swift_size_t mangledNameLength,
char *outputBuffer,
__swift_size_t *outputBufferSize,
__swift_uint32_t flags);

/// Manually allocated memory is at least 16-byte aligned in Swift.
///
/// When swift_slowAlloc is called with "default" alignment (alignMask ==
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ set(SWIFTLIB_ESSENTIAL
CString.swift
CTypes.swift
DebuggerSupport.swift
Demangle.swift
Dictionary.swift
DictionaryBridging.swift
DictionaryBuilder.swift
Expand Down
143 changes: 143 additions & 0 deletions stdlib/public/core/Demangle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019 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
//
//===----------------------------------------------------------------------===//

import SwiftShims

/// Represents the potential return types from a call to demangle.
public enum DemangleResult: Equatable {
/// The demangle completed successfully.
case success

/// The demangle resulted in truncating the result. The payload value is the
/// number of bytes necessary for a full demangle.
case truncated(Int)

/// The passed Swift mangled symbol was invalid.
case invalidSymbol
}

/// Given a mangled Swift symbol, demangle it into a human readable format.
///
/// Valid Swift symbols begin with the following prefixes:
/// ┌─────────────────────╥────────┐
/// │ Swift Version ║ │
/// ╞═════════════════════╬════════╡
/// │ Swift 3 and below ║ _T │
/// ├─────────────────────╫────────┤
/// │ Swift 4 ║ _T0 │
/// ├─────────────────────╫────────┤
/// │ Swift 4.x ║ $S │
/// ├─────────────────────╫────────┤
/// │ Swift 5+ ║ $s │
/// └─────────────────────╨────────┘
///
/// - Parameters:
/// - mangledName: A mangled Swift symbol.
/// - Returns: A human readable demangled Swift symbol.
public func demangle(_ mangledName: String) -> String? {
return mangledName.utf8CString.withUnsafeBufferPointer {
let demangledPtr = _swift_stdlib_demangle(
/* mangledName */ $0.baseAddress,
/* mangledNameLength */ $0.count - 1,
/* outputBuffer */ nil,
/* outputBufferSize */ nil,
/* flags */ 0
)

guard demangledPtr != nil else {
return nil
}

let demangledName = String(cString: demangledPtr!)
_swift_stdlib_free(demangledPtr!)
return demangledName
}
}

/// Given a mangled Swift symbol, demangle it into a human readable format.
///
/// Valid Swift symbols begin with the following prefixes:
/// ┌─────────────────────╥────────┐
/// │ Swift Version ║ │
/// ╞═════════════════════╬════════╡
/// │ Swift 3 and below ║ _T │
/// ├─────────────────────╫────────┤
/// │ Swift 4 ║ _T0 │
/// ├─────────────────────╫────────┤
/// │ Swift 4.x ║ $S │
/// ├─────────────────────╫────────┤
/// │ Swift 5+ ║ $s │
/// └─────────────────────╨────────┘
///
/// - Parameters:
/// - mangledNameBuffer: A buffer pointer pointing to a null-terminated C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If mangledNameBuffer is always null-terminated then you could change it to mangledName: UnsafePointer<CChar>, so that String arguments can be implicitly converted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have the String argument version, so I'm unsure why supporting a pointer version for implicit conversion is necessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm suggesting that:

  • either mangledNameBuffer: UnsafeBufferPointer<Int8> doesn't need to be null-terminated;

  • or mangledName: UnsafePointer<Int8> would make the third demangle function unnecessary.

On the other hand, John McCall wants to deprecate the implicit conversions.

By the way, are Int8 or UInt8 elements more suitable for UTF-8 string parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use your suggestion about using StringRef(mangledName, mangledNameLength)) mangledNameBuffer doesn't need to be null terminated. However, we must commit to either the name buffer being null terminated (because we calculate length with nameBuffer.count - 1), or must not be null terminated (nameBuffer.count).

As for Int8 or UInt8, CChar maps to Int8 and the string functions withCString and utf8CString.withUnsafeBufferPointer, etc all have Int8 element types. withUTF8 on the other hand uses UInt8, so I'm unsure. I think going with Int8 is the better call because of the CChar typealias.

Copy link
Collaborator

@xwu xwu Jul 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CChar is either signed or unsigned, depending on the platform; it reflects the signedness of the C char type.

/// string that contains the mangled Swift symbol.
/// - buffer: A pre-allocated buffer to demangle the Swift symbol into.
/// - Returns: An enum, `DemangleResult`, indicating the various result states
/// of demangling.
public func demangle(
_ mangledNameBuffer: UnsafeBufferPointer<Int8>,
into buffer: UnsafeMutableBufferPointer<Int8>
) -> DemangleResult {
var bufferSize = buffer.count

let demangledPtr = _swift_stdlib_demangle(
/* mangledName */ mangledNameBuffer.baseAddress,
/* mangledNameLength */ mangledNameBuffer.count - 1,
/* outputBuffer */ buffer.baseAddress,
/* outputBufferSize */ &bufferSize,
/* flags */ 0
)

guard demangledPtr != nil else {
return .invalidSymbol
}

// If the buffer size is still equal to the buffer count, the demangle was
// successful.
if bufferSize == buffer.count {
return .success
}

// However if it's not equal, the result was truncated. Return the amount
// needed to get a full demangle.
return .truncated(bufferSize)
}

/// Given a mangled Swift symbol, demangle it into a human readable format.
///
/// Valid Swift symbols begin with the following prefixes:
/// ┌─────────────────────╥────────┐
/// │ Swift Version ║ │
/// ╞═════════════════════╬════════╡
/// │ Swift 3 and below ║ _T │
/// ├─────────────────────╫────────┤
/// │ Swift 4 ║ _T0 │
/// ├─────────────────────╫────────┤
/// │ Swift 4.x ║ $S │
/// ├─────────────────────╫────────┤
/// │ Swift 5+ ║ $s │
/// └─────────────────────╨────────┘
///
/// - Parameters:
/// - mangledName: A mangled Swift symbol.
/// - buffer: A pre-allocated buffer to demangle the Swift symbol into.
/// - Returns: An enum, `DemangleResult`, indicating the various result states
/// of demangling.
public func demangle(
_ mangledName: String,
into buffer: UnsafeMutableBufferPointer<Int8>
) -> DemangleResult {
mangledName.utf8CString.withUnsafeBufferPointer {
demangle($0, into: buffer)
}
}
3 changes: 2 additions & 1 deletion stdlib/public/core/GroupInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@
"Comparable.swift",
"Codable.swift",
"LegacyABI.swift",
"MigrationSupport.swift"
"MigrationSupport.swift",
"Demangle.swift"
],
"Result": [
"Result.swift"
Expand Down
9 changes: 9 additions & 0 deletions stdlib/public/stubs/Stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static float swift_strtof_l(const char *nptr, char **endptr, locale_t loc) {
#include <thread>
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Compiler.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/SwiftDtoa.h"
#include "swift/Basic/Lazy.h"
Expand Down Expand Up @@ -524,3 +525,11 @@ size_t swift::_swift_stdlib_getHardwareConcurrency() {
return std::thread::hardware_concurrency();
}

char *swift::_swift_stdlib_demangle(const char *mangledName,
__swift_size_t mangledNameLength,
char *outputBuffer,
__swift_size_t *outputBufferSize,
__swift_uint32_t flags) {
return swift_demangle(mangledName, mangledNameLength, outputBuffer,
outputBufferSize, flags);
}
82 changes: 82 additions & 0 deletions test/stdlib/Demangle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// RUN: %target-run-simple-swift
// REQUIRES: executable_test

// We don't really want to excerise actual demangling here, but rather just that
// the stdlib demangle function actually works as intended.

import Swift
import StdlibUnittest

var DemangleTests = TestSuite("Demangle")

DemangleTests.test("basic string return API") {
// First, test that we get back the mangled name with invalid input.
expectEqual(demangle("abc123"), nil)
expectEqual(demangle("Si"), nil)
expectEqual(demangle("Swift is super cool!"), nil)

// Test that correct symbols are demangled. (Test all documented prefixes)
expectEqual(demangle("_TSb"), "Swift.Bool")
expectEqual(demangle("_T0Si"), "Swift.Int")
expectEqual(demangle("$SSdXSaXSq"), "[Swift.Double]?")
expectEqual(demangle("_$S8Demangle4main4argc4argvs5Int32VAF_SpySpys4Int8VGSgGtF"), "Demangle.main(argc: Swift.Int32, argv: Swift.UnsafeMutablePointer<Swift.Optional<Swift.UnsafeMutablePointer<Swift.Int8>>>) -> Swift.Int32")
expectEqual(demangle("$sSG"), "Swift.RandomNumberGenerator")
expectEqual(demangle("_$sSS7cStringSSSPys4Int8VG_tcfC"), "Swift.String.init(cString: Swift.UnsafePointer<Swift.Int8>) -> Swift.String")
}

DemangleTests.test("buffer API") {
let buffer = UnsafeMutableBufferPointer<Int8>.allocate(capacity: 140)

defer { buffer.deallocate() }

buffer[0] = 0 // Ensure that when we do String(cString: ptr) it halts at first byte.
let ptr = buffer.baseAddress!

// First, test that the buffer is still empty after failed demanglings.
expectEqual(demangle("abc123", into: buffer), .invalidSymbol)
expectEqual(String(cString: ptr), "")

expectEqual(demangle("Si", into: buffer), .invalidSymbol)
expectEqual(String(cString: ptr), "")

expectEqual(demangle("Swift is super cool!", into: buffer), .invalidSymbol)
expectEqual(String(cString: ptr), "")

// Test that correct symbols are demangled. (Test all documented prefixes)
expectEqual(demangle("_TSb", into: buffer), .success)
expectEqual(String(cString: ptr), "Swift.Bool")

expectEqual(demangle("_T0Si", into: buffer), .success)
expectEqual(String(cString: ptr), "Swift.Int")

expectEqual(demangle("$SSdXSaXSq", into: buffer), .success)
expectEqual(String(cString: ptr), "[Swift.Double]?")

expectEqual(demangle("_$S8Demangle4main4argc4argvs5Int32VAF_SpySpys4Int8VGSgGtF", into: buffer), .success)
expectEqual(String(cString: ptr), "Demangle.main(argc: Swift.Int32, argv: Swift.UnsafeMutablePointer<Swift.Optional<Swift.UnsafeMutablePointer<Swift.Int8>>>) -> Swift.Int32")

expectEqual(demangle("$sSG", into: buffer), .success)
expectEqual(String(cString: ptr), "Swift.RandomNumberGenerator")

expectEqual(demangle("_$sSS7cStringSSSPys4Int8VG_tcfC", into: buffer), .success)
expectEqual(String(cString: ptr), "Swift.String.init(cString: Swift.UnsafePointer<Swift.Int8>) -> Swift.String")

// Test the return of demangle into with a smaller buffer.
// Swift.Int requires 10 bytes, give this 9
let smolBuffer = UnsafeMutableBufferPointer<Int8>.allocate(capacity: 9)

defer { smolBuffer.deallocate() }

let smolPtr = smolBuffer.baseAddress!

let fail = demangle("$sSi", into: smolBuffer)
expectEqual(fail, .truncated(10))
expectEqual(String(cString: smolPtr), "Swift.In")

// Test nil return on successful demangle.
let success = demangle("$s4Smol3IntV", into: smolBuffer)
expectEqual(success, .success)
expectEqual(String(cString: smolPtr), "Smol.Int")
}

runAllTests()