mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
127176d72f
PiperOrigin-RevId: 559733768 Change-Id: Ia38f4c176e9f0abbfdb3a8f1109f482d8870eb0f
414 lines
14 KiB
C++
414 lines
14 KiB
C++
// Copyright 2020 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "sandboxed_api/tools/clang_generator/emitter.h"
|
|
|
|
#include <initializer_list>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
#include "absl/status/statusor.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "sandboxed_api/tools/clang_generator/frontend_action_test_util.h"
|
|
#include "sandboxed_api/tools/clang_generator/generator.h"
|
|
#include "sandboxed_api/util/status_matchers.h"
|
|
|
|
namespace sapi {
|
|
namespace {
|
|
|
|
using ::testing::ElementsAre;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::MatchesRegex;
|
|
using ::testing::SizeIs;
|
|
using ::testing::StrEq;
|
|
using ::testing::StrNe;
|
|
|
|
class EmitterForTesting : public Emitter {
|
|
public:
|
|
std::vector<std::string> SpellingsForNS(const std::string& ns_name) {
|
|
std::vector<std::string> result;
|
|
for (const RenderedType* rt : rendered_types_ordered_) {
|
|
if (rt->ns_name == ns_name) {
|
|
result.push_back(rt->spelling);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const std::vector<std::string>& GetRenderedFunctions() {
|
|
return rendered_functions_ordered_;
|
|
}
|
|
};
|
|
|
|
class EmitterTest : public FrontendActionTest {};
|
|
|
|
TEST_F(EmitterTest, BasicFunctionality) {
|
|
GeneratorOptions options;
|
|
options.set_function_names<std::initializer_list<std::string>>(
|
|
{"ExposedFunction"});
|
|
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(R"(extern "C" void ExposedFunction() {})",
|
|
std::make_unique<GeneratorAction>(emitter, options)),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
|
|
|
absl::StatusOr<std::string> header = emitter.EmitHeader(options);
|
|
EXPECT_THAT(header, IsOk());
|
|
}
|
|
|
|
TEST_F(EmitterTest, RelatedTypes) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(namespace std {
|
|
using size_t = unsigned long;
|
|
} // namespace std
|
|
using std::size_t;
|
|
typedef enum { kRed, kGreen, kBlue } Color;
|
|
struct Channel {
|
|
Color color;
|
|
size_t width;
|
|
size_t height;
|
|
};
|
|
extern "C" void Colorize(Channel* chan);
|
|
|
|
typedef struct { int member; } MyStruct;
|
|
extern "C" void Structize(MyStruct* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(2));
|
|
|
|
// Types from "std" should be skipped
|
|
EXPECT_THAT(emitter.SpellingsForNS("std"), IsEmpty());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("typedef enum { kRed, kGreen, kBlue } Color",
|
|
"struct Channel {"
|
|
" Color color;"
|
|
" size_t width;"
|
|
" size_t height; }",
|
|
"typedef struct { int member; } MyStruct"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, CollectFunctionPointer) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(typedef void (callback_t)(void*);
|
|
struct HandlerData {
|
|
int member;
|
|
callback_t* cb;
|
|
};
|
|
extern "C" int Structize(HandlerData*);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
|
|
|
EXPECT_THAT(
|
|
UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("typedef void (callback_t)(void *)",
|
|
"struct HandlerData { int member; callback_t *cb; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, TypedefNames) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(typedef enum { kNone, kSome } E;
|
|
struct A { E member; };
|
|
typedef struct { int member; } B;
|
|
typedef struct tagC { int member; } C;
|
|
extern "C" void Colorize(A*, B*, C*);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(
|
|
UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("typedef enum { kNone, kSome } E", "struct A { E member; }",
|
|
"typedef struct { int member; } B",
|
|
"struct tagC { int member; }", "typedef struct tagC C"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, NestedStruct) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A {
|
|
struct B { int number; };
|
|
B b;
|
|
int data;
|
|
};
|
|
extern "C" void Structize(A* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("struct A {"
|
|
" struct B { int number; };"
|
|
" B b;"
|
|
" int data; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, NestedAnonymousStruct) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A {
|
|
struct { int number; } b;
|
|
int data;
|
|
};
|
|
extern "C" void Structize(A* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("struct A {"
|
|
" struct { int number; } b;"
|
|
" int data; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, ParentNotCollected) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A {
|
|
struct B { int number; };
|
|
B b;
|
|
int data;
|
|
};
|
|
extern "C" void Structize(A::B* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("struct A {"
|
|
" struct B { int number; };"
|
|
" B b;"
|
|
" int data; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, StructForwardDecl) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A;
|
|
extern "C" void UsingForwardDeclaredStruct(A* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")), ElementsAre("struct A"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, AggregateStructWithDefaultedMembers) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A {
|
|
int a = 0;
|
|
int b = 42;
|
|
};
|
|
extern "C" void AggregateStruct(A* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("struct A {"
|
|
" int a = 0;"
|
|
" int b = 42; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, AggregateStructWithMethods) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A {
|
|
int a = 0;
|
|
int b = 42;
|
|
int my_mem_fn();
|
|
};
|
|
extern "C" void AggregateStruct(A* s);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
// Expect a forward decl in this case
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")), ElementsAre("struct A"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, RemoveQualifiers) {
|
|
EmitterForTesting emitter;
|
|
ASSERT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A { int data; };
|
|
extern "C" void Structize(const A* in, A* out);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("struct A { int data; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, StructByValueSkipsFunction) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A { int data; };
|
|
extern "C" int Structize(A a);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), IsEmpty());
|
|
}
|
|
|
|
TEST_F(EmitterTest, ReturnStructByValueSkipsFunction) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(struct A { int data; };
|
|
extern "C" A Structize();)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), IsEmpty());
|
|
}
|
|
|
|
TEST_F(EmitterTest, TypedefStructByValueSkipsFunction) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(typedef struct { int data; } A;
|
|
extern "C" int Structize(A a);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), IsEmpty());
|
|
}
|
|
|
|
TEST_F(EmitterTest, CollectTypedefPointerType) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(typedef struct _KernelProfileRecord {
|
|
int member;
|
|
}* KernelProfileRecord;
|
|
extern "C" const KernelProfileRecord*
|
|
GetOpenCLKernelProfileRecords(const int, long long int*);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
|
|
|
EXPECT_THAT(
|
|
UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("struct _KernelProfileRecord { int member; }",
|
|
"typedef struct _KernelProfileRecord *KernelProfileRecord"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, TypedefTypeDependencies) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(typedef bool some_other_unused;
|
|
using size_t = long long int;
|
|
typedef struct _Image Image;
|
|
typedef size_t (*StreamHandler)(const Image*, const void*,
|
|
const size_t);
|
|
enum unrelated_unused { NONE, SOME };
|
|
struct _Image {
|
|
StreamHandler stream;
|
|
int size;
|
|
};
|
|
extern "C" void Process(StreamHandler handler);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")),
|
|
ElementsAre("using size_t = long long", "struct _Image",
|
|
"typedef struct _Image Image",
|
|
"typedef size_t (*StreamHandler)(const Image *, "
|
|
"const void *, const size_t)",
|
|
"struct _Image {"
|
|
" StreamHandler stream;"
|
|
" int size; }"));
|
|
}
|
|
|
|
TEST_F(EmitterTest, OmitDependentTypes) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(template <typename T>
|
|
struct Callback {
|
|
typedef void (T::*MemberSignature)();
|
|
MemberSignature pointer;
|
|
};
|
|
struct S : public Callback<S> {
|
|
void Callable() {}
|
|
};
|
|
extern "C" void Invoke(S::MemberSignature* cb);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")), IsEmpty());
|
|
}
|
|
|
|
TEST_F(EmitterTest, SkipAbseilInternals) {
|
|
EmitterForTesting emitter;
|
|
EXPECT_THAT(
|
|
RunFrontendAction(
|
|
R"(namespace absl::internal {
|
|
typedef int Int;
|
|
}
|
|
extern "C" void TakesAnInternalInt(absl::internal::Int);
|
|
extern "C" void AbslInternalTakingAnInt(int);)",
|
|
std::make_unique<GeneratorAction>(emitter, GeneratorOptions())),
|
|
IsOk());
|
|
EXPECT_THAT(emitter.GetRenderedFunctions(), SizeIs(1));
|
|
|
|
EXPECT_THAT(UglifyAll(emitter.SpellingsForNS("")), IsEmpty());
|
|
}
|
|
|
|
TEST(IncludeGuard, CreatesRandomizedGuardForEmptyFilename) {
|
|
// Copybara will transform the string. This is intentional.
|
|
constexpr absl::string_view kGeneratedHeaderPrefix =
|
|
"SANDBOXED_API_GENERATED_HEADER_";
|
|
|
|
const std::string include_guard = GetIncludeGuard("");
|
|
EXPECT_THAT(include_guard, MatchesRegex(absl::StrCat(kGeneratedHeaderPrefix,
|
|
R"([0-9A-F]+_)")));
|
|
|
|
EXPECT_THAT(GetIncludeGuard(""), StrNe(include_guard));
|
|
}
|
|
|
|
TEST(IncludeGuard, BasicFunctionality) {
|
|
EXPECT_THAT(GetIncludeGuard("boost/graph/compressed_sparse_row_graph.hpp"),
|
|
StrEq("BOOST_GRAPH_COMPRESSED_SPARSE_ROW_GRAPH_HPP_"));
|
|
|
|
// "SAPI_" prefix is there to avoid generating guards starting with "_"
|
|
EXPECT_THAT(GetIncludeGuard("/usr/include/unistd.h"),
|
|
StrEq("SAPI_USR_INCLUDE_UNISTD_H_"));
|
|
}
|
|
|
|
TEST(IncludeGuard, AvoidReservedIdentifiers) {
|
|
EXPECT_THAT(GetIncludeGuard("9p.h"), StrEq("SAPI_9P_H_"));
|
|
EXPECT_THAT(GetIncludeGuard("double__under.h"), StrEq("DOUBLE_UNDER_H_"));
|
|
EXPECT_THAT(GetIncludeGuard("_single.h"), StrEq("SAPI_SINGLE_H_"));
|
|
EXPECT_THAT(GetIncludeGuard("__double.h"), StrEq("SAPI_DOUBLE_H_"));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace sapi
|