Skip to content

Commit 0d23f05

Browse files
committed
clang_EvalResult_getAsCXString impl
1 parent 449e2f5 commit 0d23f05

File tree

6 files changed

+96
-37
lines changed

6 files changed

+96
-37
lines changed

clang/include/clang-c/CXString.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#ifndef LLVM_CLANG_C_CXSTRING_H
1515
#define LLVM_CLANG_C_CXSTRING_H
1616

17+
#include <stddef.h>
1718
#include "clang-c/ExternC.h"
1819
#include "clang-c/Platform.h"
1920

@@ -36,6 +37,7 @@ LLVM_CLANG_C_EXTERN_C_BEGIN
3637
*/
3738
typedef struct {
3839
const void *data;
40+
size_t length;
3941
unsigned private_flags;
4042
} CXString;
4143

@@ -52,6 +54,7 @@ typedef struct {
5254
* to `std::string::c_str()`.
5355
*/
5456
CINDEX_LINKAGE const char *clang_getCString(CXString string);
57+
CINDEX_LINKAGE const char *clang_getCString2(CXString string, size_t *length);
5558

5659
/**
5760
* Free the given string.

clang/include/clang-c/Index.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5918,12 +5918,19 @@ clang_EvalResult_getAsUnsigned(CXEvalResult E);
59185918
CINDEX_LINKAGE double clang_EvalResult_getAsDouble(CXEvalResult E);
59195919

59205920
/**
5921-
* Returns the evaluation result as a constant string if the
5922-
* kind is other than Int or float. User must not free this pointer,
5923-
* instead call clang_EvalResult_dispose on the CXEvalResult returned
5924-
* by clang_Cursor_Evaluate.
5921+
* This function behaves the same as clang_EvalResult_getAsCXString, with 2
5922+
* exceptions:
5923+
* - the string literal will be truncated if a nul byte is found in the string.
5924+
* For this reason clang_EvalResult_getAsCXString is recommended.
5925+
* - User must not free this pointer, instead call clang_EvalResult_dispose on
5926+
* the CXEvalResult returned by clang_Cursor_Evaluate.
59255927
*/
59265928
CINDEX_LINKAGE const char *clang_EvalResult_getAsStr(CXEvalResult E);
5929+
/**
5930+
* Returns the evaluation result as a constant string if the
5931+
* kind is other than Int or float. This might include zero bytes.
5932+
*/
5933+
CINDEX_LINKAGE CXString clang_EvalResult_getAsCXString(CXEvalResult E);
59275934

59285935
/**
59295936
* Disposes the created Eval memory.

clang/tools/libclang/CIndex.cpp

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4589,13 +4589,13 @@ struct ExprEvalResult {
45894589
unsigned long long unsignedVal;
45904590
long long intVal;
45914591
double floatVal;
4592-
char *stringVal;
4592+
CXString stringVal;
45934593
} EvalData;
45944594
bool IsUnsignedInt;
45954595
~ExprEvalResult() {
45964596
if (EvalType != CXEval_UnExposed && EvalType != CXEval_Float &&
45974597
EvalType != CXEval_Int) {
4598-
delete[] EvalData.stringVal;
4598+
clang_disposeString(EvalData.stringVal);
45994599
}
46004600
}
46014601
};
@@ -4651,7 +4651,17 @@ const char *clang_EvalResult_getAsStr(CXEvalResult E) {
46514651
if (!E) {
46524652
return nullptr;
46534653
}
4654-
return ((ExprEvalResult *)E)->EvalData.stringVal;
4654+
return clang_getCString(((ExprEvalResult *)E)->EvalData.stringVal);
4655+
}
4656+
4657+
CXString clang_EvalResult_getAsCXString(CXEvalResult E) {
4658+
if (!E) {
4659+
return cxstring::createNull();
4660+
}
4661+
size_t length;
4662+
auto data =
4663+
clang_getCString2(((ExprEvalResult *)E)->EvalData.stringVal, &length);
4664+
return cxstring::createDup(StringRef(data, length));
46554665
}
46564666

46574667
static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
@@ -4716,11 +4726,7 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
47164726
result->EvalType = CXEval_StrLiteral;
47174727
}
47184728

4719-
std::string strRef(StrE->getString().str());
4720-
result->EvalData.stringVal = new char[strRef.size() + 1];
4721-
strncpy((char *)result->EvalData.stringVal, strRef.c_str(),
4722-
strRef.size());
4723-
result->EvalData.stringVal[strRef.size()] = '\0';
4729+
result->EvalData.stringVal = cxstring::createDup(StrE->getString());
47244730
return result.release();
47254731
}
47264732
} else if (expr->getStmtClass() == Stmt::ObjCStringLiteralClass ||
@@ -4737,10 +4743,7 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
47374743
result->EvalType = CXEval_StrLiteral;
47384744
}
47394745

4740-
std::string strRef(StrE->getString().str());
4741-
result->EvalData.stringVal = new char[strRef.size() + 1];
4742-
strncpy((char *)result->EvalData.stringVal, strRef.c_str(), strRef.size());
4743-
result->EvalData.stringVal[strRef.size()] = '\0';
4746+
result->EvalData.stringVal = cxstring::createDup(StrE->getString());
47444747
return result.release();
47454748
}
47464749

@@ -4754,13 +4757,8 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
47544757
callExpr = static_cast<CallExpr *>(CC->getSubExpr());
47554758
StringLiteral *S = getCFSTR_value(callExpr);
47564759
if (S) {
4757-
std::string strLiteral(S->getString().str());
47584760
result->EvalType = CXEval_CFStr;
4759-
4760-
result->EvalData.stringVal = new char[strLiteral.size() + 1];
4761-
strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(),
4762-
strLiteral.size());
4763-
result->EvalData.stringVal[strLiteral.size()] = '\0';
4761+
result->EvalData.stringVal = cxstring::createDup(S->getString());
47644762
return result.release();
47654763
}
47664764
}
@@ -4780,24 +4778,18 @@ static const ExprEvalResult *evaluateExpr(Expr *expr, CXCursor C) {
47804778

47814779
StringLiteral *S = getCFSTR_value(callExpr);
47824780
if (S) {
4783-
std::string strLiteral(S->getString().str());
47844781
result->EvalType = CXEval_CFStr;
4785-
result->EvalData.stringVal = new char[strLiteral.size() + 1];
4786-
strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(),
4787-
strLiteral.size());
4788-
result->EvalData.stringVal[strLiteral.size()] = '\0';
4782+
result->EvalData.stringVal = cxstring::createDup(S->getString());
47894783
return result.release();
47904784
}
47914785
}
47924786
} else if (expr->getStmtClass() == Stmt::DeclRefExprClass) {
47934787
DeclRefExpr *D = static_cast<DeclRefExpr *>(expr);
47944788
ValueDecl *V = D->getDecl();
47954789
if (V->getKind() == Decl::Function) {
4796-
std::string strName = V->getNameAsString();
47974790
result->EvalType = CXEval_Other;
4798-
result->EvalData.stringVal = new char[strName.size() + 1];
4799-
strncpy(result->EvalData.stringVal, strName.c_str(), strName.size());
4800-
result->EvalData.stringVal[strName.size()] = '\0';
4791+
result->EvalData.stringVal =
4792+
cxstring::createDup(StringRef(V->getNameAsString()));
48014793
return result.release();
48024794
}
48034795
}

clang/tools/libclang/CXString.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ namespace cxstring {
4343
CXString createEmpty() {
4444
CXString Str;
4545
Str.data = "";
46+
Str.length = 0;
4647
Str.private_flags = CXS_Unmanaged;
4748
return Str;
4849
}
4950

5051
CXString createNull() {
5152
CXString Str;
5253
Str.data = nullptr;
54+
Str.length = 0;
5355
Str.private_flags = CXS_Unmanaged;
5456
return Str;
5557
}
@@ -60,6 +62,7 @@ CXString createRef(const char *String) {
6062

6163
CXString Str;
6264
Str.data = String;
65+
Str.length = -1;
6366
Str.private_flags = CXS_Unmanaged;
6467
return Str;
6568
}
@@ -71,10 +74,7 @@ CXString createDup(const char *String) {
7174
if (String[0] == '\0')
7275
return createEmpty();
7376

74-
CXString Str;
75-
Str.data = strdup(String);
76-
Str.private_flags = CXS_Malloc;
77-
return Str;
77+
return createDup(StringRef(String));
7878
}
7979

8080
CXString createRef(StringRef String) {
@@ -91,18 +91,21 @@ CXString createRef(StringRef String) {
9191
}
9292

9393
CXString createDup(StringRef String) {
94-
CXString Result;
9594
char *Spelling = static_cast<char *>(llvm::safe_malloc(String.size() + 1));
96-
memmove(Spelling, String.data(), String.size());
95+
memcpy(Spelling, String.data(), String.size());
9796
Spelling[String.size()] = 0;
97+
98+
CXString Result;
9899
Result.data = Spelling;
100+
Result.length = String.size();
99101
Result.private_flags = (unsigned) CXS_Malloc;
100102
return Result;
101103
}
102104

103105
CXString createCXString(CXStringBuf *buf) {
104106
CXString Str;
105107
Str.data = buf;
108+
Str.length = -1;
106109
Str.private_flags = (unsigned) CXS_StringBuf;
107110
return Str;
108111
}
@@ -164,6 +167,13 @@ const char *clang_getCString(CXString string) {
164167
return static_cast<const char *>(string.data);
165168
}
166169

170+
const char *clang_getCString2(CXString string, size_t *length) {
171+
auto ret = clang_getCString(string);
172+
*length =
173+
string.length == static_cast<size_t>(-1) ? strlen(ret) : string.length;
174+
return ret;
175+
}
176+
167177
void clang_disposeString(CXString string) {
168178
switch ((CXStringFlag) string.private_flags) {
169179
case CXS_Unmanaged:

clang/tools/libclang/libclang.map

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,12 @@ LLVM_20 {
438438
clang_visitCXXMethods;
439439
};
440440

441+
LLVM_21 {
442+
global:
443+
clang_getCString2;
444+
clang_EvalResult_getAsCXString;
445+
};
446+
441447
# Example of how to add a new symbol version entry. If you do add a new symbol
442448
# version, please update the example to depend on the version you added.
443449
# LLVM_X {

clang/unittests/libclang/LibclangTest.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,47 @@ TEST_F(LibclangParseTest, EvaluateChildExpression) {
623623
nullptr);
624624
}
625625

626+
TEST_F(LibclangParseTest, StringLiteralWithZeros) {
627+
const char testSource[] = R"cpp(
628+
const char str[] = "pika\0chu";
629+
)cpp";
630+
std::string fileName = "main.cpp";
631+
WriteFile(fileName, testSource);
632+
633+
const char *Args[] = {"-xc++"};
634+
ClangTU = clang_parseTranslationUnit(Index, fileName.c_str(), Args, 1,
635+
nullptr, 0, TUFlags);
636+
637+
int nodes = 0;
638+
639+
Traverse([&nodes](CXCursor cursor, CXCursor parent) -> CXChildVisitResult {
640+
if (cursor.kind == CXCursor_StringLiteral) {
641+
CXEvalResult RE = clang_Cursor_Evaluate(cursor);
642+
EXPECT_NE(RE, nullptr);
643+
EXPECT_EQ(clang_EvalResult_getKind(RE), CXEval_StrLiteral);
644+
645+
const char expected[] = "pika\0chu";
646+
size_t expected_size = sizeof(expected) - 1;
647+
648+
auto lit = clang_EvalResult_getAsCXString(RE);
649+
size_t length;
650+
auto str = clang_getCString2(lit, &length);
651+
652+
EXPECT_TRUE(length == expected_size &&
653+
memcmp(str, expected, length) == 0);
654+
655+
clang_disposeString(lit);
656+
clang_EvalResult_dispose(RE);
657+
658+
nodes++;
659+
return CXChildVisit_Continue;
660+
}
661+
return CXChildVisit_Recurse;
662+
});
663+
664+
EXPECT_EQ(nodes, 1);
665+
}
666+
626667
class LibclangReparseTest : public LibclangParseTest {
627668
public:
628669
void DisplayDiagnostics() {

0 commit comments

Comments
 (0)