mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[libSyntax] Add a reference counted version of OwnedString
We cannot use unowned strings for token texts of incrementally parsed syntax trees since the source buffer to which reused nodes refer will have been freed for reused nodes. Always copying the token text whenever OwnedString is passed is too expensive. A reference counted copy of the string allows us to keep the token's string alive across incremental parses while eliminating unnecessary copies.
This commit is contained in:
@@ -22,117 +22,89 @@
|
|||||||
|
|
||||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/Support/TrailingObjects.h"
|
||||||
|
|
||||||
using llvm::StringRef;
|
using llvm::StringRef;
|
||||||
|
|
||||||
namespace swift {
|
namespace swift {
|
||||||
|
|
||||||
enum class StringOwnership {
|
|
||||||
/// An OwnedString holds a weak reference to the underlying string storage
|
|
||||||
/// and will never attempt to free it.
|
|
||||||
Unowned,
|
|
||||||
|
|
||||||
/// An OwnedString has its own copy of the underlying string storage and
|
|
||||||
/// will free the storage upon its destruction.
|
|
||||||
Copied,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Holds a string - either statically allocated or dynamically allocated
|
/// Holds a string - either statically allocated or dynamically allocated
|
||||||
/// and owned by this type.
|
/// and owned by this type.
|
||||||
class OwnedString {
|
class OwnedString {
|
||||||
const char *Data;
|
/// An owner that keeps the buffer of a ref counted \c OwnedString alive.
|
||||||
size_t Length;
|
class TextOwner final : public llvm::ThreadSafeRefCountedBase<TextOwner>,
|
||||||
StringOwnership Ownership = StringOwnership::Unowned;
|
public llvm::TrailingObjects<TextOwner, char> {
|
||||||
|
TextOwner(StringRef Text) {
|
||||||
void release() {
|
std::uninitialized_copy(Text.begin(), Text.end(),
|
||||||
if (Ownership == StringOwnership::Copied)
|
getTrailingObjects<char>());
|
||||||
free(const_cast<char *>(Data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void initialize(const char* Data, size_t Length, StringOwnership Ownership) {
|
|
||||||
this->Length = Length;
|
|
||||||
this->Ownership = Ownership;
|
|
||||||
if (Ownership == StringOwnership::Copied && Data) {
|
|
||||||
char *substring = static_cast<char *>(malloc(Length + 1));
|
|
||||||
assert(substring && "expected successful malloc of copy");
|
|
||||||
|
|
||||||
memcpy(substring, Data, Length);
|
|
||||||
substring[Length] = '\0';
|
|
||||||
|
|
||||||
this->Data = substring;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this->Data = Data;
|
|
||||||
}
|
|
||||||
OwnedString(const char* Data, size_t Length, StringOwnership Ownership) {
|
|
||||||
initialize(Data, Length, Ownership);
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
OwnedString(): OwnedString(nullptr, 0, StringOwnership::Unowned) {}
|
static TextOwner *make(StringRef Text) {
|
||||||
|
auto size = totalSizeToAlloc<char>(Text.size());
|
||||||
OwnedString(const char *Data, size_t Length):
|
void *data = ::operator new(size);
|
||||||
OwnedString(Data, Length, StringOwnership::Copied) {}
|
return new (data) TextOwner(Text);
|
||||||
|
|
||||||
OwnedString(StringRef Str) : OwnedString(Str.data(), Str.size()) {}
|
|
||||||
|
|
||||||
OwnedString(const char *Data) : OwnedString(StringRef(Data)) {}
|
|
||||||
|
|
||||||
OwnedString(const OwnedString &Other):
|
|
||||||
OwnedString(Other.Data, Other.Length, Other.Ownership) {}
|
|
||||||
|
|
||||||
OwnedString(OwnedString &&Other): Data(Other.Data), Length(Other.Length),
|
|
||||||
Ownership(Other.Ownership) {
|
|
||||||
Other.Data = nullptr;
|
|
||||||
Other.Ownership = StringOwnership::Unowned;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnedString& operator=(const OwnedString &Other) {
|
const char *getText() const { return getTrailingObjects<char>(); }
|
||||||
if (&Other != this) {
|
};
|
||||||
release();
|
|
||||||
initialize(Other.Data, Other.Length, Other.Ownership);
|
/// The text this owned string represents
|
||||||
}
|
StringRef Text;
|
||||||
return *this;
|
|
||||||
|
/// In case of a ref counted string an owner that keeps the buffer \c Text
|
||||||
|
/// references alive.
|
||||||
|
llvm::IntrusiveRefCntPtr<TextOwner> OwnedPtr;
|
||||||
|
|
||||||
|
OwnedString(StringRef Text, llvm::IntrusiveRefCntPtr<TextOwner> OwnedPtr)
|
||||||
|
: Text(Text), OwnedPtr(OwnedPtr) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
OwnedString() : OwnedString(/*Text=*/StringRef(), /*OwnedPtr=*/nullptr) {}
|
||||||
|
|
||||||
|
/// Create a ref counted \c OwnedString that is initialized with the text of
|
||||||
|
/// the given \c StringRef.
|
||||||
|
OwnedString(StringRef Str) : OwnedString(makeRefCounted(Str)) {}
|
||||||
|
|
||||||
|
/// Create a ref counted \c OwnedString that is initialized with the text of
|
||||||
|
/// the given buffer.
|
||||||
|
OwnedString(const char *Str) : OwnedString(StringRef(Str)) {}
|
||||||
|
|
||||||
|
/// Create an \c OwnedString that references the given string. The
|
||||||
|
/// \c OwnedString will not take ownership of that buffer and will assume that
|
||||||
|
/// the buffer outlives its lifetime.
|
||||||
|
static OwnedString makeUnowned(StringRef Str) {
|
||||||
|
return OwnedString(Str, /*OwnedPtr=*/nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnedString& operator=(OwnedString &&Other) {
|
/// Create an \c OwnedString that keeps its contents in a reference counted
|
||||||
if (&Other != this) {
|
/// buffer. The contents of \p Str will be copied initially and are allowed to
|
||||||
release();
|
/// be disposed after the \c OwnedString has been created.
|
||||||
this->Data = Other.Data;
|
static OwnedString makeRefCounted(StringRef Str) {
|
||||||
this->Length = Other.Length;
|
if (Str.empty()) {
|
||||||
this->Ownership = Other.Ownership;
|
// Copying an empty string doesn't make sense. Just create an unowned
|
||||||
Other.Ownership = StringOwnership::Unowned;
|
// string that points to the empty string.
|
||||||
Other.Data = nullptr;
|
return makeUnowned(Str);
|
||||||
|
} else {
|
||||||
|
llvm::IntrusiveRefCntPtr<TextOwner> OwnedPtr(TextOwner::make(Str));
|
||||||
|
return OwnedString(StringRef(OwnedPtr->getText(), Str.size()),
|
||||||
|
std::move(OwnedPtr));
|
||||||
}
|
}
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
OwnedString copy() const {
|
|
||||||
return OwnedString(Data, Length, StringOwnership::Copied);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the string in bytes.
|
/// Returns the length of the string in bytes.
|
||||||
size_t size() const {
|
size_t size() const { return Text.size(); }
|
||||||
return Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the length is 0.
|
/// Returns true if the length is 0.
|
||||||
bool empty() const {
|
bool empty() const { return size() == 0; }
|
||||||
return Length == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a StringRef to the underlying data. No copy is made and no
|
/// Returns a StringRef to the underlying data. No copy is made and no
|
||||||
/// ownership changes take place.
|
/// ownership changes take place.
|
||||||
StringRef str() const {
|
StringRef str() const { return Text; }
|
||||||
return StringRef { Data, Length };
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const OwnedString &Right) const {
|
bool operator==(const OwnedString &Right) const {
|
||||||
return str() == Right.str();
|
return str() == Right.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
~OwnedString() {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace swift
|
} // end namespace swift
|
||||||
|
|||||||
@@ -404,12 +404,17 @@ public:
|
|||||||
return static_cast<tok>(Bits.Token.TokenKind);
|
return static_cast<tok>(Bits.Token.TokenKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the text of the token.
|
/// Return the text of the token as an \c OwnedString. Keeping a reference to
|
||||||
StringRef getTokenText() const {
|
/// this string will keep it alive even if the syntax node gets freed.
|
||||||
|
OwnedString getOwnedTokenText() const {
|
||||||
assert(isToken());
|
assert(isToken());
|
||||||
return getTrailingObjects<OwnedString>()->str();
|
return *getTrailingObjects<OwnedString>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the text of the token as a reference. The referenced buffer may
|
||||||
|
/// disappear when the syntax node gets freed.
|
||||||
|
StringRef getTokenText() const { return getOwnedTokenText().str(); }
|
||||||
|
|
||||||
/// Return the leading trivia list of the token.
|
/// Return the leading trivia list of the token.
|
||||||
ArrayRef<TriviaPiece> getLeadingTrivia() const {
|
ArrayRef<TriviaPiece> getLeadingTrivia() const {
|
||||||
assert(isToken());
|
assert(isToken());
|
||||||
@@ -434,7 +439,7 @@ public:
|
|||||||
/// trivia instead.
|
/// trivia instead.
|
||||||
RC<RawSyntax>
|
RC<RawSyntax>
|
||||||
withLeadingTrivia(ArrayRef<TriviaPiece> NewLeadingTrivia) const {
|
withLeadingTrivia(ArrayRef<TriviaPiece> NewLeadingTrivia) const {
|
||||||
return make(getTokenKind(), getTokenText(), NewLeadingTrivia,
|
return make(getTokenKind(), getOwnedTokenText(), NewLeadingTrivia,
|
||||||
getTrailingTrivia(), getPresence());
|
getTrailingTrivia(), getPresence());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,7 +451,7 @@ public:
|
|||||||
/// trivia instead.
|
/// trivia instead.
|
||||||
RC<RawSyntax>
|
RC<RawSyntax>
|
||||||
withTrailingTrivia(ArrayRef<TriviaPiece> NewTrailingTrivia) const {
|
withTrailingTrivia(ArrayRef<TriviaPiece> NewTrailingTrivia) const {
|
||||||
return make(getTokenKind(), getTokenText(), getLeadingTrivia(),
|
return make(getTokenKind(), getOwnedTokenText(), getLeadingTrivia(),
|
||||||
NewTrailingTrivia, getPresence());
|
NewTrailingTrivia, getPresence());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,9 +162,9 @@ template <> struct MappingTraits<swift::RC<swift::RawSyntax>> {
|
|||||||
StringRef nodeIdString;
|
StringRef nodeIdString;
|
||||||
in.mapRequired("id", nodeIdString);
|
in.mapRequired("id", nodeIdString);
|
||||||
unsigned nodeId = std::atoi(nodeIdString.data());
|
unsigned nodeId = std::atoi(nodeIdString.data());
|
||||||
value =
|
value = swift::RawSyntax::make(
|
||||||
swift::RawSyntax::make(tokenKind, text, leadingTrivia, trailingTrivia,
|
tokenKind, swift::OwnedString::makeRefCounted(text), leadingTrivia,
|
||||||
presence, /*Arena=*/nullptr, nodeId);
|
trailingTrivia, presence, /*Arena=*/nullptr, nodeId);
|
||||||
} else {
|
} else {
|
||||||
swift::SyntaxKind kind;
|
swift::SyntaxKind kind;
|
||||||
in.mapRequired("kind", kind);
|
in.mapRequired("kind", kind);
|
||||||
|
|||||||
@@ -454,7 +454,8 @@ struct MappingTraits<swift::syntax::TriviaPiece> {
|
|||||||
% else:
|
% else:
|
||||||
StringRef text;
|
StringRef text;
|
||||||
in.mapRequired("value", text);
|
in.mapRequired("value", text);
|
||||||
return swift::syntax::TriviaPiece(kind, text);
|
return swift::syntax::TriviaPiece(
|
||||||
|
kind, swift::OwnedString::makeRefCounted(text));
|
||||||
% end
|
% end
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2182,7 +2182,8 @@ void Lexer::lexImpl() {
|
|||||||
size_t BOMLen = ContentStart - BufferStart;
|
size_t BOMLen = ContentStart - BufferStart;
|
||||||
assert(BOMLen == 3 && "UTF-8 BOM is 3 bytes");
|
assert(BOMLen == 3 && "UTF-8 BOM is 3 bytes");
|
||||||
// Add UTF-8 BOM to LeadingTrivia.
|
// Add UTF-8 BOM to LeadingTrivia.
|
||||||
LeadingTrivia.push_back(TriviaPiece::garbageText({CurPtr, BOMLen}));
|
auto Text = OwnedString::makeRefCounted(StringRef(CurPtr, BOMLen));
|
||||||
|
LeadingTrivia.push_back(TriviaPiece::garbageText(Text));
|
||||||
CurPtr += BOMLen;
|
CurPtr += BOMLen;
|
||||||
}
|
}
|
||||||
NextToken.setAtStartOfLine(true);
|
NextToken.setAtStartOfLine(true);
|
||||||
@@ -2407,18 +2408,18 @@ Restart:
|
|||||||
bool isDocComment = CurPtr[1] == '/';
|
bool isDocComment = CurPtr[1] == '/';
|
||||||
skipSlashSlashComment(/*EatNewline=*/false);
|
skipSlashSlashComment(/*EatNewline=*/false);
|
||||||
size_t Length = CurPtr - TriviaStart;
|
size_t Length = CurPtr - TriviaStart;
|
||||||
Pieces.push_back(isDocComment
|
auto Text = OwnedString::makeRefCounted(StringRef(TriviaStart, Length));
|
||||||
? TriviaPiece::docLineComment({TriviaStart, Length})
|
Pieces.push_back(isDocComment ? TriviaPiece::docLineComment(Text)
|
||||||
: TriviaPiece::lineComment({TriviaStart, Length}));
|
: TriviaPiece::lineComment(Text));
|
||||||
goto Restart;
|
goto Restart;
|
||||||
} else if (*CurPtr == '*') {
|
} else if (*CurPtr == '*') {
|
||||||
// '/* ... */' comment.
|
// '/* ... */' comment.
|
||||||
bool isDocComment = CurPtr[1] == '*';
|
bool isDocComment = CurPtr[1] == '*';
|
||||||
skipSlashStarComment();
|
skipSlashStarComment();
|
||||||
size_t Length = CurPtr - TriviaStart;
|
size_t Length = CurPtr - TriviaStart;
|
||||||
Pieces.push_back(isDocComment
|
auto Text = OwnedString::makeRefCounted(StringRef(TriviaStart, Length));
|
||||||
? TriviaPiece::docBlockComment({TriviaStart, Length})
|
Pieces.push_back(isDocComment ? TriviaPiece::docBlockComment(Text)
|
||||||
: TriviaPiece::blockComment({TriviaStart, Length}));
|
: TriviaPiece::blockComment(Text));
|
||||||
goto Restart;
|
goto Restart;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2430,7 +2431,8 @@ Restart:
|
|||||||
diagnose(TriviaStart, diag::lex_hashbang_not_allowed);
|
diagnose(TriviaStart, diag::lex_hashbang_not_allowed);
|
||||||
skipHashbang(/*EatNewline=*/false);
|
skipHashbang(/*EatNewline=*/false);
|
||||||
size_t Length = CurPtr - TriviaStart;
|
size_t Length = CurPtr - TriviaStart;
|
||||||
Pieces.push_back(TriviaPiece::garbageText({TriviaStart, Length}));
|
auto Text = OwnedString::makeRefCounted(StringRef(TriviaStart, Length));
|
||||||
|
Pieces.push_back(TriviaPiece::garbageText(Text));
|
||||||
goto Restart;
|
goto Restart;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2439,7 +2441,8 @@ Restart:
|
|||||||
if (tryLexConflictMarker(/*EatNewline=*/false)) {
|
if (tryLexConflictMarker(/*EatNewline=*/false)) {
|
||||||
// Conflict marker.
|
// Conflict marker.
|
||||||
size_t Length = CurPtr - TriviaStart;
|
size_t Length = CurPtr - TriviaStart;
|
||||||
Pieces.push_back(TriviaPiece::garbageText({TriviaStart, Length}));
|
auto Text = OwnedString::makeRefCounted(StringRef(TriviaStart, Length));
|
||||||
|
Pieces.push_back(TriviaPiece::garbageText(Text));
|
||||||
goto Restart;
|
goto Restart;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -2448,7 +2451,8 @@ Restart:
|
|||||||
case NulCharacterKind::Embedded: {
|
case NulCharacterKind::Embedded: {
|
||||||
diagnoseEmbeddedNul(Diags, CurPtr - 1);
|
diagnoseEmbeddedNul(Diags, CurPtr - 1);
|
||||||
size_t Length = CurPtr - TriviaStart;
|
size_t Length = CurPtr - TriviaStart;
|
||||||
Pieces.push_back(TriviaPiece::garbageText({TriviaStart, Length}));
|
auto Text = OwnedString::makeRefCounted(StringRef(TriviaStart, Length));
|
||||||
|
Pieces.push_back(TriviaPiece::garbageText(Text));
|
||||||
goto Restart;
|
goto Restart;
|
||||||
}
|
}
|
||||||
case NulCharacterKind::CodeCompletion:
|
case NulCharacterKind::CodeCompletion:
|
||||||
@@ -2494,7 +2498,8 @@ Restart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t Length = CurPtr - TriviaStart;
|
size_t Length = CurPtr - TriviaStart;
|
||||||
Pieces.push_back(TriviaPiece::garbageText({TriviaStart, Length}));
|
auto Text = OwnedString::makeRefCounted(StringRef(TriviaStart, Length));
|
||||||
|
Pieces.push_back(TriviaPiece::garbageText(Text));
|
||||||
goto Restart;
|
goto Restart;
|
||||||
}
|
}
|
||||||
// Reset the cursor.
|
// Reset the cursor.
|
||||||
|
|||||||
@@ -312,15 +312,15 @@ swift::tokenizeWithTrivia(const LangOptions &LangOpts, const SourceManager &SM,
|
|||||||
syntax::AbsolutePosition RunningPos;
|
syntax::AbsolutePosition RunningPos;
|
||||||
|
|
||||||
tokenize(
|
tokenize(
|
||||||
LangOpts, SM, BufferID, Offset, EndOffset,
|
LangOpts, SM, BufferID, Offset, EndOffset, Diags,
|
||||||
Diags,
|
|
||||||
CommentRetentionMode::AttachToNextToken, TriviaRetentionMode::WithTrivia,
|
CommentRetentionMode::AttachToNextToken, TriviaRetentionMode::WithTrivia,
|
||||||
/*TokenizeInterpolatedString=*/false,
|
/*TokenizeInterpolatedString=*/false,
|
||||||
/*SplitTokens=*/ArrayRef<Token>(),
|
/*SplitTokens=*/ArrayRef<Token>(),
|
||||||
[&](const Token &Tok, const Trivia &LeadingTrivia,
|
[&](const Token &Tok, const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia) {
|
const Trivia &TrailingTrivia) {
|
||||||
|
auto Text = OwnedString::makeRefCounted(Tok.getText());
|
||||||
auto ThisToken =
|
auto ThisToken =
|
||||||
RawSyntax::make(Tok.getKind(), Tok.getText(), LeadingTrivia.Pieces,
|
RawSyntax::make(Tok.getKind(), Text, LeadingTrivia.Pieces,
|
||||||
TrailingTrivia.Pieces, SourcePresence::Present);
|
TrailingTrivia.Pieces, SourcePresence::Present);
|
||||||
|
|
||||||
auto ThisTokenPos = ThisToken->accumulateAbsolutePosition(RunningPos);
|
auto ThisTokenPos = ThisToken->accumulateAbsolutePosition(RunningPos);
|
||||||
|
|||||||
@@ -163,9 +163,9 @@ void SyntaxParsingContext::addToken(Token &Tok, Trivia &LeadingTrivia,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto &Arena = getArena();
|
auto &Arena = getArena();
|
||||||
addRawSyntax(RawSyntax::getToken(Arena, Tok.getKind(), Tok.getText(),
|
auto Text = OwnedString::makeRefCounted(Tok.getText());
|
||||||
LeadingTrivia.Pieces,
|
addRawSyntax(RawSyntax::getToken(
|
||||||
TrailingTrivia.Pieces));
|
Arena, Tok.getKind(), Text, LeadingTrivia.Pieces, TrailingTrivia.Pieces));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add Syntax to the parts.
|
/// Add Syntax to the parts.
|
||||||
@@ -313,7 +313,7 @@ void finalizeSourceFile(RootContextData &RootData,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!EOFToken)
|
if (!EOFToken)
|
||||||
EOFToken = RawSyntax::missing(tok::eof, "");
|
EOFToken = RawSyntax::missing(tok::eof, OwnedString::makeUnowned(""));
|
||||||
|
|
||||||
auto newRaw = SyntaxFactory::createRaw(
|
auto newRaw = SyntaxFactory::createRaw(
|
||||||
SyntaxKind::SourceFile,
|
SyntaxKind::SourceFile,
|
||||||
@@ -352,7 +352,8 @@ void SyntaxParsingContext::synthesize(tok Kind, StringRef Text) {
|
|||||||
return;
|
return;
|
||||||
if (Text.empty())
|
if (Text.empty())
|
||||||
Text = getTokenText(Kind);
|
Text = getTokenText(Kind);
|
||||||
getStorage().push_back(RawSyntax::missing(Kind, Text));
|
auto OwnedText = OwnedString::makeRefCounted(Text);
|
||||||
|
getStorage().push_back(RawSyntax::missing(Kind, OwnedText));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyntaxParsingContext::synthesize(SyntaxKind Kind) {
|
void SyntaxParsingContext::synthesize(SyntaxKind Kind) {
|
||||||
|
|||||||
@@ -233,7 +233,8 @@ SyntaxFactory::makeBlank${node.syntax_kind}(SyntaxArena *Arena) {
|
|||||||
SyntaxFactory::make${token.name}Keyword(const Trivia &LeadingTrivia,
|
SyntaxFactory::make${token.name}Keyword(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeToken(tok::${token.kind}, "${token.text}",
|
return makeToken(tok::${token.kind},
|
||||||
|
OwnedString::makeUnowned("${token.text}"),
|
||||||
LeadingTrivia, TrailingTrivia,
|
LeadingTrivia, TrailingTrivia,
|
||||||
SourcePresence::Present, Arena);
|
SourcePresence::Present, Arena);
|
||||||
}
|
}
|
||||||
@@ -242,7 +243,8 @@ SyntaxFactory::makeBlank${node.syntax_kind}(SyntaxArena *Arena) {
|
|||||||
SyntaxFactory::make${token.name}Token(const Trivia &LeadingTrivia,
|
SyntaxFactory::make${token.name}Token(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeToken(tok::${token.kind}, "${token.text}",
|
return makeToken(tok::${token.kind},
|
||||||
|
OwnedString::makeUnowned("${token.text}"),
|
||||||
LeadingTrivia, TrailingTrivia,
|
LeadingTrivia, TrailingTrivia,
|
||||||
SourcePresence::Present, Arena);
|
SourcePresence::Present, Arena);
|
||||||
}
|
}
|
||||||
@@ -303,30 +305,35 @@ TypeSyntax SyntaxFactory::makeTypeIdentifier(OwnedString TypeName,
|
|||||||
TypeSyntax SyntaxFactory::makeAnyTypeIdentifier(const Trivia &LeadingTrivia,
|
TypeSyntax SyntaxFactory::makeAnyTypeIdentifier(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeTypeIdentifier("Any", LeadingTrivia, TrailingTrivia, Arena);
|
return makeTypeIdentifier(OwnedString::makeUnowned("Any"), LeadingTrivia,
|
||||||
|
TrailingTrivia, Arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeSyntax SyntaxFactory::makeSelfTypeIdentifier(const Trivia &LeadingTrivia,
|
TypeSyntax SyntaxFactory::makeSelfTypeIdentifier(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeTypeIdentifier("Self", LeadingTrivia, TrailingTrivia, Arena);
|
return makeTypeIdentifier(OwnedString::makeUnowned("Self"),
|
||||||
|
LeadingTrivia, TrailingTrivia, Arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenSyntax SyntaxFactory::makeTypeToken(const Trivia &LeadingTrivia,
|
TokenSyntax SyntaxFactory::makeTypeToken(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeIdentifier("Type", LeadingTrivia, TrailingTrivia, Arena);
|
return makeIdentifier(OwnedString::makeUnowned("Type"),
|
||||||
|
LeadingTrivia, TrailingTrivia, Arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenSyntax SyntaxFactory::makeProtocolToken(const Trivia &LeadingTrivia,
|
TokenSyntax SyntaxFactory::makeProtocolToken(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeIdentifier("Protocol", LeadingTrivia, TrailingTrivia, Arena);
|
return makeIdentifier(OwnedString::makeUnowned("Protocol"),
|
||||||
|
LeadingTrivia, TrailingTrivia, Arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenSyntax SyntaxFactory::makeEqualityOperator(const Trivia &LeadingTrivia,
|
TokenSyntax SyntaxFactory::makeEqualityOperator(const Trivia &LeadingTrivia,
|
||||||
const Trivia &TrailingTrivia,
|
const Trivia &TrailingTrivia,
|
||||||
SyntaxArena *Arena) {
|
SyntaxArena *Arena) {
|
||||||
return makeToken(tok::oper_binary_spaced, "==", LeadingTrivia, TrailingTrivia,
|
return makeToken(tok::oper_binary_spaced, OwnedString::makeUnowned("=="),
|
||||||
SourcePresence::Present, Arena);
|
LeadingTrivia, TrailingTrivia, SourcePresence::Present,
|
||||||
|
Arena);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,177 +18,43 @@ using namespace swift;
|
|||||||
TEST(OwnedStringTest, char_pointer_empty) {
|
TEST(OwnedStringTest, char_pointer_empty) {
|
||||||
const char *data = "";
|
const char *data = "";
|
||||||
const size_t length = strlen(data);
|
const size_t length = strlen(data);
|
||||||
OwnedString ownedString(data);
|
OwnedString ownedString = OwnedString::makeUnowned(data);
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
EXPECT_EQ(length, ownedString.size());
|
||||||
EXPECT_TRUE(ownedString.empty());
|
EXPECT_TRUE(ownedString.empty());
|
||||||
|
EXPECT_EQ(data, ownedString.str().data());
|
||||||
OwnedString copy = ownedString.copy();
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_TRUE(copy.empty());
|
|
||||||
|
|
||||||
StringRef str = copy.str();
|
|
||||||
EXPECT_EQ("", str);
|
|
||||||
EXPECT_EQ(length, str.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OwnedStringTest, char_pointer_non_empty) {
|
TEST(OwnedStringTest, char_pointer_non_empty) {
|
||||||
const char *data = "string";
|
const char *data = "string";
|
||||||
const size_t length = strlen(data);
|
const size_t length = strlen(data);
|
||||||
OwnedString ownedString(data);
|
OwnedString ownedString = OwnedString::makeUnowned(data);
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
EXPECT_EQ(length, ownedString.size());
|
||||||
EXPECT_FALSE(ownedString.empty());
|
EXPECT_FALSE(ownedString.empty());
|
||||||
|
EXPECT_EQ(data, ownedString.str().data());
|
||||||
OwnedString copy = ownedString.copy();
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_FALSE(copy.empty());
|
|
||||||
|
|
||||||
StringRef str = copy.str();
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
EXPECT_EQ(length, strlen(str.data()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OwnedStringTest, char_pointer_length_equal) {
|
TEST(OwnedStringTest, ref_counted_copies_buffer) {
|
||||||
const char *data = "string";
|
char *data = static_cast<char *>(malloc(6));
|
||||||
|
memcpy(data, "hello", 6);
|
||||||
size_t length = strlen(data);
|
size_t length = strlen(data);
|
||||||
OwnedString ownedString(data, length);
|
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
OwnedString ownedString =
|
||||||
EXPECT_FALSE(ownedString.empty());
|
OwnedString::makeRefCounted(StringRef(data, length));
|
||||||
|
|
||||||
OwnedString copy = ownedString.copy();
|
EXPECT_EQ(ownedString.str(), "hello");
|
||||||
EXPECT_EQ(length, copy.size());
|
EXPECT_NE(ownedString.str().data(), data);
|
||||||
EXPECT_FALSE(copy.empty());
|
|
||||||
|
|
||||||
// Make sure we correctly copied the data and that it is null
|
memcpy(data, "world", 6);
|
||||||
// terminated.
|
|
||||||
StringRef str = copy.str();
|
// Even if the original buffer changes, the string should stay the same
|
||||||
EXPECT_EQ("string", str);
|
EXPECT_EQ(ownedString.str(), "hello");
|
||||||
EXPECT_EQ(length, strlen(str.data()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OwnedStringTest, char_pointer_length_nonzero) {
|
TEST(OwnedStringTest, ref_counted_assignment) {
|
||||||
const char *data = "string";
|
OwnedString str = OwnedString::makeRefCounted("hello");
|
||||||
const size_t length = 1;
|
OwnedString copy = str;
|
||||||
OwnedString ownedString(data, length);
|
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
EXPECT_EQ(str.str().data(), copy.str().data());
|
||||||
EXPECT_FALSE(ownedString.empty());
|
|
||||||
|
|
||||||
OwnedString copy = ownedString.copy();
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_FALSE(copy.empty());
|
|
||||||
|
|
||||||
// Make sure we correctly copied the data and that it is null
|
|
||||||
// terminated.
|
|
||||||
StringRef str = copy.str();
|
|
||||||
EXPECT_EQ("s", str);
|
|
||||||
EXPECT_EQ(1UL, strlen(str.data()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OwnedStringTest, char_pointer_length_zero) {
|
|
||||||
const char *data = "string";
|
|
||||||
const size_t length = 0;
|
|
||||||
OwnedString ownedString(data, length);
|
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
|
||||||
EXPECT_TRUE(ownedString.empty());
|
|
||||||
|
|
||||||
OwnedString copy = ownedString.copy();
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_TRUE(copy.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OwnedStringTest, copy_original_new_different) {
|
|
||||||
// Initialize a mutable string.
|
|
||||||
const char *original = "string";
|
|
||||||
const size_t length = strlen(original);
|
|
||||||
char *data = static_cast<char *>(malloc(length + 1));
|
|
||||||
memcpy(data, original, length);
|
|
||||||
data[length] = '\0';
|
|
||||||
|
|
||||||
// Create an OwnedString.
|
|
||||||
OwnedString ownedString(data, length);
|
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
|
||||||
EXPECT_FALSE(ownedString.empty());
|
|
||||||
|
|
||||||
// Copy the string
|
|
||||||
OwnedString copy = ownedString.copy();
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_FALSE(copy.empty());
|
|
||||||
|
|
||||||
// Make sure we correctly copied the data and that it is null
|
|
||||||
// terminated.
|
|
||||||
StringRef str = copy.str();
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
EXPECT_EQ(length, strlen(str.data()));
|
|
||||||
|
|
||||||
// Make sure updating the original pointer doesn't affect the copy.
|
|
||||||
data[0] = 'a';
|
|
||||||
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OwnedStringTest, copy_constructor_original_not_copy) {
|
|
||||||
// Initialize a mutable string.
|
|
||||||
const char *original = "string";
|
|
||||||
const size_t length = strlen(original);
|
|
||||||
char *data = static_cast<char *>(malloc(length + 1));
|
|
||||||
memcpy(data, original, length);
|
|
||||||
data[length] = '\0';
|
|
||||||
|
|
||||||
// Create an OwnedString.
|
|
||||||
OwnedString ownedString(data, length);
|
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
|
||||||
EXPECT_FALSE(ownedString.empty());
|
|
||||||
|
|
||||||
// Copy the string
|
|
||||||
OwnedString copy = OwnedString(ownedString);
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_FALSE(copy.empty());
|
|
||||||
|
|
||||||
// Make sure we correctly copied the data and that it is null
|
|
||||||
// terminated.
|
|
||||||
StringRef str = copy.str();
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
EXPECT_EQ(length, strlen(str.data()));
|
|
||||||
|
|
||||||
// Make sure updating the original pointer doesn't affect the copy.
|
|
||||||
data[0] = 'a';
|
|
||||||
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(OwnedStringTest, copy_constructor_original_copy) {
|
|
||||||
// Initialize a mutable string.
|
|
||||||
const char *original = "string";
|
|
||||||
const size_t length = strlen(original);
|
|
||||||
char *data = static_cast<char *>(malloc(length + 1));
|
|
||||||
memcpy(data, original, length);
|
|
||||||
data[length] = '\0';
|
|
||||||
|
|
||||||
// Create an OwnedString.
|
|
||||||
OwnedString ownedString(data, length);
|
|
||||||
|
|
||||||
EXPECT_EQ(length, ownedString.size());
|
|
||||||
EXPECT_FALSE(ownedString.empty());
|
|
||||||
|
|
||||||
// Copy the string
|
|
||||||
OwnedString copy = OwnedString(ownedString.copy());
|
|
||||||
EXPECT_EQ(length, copy.size());
|
|
||||||
EXPECT_FALSE(copy.empty());
|
|
||||||
|
|
||||||
// Make sure we correctly copied the data and that it is null
|
|
||||||
// terminated.
|
|
||||||
StringRef str = copy.str();
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
EXPECT_EQ(length, strlen(str.data()));
|
|
||||||
|
|
||||||
// Make sure updating the original pointer doesn't affect the copy.
|
|
||||||
data[0] = 'a';
|
|
||||||
|
|
||||||
EXPECT_EQ("string", str);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ def make_missing_child(child):
|
|||||||
token = child.main_token()
|
token = child.main_token()
|
||||||
tok_kind = token.kind if token else "unknown"
|
tok_kind = token.kind if token else "unknown"
|
||||||
tok_text = token.text if token else ""
|
tok_text = token.text if token else ""
|
||||||
return 'RawSyntax::missing(tok::%s, "%s")' % (tok_kind, tok_text)
|
return \
|
||||||
|
'RawSyntax::missing(tok::%s, OwnedString::makeUnowned("%s"))' % \
|
||||||
|
(tok_kind, tok_text)
|
||||||
else:
|
else:
|
||||||
missing_kind = "Unknown" if child.syntax_kind == "Syntax" \
|
missing_kind = "Unknown" if child.syntax_kind == "Syntax" \
|
||||||
else child.syntax_kind
|
else child.syntax_kind
|
||||||
|
|||||||
Reference in New Issue
Block a user