fuzzprep 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fuzzprep/__init__.py +3 -0
- fuzzprep/__main__.py +4 -0
- fuzzprep/agents/harness_builder/SKILL.md +156 -0
- fuzzprep/agents/library_builder/SKILL.md +147 -0
- fuzzprep/agents/scripts/check_build.sh +87 -0
- fuzzprep/agents/scripts/check_build_in_container.sh +73 -0
- fuzzprep/agents/scripts/check_dockerfile_from_scratch.sh +33 -0
- fuzzprep/cli.py +1109 -0
- fuzzprep/core/__init__.py +0 -0
- fuzzprep/core/agent_stream.py +304 -0
- fuzzprep/core/files.py +35 -0
- fuzzprep/core/paths.py +25 -0
- fuzzprep/core/reporting.py +218 -0
- fuzzprep/core/repos.py +217 -0
- fuzzprep/core/resources.py +41 -0
- fuzzprep/core/subprocesses.py +197 -0
- fuzzprep/feature_extractor/__init__.py +0 -0
- fuzzprep/feature_extractor/benchmark_yaml.py +92 -0
- fuzzprep/feature_extractor/extraction.py +184 -0
- fuzzprep/feature_extractor/models.py +96 -0
- fuzzprep/feature_extractor/native/.clang-format +1 -0
- fuzzprep/feature_extractor/native/CMakeLists.txt +90 -0
- fuzzprep/feature_extractor/native/include/feature_extractor.hpp +136 -0
- fuzzprep/feature_extractor/native/src/extraction_action.cpp +294 -0
- fuzzprep/feature_extractor/native/src/json_writer.cpp +154 -0
- fuzzprep/feature_extractor/native/src/macro_callbacks.cpp +75 -0
- fuzzprep/feature_extractor/native/src/main.cpp +130 -0
- fuzzprep/feature_extractor/native_build.py +99 -0
- fuzzprep/library_builder/__init__.py +0 -0
- fuzzprep/library_builder/agents.py +472 -0
- fuzzprep/library_builder/analysis.py +145 -0
- fuzzprep/library_builder/build_parameters.py +158 -0
- fuzzprep/library_builder/dependency_resolution.py +139 -0
- fuzzprep/library_builder/environments/__init__.py +0 -0
- fuzzprep/library_builder/environments/base.py +89 -0
- fuzzprep/library_builder/environments/gate.py +96 -0
- fuzzprep/library_builder/environments/local.py +205 -0
- fuzzprep/library_builder/environments/oss_fuzz.py +485 -0
- fuzzprep/library_builder/environments/verification.py +125 -0
- fuzzprep/library_builder/exploration.py +217 -0
- fuzzprep/library_builder/generation.py +325 -0
- fuzzprep/library_builder/harness_explorer.py +257 -0
- fuzzprep/library_builder/models.py +169 -0
- fuzzprep/library_builder/package_names.json +33 -0
- fuzzprep/library_builder/package_names.py +40 -0
- fuzzprep/library_builder/scripts.py +389 -0
- fuzzprep/library_builder/stats.py +102 -0
- fuzzprep/library_builder/symbol_patterns.json +65 -0
- fuzzprep/library_builder/timeouts.py +14 -0
- fuzzprep/library_builder/workspace.py +250 -0
- fuzzprep-0.1.0.dist-info/METADATA +255 -0
- fuzzprep-0.1.0.dist-info/RECORD +56 -0
- fuzzprep-0.1.0.dist-info/WHEEL +4 -0
- fuzzprep-0.1.0.dist-info/entry_points.txt +3 -0
- fuzzprep-0.1.0.dist-info/licenses/LICENSE +202 -0
- fuzzprep-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +52 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from fuzzprep.feature_extractor.models import (
|
|
7
|
+
SCHEMA_VERSION,
|
|
8
|
+
EnumDefinition,
|
|
9
|
+
Enumerator,
|
|
10
|
+
FeatureArtifactSet,
|
|
11
|
+
Field,
|
|
12
|
+
FunctionSignature,
|
|
13
|
+
MacroDefinition,
|
|
14
|
+
Param,
|
|
15
|
+
StructUnionDefinition,
|
|
16
|
+
Typedef,
|
|
17
|
+
)
|
|
18
|
+
from fuzzprep.library_builder.models import Language
|
|
19
|
+
|
|
20
|
+
_REQUIRED_FIELDS = (
|
|
21
|
+
"schema_version",
|
|
22
|
+
"project_name",
|
|
23
|
+
"language",
|
|
24
|
+
"functions",
|
|
25
|
+
"typedefs",
|
|
26
|
+
"macros",
|
|
27
|
+
"enums",
|
|
28
|
+
"records",
|
|
29
|
+
"warnings",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
_LANGUAGE_BY_VALUE = {lang.value: lang for lang in Language}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FeatureArtifactError(Exception):
|
|
36
|
+
"""The feature artifact JSON is missing, malformed, or from an incompatible schema."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MissingCompileCommandsError(Exception):
|
|
40
|
+
"""The output directory has no compile_commands.json to extract features from."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MissingFeatureArtifactError(Exception):
|
|
44
|
+
"""The output directory has no features.json; extract-features must run first."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def extract_features(output_dir: Path) -> FeatureArtifactSet:
|
|
48
|
+
"""Extract a library's declarations into features.json, returning the parsed result.
|
|
49
|
+
|
|
50
|
+
Reads <output_dir>/compile_commands.json, builds and invokes the native tool, and
|
|
51
|
+
overwrites <output_dir>/features.json with the result.
|
|
52
|
+
"""
|
|
53
|
+
from fuzzprep.core.subprocesses import run_command_streaming
|
|
54
|
+
from fuzzprep.feature_extractor.native_build import build_native_tool
|
|
55
|
+
|
|
56
|
+
compile_commands = output_dir / "compile_commands.json"
|
|
57
|
+
if not compile_commands.is_file():
|
|
58
|
+
raise MissingCompileCommandsError(
|
|
59
|
+
f"{compile_commands} not found. Generate one with CMake "
|
|
60
|
+
"(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON) or by wrapping a non-CMake build "
|
|
61
|
+
"with 'bear -- <build command>'."
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
binary = build_native_tool()
|
|
65
|
+
features_json = output_dir / "features.json"
|
|
66
|
+
project_name = output_dir.resolve().name
|
|
67
|
+
cmd = [str(binary.resolve()), str(output_dir.resolve()), str(features_json), project_name]
|
|
68
|
+
result = run_command_streaming(
|
|
69
|
+
cmd,
|
|
70
|
+
Path.cwd(),
|
|
71
|
+
timeout=600,
|
|
72
|
+
)
|
|
73
|
+
if result.exit_code != 0:
|
|
74
|
+
raise FeatureArtifactError(
|
|
75
|
+
f"Native feature_extractor tool failed\n"
|
|
76
|
+
"Run below command to reproduce:\n"
|
|
77
|
+
f"cd {Path.cwd()} && "
|
|
78
|
+
f"{' '.join(cmd)}"
|
|
79
|
+
)
|
|
80
|
+
features_json.write_text(json.dumps(json.loads(features_json.read_text()), indent=2) + "\n")
|
|
81
|
+
return load_feature_artifact(features_json)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def load_feature_artifact(path: Path) -> FeatureArtifactSet:
|
|
85
|
+
"""Parse and validate a features.json file into a typed FeatureArtifactSet.
|
|
86
|
+
|
|
87
|
+
Raises FeatureArtifactError with an actionable message on a missing file,
|
|
88
|
+
malformed JSON, missing/mistyped fields, or a schema_version mismatch.
|
|
89
|
+
"""
|
|
90
|
+
if not path.exists():
|
|
91
|
+
raise FeatureArtifactError(f"{path} does not exist. Run 'fuzzprep extract-features' first.")
|
|
92
|
+
try:
|
|
93
|
+
data = json.loads(path.read_text())
|
|
94
|
+
except json.JSONDecodeError as exc:
|
|
95
|
+
raise FeatureArtifactError(f"{path} is not valid JSON: {exc}") from exc
|
|
96
|
+
|
|
97
|
+
if not isinstance(data, dict):
|
|
98
|
+
raise FeatureArtifactError(f"{path} does not contain a JSON object.")
|
|
99
|
+
|
|
100
|
+
project_name, language = _validate_header_fields(path, data)
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
return FeatureArtifactSet(
|
|
104
|
+
schema_version=data["schema_version"],
|
|
105
|
+
project_name=project_name,
|
|
106
|
+
language=language,
|
|
107
|
+
functions=[_parse_function(f) for f in data["functions"]],
|
|
108
|
+
typedefs=[_parse_typedef(t) for t in data["typedefs"]],
|
|
109
|
+
macros=[_parse_macro(m) for m in data["macros"]],
|
|
110
|
+
enums=[_parse_enum(e) for e in data["enums"]],
|
|
111
|
+
records=[_parse_record(r) for r in data["records"]],
|
|
112
|
+
warnings=[str(w) for w in data["warnings"]],
|
|
113
|
+
)
|
|
114
|
+
except (KeyError, TypeError) as exc:
|
|
115
|
+
raise FeatureArtifactError(f"{path} has a malformed entry: {exc}") from exc
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _validate_header_fields(path: Path, data: dict) -> tuple[str, Language]:
|
|
119
|
+
"""Validate schema_version/project_name/language, returning the latter two."""
|
|
120
|
+
missing = [key for key in _REQUIRED_FIELDS if key not in data]
|
|
121
|
+
if missing:
|
|
122
|
+
raise FeatureArtifactError(f"{path} is missing required field(s): {', '.join(missing)}")
|
|
123
|
+
|
|
124
|
+
if data["schema_version"] != SCHEMA_VERSION:
|
|
125
|
+
raise FeatureArtifactError(
|
|
126
|
+
f"{path} has schema_version {data['schema_version']!r}, "
|
|
127
|
+
f"expected {SCHEMA_VERSION}. Re-run 'fuzzprep extract-features'."
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
project_name = data["project_name"]
|
|
131
|
+
if not isinstance(project_name, str) or not project_name:
|
|
132
|
+
raise FeatureArtifactError(f"{path} has an empty or non-string project_name.")
|
|
133
|
+
|
|
134
|
+
language = _LANGUAGE_BY_VALUE.get(data["language"])
|
|
135
|
+
if language is None:
|
|
136
|
+
raise FeatureArtifactError(f"{path} has an unrecognized language: {data['language']!r}")
|
|
137
|
+
|
|
138
|
+
return project_name, language
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _parse_function(data: dict) -> FunctionSignature:
|
|
142
|
+
return FunctionSignature(
|
|
143
|
+
name=data["name"],
|
|
144
|
+
return_type=data["return_type"],
|
|
145
|
+
params=[Param(name=p["name"], type=p["type"]) for p in data["params"]],
|
|
146
|
+
signature=data["signature"],
|
|
147
|
+
is_public_api=data["is_public_api"],
|
|
148
|
+
header_path=data["header_path"],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _parse_typedef(data: dict) -> Typedef:
|
|
153
|
+
return Typedef(
|
|
154
|
+
name=data["name"],
|
|
155
|
+
underlying_type=data["underlying_type"],
|
|
156
|
+
header_path=data["header_path"],
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _parse_macro(data: dict) -> MacroDefinition:
|
|
161
|
+
return MacroDefinition(
|
|
162
|
+
name=data["name"],
|
|
163
|
+
is_function_like=data["is_function_like"],
|
|
164
|
+
params=[str(p) for p in data["params"]],
|
|
165
|
+
value=data["value"],
|
|
166
|
+
header_path=data["header_path"],
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _parse_enum(data: dict) -> EnumDefinition:
|
|
171
|
+
return EnumDefinition(
|
|
172
|
+
name=data["name"],
|
|
173
|
+
enumerators=[Enumerator(name=e["name"], value=e["value"]) for e in data["enumerators"]],
|
|
174
|
+
header_path=data["header_path"],
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _parse_record(data: dict) -> StructUnionDefinition:
|
|
179
|
+
return StructUnionDefinition(
|
|
180
|
+
name=data["name"],
|
|
181
|
+
kind=data["kind"],
|
|
182
|
+
fields=[Field(name=f["name"], type=f["type"]) for f in data["fields"]],
|
|
183
|
+
header_path=data["header_path"],
|
|
184
|
+
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from fuzzprep.library_builder.models import Language
|
|
6
|
+
|
|
7
|
+
SCHEMA_VERSION = 1
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Param:
|
|
12
|
+
name: str
|
|
13
|
+
type: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class FunctionSignature:
|
|
18
|
+
name: str
|
|
19
|
+
return_type: str
|
|
20
|
+
params: list[Param]
|
|
21
|
+
signature: str
|
|
22
|
+
is_public_api: bool
|
|
23
|
+
header_path: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class Typedef:
|
|
28
|
+
name: str
|
|
29
|
+
underlying_type: str
|
|
30
|
+
header_path: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class MacroDefinition:
|
|
35
|
+
name: str
|
|
36
|
+
is_function_like: bool
|
|
37
|
+
params: list[str]
|
|
38
|
+
value: str
|
|
39
|
+
header_path: str
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Enumerator:
|
|
44
|
+
name: str
|
|
45
|
+
value: int
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class EnumDefinition:
|
|
50
|
+
name: str | None
|
|
51
|
+
enumerators: list[Enumerator]
|
|
52
|
+
header_path: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Field:
|
|
57
|
+
name: str
|
|
58
|
+
type: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class StructUnionDefinition:
|
|
63
|
+
name: str | None
|
|
64
|
+
kind: str # "struct" | "union"
|
|
65
|
+
fields: list[Field]
|
|
66
|
+
header_path: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class FeatureArtifactSet:
|
|
71
|
+
schema_version: int
|
|
72
|
+
project_name: str
|
|
73
|
+
language: Language
|
|
74
|
+
functions: list[FunctionSignature] = field(default_factory=list)
|
|
75
|
+
typedefs: list[Typedef] = field(default_factory=list)
|
|
76
|
+
macros: list[MacroDefinition] = field(default_factory=list)
|
|
77
|
+
enums: list[EnumDefinition] = field(default_factory=list)
|
|
78
|
+
records: list[StructUnionDefinition] = field(default_factory=list)
|
|
79
|
+
warnings: list[str] = field(default_factory=list)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class BenchmarkFunction:
|
|
84
|
+
name: str
|
|
85
|
+
signature: str
|
|
86
|
+
return_type: str
|
|
87
|
+
params: list[Param]
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class BenchmarkYaml:
|
|
92
|
+
project: str
|
|
93
|
+
language: str
|
|
94
|
+
target_name: str
|
|
95
|
+
target_path: str
|
|
96
|
+
functions: list[BenchmarkFunction] = field(default_factory=list)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
BasedOnStyle: LLVM
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.20)
|
|
2
|
+
project(feature_extractor CXX C)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
7
|
+
|
|
8
|
+
if(NOT CMAKE_BUILD_TYPE)
|
|
9
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
10
|
+
endif()
|
|
11
|
+
|
|
12
|
+
find_package(Clang REQUIRED CONFIG)
|
|
13
|
+
find_package(LLVM REQUIRED CONFIG)
|
|
14
|
+
|
|
15
|
+
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
|
16
|
+
message(STATUS "Using ClangConfig.cmake in: ${Clang_DIR}")
|
|
17
|
+
|
|
18
|
+
list(GET LLVM_LIBRARY_DIRS 0 _llvm_library_dir)
|
|
19
|
+
set(CLANG_RESOURCE_DIR "${_llvm_library_dir}/clang/${LLVM_VERSION_MAJOR}")
|
|
20
|
+
message(STATUS "Using clang resource dir: ${CLANG_RESOURCE_DIR}")
|
|
21
|
+
|
|
22
|
+
# -resource-dir only covers clang's own builtin headers (stddef.h, stdarg.h, ...) — on
|
|
23
|
+
# macOS, platform libc headers (inttypes.h, stdio.h, ...) live under the Xcode SDK and
|
|
24
|
+
# are found only via -isysroot, which main.cpp adds explicitly rather than relying on
|
|
25
|
+
# ClangTool's ad hoc driver invocation to auto-detect it.
|
|
26
|
+
set(CLANG_SYSROOT "")
|
|
27
|
+
if(APPLE)
|
|
28
|
+
execute_process(
|
|
29
|
+
COMMAND xcrun --show-sdk-path
|
|
30
|
+
OUTPUT_VARIABLE CLANG_SYSROOT
|
|
31
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
32
|
+
ERROR_QUIET
|
|
33
|
+
)
|
|
34
|
+
message(STATUS "Using macOS SDK sysroot: ${CLANG_SYSROOT}")
|
|
35
|
+
endif()
|
|
36
|
+
|
|
37
|
+
# cJSON is linked statically into feature_extractor, so its MIT notice has to travel with any
|
|
38
|
+
# redistributed binary. THIRD_PARTY_NOTICES.md at the repository root carries that notice —
|
|
39
|
+
# update it when this pin moves or another dependency is added here.
|
|
40
|
+
include(FetchContent)
|
|
41
|
+
set(ENABLE_CJSON_TEST OFF CACHE BOOL "" FORCE)
|
|
42
|
+
set(ENABLE_CJSON_UTILS OFF CACHE BOOL "" FORCE)
|
|
43
|
+
set(CJSON_OVERRIDE_BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
|
|
44
|
+
set(CJSON_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
|
45
|
+
FetchContent_Declare(
|
|
46
|
+
cjson
|
|
47
|
+
GIT_REPOSITORY https://github.com/DaveGamble/cJSON.git
|
|
48
|
+
GIT_TAG c859b25da02955fef659d658b8f324b5cde87be3 # v1.7.19
|
|
49
|
+
GIT_SHALLOW TRUE
|
|
50
|
+
)
|
|
51
|
+
FetchContent_MakeAvailable(cjson)
|
|
52
|
+
|
|
53
|
+
add_executable(feature_extractor
|
|
54
|
+
src/main.cpp
|
|
55
|
+
src/extraction_action.cpp
|
|
56
|
+
src/macro_callbacks.cpp
|
|
57
|
+
src/json_writer.cpp
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
target_include_directories(feature_extractor PRIVATE include)
|
|
61
|
+
target_include_directories(feature_extractor SYSTEM PRIVATE
|
|
62
|
+
${LLVM_INCLUDE_DIRS}
|
|
63
|
+
${CLANG_INCLUDE_DIRS}
|
|
64
|
+
${cjson_SOURCE_DIR}
|
|
65
|
+
)
|
|
66
|
+
target_compile_definitions(feature_extractor PRIVATE ${LLVM_DEFINITIONS})
|
|
67
|
+
target_compile_definitions(feature_extractor PRIVATE
|
|
68
|
+
CLANG_RESOURCE_DIR="${CLANG_RESOURCE_DIR}"
|
|
69
|
+
CLANG_SYSROOT="${CLANG_SYSROOT}"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
if(NOT LLVM_ENABLE_RTTI)
|
|
73
|
+
target_compile_options(feature_extractor PRIVATE -fno-rtti)
|
|
74
|
+
endif()
|
|
75
|
+
|
|
76
|
+
target_compile_options(feature_extractor PRIVATE
|
|
77
|
+
-Wall -Wextra -Werror -Wpedantic -Wshadow
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
target_link_libraries(feature_extractor PRIVATE
|
|
81
|
+
clangTooling
|
|
82
|
+
clangASTMatchers
|
|
83
|
+
clangBasic
|
|
84
|
+
clangFrontend
|
|
85
|
+
clangAST
|
|
86
|
+
clangLex
|
|
87
|
+
cjson
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
install(TARGETS feature_extractor RUNTIME DESTINATION bin)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <clang/Basic/LangOptions.h>
|
|
4
|
+
#include <clang/Basic/SourceManager.h>
|
|
5
|
+
#include <clang/Frontend/FrontendAction.h>
|
|
6
|
+
#include <clang/Lex/PPCallbacks.h>
|
|
7
|
+
#include <clang/Tooling/Tooling.h>
|
|
8
|
+
#include <llvm/ADT/StringRef.h>
|
|
9
|
+
|
|
10
|
+
#include <memory>
|
|
11
|
+
#include <optional>
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <unordered_set>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace feature_extractor {
|
|
17
|
+
|
|
18
|
+
struct Param {
|
|
19
|
+
std::string name;
|
|
20
|
+
std::string type;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
struct FunctionInfo {
|
|
24
|
+
std::string name;
|
|
25
|
+
std::string return_type;
|
|
26
|
+
std::vector<Param> params;
|
|
27
|
+
std::string signature;
|
|
28
|
+
bool is_public_api = false;
|
|
29
|
+
std::string header_path;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
struct TypedefInfo {
|
|
33
|
+
std::string name;
|
|
34
|
+
std::string underlying_type;
|
|
35
|
+
std::string header_path;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
struct MacroInfo {
|
|
39
|
+
std::string name;
|
|
40
|
+
bool is_function_like = false;
|
|
41
|
+
std::vector<std::string> params;
|
|
42
|
+
std::string value;
|
|
43
|
+
std::string header_path;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
struct Enumerator {
|
|
47
|
+
std::string name;
|
|
48
|
+
long long value = 0;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
struct EnumInfo {
|
|
52
|
+
std::optional<std::string> name;
|
|
53
|
+
std::vector<Enumerator> enumerators;
|
|
54
|
+
std::string header_path;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
struct Field {
|
|
58
|
+
std::string name;
|
|
59
|
+
std::string type;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
struct RecordInfo {
|
|
63
|
+
std::optional<std::string> name;
|
|
64
|
+
std::string kind; // "struct" | "union"
|
|
65
|
+
std::vector<Field> fields;
|
|
66
|
+
std::string header_path;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
struct FeatureArtifact {
|
|
70
|
+
int schema_version = 1;
|
|
71
|
+
std::string project_name;
|
|
72
|
+
std::string language; // "c" | "c++" — must match fuzzprep.library_builder.models.Language
|
|
73
|
+
std::vector<FunctionInfo> functions;
|
|
74
|
+
std::vector<TypedefInfo> typedefs;
|
|
75
|
+
std::vector<MacroInfo> macros;
|
|
76
|
+
std::vector<EnumInfo> enums;
|
|
77
|
+
std::vector<RecordInfo> records;
|
|
78
|
+
std::vector<std::string> warnings;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// The project source root: the common ancestor of every translation unit's file
|
|
82
|
+
// path, which for an out-of-tree build is not where compile_commands.json lives.
|
|
83
|
+
// header_path values are relative to it, and a declaration inside it counts as
|
|
84
|
+
// library-owned rather than third-party or system.
|
|
85
|
+
struct ProjectContext {
|
|
86
|
+
std::string project_root;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Not thread-safe. ClangTool runs one translation unit at a time, so one
|
|
90
|
+
// collector can be shared across every TU to accumulate and deduplicate
|
|
91
|
+
// declarations that appear in more than one (via a shared header, say).
|
|
92
|
+
class FeatureCollector {
|
|
93
|
+
public:
|
|
94
|
+
void addFunction(FunctionInfo info);
|
|
95
|
+
void addTypedef(TypedefInfo info);
|
|
96
|
+
void addMacro(MacroInfo info);
|
|
97
|
+
void addEnum(EnumInfo info);
|
|
98
|
+
void addRecord(RecordInfo info);
|
|
99
|
+
void addWarning(std::string warning);
|
|
100
|
+
|
|
101
|
+
const FeatureArtifact &artifact() const { return artifact_; }
|
|
102
|
+
|
|
103
|
+
private:
|
|
104
|
+
FeatureArtifact artifact_;
|
|
105
|
+
std::unordered_set<std::string> seen_functions_;
|
|
106
|
+
std::unordered_set<std::string> seen_typedefs_;
|
|
107
|
+
std::unordered_set<std::string> seen_macros_;
|
|
108
|
+
std::unordered_set<std::string> seen_enums_;
|
|
109
|
+
std::unordered_set<std::string> seen_records_;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// True when abs_path is inside ctx.project_root, i.e. library-owned rather than a
|
|
113
|
+
// system or third-party header.
|
|
114
|
+
bool isWithinProject(const ProjectContext &ctx, llvm::StringRef abs_path);
|
|
115
|
+
|
|
116
|
+
// abs_path relative to ctx.project_root when it is within the project, and
|
|
117
|
+
// unchanged otherwise.
|
|
118
|
+
std::string relativeHeaderPath(const ProjectContext &ctx,
|
|
119
|
+
llvm::StringRef abs_path);
|
|
120
|
+
|
|
121
|
+
// Builds the ASTFrontendAction that extracts functions, typedefs, enums, and
|
|
122
|
+
// records from one translation unit, and registers the macro-extracting
|
|
123
|
+
// PPCallbacks alongside it. Both write into collector.
|
|
124
|
+
std::unique_ptr<clang::tooling::FrontendActionFactory>
|
|
125
|
+
newExtractionActionFactory(FeatureCollector &collector, ProjectContext ctx);
|
|
126
|
+
|
|
127
|
+
// Registers a PPCallbacks that extracts macro definitions into collector.
|
|
128
|
+
std::unique_ptr<clang::PPCallbacks>
|
|
129
|
+
newMacroCollectorCallbacks(FeatureCollector &collector, ProjectContext ctx,
|
|
130
|
+
const clang::SourceManager &sm,
|
|
131
|
+
const clang::LangOptions &lang_opts);
|
|
132
|
+
|
|
133
|
+
// Serializes artifact to the JSON shape features.json readers expect.
|
|
134
|
+
std::string writeJson(const FeatureArtifact &artifact);
|
|
135
|
+
|
|
136
|
+
} // namespace feature_extractor
|