├── NOTICE.txt ├── .gitignore ├── vcpkg.json ├── cmake ├── install-config.cmake ├── find │ ├── Finddoctest.cmake │ └── FindMicrosoft.GSL.cmake └── install-rules.cmake ├── samples └── sgraph-js │ ├── sgraph │ ├── vendor.js │ ├── charts.js │ ├── util.js │ ├── literals.js │ ├── dirs.js │ ├── types.js │ ├── resolve.js │ └── io.js │ ├── ui │ ├── sidebar.js │ ├── filter.js │ └── options.js │ └── globals.js ├── test ├── main.cxx ├── CMakeLists.txt └── m.ixx ├── TODO.md ├── src ├── ifc-dom │ ├── syntax.cxx │ ├── sentences.cxx │ ├── charts.cxx │ ├── literals.cxx │ ├── common.hxx │ ├── names.cxx │ ├── stmts.cxx │ └── types.cxx ├── assert.cxx ├── ifc-printer │ ├── printer.hxx │ ├── main.cxx │ └── printer.cxx ├── ifc-reader │ └── reader.cxx ├── file.cxx ├── input-file.cxx ├── hash_win.cxx ├── tools │ └── ifc.cxx └── sha256.cxx ├── CODE_OF_CONDUCT.md ├── SUPPORT.md ├── include └── ifc │ ├── assertions.hxx │ ├── basic-types.hxx │ ├── version.hxx │ ├── pp-forms.hxx │ ├── underlying.hxx │ ├── tooling.hxx │ ├── util.hxx │ ├── pathname.hxx │ ├── dom │ └── node.hxx │ └── index-utils.hxx ├── .github └── workflows │ ├── cmake.yml │ └── codeql.yml ├── ifc-driver.md ├── SECURITY.md ├── CMakeLists.txt ├── README.md ├── CMakePresets.json ├── HACKING.md ├── .clang-format ├── CONTRIBUTING.md ├── BUILDING.md └── LICENSE.TXT /NOTICE.txt: -------------------------------------------------------------------------------- 1 | IFC SDK 2 | Copyright (c) Microsoft Corporation. 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /prefix/ 3 | /linux-build/ 4 | /CMakeUserPresets.json 5 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "microsoft-ifc-sdk", 3 | "version-semver": "0.43.5", 4 | "dependencies": [ 5 | "ms-gsl", 6 | "doctest" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /cmake/install-config.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Microsoft Corporation. 2 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | include(CMakeFindDependencyMacro) 5 | find_dependency(Microsoft.GSL) 6 | 7 | include("${CMAKE_CURRENT_LIST_DIR}/Microsoft.IFCTargets.cmake") 8 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/vendor.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | function symbolic_for_vendor_sort(sort) { 5 | switch (sort) { 6 | default: 7 | console.error(`Bad sort: ${sort}`); 8 | return null; 9 | } 10 | } -------------------------------------------------------------------------------- /test/main.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int, char**) 5 | { 6 | std::cout << "Current format version: " << static_cast(ifc::CurrentFormatVersion.major) << '.' 7 | << static_cast(ifc::CurrentFormatVersion.minor) << '\n'; 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Direction and Tasks 2 | 3 | The IFC SDK repo is an experimental project. We intend to continually evolve it to adapt it to new versions of ISO C++ and the IFC Specification. 4 | 5 | ## TODO 6 | The SDK is currently a collection of header files. 7 | - Restructure it as C++ modules only. 8 | - Make it available through package managers 9 | - Add cross-platform testsuites 10 | -------------------------------------------------------------------------------- /src/ifc-dom/syntax.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | 6 | namespace ifc::util { 7 | void load(Loader&, Node& node, SyntaxIndex index) 8 | { 9 | // very lonely at the moment. Add syntax handling if/when desired. 10 | node.id = to_string(index); 11 | } 12 | 13 | } // namespace ifc::util 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /src/ifc-dom/sentences.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | 6 | namespace ifc::util { 7 | void load(Loader&, Node& node, SentenceIndex index) 8 | { 9 | // At some point all of the token based initializers will be going 10 | // away. If that does not happen. We can add token loading here. 11 | node.id = to_string(index); 12 | } 13 | } // namespace ifc::util 14 | -------------------------------------------------------------------------------- /src/assert.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include 5 | #include 6 | #include "ifc/assertions.hxx" 7 | 8 | // Lightweight standalone version of ifc_assert. Consumers can replace this. 9 | void ifc_assert(const char* text, const char* file, int line) 10 | { 11 | fprintf(stderr, "assertion failure: ``%s'' in file ``%s'' at line %d\n", text, file, line); 12 | exit(-1); 13 | } 14 | 15 | void ifc_verify(const char* text, const char* file, int line) 16 | { 17 | fprintf(stderr, "verify failure: ``%s'' in file ``%s'' at line %d\n", text, file, line); 18 | exit(-1); 19 | } 20 | -------------------------------------------------------------------------------- /cmake/find/Finddoctest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Microsoft Corporation. 2 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | # This module is an opt-in find module _only_ for developers of the SDK who 5 | # cannot use vcpkg. To tell CMake about this find module, pass this file's 6 | # directory via the CMAKE_MODULE_PATH command line option. This will satisfy a 7 | # simple find_package(doctest REQUIRED) call by default. 8 | 9 | include(FetchContent) 10 | 11 | FetchContent_Declare( 12 | doctest 13 | GIT_REPOSITORY https://github.com/doctest/doctest.git 14 | GIT_TAG v2.4.11 15 | GIT_SHALLOW YES 16 | ) 17 | 18 | FetchContent_MakeAvailable(doctest) 19 | 20 | set(doctest_FOUND 1) 21 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | Use GitHub [discussions](https://github.com/microsoft/ifc/discussions) to ask questions or discuss the SDK. 10 | 11 | For help and questions about using this project, please review [README.md](README.md), [CONTRIBUTING.md](CONTRIBUTING.md), and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). We will also try to maintain links to blog posts about the IFC SDK as well. 12 | 13 | ## Microsoft Support Policy 14 | 15 | Support for the **IFC SDK** is limited to the resources listed above. 16 | -------------------------------------------------------------------------------- /include/ifc/assertions.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_ASSERTIONS_HXX_INCLUDED 5 | #define IFC_ASSERTIONS_HXX_INCLUDED 6 | 7 | void ifc_assert(char const* message, char const* file, int line); 8 | 9 | // IFCVERIFY is always on. 10 | #define IFCVERIFY(expression) (void)((not not(expression)) or (ifc_assert(#expression, __FILE__, __LINE__), 0)) 11 | 12 | #ifdef NDEBUG 13 | #define IFCASSERT(expression) ((void)0) 14 | #else 15 | #define IFCASSERT(expression) (void)((not not(expression)) or (ifc_assert(#expression, __FILE__, __LINE__), 0)) 16 | #endif // NDEBUG 17 | 18 | /* Note that a consumer of this library that wants to "handle" assertions in a 19 | different way should override ifc_assert. */ 20 | 21 | #endif // IFC_ASSERTIONS_HXX_INCLUDED 22 | -------------------------------------------------------------------------------- /src/ifc-printer/printer.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_TOOLS_PRINTER_HXX 5 | #define IFC_TOOLS_PRINTER_HXX 6 | 7 | #include "ifc/dom/node.hxx" 8 | #include 9 | 10 | namespace ifc::util { 11 | enum class PrintOptions : std::int8_t { 12 | None = 0, 13 | Use_color = 1 << 0, 14 | Top_level_index = 1 << 1, 15 | }; 16 | [[nodiscard]] constexpr PrintOptions operator|(PrintOptions e1, PrintOptions e2) noexcept 17 | { 18 | return static_cast(ifc::to_underlying(e1) | ifc::to_underlying(e2)); 19 | } 20 | constexpr PrintOptions& operator|=(PrintOptions& e1, PrintOptions e2) noexcept 21 | { 22 | return e1 = e1 | e2; 23 | } 24 | 25 | void print(const Node& gs, std::ostream& os, PrintOptions options = PrintOptions::None); 26 | } // namespace ifc::util 27 | 28 | #endif // IFC_TOOLS_PRINTER_HXX -------------------------------------------------------------------------------- /cmake/find/FindMicrosoft.GSL.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Microsoft Corporation. 2 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | # This module is an opt-in find module _only_ for developers of the SDK who 5 | # cannot use vcpkg. To tell CMake about this find module, pass this file's 6 | # directory via the CMAKE_MODULE_PATH command line option. This will satisfy a 7 | # simple find_package(Microsoft.GSL REQUIRED) call by default. 8 | # Do not use for release builds, because the GSL targets will also be added to 9 | # the install rules of the SDK. 10 | 11 | include(FetchContent) 12 | 13 | FetchContent_Declare( 14 | Microsoft.GSL 15 | GIT_REPOSITORY https://github.com/microsoft/GSL.git 16 | GIT_TAG v4.0.0 17 | GIT_SHALLOW YES 18 | ) 19 | 20 | # required to satisfy install(EXPORT) in the SDK 21 | set(GSL_INSTALL 1) 22 | if(CMAKE_SKIP_INSTALL_RULES) 23 | set(GSL_INSTALL 0) 24 | endif() 25 | 26 | FetchContent_MakeAvailable(Microsoft.GSL) 27 | 28 | set(Microsoft.GSL_FOUND 1) 29 | -------------------------------------------------------------------------------- /samples/sgraph-js/ui/sidebar.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | function decl_preview_active() { 5 | return $('#sidebar').hasClass('active'); 6 | } 7 | 8 | function show_decl_preview() { 9 | $("#sidebar").addClass("active"); 10 | $("#sidebar").css({"margin-left" : "0px"}); 11 | } 12 | 13 | function hide_decl_preview() { 14 | $("#sidebar").removeClass("active"); 15 | $("#sidebar").css({"margin-left" : -$("#sidebar").width()}); 16 | } 17 | 18 | function init_decl_preview() { 19 | $("#sidebarCollapse").on("click", function() { 20 | if (decl_preview_active()) { 21 | hide_decl_preview(); 22 | } 23 | else { 24 | show_decl_preview(); 25 | } 26 | }); 27 | hide_decl_preview(); 28 | } 29 | 30 | function set_decl_preview_content(content) { 31 | if (!decl_preview_active()) 32 | return; 33 | $('#sidebar-content') 34 | .html(content); 35 | } -------------------------------------------------------------------------------- /include/ifc/basic-types.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_BASIC_TYPES 5 | #define IFC_BASIC_TYPES 6 | 7 | #include 8 | #include 9 | 10 | namespace ifc { 11 | enum class ColumnNumber : std::int32_t { 12 | Invalid = -1, 13 | }; 14 | enum LineNumber : std::int32_t { 15 | Max = 0x00ffffff, 16 | }; 17 | 18 | // Return the next line number. 19 | constexpr LineNumber next(LineNumber n) 20 | { 21 | return LineNumber{ifc::to_underlying(n) + 1}; 22 | } 23 | 24 | // Return the previous line number. 25 | constexpr LineNumber previous(LineNumber n) 26 | { 27 | return LineNumber{ifc::to_underlying(n) - 1}; 28 | } 29 | 30 | // Return the next column number. 31 | constexpr ColumnNumber next(ColumnNumber n) 32 | { 33 | return ColumnNumber{ifc::to_underlying(n) + 1}; 34 | } 35 | } // namespace ifc 36 | 37 | #endif // IFC_BASIC_TYPES 38 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/charts.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class ChartNone { 5 | static partition_name = "chart.none"; 6 | } 7 | 8 | class UnilevelChart { 9 | static partition_name = "chart.unilevel"; 10 | 11 | constructor(reader) { 12 | this.seq = new Sequence(ParameterDecl, reader); 13 | this.requirement = new ExprIndex(reader); 14 | } 15 | } 16 | 17 | class MultilevelChart { 18 | static partition_name = "chart.multilevel"; 19 | 20 | constructor(reader) { 21 | this.seq = new HeapSequence(HeapSort.Values.Chart, reader); 22 | } 23 | } 24 | 25 | function symbolic_for_chart_sort(sort) { 26 | switch (sort) { 27 | case ChartIndex.Sort.None: 28 | return ChartNone; 29 | case ChartIndex.Sort.Unilevel: 30 | return UnilevelChart; 31 | case ChartIndex.Sort.Multilevel: 32 | return MultilevelChart; 33 | default: 34 | console.error(`Bad sort: ${sort}`); 35 | return null; 36 | } 37 | } -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest, windows-latest] 14 | runs-on: ${{ matrix.os }} 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Use GitHub Actions provided vcpkg 20 | shell: pwsh 21 | run: Add-Content "$env:GITHUB_ENV" "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" 22 | 23 | - name: Configure CMake 24 | shell: pwsh 25 | run: cmake "--preset=ci-$("${{matrix.os}}".split("-")[0])" 26 | -D CMAKE_BUILD_TYPE=Release 27 | 28 | - name: Build 29 | run: cmake --build build --config Release 30 | 31 | - name: Install 32 | run: | 33 | cmake --install build --prefix prefix --config Release --component Printer 34 | cmake --install build --prefix prefix --config Release --component Development 35 | 36 | - name: Test 37 | working-directory: build 38 | run: ctest -C Release 39 | -------------------------------------------------------------------------------- /ifc-driver.md: -------------------------------------------------------------------------------- 1 | # The `ifc` Tool 2 | 3 | The IFC SDK contains a command-line executable named `ifc`. It is a simple driver that acts as an extendable interface for tooling around IFC files. The pattern of invocation is 4 | 5 | > `ifc` _cmd_ _file1_ _file2_ _..._ 6 | 7 | 8 | where: 9 | - _cmd_ is either a built-in sucommand, or an external extension executable 10 | - _file1_, _file2_, _..._ are the IFC files to be acted on 11 | 12 | At the moment, the tool supports only one built-in command: `version`. That is, the invocation 13 | 14 | > `ifc` `version` _file1_ _file2_ _..._ 15 | 16 | will display each _file_ along with the version of the of IFC Specification it was generated for. 17 | 18 | An IFC external extension `cmd` is any executable named `ifc-`_cmd_ that can be found via the `PATH` environment variable. Such an executable will be invoked by the `ifc` driver along with the rest of the command-line arguments it was originally invoked with. It is a convenient way to structure tooling around IFC files, making your own extension appear as if it was a built-in facility. 19 | -------------------------------------------------------------------------------- /include/ifc/version.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_VERSION_INCLUDED 5 | #define IFC_VERSION_INCLUDED 6 | 7 | #include 8 | #include 9 | 10 | namespace ifc { 11 | // File format versioning description 12 | enum class Version : std::uint8_t {}; 13 | struct FormatVersion { 14 | Version major; 15 | Version minor; 16 | 17 | auto operator<=>(const FormatVersion&) const = default; 18 | }; 19 | 20 | // Minimum and current version components. 21 | inline constexpr Version MajorVersion { 0 }; 22 | inline constexpr Version MinimumMinorVersion { 43 }; 23 | inline constexpr Version CurrentMinorVersion { 44 }; 24 | 25 | // Minimum supported file format version 26 | inline constexpr FormatVersion MinimumFormatVersion{MajorVersion, MinimumMinorVersion}; 27 | 28 | // The current version of file format emitted by the toolset 29 | inline constexpr FormatVersion CurrentFormatVersion{MajorVersion, CurrentMinorVersion}; 30 | } // namespace ifc 31 | 32 | #endif // IFC_VERSION_INCLUDED 33 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: CodeQL 2 | 3 | on: 4 | push: 5 | branches: ["main", "release/*"] 6 | pull_request: 7 | branches: ["main", "release/*"] 8 | schedule: 9 | - cron: '22 2 * * 6' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | strategy: 15 | fail-fast: false 16 | 17 | matrix: 18 | os: [ubuntu-latest, windows-latest] 19 | 20 | runs-on: ${{ matrix.os }} 21 | 22 | timeout-minutes: 360 23 | 24 | permissions: 25 | actions: read 26 | contents: read 27 | security-events: write 28 | 29 | steps: 30 | - uses: actions/checkout@v3 31 | 32 | - name: Use GitHub Actions provided vcpkg 33 | shell: pwsh 34 | run: Add-Content "$env:GITHUB_ENV" "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" 35 | 36 | - name: Initialize CodeQL 37 | uses: github/codeql-action/init@v3 38 | with: 39 | languages: cpp 40 | 41 | - name: Configure CMake 42 | shell: pwsh 43 | run: cmake "--preset=ci-$("${{matrix.os}}".split("-")[0])" 44 | -D CMAKE_BUILD_TYPE=Release 45 | 46 | - name: Build 47 | run: cmake --build build --config Release 48 | 49 | - name: Perform CodeQL Analysis 50 | uses: github/codeql-action/analyze@v3 51 | with: 52 | category: /language:cpp 53 | -------------------------------------------------------------------------------- /src/ifc-dom/charts.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | 6 | namespace ifc::util { 7 | Node* Loader::try_get(ChartIndex index) 8 | { 9 | if (index.sort() == ChartSort::None) 10 | return nullptr; 11 | 12 | NodeKey key(index); 13 | auto [it, inserted] = all_nodes.emplace(key, key); 14 | if (inserted) 15 | ifc::util::load(*this, it->second, index); 16 | return &it->second; 17 | } 18 | 19 | void load(Loader& ctx, Node& node, ChartIndex index) 20 | { 21 | node.id = sort_name(index.sort()); 22 | switch (index.sort()) 23 | { 24 | case ChartSort::Unilevel: { 25 | auto& params = ctx.reader.get(index); 26 | for (auto& item : ctx.reader.sequence(params)) 27 | node.children.push_back(&ctx.get(item)); 28 | break; 29 | } 30 | case ChartSort::Multilevel: { 31 | auto& params = ctx.reader.get(index); 32 | for (auto& item : ctx.reader.sequence(params)) 33 | node.children.push_back(&ctx.get(item)); 34 | break; 35 | } 36 | default: 37 | break; 38 | } 39 | } 40 | 41 | 42 | } // namespace ifc::util 43 | -------------------------------------------------------------------------------- /src/ifc-reader/reader.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "ifc/util.hxx" 5 | #include "ifc/reader.hxx" 6 | 7 | namespace ifc { 8 | constexpr std::string_view analysis_partition_prefix = ".msvc.code-analysis."; 9 | 10 | Reader::Reader(const ifc::InputIfc& ifc_) : ifc(ifc_) 11 | { 12 | if (not ifc.header()) 13 | throw InputIfc::MissingIfcHeader{}; 14 | read_table_of_contents(); 15 | } 16 | 17 | void Reader::read_table_of_contents() 18 | { 19 | for (auto& summary : ifc.partition_table()) 20 | { 21 | IFCASSERT(not index_like::null(summary.name)); 22 | IFCASSERT(not summary.empty()); 23 | std::string_view name = get(summary.name); 24 | // Partitions created by the analysis plugins are not members of the usual table of 25 | // contents structure. 26 | if (name.starts_with(analysis_partition_prefix)) 27 | { 28 | // TODO: Add handling for static analysis partitions once we know what they are. 29 | // supplemental_partitions.push_back({ .name = name, .summary = summary }); 30 | } 31 | else 32 | { 33 | summary_by_partition_name(toc, name) = summary; 34 | } 35 | } 36 | } 37 | 38 | } // namespace ifc 39 | -------------------------------------------------------------------------------- /src/file.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include 5 | 6 | namespace ifc { 7 | void InputIfc::validate_content_integrity(const InputIfc& file) 8 | { 9 | // Verify integrity of ifc. To do this we know that the header content after the hash 10 | // starts after the interface signature and the first 256 bits. 11 | constexpr std::size_t hash_start = sizeof(InterfaceSignature); 12 | constexpr std::size_t contents_start = hash_start + sizeof(SHA256Hash); 13 | static_assert(contents_start == 36); // 4 bytes for Signature + 8*4 bytes for SHA2 14 | const auto& contents = file.contents(); 15 | auto result = hash_bytes(contents.data() + contents_start, contents.data() + contents.size()); 16 | auto actual_first = reinterpret_cast(result.value.data()); 17 | auto actual_last = actual_first + std::size(result.value) * 4; 18 | auto expected_first = reinterpret_cast(&contents[hash_start]); 19 | auto expected_last = expected_first + sizeof(SHA256Hash); 20 | if (not std::equal(actual_first, actual_last, expected_first, expected_last)) 21 | { 22 | throw IntegrityCheckFailed{bytes_to_hash(expected_first, expected_last), result}; 23 | } 24 | } 25 | 26 | } // namespace ifc 27 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Microsoft Corporation. 2 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | cmake_minimum_required(VERSION 3.26) 5 | 6 | project(ifc-sdk-tests CXX) 7 | 8 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 9 | find_package(Microsoft.IFC REQUIRED) 10 | endif() 11 | 12 | find_package(doctest CONFIG REQUIRED) 13 | 14 | add_executable(ifc-test main.cxx) 15 | 16 | target_compile_features(ifc-test PRIVATE cxx_std_23) 17 | 18 | # Libs for ifc-test 19 | target_link_libraries(ifc-test PRIVATE Microsoft.IFC::SDK) 20 | 21 | if (WIN32) 22 | # Only enabled for MSVC for now. 23 | add_executable(ifc-basic basic.cxx) 24 | # MSVC-specific testing 25 | # Test IFCs 26 | add_custom_target(test-ifcs 27 | COMMAND 28 | ${CMAKE_CXX_COMPILER} 29 | /nologo 30 | /std:c++20 31 | /ifcOutput${CMAKE_BINARY_DIR}/m.ifc 32 | /c 33 | ${CMAKE_CURRENT_SOURCE_DIR}/m.ixx) 34 | 35 | add_dependencies(ifc-basic test-ifcs) 36 | 37 | target_compile_features(ifc-basic PRIVATE cxx_std_23) 38 | 39 | target_compile_definitions(ifc-basic PRIVATE IFC_FILE="${CMAKE_BINARY_DIR}/m.ifc") 40 | 41 | # Libs for ifc-basic 42 | target_link_libraries(ifc-basic PRIVATE Microsoft.IFC::SDK) 43 | target_link_libraries(ifc-basic PRIVATE doctest::doctest) 44 | endif() 45 | 46 | enable_testing() 47 | 48 | add_test(NAME ifc-test COMMAND ifc-test) 49 | 50 | if (WIN32) 51 | add_test(NAME ifc-basic COMMAND ifc-basic) 52 | endif() 53 | -------------------------------------------------------------------------------- /include/ifc/pp-forms.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_PP_FORMS_INCLUDED 5 | #define IFC_PP_FORMS_INCLUDED 6 | 7 | #include "index-utils.hxx" 8 | 9 | namespace ifc { 10 | // Classification of pre-syntactic forms used to group 11 | // pp-tokens during, and out of, translation phases 1-4. 12 | enum class FormSort : std::uint8_t { 13 | Identifier, // identifier form 14 | Number, // number form 15 | Character, // built-in or ud-suffixed character literal form 16 | String, // built-int or ud-suffixed string literal form 17 | Operator, // operator or punctuator form 18 | Keyword, // implementation-defined keywords form - e.g. 'module', 'export', 'import'. 19 | Whitespace, // whitespace forms - including comments 20 | Parameter, // reference to a macro parameter form 21 | Stringize, // stringification form 22 | Catenate, // catenation form 23 | Pragma, // _Pragma form 24 | Header, // header name 25 | Parenthesized, // parenthesized form 26 | Tuple, // A sequence of zero or more forms 27 | Junk, // invalid form 28 | Count 29 | }; 30 | 31 | struct FormIndex : index_like::Over { 32 | using Over::Over; 33 | }; 34 | } // namespace ifc 35 | 36 | #endif // IFC_PP_FORMS_INCLUDED 37 | -------------------------------------------------------------------------------- /test/m.ixx: -------------------------------------------------------------------------------- 1 | export module m; 2 | 3 | export 4 | void glb_void_void_func(); 5 | 6 | void glb_void_void_func_not_exported(); 7 | 8 | export 9 | extern void glb_void_void_extern_func(); 10 | 11 | extern void glb_void_void_extern_func_not_exported(); 12 | 13 | export 14 | int glb_int_int_func(int); 15 | 16 | export 17 | int glb_int_int_char_char_ptr_func(int, char, char*); 18 | 19 | export 20 | namespace FunctionTraits 21 | { 22 | // These exercise inspecting each bit from FunctionTraits. 23 | void trait_none(); 24 | 25 | inline void trait_inline() { } 26 | 27 | constexpr void trait_constexpr() { } 28 | 29 | [[noreturn]] void trait_noreturn() { } 30 | 31 | void trait_deleted() = delete; 32 | 33 | template 34 | void trait_constrained() requires true; 35 | 36 | consteval void trait_immediate() { } 37 | 38 | // Class-based traits. 39 | struct ClassScope 40 | { 41 | explicit ClassScope(); // Explicit trait. 42 | 43 | virtual void trait_virtual(); 44 | 45 | virtual void trait_pure_virtual() = 0; 46 | 47 | bool operator==(const ClassScope&) const = default; // Defaulted trait. 48 | 49 | virtual void trait_final(); 50 | virtual void trait_override(); 51 | }; 52 | 53 | struct DerivedClassScope : ClassScope 54 | { 55 | void trait_final() final; 56 | void trait_override() override; 57 | }; 58 | } // namespace FunctionTraits -------------------------------------------------------------------------------- /include/ifc/underlying.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_UNDERLYING_H 5 | #define IFC_UNDERLYING_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace ifc { 12 | template 13 | concept Enum = std::is_enum_v; 14 | 15 | // Raw underlying integer type of an enumeration. 16 | template 17 | using raw = std::underlying_type_t; 18 | 19 | // Consider moving to std::to_underlying (C++23) at some point. 20 | template 21 | constexpr raw to_underlying(T e) noexcept 22 | { 23 | return static_cast>(e); 24 | } 25 | 26 | // The minimum number of bits necessary to represent a whole number, where zero takes 1 bit. 27 | template 28 | constexpr unsigned int bit_length(T n) noexcept 29 | { 30 | return (n == 0) ? 1u : static_cast(std::bit_width(n)); 31 | } 32 | 33 | // FIXME: This should be constrained with 'Enum', but due to an overlapping definition inside the msvc codebase, this needs to remain unconstrained. 34 | // so it does not compete with the copy in the msvc codebase which is constrained with 'Enum' during overload resolution. 35 | template 36 | constexpr bool implies(T x, T y) noexcept 37 | { 38 | return (to_underlying(x) & to_underlying(y)) == to_underlying(y); 39 | } 40 | } // namespace ifc 41 | #endif // IFC_UNDERLYING_H 42 | -------------------------------------------------------------------------------- /cmake/install-rules.cmake: -------------------------------------------------------------------------------- 1 | # Copyright Microsoft Corporation. 2 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | include(CMakePackageConfigHelpers) 5 | include(GNUInstallDirs) 6 | 7 | if(BUILD_PRINTER) 8 | install(TARGETS ifc-printer DESTINATION . COMPONENT Printer) 9 | endif() 10 | 11 | # find_package() call for consumers to find this project 12 | set(package Microsoft.IFC) 13 | 14 | # everything else is dev only 15 | set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME Development) 16 | 17 | install( 18 | DIRECTORY include/ 19 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 20 | ) 21 | 22 | install( 23 | TARGETS ifc 24 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 25 | ) 26 | 27 | install( 28 | TARGETS ifc-dom ifc-reader SDK 29 | EXPORT Microsoft.IFCTargets 30 | INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 31 | ) 32 | 33 | write_basic_package_version_file( 34 | "${package}ConfigVersion.cmake" 35 | COMPATIBILITY SameMajorVersion 36 | ) 37 | 38 | # Allow package maintainers to freely override the path for the configs 39 | set( 40 | IFC_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/${package}" 41 | CACHE PATH "CMake package config location relative to the install prefix" 42 | ) 43 | mark_as_advanced(IFC_INSTALL_CMAKEDIR) 44 | 45 | install( 46 | FILES cmake/install-config.cmake 47 | DESTINATION "${IFC_INSTALL_CMAKEDIR}" 48 | RENAME "${package}Config.cmake" 49 | ) 50 | 51 | install( 52 | FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" 53 | DESTINATION "${IFC_INSTALL_CMAKEDIR}" 54 | ) 55 | 56 | install( 57 | EXPORT Microsoft.IFCTargets 58 | NAMESPACE Microsoft.IFC:: 59 | DESTINATION "${IFC_INSTALL_CMAKEDIR}" 60 | ) 61 | 62 | include(CPack) 63 | -------------------------------------------------------------------------------- /src/ifc-dom/literals.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | #include "ifc/util.hxx" 6 | 7 | namespace ifc::util { 8 | std::string to_string(Loader& ctx, LitIndex index) 9 | { 10 | switch (index.sort()) 11 | { 12 | case LiteralSort::Immediate: 13 | return std::to_string(ifc::to_underlying(index.index())); 14 | case LiteralSort::Integer: 15 | return std ::to_string(ctx.reader.get(index)); 16 | case LiteralSort::FloatingPoint: 17 | return std ::to_string(ctx.reader.get(index)); 18 | default: 19 | return "unknown-literal-sort-" + std::to_string(ifc::to_underlying(index.sort())); 20 | } 21 | } 22 | 23 | std::string to_string(Loader& ctx, StringIndex index) 24 | { 25 | auto& str = ctx.reader.get(index); 26 | std::string suffix = index_like::null(str.suffix) ? "" : ctx.reader.get(str.suffix); 27 | std::string prefix; 28 | switch (index.sort()) 29 | { 30 | case StringSort::UTF8: 31 | prefix = "u8"; 32 | [[fallthrough]]; 33 | case StringSort::Ordinary: 34 | return prefix + "\"" + std::string(ctx.reader.get(str.start), ifc::to_underlying(str.size)) + "\"" 35 | + suffix; 36 | case StringSort::UTF16: 37 | prefix = "u16"; 38 | break; 39 | case StringSort::UTF32: 40 | prefix = "u32"; 41 | break; 42 | case StringSort::Wide: 43 | prefix = "L"; 44 | break; 45 | default: 46 | return "unknown-string-sort-" + std::to_string(ifc::to_underlying(index.sort())); 47 | } 48 | return prefix + "\"\"" + suffix; 49 | } 50 | } // namespace ifc::util 51 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/util.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | function has_property(obj, prop) { 5 | return obj.hasOwnProperty(prop); 6 | } 7 | 8 | function is_object(obj) { 9 | return typeof obj === 'object'; 10 | } 11 | 12 | function remove_all_children(content) { 13 | // Yes, it is intentional to check for the first and remove the last. 14 | while (content.firstChild != null) { 15 | content.removeChild(content.lastChild); 16 | } 17 | } 18 | 19 | function valid_integral_value(str) { 20 | if (str == "") 21 | return false; 22 | if (isNaN(str)) 23 | return false; 24 | return true; 25 | } 26 | 27 | function mark_edit_valid(edit) { 28 | edit.classList.remove(invalid_css_class); 29 | } 30 | 31 | function mark_edit_invalid(edit, tooltip, reason) { 32 | tooltip.innerHTML = reason; 33 | edit.classList.add(invalid_css_class); 34 | } 35 | 36 | // Implementation pulled from: https://www.freecodecamp.org/news/javascript-debounce-example/ 37 | function debounce(func, timeout = 300) { 38 | let timer = undefined; 39 | return (...args) => { 40 | clearTimeout(timer); 41 | timer = setTimeout(() => { func.apply(this, args); }, timeout); 42 | }; 43 | } 44 | 45 | function binary_search(array, pred) { 46 | let low = -1; 47 | let high = array.length; 48 | while (low + 1 < high) { 49 | const mid = low + ((high - low) >> 1); 50 | if (pred(array[mid])) { 51 | high = mid; 52 | } else { 53 | low = mid; 54 | } 55 | } 56 | return high; 57 | } 58 | 59 | // 'pred' should model "val <= x". 60 | function lower_bound(array, pred) { 61 | return binary_search(array, pred); 62 | } 63 | 64 | // Useful array prototypes. 65 | Array.prototype.equal = function(other) { 66 | if (this.length != other.length) 67 | return false; 68 | for (var i = 0; i < this.length; ++i) { 69 | if (this[i] != other[i]) 70 | return false; 71 | } 72 | return true; 73 | } -------------------------------------------------------------------------------- /src/ifc-dom/common.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_UTIL_DOM_COMMON_H 5 | #define IFC_UTIL_DOM_COMMON_H 6 | 7 | #include "ifc/dom/node.hxx" 8 | #include "ifc/reader.hxx" 9 | 10 | namespace ifc::util { 11 | std::string to_string(Loader&, NameIndex); 12 | std::string to_string(Loader&, LitIndex); 13 | std::string to_string(Loader&, StringIndex); 14 | std::string to_string(Loader&, symbolic::NoexceptSpecification); 15 | std::string expr_list(Loader&, ExprIndex, std::string delimiters = ""); 16 | 17 | namespace detail { 18 | struct LoaderVisitorBase { 19 | Loader& ctx; 20 | Node& node; 21 | 22 | template 23 | void add_child(Index abstract_index) 24 | { 25 | node.children.push_back(&ctx.get(abstract_index)); 26 | } 27 | 28 | template 29 | void add_child(char const*, Index abstract_index) 30 | { 31 | // ignore name for now 32 | node.children.push_back(&ctx.get(abstract_index)); 33 | } 34 | 35 | void add_type(char const* name, TypeIndex type) 36 | { 37 | add_prop(name, ctx.ref(type)); 38 | } 39 | 40 | void add_prop(char const* name, std::string&& str) 41 | { 42 | if (not str.empty()) 43 | node.props.emplace(name, str); 44 | } 45 | 46 | template 47 | void add_child_if_not_null(Index abstract_index) 48 | { 49 | if (not null(abstract_index)) 50 | node.children.push_back(&ctx.get(abstract_index)); 51 | } 52 | 53 | template 54 | void add_child_if_not_null(char const*, Index abstract_index) 55 | { 56 | if (not null(abstract_index)) 57 | node.children.push_back(&ctx.get(abstract_index)); 58 | } 59 | }; 60 | } // namespace detail 61 | } // namespace ifc::util 62 | 63 | #endif // IFC_UTIL_DOM_COMMON_H 64 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/literals.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class ImmediateLiteral { 5 | // No partition 6 | 7 | constructor(index) { 8 | this.value = index.index; 9 | } 10 | } 11 | 12 | class IntegerLiteral { 13 | static partition_name = "const.i64"; 14 | 15 | constructor(reader) { 16 | const array8 = new Uint8Array([ reader.read_uint8(), 17 | reader.read_uint8(), 18 | reader.read_uint8(), 19 | reader.read_uint8(), 20 | reader.read_uint8(), 21 | reader.read_uint8(), 22 | reader.read_uint8(), 23 | reader.read_uint8(), ]); 24 | const array64 = new BigUint64Array(array8.buffer); 25 | const uint64 = array64[0]; 26 | this.value = uint64; 27 | } 28 | } 29 | 30 | class FloatLiteral { 31 | static partition_name = "const.f64"; 32 | 33 | constructor(reader) { 34 | const array8 = new Uint8Array([ reader.read_uint8(), 35 | reader.read_uint8(), 36 | reader.read_uint8(), 37 | reader.read_uint8(), 38 | reader.read_uint8(), 39 | reader.read_uint8(), 40 | reader.read_uint8(), 41 | reader.read_uint8(), ]); 42 | const view = new DataView(array8.buffer); 43 | // Note: these values are stored in little-endian. 44 | this.value = view.getFloat64(0, true); 45 | this.size = reader.read_uint16(); 46 | } 47 | } 48 | 49 | function symbolic_for_lit_sort(sort) { 50 | switch (sort) { 51 | case LitIndex.Sort.Integer: 52 | return IntegerLiteral; 53 | case LitIndex.Sort.FloatingPoint: 54 | return FloatLiteral; 55 | case LitIndex.Sort.Immediate: 56 | default: 57 | console.error(`Bad sort: ${sort}`); 58 | return null; 59 | } 60 | } -------------------------------------------------------------------------------- /src/ifc-dom/names.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | 6 | namespace ifc::util { 7 | struct NameVisitor { 8 | Loader& ctx; 9 | 10 | std::string operator()(const symbolic::OperatorFunctionId& val) 11 | { 12 | return std::string("operator") + ctx.reader_get(val.name); 13 | } 14 | std::string operator()(const symbolic::ConversionFunctionId& val) 15 | { 16 | return std::string("operator ") + ctx.ref(val.target); 17 | } 18 | std::string operator()(const symbolic::LiteralOperatorId& val) 19 | { 20 | return std::string("operator ") + ctx.reader_get(val.name_index); 21 | } 22 | std::string operator()(const symbolic::TemplateName& val) 23 | { 24 | return std::string("TODO: ") + sort_name(val.algebra_sort); 25 | } 26 | std::string operator()(const symbolic::SpecializationName& val) 27 | { 28 | return std::string("TODO: ") + sort_name(val.algebra_sort); 29 | } 30 | std::string operator()(const symbolic::SourceFileName& val) 31 | { 32 | return std::string("src:") + ctx.reader_get(val.name) + ":" + ctx.reader_get(val.include_guard); 33 | } 34 | std::string operator()(const symbolic::GuideName& val) 35 | { 36 | return "guide(" + ctx.ref(val.primary_template) + ")"; 37 | } 38 | 39 | std::string operator()(TextOffset offset) 40 | { 41 | if (index_like::null(offset)) 42 | return ""; 43 | return ctx.reader.get(offset); 44 | } 45 | }; 46 | 47 | std::string to_string(Loader& ctx, NameIndex index) 48 | { 49 | return ctx.reader.visit(index, NameVisitor{ctx}); 50 | } 51 | 52 | std::string Loader::ref(const symbolic::Identity& id) 53 | { 54 | return to_string(*this, id.name); 55 | } 56 | 57 | std::string Loader::ref(const symbolic::Identity& id) 58 | { 59 | if (index_like::null(id.name)) 60 | return ""; 61 | return reader.get(id.name); 62 | } 63 | 64 | void load(Loader&, Node& node, NameIndex index) 65 | { 66 | node.id = to_string(index); 67 | } 68 | 69 | } // namespace ifc::util 70 | -------------------------------------------------------------------------------- /include/ifc/tooling.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_TOOLING_INCLUDED 5 | #define IFC_TOOLING_INCLUDED 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace ifc { 14 | // Filesystem component. Forward to standard facilities. 15 | namespace fs = std::filesystem; 16 | } 17 | 18 | // Basic datatypes for tooling extension. 19 | namespace ifc::tool { 20 | // -- String type preferred by the host OS to specify pathnames. 21 | using SystemPath = std::filesystem::path::string_type; 22 | 23 | // -- Native pathname character type. 24 | using NativeChar = SystemPath::value_type; 25 | 26 | // -- A read-only view type over native strings. 27 | using StringView = std::basic_string_view; 28 | 29 | // -- A container type for native string. 30 | using String = std::basic_string; 31 | 32 | // -- Type of an IFC subcommand name 33 | using Name = StringView; 34 | 35 | // -- A container type for the arguments to a subcommand. 36 | using Arguments = std::vector; 37 | 38 | // -- Base class for an ifc subcommand extension. 39 | struct Extension { 40 | virtual Name name() const = 0; 41 | virtual int run_with(const Arguments&) const = 0; 42 | }; 43 | 44 | // -- Type for the error code values used by the host OS. 45 | #ifdef _WIN32 46 | using ErrorCode = unsigned long; // DWORD 47 | #else 48 | using ErrorCode = int; 49 | #endif 50 | 51 | // -- Exception type used to signal inability of the host OS to access a file. 52 | struct AccessError { 53 | SystemPath path; 54 | ErrorCode error_code; 55 | }; 56 | 57 | // -- Exception type used to signal the file designated by th `path` is not a regular file. 58 | struct RegularFileError { 59 | SystemPath path; 60 | }; 61 | 62 | // -- Exception type used to signal inability of the host OS to memory-map a file. 63 | struct FileMappingError { 64 | SystemPath path; 65 | ErrorCode error_code; 66 | }; 67 | 68 | // -- Input file mapped to memory as sequence of raw bytes. 69 | struct InputFile { 70 | using View = gsl::span; 71 | 72 | explicit InputFile(const SystemPath&); 73 | InputFile(InputFile&&) noexcept; 74 | ~InputFile(); 75 | View contents() const noexcept { return view; } 76 | private: 77 | View view; 78 | }; 79 | 80 | } 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/ifc/util.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_UTILS_H 5 | #define IFC_UTILS_H 6 | 7 | #include 8 | #include "ifc/abstract-sgraph.hxx" 9 | 10 | namespace ifc::util { 11 | // return a single string for various IFC flags and enums 12 | 13 | std::string to_string(Access access); 14 | std::string to_string(BasicSpecifiers basic); 15 | std::string to_string(ReachableProperties reachable); 16 | std::string to_string(Qualifier reachable); 17 | std::string to_string(symbolic::ExpansionMode mode); 18 | std::string to_string(ScopeTraits traits); 19 | std::string to_string(ObjectTraits traits); 20 | std::string to_string(FunctionTraits traits); 21 | std::string to_string(symbolic::ReadExpr::Kind kind); 22 | std::string to_string(CallingConvention conv); 23 | std::string to_string(NoexceptSort sort); 24 | std::string to_string(symbolic::ExpressionListExpr::Delimiter delimiter); 25 | std::string to_string(symbolic::DestructorCallExpr::Kind kind); 26 | std::string to_string(symbolic::InitializerExpr::Kind kind); 27 | std::string to_string(symbolic::Associativity kind); 28 | std::string to_string(GuideTraits traits); 29 | std::string to_string(symbolic::BaseClassTraits traits); 30 | std::string to_string(symbolic::SourceLocation locus); 31 | 32 | // fundamental types and type basis can be converted to string. 33 | std::string to_string(symbolic::TypeBasis basis); 34 | std::string to_string(const symbolic::FundamentalType& type); 35 | 36 | // those implemented in their own operators.cxx as there are a lot of them! 37 | std::string to_string(MonadicOperator assort); 38 | std::string to_string(DyadicOperator assort); 39 | std::string to_string(TriadicOperator assort); 40 | std::string to_string(const Operator& assort); 41 | std::string to_string(StorageOperator assort); 42 | std::string to_string(VariadicOperator assort); 43 | 44 | // return a single string of the form "decl.variable-N" for various abstract indices 45 | template 46 | std::string to_string(Key index) 47 | { 48 | return sort_name(index.sort()) + ("-" + std::to_string(ifc::to_underlying(index.index()))); 49 | } 50 | 51 | inline std::string to_string(SentenceIndex index) 52 | { 53 | return "sentence-" + std::to_string(ifc::to_underlying(index)); 54 | } 55 | 56 | inline std::string to_string(ScopeIndex index) 57 | { 58 | return "scope-" + std::to_string(ifc::to_underlying(index)); 59 | } 60 | } // namespace ifc::util 61 | #endif // IFC_UTILS_H 62 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /src/input-file.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifdef _WIN32 5 | # include 6 | #else 7 | # include 8 | # include 9 | # include 10 | # include 11 | # include 12 | #endif 13 | 14 | #include "ifc/tooling.hxx" 15 | 16 | namespace ifc::tool { 17 | #ifdef _WIN32 18 | // Helper type for automatically closing a handle on scope exit. 19 | struct SystemHandle { 20 | SystemHandle(HANDLE h) : handle{h} { } 21 | bool valid() const { return handle != INVALID_HANDLE_VALUE; } 22 | auto get_handle() const { return handle; } 23 | ~SystemHandle() 24 | { 25 | if (valid()) 26 | CloseHandle(handle); 27 | } 28 | private: 29 | HANDLE handle; 30 | }; 31 | #endif 32 | 33 | InputFile::InputFile(const SystemPath& path) 34 | { 35 | #ifdef _WIN32 36 | // FIXME: Handle the situation of large files on a 32-bit program. 37 | static_assert(sizeof(LARGE_INTEGER) == sizeof(std::size_t)); 38 | 39 | SystemHandle file = CreateFileW(path.c_str(), GENERIC_READ, 0, nullptr, 40 | OPEN_EXISTING, 41 | FILE_ATTRIBUTE_NORMAL, nullptr); 42 | if (not file.valid()) 43 | throw AccessError{ path, GetLastError() }; 44 | LARGE_INTEGER s { }; 45 | if (not GetFileSizeEx(file.get_handle(), &s)) 46 | throw AccessError{ path, GetLastError() }; 47 | if (s.QuadPart == 0) 48 | return; 49 | SystemHandle mapping = CreateFileMapping(file.get_handle(), nullptr, PAGE_READONLY, 0, 0, nullptr); 50 | if (mapping.get_handle() == nullptr) 51 | throw FileMappingError{ path, GetLastError() }; 52 | auto start = MapViewOfFile(mapping.get_handle(), FILE_MAP_READ, 0, 0, 0); 53 | view = { reinterpret_cast(start), static_cast(s.QuadPart) }; 54 | #else 55 | struct stat s { }; 56 | errno = 0; 57 | if (stat(path.c_str(), &s) < 0) 58 | throw AccessError{ path, errno }; 59 | else if (not S_ISREG(s.st_mode)) 60 | throw RegularFileError{ path }; 61 | 62 | // Don't labor too hard with empty files. 63 | if (s.st_size == 0) 64 | return; 65 | 66 | auto fd = open(path.c_str(), O_RDONLY); 67 | if (fd < 0) 68 | throw AccessError{ path, errno }; 69 | auto start = mmap(nullptr, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 70 | close(fd); 71 | if (start == MAP_FAILED) 72 | throw FileMappingError{ path }; 73 | view = { static_cast(start), static_cast(s.st_size) }; 74 | #endif 75 | } 76 | 77 | InputFile::InputFile(InputFile&& src) noexcept : view{src.view} 78 | { 79 | src.view = { }; 80 | } 81 | 82 | InputFile::~InputFile() 83 | { 84 | if (not view.empty()) 85 | { 86 | #ifdef _WIN32 87 | UnmapViewOfFile(view.data()); 88 | #else 89 | munmap(const_cast(view.data()), view.size()); 90 | #endif 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Microsoft Corporation. 2 | # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | cmake_minimum_required(VERSION 3.28) 5 | 6 | # Corresponds to https://github.com/microsoft/ifc-spec 7 | set(ifc_spec_version 0.43) 8 | 9 | project( 10 | ifc-sdk 11 | VERSION "${ifc_spec_version}.5" 12 | LANGUAGES CXX 13 | ) 14 | 15 | option(DEVELOPER_MODE "Enable project options and targets for developers of ifc-sdk" OFF) 16 | mark_as_advanced(DEVELOPER_MODE) 17 | include(CMakeDependentOption) 18 | 19 | find_package(Microsoft.GSL REQUIRED) 20 | 21 | # Those pesky min/max macros. 22 | if(WIN32) 23 | add_compile_definitions(NOMINMAX) 24 | endif() 25 | 26 | # The `reader` component. 27 | add_library( 28 | ifc-reader STATIC 29 | src/file.cxx 30 | src/sgraph.cxx 31 | src/input-file.cxx 32 | src/ifc-reader/operators.cxx 33 | src/ifc-reader/reader.cxx 34 | src/ifc-reader/util.cxx 35 | ) 36 | add_library(Microsoft.IFC::Core ALIAS ifc-reader) 37 | set_property(TARGET ifc-reader PROPERTY EXPORT_NAME Core) 38 | if(WIN32) 39 | target_compile_definitions(ifc-reader PRIVATE NOMINMAX) 40 | target_link_libraries(ifc-reader PRIVATE bcrypt) 41 | target_sources(ifc-reader PRIVATE src/hash_win.cxx) 42 | else() 43 | target_sources(ifc-reader PRIVATE src/sha256.cxx) 44 | endif() 45 | target_link_libraries(ifc-reader PUBLIC Microsoft.GSL::GSL) 46 | target_compile_features(ifc-reader PUBLIC cxx_std_23) 47 | target_include_directories(ifc-reader PUBLIC "\$") 48 | 49 | # The `dom` component. 50 | add_library( 51 | ifc-dom STATIC 52 | src/ifc-dom/charts.cxx 53 | src/ifc-dom/decls.cxx 54 | src/ifc-dom/exprs.cxx 55 | src/ifc-dom/literals.cxx 56 | src/ifc-dom/names.cxx 57 | src/ifc-dom/sentences.cxx 58 | src/ifc-dom/stmts.cxx 59 | src/ifc-dom/syntax.cxx 60 | src/ifc-dom/types.cxx 61 | ) 62 | add_library(Microsoft.IFC::DOM ALIAS ifc-dom) 63 | set_property(TARGET ifc-dom PROPERTY EXPORT_NAME DOM) 64 | target_link_libraries(ifc-dom PUBLIC ifc-reader) 65 | target_compile_features(ifc-dom PUBLIC cxx_std_23) 66 | target_include_directories(ifc-dom PUBLIC "\$") 67 | 68 | # The `ifc` tool executable. 69 | add_executable( 70 | ifc 71 | src/tools/ifc.cxx 72 | ) 73 | add_executable(Microsoft.IFC::Tool ALIAS ifc) 74 | set_property(TARGET ifc PROPERTY EXPORT_NAME Tool) 75 | target_compile_features(ifc PUBLIC cxx_std_23) 76 | target_link_libraries(ifc ifc-reader) 77 | target_include_directories(ifc PUBLIC "\$") 78 | 79 | # The IFC SDK comprises the `reader`, the `dom`, and the tool. 80 | add_library(SDK INTERFACE) 81 | add_library(Microsoft.IFC::SDK ALIAS SDK) 82 | target_link_libraries(SDK INTERFACE ifc-reader ifc-dom) 83 | 84 | cmake_dependent_option(BUILD_PRINTER "Build the ifc-printer executable" OFF "NOT DEVELOPER_MODE" ON) 85 | if(BUILD_PRINTER) 86 | add_executable( 87 | ifc-printer 88 | src/ifc-printer/main.cxx 89 | src/ifc-printer/printer.cxx 90 | src/assert.cxx 91 | ) 92 | target_link_libraries(ifc-printer PRIVATE ifc-dom ifc-reader) 93 | target_compile_features(ifc-printer PRIVATE cxx_std_23) 94 | endif() 95 | 96 | if(NOT CMAKE_SKIP_INSTALL_RULES) 97 | include(cmake/install-rules.cmake) 98 | endif() 99 | 100 | if(NOT DEVELOPER_MODE) 101 | return() 102 | endif() 103 | 104 | include(CTest) 105 | if(BUILD_TESTING) 106 | add_subdirectory(test) 107 | endif() 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IFC SDK 2 | 3 | The IFC SDK provides datatypes and code supporting the read and write of IFC files. 4 | This is an experimental project aiming at advancing implementation and uses of C++ Modules at large. 5 | The SDK contains C++ 6 | datatypes that can be memory-mapped directly onto the on-disk format. 7 | See the [IFC Specification](https://github.com/microsoft/ifc-spec) for more details. 8 | 9 | If you want to participate in the development of the IFC SDK, you are welcome! You can report issues, comment on pull requests, and learn about what we are working on. You can also submit pull requests to fix bugs or add features reflecting the [Specification](https://github.com/microsoft/ifc-spec): see [CONTRIBUTING.md](CONTRIBUTING.md) for more information. 10 | 11 | # Building and Running 12 | This project uses CMake for defining projects, and the code has been tested to work on Windows and Linux. It can be built and run under WSL (Windows Subsystem for Linux) as well. 13 | 14 | The JavaScript IFC viewer can be run by loading the [index.html](samples/sgraph-js/index.html) file in a browser. 15 | 16 | IFC files to experiment with can be generated by using a current Microsoft C++ compiler. There are 17 | a couple of IFC files checked into the repo for basic testing purposes. Longer term, we want to be 18 | able to generate IFC files using the SDK itself. To generate an IFC file, you can use a current Visual C++ compiler. The following will generate an IFC containing almost everything in the test.cpp translation unit. Currently, non-inline functions or methods will not be generated into the IFC. 19 | 20 | ```shell 21 | cl /std:c++20 /exportHeader test.cpp 22 | ``` 23 | 24 | This will create an IFC file named test.cpp.ifc. This can be dumped with ifc-printer like this. 25 | 26 | ```shell 27 | ifc-printer.exe --color test.cpp.ifc 28 | ``` 29 | 30 | See [BUILDING.md](BUILDING.md) for more information. 31 | 32 | # Contributing 33 | This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to grant us the right to use your contribution. For details, visit https://cla.opensource.microsoft.com. 34 | 35 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR (Pull Request) appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 36 | 37 | See [CONTRIBUTING.md](CONTRIBUTING.md) for more information. 38 | 39 | # Trademarks 40 | 41 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 42 | trademarks or logos is subject to and must follow 43 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 44 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 45 | Any use of third-party trademarks or logos are subject to those third-party's policies. 46 | 47 | # Microsoft Open Source Code of Conduct 48 | 49 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 50 | 51 | Resources: 52 | 53 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 54 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 55 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 56 | 57 | # License 58 | 59 | Copyright (c) Microsoft Corporation. 60 | 61 | SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -------------------------------------------------------------------------------- /src/ifc-dom/stmts.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | #include "ifc/util.hxx" 6 | 7 | namespace ifc::util { 8 | namespace { 9 | struct StmtLoader : detail::LoaderVisitorBase { 10 | void operator()(const symbolic::ReturnStmt& ret) 11 | { 12 | node.props.emplace("function_type", ctx.ref(ret.function_type)); 13 | node.props.emplace("expression_type", ctx.ref(ret.type)); 14 | add_child_if_not_null(ret.expr); 15 | } 16 | 17 | void operator()(const symbolic::TupleStmt& stmt_tuple) 18 | { 19 | for (auto item : ctx.reader.sequence(stmt_tuple)) 20 | add_child(item); 21 | } 22 | 23 | void operator()(const symbolic::BlockStmt& stmt_block) 24 | { 25 | node.props.emplace("locus", to_string(stmt_block.locus)); 26 | for (auto item : ctx.reader.sequence(stmt_block)) 27 | add_child(item); 28 | } 29 | 30 | void operator()(const symbolic::DeclStmt& decl_stmt) 31 | { 32 | add_child(decl_stmt.decl); 33 | } 34 | 35 | void operator()(const symbolic::ExpressionStmt& expr_stmt) 36 | { 37 | add_child(expr_stmt.expr); 38 | } 39 | 40 | void operator()(const symbolic::IfStmt& stmt) 41 | { 42 | add_child_if_not_null(stmt.init); 43 | add_child(stmt.condition); 44 | add_child(stmt.consequence); 45 | add_child(stmt.alternative); 46 | } 47 | 48 | void operator()(const symbolic::WhileStmt& while_stmt) 49 | { 50 | add_child(while_stmt.condition); 51 | add_child(while_stmt.body); 52 | } 53 | 54 | void operator()(const symbolic::DoWhileStmt& do_stmt) 55 | { 56 | node.props.emplace("locus", to_string(do_stmt.locus)); 57 | add_child(do_stmt.body); 58 | add_child(do_stmt.condition); 59 | } 60 | 61 | void operator()(const symbolic::ForStmt& for_stmt) 62 | { 63 | add_child(for_stmt.init); 64 | add_child(for_stmt.condition); 65 | add_child(for_stmt.increment); 66 | add_child(for_stmt.body); 67 | } 68 | 69 | void operator()(const symbolic::SwitchStmt& switch_stmt) 70 | { 71 | add_child_if_not_null(switch_stmt.init); 72 | add_child(switch_stmt.control); 73 | add_child(switch_stmt.body); 74 | } 75 | 76 | void operator()(const symbolic::LabeledStmt& labeled) 77 | { 78 | add_child(labeled.label); 79 | add_child(labeled.statement); 80 | } 81 | 82 | void operator()(const symbolic::GotoStmt& goto_stmt) 83 | { 84 | add_child(goto_stmt.target); 85 | } 86 | 87 | // nothing todo for these 88 | // clang-format off 89 | void operator()(const symbolic::BreakStmt&) {} 90 | void operator()(const symbolic::ContinueStmt&){} 91 | // clang-format on 92 | 93 | template 94 | void operator()(const T&) 95 | { 96 | // a new statement was added? 97 | node.id = "!!!!! TODO: " + node.id; 98 | } 99 | }; 100 | } // namespace 101 | 102 | void load(Loader& ctx, Node& node, StmtIndex index) 103 | { 104 | if (null(index)) 105 | { 106 | node.id = "no-stmt"; 107 | return; 108 | } 109 | 110 | node.id = sort_name(index.sort()); 111 | ctx.reader.visit(index, StmtLoader{ctx, node}); 112 | } 113 | 114 | } // namespace ifc::util 115 | -------------------------------------------------------------------------------- /samples/sgraph-js/globals.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | const file_selector = document.getElementById('file-selector'); 5 | const title = document.getElementById('title'); 6 | const output = document.getElementById('output'); 7 | 8 | // Sidebar 9 | const name_filter = document.getElementById('name-filter'); 10 | const sort_filter = document.getElementById('sort-filter'); 11 | const center_view = document.getElementById('center-view'); 12 | const prop_filter_exported = document.getElementById('prop-filter-exported'); 13 | const prop_filter_non_exported = document.getElementById('prop-filter-non-exported'); 14 | const narrow_graph_context = document.getElementById('filter-narrow-context'); 15 | 16 | // Options dialog 17 | const decl_color_dropdown = document.getElementById('decl-color-dropdown'); 18 | const decl_color_selector = document.getElementById('decl-color-selector'); 19 | const reset_decl_colors_btn = document.getElementById('reset-decl-colors'); 20 | const transpose_graph_toggle = document.getElementById('transpose-graph-toggle'); 21 | const class_color_dropdown = document.getElementById('class-color-dropdown'); 22 | const class_color_selector = document.getElementById('class-color-selector'); 23 | const reset_class_colors_btn = document.getElementById('reset-class-colors'); 24 | const filter_opacity_edit = document.getElementById('filter-opacity-edit'); 25 | const bad_filter_opacity_text = document.getElementById('bad-filter-opacity-text'); 26 | const graph_fps_toggle = document.getElementById('graph-fps-toggle'); 27 | const graph_animations = document.getElementById('graph-animations'); 28 | 29 | // Various helper constants. 30 | const invalid_css_class = "is-invalid"; 31 | 32 | // IFC explorer dialog 33 | const ifc_explorer = { 34 | button: document.getElementById('ifc-explorer-button'), 35 | history: { 36 | sidebar: document.getElementById('ifc-explorer-history'), 37 | stack: new Array(), 38 | max_history: 20 39 | }, 40 | decls: { 41 | tab: '#ifc-explorer-decls-tab', 42 | content: document.getElementById('ifc-explorer-content-decls'), 43 | sort_dropdown: document.getElementById('ifc-explorer-content-decls-sort-dropdown'), 44 | index_edit: document.getElementById('ifc-explorer-content-decls-index-edit'), 45 | load: document.getElementById('ifc-explorer-content-decls-load'), 46 | validation_tooltip: document.getElementById('ifc-explorer-content-decls-load-validation') 47 | }, 48 | types: { 49 | tab: '#ifc-explorer-types-tab', 50 | content: document.getElementById('ifc-explorer-content-types'), 51 | sort_dropdown: document.getElementById('ifc-explorer-content-types-sort-dropdown'), 52 | index_edit: document.getElementById('ifc-explorer-content-types-index-edit'), 53 | load: document.getElementById('ifc-explorer-content-types-load'), 54 | validation_tooltip: document.getElementById('ifc-explorer-content-types-load-validation') 55 | }, 56 | exprs: { 57 | tab: '#ifc-explorer-exprs-tab', 58 | content: document.getElementById('ifc-explorer-content-exprs'), 59 | sort_dropdown: document.getElementById('ifc-explorer-content-exprs-sort-dropdown'), 60 | index_edit: document.getElementById('ifc-explorer-content-exprs-index-edit'), 61 | load: document.getElementById('ifc-explorer-content-exprs-load'), 62 | validation_tooltip: document.getElementById('ifc-explorer-content-exprs-load-validation') 63 | }, 64 | files: { 65 | content: document.getElementById('ifc-explorer-content-ifc-files') 66 | }, 67 | toc: { 68 | content: document.getElementById('ifc-explorer-content-toc') 69 | }, 70 | header: { 71 | content: document.getElementById('ifc-explorer-content-ifc-header') 72 | } 73 | }; 74 | 75 | // Scripting globals 76 | var graph = { 77 | data: { 78 | original_data: null 79 | }, 80 | drawing: { 81 | native_width: 1260.0, 82 | native_height: 800.0, 83 | custom_base_element: null, 84 | working_data: null, 85 | last_update_time: 0, 86 | timer: null, 87 | canvas_dirty: false, 88 | backing_canvas_dirty: false, 89 | node_mapper: null, 90 | partition: null 91 | }, 92 | element: document.getElementById('icicle') 93 | }; 94 | var graph_filter = null; 95 | var options = null; 96 | var sgraph = { resolver: null, header: null }; -------------------------------------------------------------------------------- /samples/sgraph-js/ui/filter.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | const PropertyFilters = { 5 | None: 0, 6 | Exported: 1 << 0, // Filter out exported declarations. 7 | NonExported: 1 << 1, // Filter out non-exported declarations. 8 | OnlyCLinkage: 1 << 2 // Only show declarations with C-linkage. 9 | }; 10 | 11 | class GraphFilter { 12 | constructor() { 13 | this.name = ''; 14 | this.sort = -1; 15 | this.prop_filters = PropertyFilters.None; 16 | this.narrow_context = false; 17 | } 18 | 19 | reset() { 20 | this.name = ''; 21 | this.sort = -1; 22 | this.prop_filters = PropertyFilters.None; 23 | } 24 | 25 | empty() { 26 | return !this.filter_name() && !this.filter_sort() && this.filter_props() == PropertyFilters.None; 27 | } 28 | 29 | filter_name() { 30 | return this.name != ''; 31 | } 32 | 33 | filter_sort() { 34 | return this.sort != -1; 35 | } 36 | 37 | filter_props() { 38 | return this.prop_filters; 39 | } 40 | } 41 | 42 | var filter_timeout_delay = 250; // 0.25s 43 | var filter_timeout; 44 | 45 | function filter_names_keyup(e, text_input) { 46 | clearInterval(filter_timeout); 47 | filter_timeout = setInterval(function() { 48 | graph_filter.name = text_input.value; 49 | apply_graph_filter(graph_filter); 50 | clearInterval(filter_timeout); 51 | }, filter_timeout_delay); 52 | } 53 | 54 | function filter_names_keydown(e, text_input) { 55 | clearInterval(filter_timeout); 56 | } 57 | 58 | function filter_sort_changed(e, select_box) { 59 | graph_filter.sort = parseInt(select_box.value); 60 | apply_graph_filter(graph_filter); 61 | } 62 | 63 | function populate_sort_filter() { 64 | // Setup the options for the types. 65 | // Create the 'null' option. 66 | var nullopt = document.createElement("option"); 67 | nullopt.textContent = ''; 68 | nullopt.value = -1; 69 | sort_filter.appendChild(nullopt); 70 | Object.entries(DeclIndex.Sort) 71 | .sort(function(lhs, rhs) { 72 | if (lhs[0] < rhs[0]) 73 | return -1; 74 | if (lhs[0] > rhs[0]) 75 | return 1; 76 | return 0; 77 | }) 78 | .forEach(element => { 79 | if (element[1] != DeclIndex.Sort.Count) { 80 | var entry = document.createElement("option"); 81 | entry.textContent = element[0]; 82 | entry.value = element[1]; 83 | sort_filter.appendChild(entry); 84 | } 85 | }); 86 | } 87 | 88 | function apply_bit_to(num, bit, value) { 89 | return (num & ~(1 << bit) | (value << bit)); 90 | } 91 | 92 | function toggle_filter_exported_decls(e, checkbox) { 93 | const value = checkbox.checked; 94 | graph_filter.prop_filters = apply_bit_to(graph_filter.prop_filters, 0, value); 95 | apply_graph_filter(graph_filter); 96 | } 97 | 98 | function toggle_filter_non_exported_decls(e, checkbox) { 99 | const value = checkbox.checked; 100 | graph_filter.prop_filters = apply_bit_to(graph_filter.prop_filters, 1, value); 101 | apply_graph_filter(graph_filter); 102 | } 103 | 104 | function toggle_narrow_graph_context(e, checkbox) { 105 | graph_filter.narrow_context = checkbox.checked; 106 | // If this becomes 'unchecked' then we need to rebuild the graph 107 | // so the filter overlay shows every node. 108 | if (!graph_filter.narrow_context) { 109 | rebuild_original_graph(); 110 | } 111 | apply_graph_filter(graph_filter); 112 | } 113 | 114 | function init_filters() { 115 | graph_filter = new GraphFilter(); 116 | 117 | populate_sort_filter(); 118 | 119 | name_filter.addEventListener("keyup", event => filter_names_keyup(event, name_filter)); 120 | name_filter.addEventListener("keydown", event => filter_names_keydown(event, name_filter)); 121 | sort_filter.addEventListener("change", event => filter_sort_changed(event, sort_filter)); 122 | center_view.addEventListener("click", e => reset_view()); 123 | prop_filter_exported.addEventListener("click", event => toggle_filter_exported_decls(event, prop_filter_exported)); 124 | prop_filter_non_exported.addEventListener("click", event => toggle_filter_non_exported_decls(event, prop_filter_non_exported)); 125 | narrow_graph_context.addEventListener("click", event => toggle_narrow_graph_context(event, narrow_graph_context)); 126 | } -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 8, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 28, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "cmake-pedantic", 11 | "hidden": true, 12 | "warnings": { 13 | "dev": true, 14 | "deprecated": true, 15 | "uninitialized": true, 16 | "unusedCli": true, 17 | "systemVars": false 18 | }, 19 | "errors": { 20 | "dev": true, 21 | "deprecated": true 22 | } 23 | }, 24 | { 25 | "name": "dev-mode", 26 | "hidden": true, 27 | "inherits": "cmake-pedantic", 28 | "cacheVariables": { 29 | "DEVELOPER_MODE": "ON" 30 | } 31 | }, 32 | { 33 | "name": "vcpkg", 34 | "hidden": true, 35 | "cacheVariables": { 36 | "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" 37 | } 38 | }, 39 | { 40 | "name": "vcpkg-win64-static", 41 | "hidden": true, 42 | "cacheVariables": { 43 | "VCPKG_TARGET_TRIPLET": "x64-windows-static-md" 44 | } 45 | }, 46 | { 47 | "name": "ci-std", 48 | "description": "This preset makes sure the project actually builds with at least the specified standard", 49 | "hidden": true, 50 | "cacheVariables": { 51 | "CMAKE_CXX_EXTENSIONS": "OFF", 52 | "CMAKE_CXX_STANDARD": "23", 53 | "CMAKE_CXX_STANDARD_REQUIRED": "ON" 54 | } 55 | }, 56 | { 57 | "name": "flags-gcc-clang", 58 | "description": "These flags are supported by both GCC and Clang", 59 | "hidden": true, 60 | "cacheVariables": { 61 | "CMAKE_CXX_FLAGS": "-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -fstack-protector-strong -fcf-protection=full -fstack-clash-protection -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Wnon-virtual-dtor -Wold-style-cast", 62 | "CMAKE_EXE_LINKER_FLAGS": "-Wl,--allow-shlib-undefined,--as-needed,-z,noexecstack,-z,relro,-z,now" 63 | } 64 | }, 65 | { 66 | "name": "flags-appleclang", 67 | "hidden": true, 68 | "cacheVariables": { 69 | "CMAKE_CXX_FLAGS": "-fstack-protector-strong -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wformat=2 -Wundef -Werror=float-equal -Wshadow -Wcast-align -Wunused -Wnull-dereference -Wdouble-promotion -Wimplicit-fallthrough -Wextra-semi -Wnon-virtual-dtor -Wold-style-cast" 70 | } 71 | }, 72 | { 73 | "name": "flags-msvc", 74 | "description": "Note that all the flags after /W4 are required for MSVC to conform to the language standard", 75 | "hidden": true, 76 | "cacheVariables": { 77 | "CMAKE_CXX_FLAGS": "/DWIN32 /sdl /guard:cf /utf-8 /diagnostics:caret /w14165 /w44242 /w44254 /w44263 /w34265 /w34287 /w44296 /w44365 /w44388 /w44464 /w14545 /w14546 /w14547 /w14549 /w14555 /w34619 /w34640 /w24826 /w14905 /w14906 /w14928 /w45038 /W4 /volatile:iso /Zc:inline /Zc:preprocessor /Zc:enumTypes /Zc:__cplusplus /Zc:externConstexpr /Zc:throwingNew /EHsc", 78 | "CMAKE_EXE_LINKER_FLAGS": "/machine:x64 /guard:cf" 79 | } 80 | }, 81 | { 82 | "name": "ci-linux", 83 | "generator": "Unix Makefiles", 84 | "hidden": true, 85 | "inherits": ["flags-gcc-clang", "ci-std"], 86 | "cacheVariables": { 87 | "CMAKE_BUILD_TYPE": "Release" 88 | } 89 | }, 90 | { 91 | "name": "ci-darwin", 92 | "generator": "Unix Makefiles", 93 | "hidden": true, 94 | "inherits": ["flags-appleclang", "ci-std"], 95 | "cacheVariables": { 96 | "CMAKE_BUILD_TYPE": "Release" 97 | } 98 | }, 99 | { 100 | "name": "ci-win64", 101 | "inherits": ["flags-msvc", "ci-std"], 102 | "generator": "Visual Studio 17 2022", 103 | "architecture": "x64", 104 | "hidden": true 105 | }, 106 | { 107 | "name": "ci-build", 108 | "binaryDir": "${sourceDir}/build", 109 | "hidden": true 110 | }, 111 | { 112 | "name": "ci-ubuntu", 113 | "inherits": ["ci-build", "ci-linux", "dev-mode", "vcpkg"] 114 | }, 115 | { 116 | "name": "ci-macos", 117 | "inherits": ["ci-build", "ci-darwin", "dev-mode", "vcpkg"] 118 | }, 119 | { 120 | "name": "ci-windows", 121 | "inherits": ["ci-build", "ci-win64", "dev-mode", "vcpkg", "vcpkg-win64-static"] 122 | }, 123 | { 124 | "name": "test-msvc", 125 | "inherits": ["ci-build", "ci-win64", "dev-mode", "vcpkg", "vcpkg-win64-static"] 126 | } 127 | ] 128 | } 129 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/dirs.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class Phases { 5 | static Values = { 6 | Unknown: 0, 7 | Reading: 1 << 0, 8 | Lexing: 1 << 1, 9 | Preprocessing: 1 << 2, 10 | Parsing: 1 << 3, 11 | Importing: 1 << 4, 12 | NameResolution: 1 << 5, 13 | Typing: 1 << 6, 14 | Evaluation: 1 << 7, 15 | Instantiation: 1 << 8, 16 | Analysis: 1 << 9, 17 | CodeGeneration: 1 << 10, 18 | Linking: 1 << 11, 19 | Loading: 1 << 12, 20 | Execution: 1 << 13, 21 | }; 22 | 23 | constructor(reader) { 24 | this.value = reader.read_uint32(); 25 | } 26 | } 27 | 28 | class EmptyDir { 29 | static partition_name = "dir.empty"; 30 | 31 | constructor(reader) { 32 | this.locus = new SourceLocation(reader); 33 | } 34 | } 35 | 36 | class AttributeDir { 37 | static partition_name = "dir.attribute"; 38 | 39 | constructor(reader) { 40 | this.locus = new SourceLocation(reader); 41 | // TODO: AttrIndex 42 | this.attr = reader.read_uint32(); 43 | } 44 | } 45 | 46 | class PragmaDir { 47 | static partition_name = "dir.pragma"; 48 | 49 | constructor(reader) { 50 | this.locus = new SourceLocation(reader); 51 | this.words = new SentenceIndex(reader); 52 | } 53 | } 54 | 55 | class UsingDir { 56 | static partition_name = "dir.using"; 57 | 58 | constructor(reader) { 59 | this.locus = new SourceLocation(reader); 60 | this.nominated = new ExprIndex(reader); 61 | this.resolution = new DeclIndex(reader); 62 | } 63 | } 64 | 65 | class UsingDeclarationDir { 66 | static partition_name = "dir.decl-use"; 67 | 68 | constructor(reader) { 69 | this.locus = new SourceLocation(reader); 70 | this.path = new ExprIndex(reader); 71 | this.result = new DeclIndex(reader); 72 | } 73 | } 74 | 75 | class ExprDir { 76 | static partition_name = "dir.expr"; 77 | 78 | constructor(reader) { 79 | this.locus = new SourceLocation(reader); 80 | this.expr = new ExprIndex(reader); 81 | this.phases = new Phases(reader); 82 | } 83 | } 84 | 85 | class StructuredBindingDir { 86 | static partition_name = "dir.struct-binding"; 87 | 88 | constructor(reader) { 89 | this.locus = new SourceLocation(reader); 90 | this.bindings = new Sequence(DeclIndex, reader); 91 | this.names = new Sequence(TextOffset, reader); 92 | } 93 | } 94 | 95 | class SpecifiersSpreadDir { 96 | static partition_name = "dir.specifiers-spread"; 97 | 98 | constructor(reader) { 99 | this.locus = new SourceLocation(reader); 100 | } 101 | } 102 | 103 | class TupleDir { 104 | static partition_name = "dir.tuple"; 105 | 106 | constructor(reader) { 107 | this.seq = new HeapSequence(HeapSort.Values.Dir); 108 | } 109 | } 110 | 111 | function symbolic_for_dir_sort(sort) { 112 | switch (sort) { 113 | case DirIndex.Sort.Empty: 114 | return EmptyDir; 115 | case DirIndex.Sort.Attribute: 116 | return AttributeDir; 117 | case DirIndex.Sort.Pragma: 118 | return PragmaDir; 119 | case DirIndex.Sort.Using: 120 | return UsingDir; 121 | case DirIndex.Sort.DeclUse: 122 | return UsingDeclarationDir; 123 | case DirIndex.Sort.Expr: 124 | return ExprDir; 125 | case DirIndex.Sort.StructuredBinding: 126 | return StructuredBindingDir; 127 | case DirIndex.Sort.SpecifiersSpread: 128 | return SpecifiersSpreadDir; 129 | case DirIndex.Sort.Tuple: 130 | return TupleDir; 131 | case DirIndex.Sort.Unused0: 132 | case DirIndex.Sort.Unused1: 133 | case DirIndex.Sort.Unused2: 134 | case DirIndex.Sort.Unused3: 135 | case DirIndex.Sort.Unused4: 136 | case DirIndex.Sort.Unused5: 137 | case DirIndex.Sort.Unused6: 138 | case DirIndex.Sort.Unused7: 139 | case DirIndex.Sort.Unused8: 140 | case DirIndex.Sort.Unused9: 141 | case DirIndex.Sort.Unused10: 142 | case DirIndex.Sort.Unused11: 143 | case DirIndex.Sort.Unused12: 144 | case DirIndex.Sort.Unused13: 145 | case DirIndex.Sort.Unused14: 146 | case DirIndex.Sort.Unused15: 147 | case DirIndex.Sort.Unused16: 148 | case DirIndex.Sort.Unused17: 149 | case DirIndex.Sort.Unused18: 150 | case DirIndex.Sort.Unused19: 151 | case DirIndex.Sort.Unused20: 152 | case DirIndex.Sort.Unused21: 153 | case DirIndex.Sort.VendorExtension: 154 | case DirIndex.Sort.Count: 155 | console.error(`Bad sort: ${sort}`); 156 | return null; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | # Hacking 2 | 3 | Here is some wisdom to help you build and test this project as a developer and 4 | potential contributor. 5 | 6 | If you plan to contribute, please read the [CONTRIBUTING](CONTRIBUTING.md) 7 | guide. 8 | 9 | ## Developer mode 10 | 11 | Build system targets that are only useful for developers of this project are 12 | hidden if the `DEVELOPER_MODE` option is disabled. Enabling this option makes 13 | tests and other developer targets and options available. Not enabling this 14 | option means that you are a consumer of this project and thus you have no need 15 | for these targets and options. 16 | 17 | Developer mode is always set to on in CI workflows. 18 | 19 | ### Presets 20 | 21 | This project makes use of [presets][1] to simplify the process of configuring 22 | the project. As a developer, you are recommended to always have the [latest 23 | CMake version][latest cmake] installed to make use of the latest 24 | Quality-of-Life additions. 25 | 26 | You have a few options to pass `DEVELOPER_MODE` to the configure command, but 27 | this project prefers to use presets. 28 | 29 | As a developer, you should create a `CMakeUserPresets.json` file at the root of 30 | the project: 31 | 32 | ```json 33 | { 34 | "version": 2, 35 | "cmakeMinimumRequired": { 36 | "major": 3, 37 | "minor": 14, 38 | "patch": 0 39 | }, 40 | "configurePresets": [ 41 | { 42 | "name": "dev", 43 | "binaryDir": "${sourceDir}/build/dev", 44 | "inherits": ["dev-mode", "vcpkg", "ci-"], 45 | "cacheVariables": { 46 | "CMAKE_BUILD_TYPE": "Debug" 47 | } 48 | } 49 | ], 50 | "buildPresets": [ 51 | { 52 | "name": "dev", 53 | "configurePreset": "dev", 54 | "configuration": "Debug" 55 | } 56 | ], 57 | "testPresets": [ 58 | { 59 | "name": "dev", 60 | "configurePreset": "dev", 61 | "configuration": "Debug", 62 | "output": { 63 | "outputOnFailure": true 64 | } 65 | } 66 | ] 67 | } 68 | ``` 69 | 70 | You should replace `` in your newly created presets file with the name of 71 | the operating system you have, which may be `win64`, `linux` or `darwin`. You 72 | can see what these correspond to in the 73 | [`CMakePresets.json`](CMakePresets.json) file. 74 | 75 | `CMakeUserPresets.json` is also the perfect place in which you can put all 76 | sorts of things that you would otherwise want to pass to the configure command 77 | in the terminal. 78 | 79 | > **Note** 80 | > Some editors are pretty greedy with how they open projects with presets. 81 | > Some just pick a preset and start configuring without further input from the developer, 82 | > which can be confusing. Make sure that your editor configures what and when you 83 | > actually want it to, for example in CLion you have to make sure only the 84 | > `dev-dev preset` has `Enable profile` ticked in 85 | > `File > Settings... > Build, Execution, Deployment > CMake` and in Visual 86 | > Studio you have to set the option `Never run configure step automatically` 87 | > in `Tools > Options > CMake` **prior to opening the project**, after which 88 | > you can manually configure using `Project > Configure Cache`. 89 | 90 | ### Dependency manager 91 | 92 | The above preset will make use of the [vcpkg][vcpkg] dependency manager. After 93 | installing it, make sure the `VCPKG_ROOT` environment variable is pointing at 94 | the directory where the vcpkg executable is or you can directly set 95 | `CMAKE_TOOLCHAIN_FILE` with an absolute path. On Windows, you might also want 96 | to inherit from the `vcpkg-win64-static` preset, which will make vcpkg install 97 | the dependencies as static libraries. This is only necessary if you don't want 98 | to setup `PATH` to run tests. 99 | 100 | ### Configure, build and test 101 | 102 | If you followed the above instructions, then you can configure, build and test 103 | the project respectively with the following commands from the project root on 104 | any operating system with any build system: 105 | 106 | ```sh 107 | cmake --preset=dev 108 | cmake --build --preset=dev 109 | ctest --preset=dev 110 | ``` 111 | 112 | If you are using a compatible editor (e.g. VSCode) or IDE (e.g. VS), you will 113 | also be able to select the above created user presets for automatic 114 | integration. 115 | 116 | Please note that both the build and test commands accept a `-j` flag to specify 117 | the number of jobs to use, which should ideally be specified to the number of 118 | threads your CPU has. You may also want to add that to your user preset using 119 | the `jobs` property, see the [presets documentation][presets] for more 120 | details. 121 | 122 | [latest cmake]: https://cmake.org/download/ 123 | [vcpkg]: https://github.com/microsoft/vcpkg 124 | [presets]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html 125 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # ClangFormat rules for Edge-lite style used in vc/common, etc. 3 | Language: Cpp 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: DontAlign 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortEnumsOnASingleLine: true 15 | AllowShortFunctionsOnASingleLine: Empty 16 | AllowShortIfStatementsOnASingleLine: false 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: true 26 | AfterEnum: false 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: true 32 | BeforeCatch: true 33 | BeforeElse: true 34 | IndentBraces: false 35 | SplitEmptyFunction: false 36 | SplitEmptyRecord: false 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: NonAssignment 39 | BreakBeforeBraces: Custom 40 | BreakBeforeConceptDeclarations: Always 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: false 43 | BreakConstructorInitializers: BeforeColon 44 | BreakStringLiterals: false 45 | ColumnLimit: 120 46 | CommentPragmas: '' 47 | CompactNamespaces: false 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 49 | ConstructorInitializerIndentWidth: 2 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: true 52 | DerivePointerAlignment: false 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | FixNamespaceComments: true 56 | ForEachMacros: [] 57 | IncludeBlocks: Preserve 58 | IncludeCategories: [] 59 | IncludeIsMainRegex: '' 60 | IndentCaseLabels: false 61 | IndentPPDirectives: None 62 | IndentWidth: 4 63 | IndentWrappedFunctionNames: false 64 | KeepEmptyLinesAtTheStartOfBlocks: false 65 | MacroBlockBegin: '^(CUSTOM_VALUE|OBJECT)_.*_JSON' 66 | MacroBlockEnd: '^END_(CUSTOM_VALUE|OBJECT)_.*_JSON' 67 | MaxEmptyLinesToKeep: 2 68 | NamespaceIndentation: All 69 | PenaltyBreakAssignment: 2 70 | PenaltyBreakBeforeFirstCallParameter: 19 71 | PenaltyBreakComment: 300 72 | PenaltyBreakFirstLessLess: 120 73 | PenaltyBreakString: 1000 74 | PenaltyExcessCharacter: 1000000 75 | PenaltyReturnTypeOnItsOwnLine: 1000 76 | PointerAlignment: Left 77 | RawStringFormats: [] 78 | ReflowComments: true 79 | #RequiresExpressionIndentation: OuterScope 80 | SortIncludes: false 81 | SortUsingDeclarations: true 82 | SpaceAfterCStyleCast: false 83 | SpaceAfterTemplateKeyword: false 84 | SpaceBeforeAssignmentOperators: true 85 | SpaceBeforeParens: ControlStatements 86 | SpaceInEmptyParentheses: false 87 | SpacesBeforeTrailingComments: 1 88 | SpacesInAngles: false 89 | SpacesInCStyleCastParentheses: false 90 | SpacesInParentheses: false 91 | SpacesInSquareBrackets: false 92 | Standard: Cpp11 93 | StatementMacros: [] 94 | TabWidth: 8 95 | UseTab: Never 96 | ... 97 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to grant us 5 | the right to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | ## Scope 12 | The IFC SDK is an implementation of the [IFC Specification](https://github.com/microsoft/ifc-spec). We are purposefully 13 | limiting its scope to I/O operations for reading and writing IFC files, and to 14 | simple utilities for formatting or viewing IFC files. We welcome and we are looking 15 | for contributions that fix gaps between the SDK and the Specification, or 16 | for changes required to support C++ standards starting from C++20 and upwards. 17 | 18 | We are not yet accepting contributions that expand the project scope beyond adherence to the Specification, as explained above. However, if you build cool apps or projects on top of the SDK, we would love to hear about them. 19 | 20 | We are making the SDK available to the C++ community in the hope of helping to 21 | advance C++ Modules implementation across C++ compilers, and C++ Modules adoption in the C++ community at large. 22 | 23 | ## Formatting 24 | There is a [.clang-format](.clang-format) file in the repo that should work with many editors automatically. Use the comment strings `// clang format off` and `// clang format on` to prevent automatic formatting when necessary to preserve specific formatting. 25 | 26 | ## Coding Conventions 27 | Type names (e.g., classes, and enums) should use `PascalCase` naming convention. 28 | 29 | Functions, methods, data members, parameters, and variables should use `snake_case` naming convention (e.g., `bit_length`). 30 | 31 | Use the keywords `and`, `or`, and `not` rather than `&&`, `||`, and `!` for logical operators. 32 | 33 | ## Submitting a Pull Request 34 | 35 | The SDK repo has open issues that track work which needs to be completed. 36 | If you are unsure of where to start, you may want to: 37 | 38 | * look for pinned issues, or 39 | * check issues under the labels [`good first issue`][label:"good first issue"], 40 | [`high priority`][label:"high priority"], or [`help wanted`][label:"help wanted"]. 41 | 42 | ## Reviewing a Pull Request 43 | 44 | We love code reviews from contributors! Reviews from other contributors can often accelerate the reviewing process 45 | by helping a PR reach a more finished state before maintainers review the changes. As a result, such a PR may require 46 | fewer maintainer review iterations before reaching a "Ready to Merge" state. 47 | 48 | To gain insight into our Code Review process, you can check out: 49 | 50 | * pull requests which are [undergoing review][review:changes-requested] 51 | * [Advice for Reviewing Pull Requests][wiki:advice-for-reviewing] 52 | 53 | ## PR Checklist 54 | 55 | Before submitting a pull request, please ensure: 56 | 57 | * You have read the [HACKING.md](HACKING.md) document. 58 | * Code has been formatted using the provided .clang-format file. 59 | * Naming conventions are following the recommendations. 60 | * The project has been built in developer mode (`DEVELOPER_MODE` is set to `ON`). 61 | * Tests have been run locally (for at least one platform). 62 | * Changes that will impact the binary format of an IFC will need to synchronize with the compiler(s) affected and will likely require a version change. 63 | 64 | If your changes are derived from any other project, you _must_ mention it in the pull request description, 65 | so we can determine whether the license is compatible and whether any other steps need to be taken. 66 | 67 | # Microsoft Open Source Code of Conduct 68 | 69 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 70 | 71 | Resources: 72 | 73 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 74 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 75 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 76 | 77 | [label:"good first issue"]: 78 | https://github.com/microsoft/IFC/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22 79 | [label:"high priority"]: https://github.com/microsoft/IFC/issues?q=is%3Aopen+is%3Aissue+label%3A%22high+priority%22 80 | [label:"help wanted"]: https://github.com/microsoft/IFC/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22 81 | [review:changes-requested]: https://github.com/microsoft/IFC/pulls?q=is%3Apr+is%3Aopen+review%3Achanges-requested 82 | [wiki:advice-for-reviewing]: https://github.com/microsoft/IFC/wiki/Advice-for-Reviewing-Pull-Requests 83 | [NOTICE.txt]: https://github.com/microsoft/IFC/blob/main/NOTICE.txt 84 | -------------------------------------------------------------------------------- /src/hash_win.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | // Windows Implementation of SHA256 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace { 14 | // Tags for catch handler. 15 | struct OpenAlgorithmError { 16 | NTSTATUS result; 17 | }; 18 | struct HashLengthPropertyError { 19 | NTSTATUS result; 20 | }; 21 | struct ObjectLengthPropertyError { 22 | NTSTATUS result; 23 | }; 24 | struct CreateHashError { 25 | NTSTATUS result; 26 | }; 27 | struct HashDataError { 28 | NTSTATUS result; 29 | }; 30 | struct FinishHashError { 31 | NTSTATUS result; 32 | }; 33 | // A simple helper class designed to be an RAII container for the Windows crypto machinery. 34 | class SHA256Helper { 35 | public: 36 | SHA256Helper() 37 | { 38 | digest_ntstatus(BCryptOpenAlgorithmProvider(&alg_handle_, BCRYPT_SHA256_ALGORITHM, 39 | /*pszImplementation = */ nullptr, 40 | /*dwFlags = */ 0)); 41 | 42 | DWORD cb_result = 0; 43 | digest_ntstatus(BCryptGetProperty(alg_handle_, BCRYPT_HASH_LENGTH, 44 | reinterpret_cast(&hash_byte_length_), 45 | sizeof hash_byte_length_, &cb_result, 46 | /*dwFlags = */ 0)); 47 | 48 | digest_ntstatus(BCryptGetProperty(alg_handle_, BCRYPT_OBJECT_LENGTH, 49 | reinterpret_cast(&object_byte_length_), 50 | sizeof object_byte_length_, &cb_result, 51 | /*dwFlags = */ 0)); 52 | } 53 | 54 | ~SHA256Helper() 55 | { 56 | if (alg_handle_ != nullptr) 57 | { 58 | BCryptCloseAlgorithmProvider(alg_handle_, /*dwFlags = */ 0); 59 | } 60 | } 61 | 62 | ifc::SHA256Hash hash(const std::byte* first, const std::byte* last) 63 | { 64 | BCRYPT_HASH_HANDLE hash_handle = nullptr; 65 | 66 | using ByteVector = std::vector; 67 | ByteVector object_buf(object_byte_length_); 68 | 69 | digest_ntstatus(BCryptCreateHash(alg_handle_, &hash_handle, object_buf.data(), 70 | object_byte_length_, 71 | /*pbSecret = */ nullptr, 72 | /*cbSecret = */ 0, 73 | /*dwFlags = */ 0)); 74 | auto final_act = gsl::finally([&] { 75 | if (hash_handle != nullptr) 76 | { 77 | BCryptDestroyHash(hash_handle); 78 | } 79 | }); 80 | digest_ntstatus(BCryptHashData(hash_handle, 81 | reinterpret_cast(const_cast(first)), 82 | static_cast(std::distance(first, last)), 83 | /*dwFlags = */ 0)); 84 | ifc::SHA256Hash hash = {}; 85 | // uint32_t array should map to a uint8_t[32] array 86 | IFCASSERT(hash_byte_length_ == std::size(hash.value) * 4); 87 | digest_ntstatus( 88 | BCryptFinishHash(hash_handle, reinterpret_cast(hash.value.data()), hash_byte_length_, 89 | /*dwFlags = */ 0)); 90 | return hash; 91 | } 92 | 93 | private: 94 | template 95 | void digest_ntstatus(NTSTATUS result) 96 | { 97 | // Success for an NTSTATUS is >= 0. 98 | if (result < 0) 99 | { 100 | throw T{result}; 101 | } 102 | } 103 | 104 | BCRYPT_ALG_HANDLE alg_handle_ = nullptr; 105 | DWORD hash_byte_length_ = 0; 106 | DWORD object_byte_length_ = 0; 107 | }; 108 | 109 | } // namespace 110 | 111 | namespace ifc { 112 | SHA256Hash hash_bytes(const std::byte* first, const std::byte* last) 113 | { 114 | SHA256Helper helper; 115 | return helper.hash(first, last); 116 | } 117 | } // namespace ifc 118 | -------------------------------------------------------------------------------- /src/ifc-printer/main.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ifc/tooling.hxx" 10 | #include "ifc/reader.hxx" 11 | #include "ifc/dom/node.hxx" 12 | #include "printer.hxx" 13 | 14 | // -- A macro to abstract over differences of host literal string views. 15 | #ifdef WIN32 16 | # define WIDEN(S) L ## S ## sv 17 | # define NSV(S) WIDEN(S) 18 | #else 19 | # define NSV(S) S ## sv 20 | #endif 21 | 22 | // -- Abstract over standard output and error channels. 23 | #ifdef WIN32 24 | auto& std_error = std::wcerr; 25 | #else 26 | auto& std_error = std::cerr; 27 | #endif 28 | 29 | using namespace ifc::util; 30 | using namespace std::literals; 31 | 32 | namespace { 33 | struct Arguments { 34 | PrintOptions options = PrintOptions::None; 35 | 36 | // Files to process. 37 | std::vector files; 38 | }; 39 | 40 | 41 | void print_help(const ifc::fs::path& path) 42 | { 43 | auto name = path.stem().string(); 44 | std::cout << "Usage:\n\n"; 45 | std::cout << name << " ifc-file1 [ifc-file2 ...] [--color/-c]\n"; 46 | std::cout << name << " --help/-h\n"; 47 | } 48 | 49 | Arguments process_args(int argc, ifc::tool::NativeChar* argv[]) 50 | { 51 | Arguments result; 52 | for (int i = 1; i < argc; ++i) 53 | { 54 | if (argv[i] == NSV("--help")) 55 | { 56 | print_help(argv[0]); 57 | exit(0); 58 | } 59 | else if (argv[i] == NSV("--color")) 60 | { 61 | result.options |= PrintOptions::Use_color; 62 | } 63 | // Future flags to add as needed 64 | // --location: print locations 65 | // --header: print module header 66 | // etc. 67 | 68 | else if (argv[i][0] != '-') 69 | { 70 | result.files.emplace_back(argv[i]); 71 | } 72 | else 73 | { 74 | std_error << NSV("Unknown command line argument '") << argv[i] << NSV("'\n"); 75 | print_help(argv[0]); 76 | std::exit(1); 77 | } 78 | } 79 | 80 | if (result.files.empty()) 81 | { 82 | std_error << NSV("Specify filepath of an ifc file\n"); 83 | print_help(argv[0]); 84 | std::exit(1); 85 | } 86 | 87 | return result; 88 | } 89 | 90 | void process_ifc(const ifc::fs::path& path, PrintOptions options) 91 | { 92 | ifc::tool::InputFile container { path }; 93 | 94 | ifc::InputIfc file { container.contents() }; 95 | ifc::Pathname name{path.u8string().c_str()}; 96 | file.validate(name, ifc::Architecture::Unknown, ifc::Pathname{}, 97 | ifc::IfcOptions::IntegrityCheck); 98 | 99 | ifc::Reader reader(file); 100 | ifc::util::Loader loader(reader); 101 | auto& gs = loader.get(reader.ifc.header()->global_scope); 102 | print(gs, std::cout, options); 103 | 104 | // Make sure that we resolve and print all 105 | // referenced nodes. 106 | options |= PrintOptions::Top_level_index; 107 | while (not loader.referenced_nodes.empty()) 108 | { 109 | auto it = loader.referenced_nodes.begin(); 110 | const auto node_key = *it; 111 | loader.referenced_nodes.erase(it); 112 | 113 | auto& item = loader.get(node_key); 114 | print(item, std::cout, options); 115 | } 116 | } 117 | } 118 | 119 | #ifdef WIN32 120 | int wmain(int argc, wchar_t* argv[]) 121 | #else 122 | int main(int argc, char* argv[]) 123 | #endif 124 | { 125 | std::size_t error_count = 0; 126 | 127 | Arguments arguments = process_args(argc, argv); 128 | for (const auto& file : arguments.files) 129 | { 130 | try { 131 | process_ifc(file, arguments.options); 132 | continue; 133 | } 134 | catch (const ifc::IfcArchMismatch&) 135 | { 136 | std::cerr << file.string() << ": ifc architecture mismatch\n"; 137 | } 138 | catch(const ifc::error_condition::UnexpectedVisitor& e) 139 | { 140 | std::cerr << file.string() 141 | << ": visit unexpected " << e.category << ", " 142 | << e.sort << "\n"; 143 | } 144 | catch(const ifc::InputIfc::MissingIfcHeader&) 145 | { 146 | std::cerr << file.string() << ": Missing ifc binary file header\n"; 147 | } 148 | catch (...) 149 | { 150 | std::cerr << file.string() << ": unknown exception caught\n"; 151 | } 152 | ++error_count; 153 | } 154 | 155 | return error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE; 156 | } 157 | -------------------------------------------------------------------------------- /include/ifc/pathname.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_PATHNAME_INCLUDED 5 | #define IFC_PATHNAME_INCLUDED 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace ifc { 16 | 17 | // Evaluate to true if the type parameter is a character type. 18 | // clang-format off 19 | template 20 | concept CharType = std::is_same_v 21 | or std::is_same_v 22 | or std::is_same_v 23 | or std::is_same_v; 24 | // clang-format on 25 | 26 | // Return the number of characters in a C-style NUL-terminated byte string. 27 | template 28 | inline std::size_t ntbs_length(const T* s) 29 | { 30 | using S = const char*; 31 | return std::strlen(S(s)); 32 | } 33 | 34 | // Data structure for manipulating path names. Existing code happily mixes 35 | // C-style 'const char*' and 'const unsigned char*' at various places for path names. 36 | // So this type can be 'std::string' without merry type punning all over the place. 37 | // It can't be current filesystem path either. It is somewhere a minimal cross. 38 | // Note: The private inheritance is used as implementation detail to avoid gratuitous 39 | // boilerplate, and it is perfectly OK. 40 | // Note: This data structure uses the standard allocator, for simple reasons: 41 | // (1) Most pathnames do not grow once fully constructed/minted. 42 | // (2) They have either indefinite lifetime (because they get added to global search paths 43 | // of file reference lists), or they are short-lived (because they are used locally 44 | // to build paths to query the filesystem.) 45 | // But they don't display any elaborated lifetime structure. 46 | // (3) The standard allocators are always ready when the global objects are initialized. 47 | struct Pathname : private std::vector { 48 | using Base = std::vector; 49 | using Base::const_iterator; 50 | using Base::reserve; 51 | 52 | Pathname() 53 | { 54 | Base::push_back({}); 55 | } 56 | 57 | template 58 | Pathname(const T* s, std::size_t len) 59 | : Base{reinterpret_cast(s), reinterpret_cast(s) + len} 60 | { 61 | Base::push_back({}); 62 | } 63 | 64 | template 65 | Pathname(const T* s) : Pathname{s, ntbs_length(s)} 66 | {} 67 | 68 | Pathname(std::u8string_view utf8) : Base{utf8.begin(), utf8.end()} 69 | { 70 | Base::push_back({}); 71 | } 72 | 73 | std::size_t length() const 74 | { 75 | return Base::size() - 1; 76 | } 77 | 78 | bool empty() const 79 | { 80 | return length() == 0; 81 | } 82 | 83 | explicit operator bool() const 84 | { 85 | return not empty(); 86 | } 87 | 88 | using Base::begin; 89 | 90 | iterator end() 91 | { 92 | return Base::end() - 1; 93 | } 94 | 95 | const_iterator end() const 96 | { 97 | return Base::end() - 1; 98 | } 99 | 100 | const value_type* c_str() const 101 | { 102 | return Base::data(); 103 | } 104 | 105 | value_type back() const 106 | { 107 | const auto n = length(); 108 | IFCASSERT(n != 0); 109 | return begin()[static_cast(n - 1)]; 110 | } 111 | 112 | void prepend(value_type c) 113 | { 114 | Base::insert(begin(), c); 115 | } 116 | 117 | void append(value_type c) 118 | { 119 | Base::insert(end(), c); 120 | } 121 | 122 | void append(const value_type* s) 123 | { 124 | Base::insert(end(), s, s + ntbs_length(s)); 125 | } 126 | 127 | void append(const Pathname& x) 128 | { 129 | Base::insert(end(), x.begin(), x.end()); 130 | } 131 | 132 | const Base& impl() const 133 | { 134 | return *this; 135 | } 136 | 137 | Pathname& minted() 138 | { 139 | Base::shrink_to_fit(); 140 | return *this; 141 | } 142 | 143 | Pathname& extend_with_type(const value_type* ext) 144 | { 145 | append(u8'.'); 146 | append(ext); 147 | return minted(); 148 | } 149 | 150 | std::strong_ordering operator<=>(const Pathname&) const = default; 151 | 152 | bool operator==(const Pathname&) const = default; 153 | bool operator==(std::u8string_view sv) const 154 | { 155 | return std::equal(begin(), end(), std::begin(sv), std::end(sv)); 156 | } 157 | }; 158 | 159 | } // namespace ifc 160 | 161 | #endif // IFC_PATHNAME_INCLUDED 162 | -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building with CMake 2 | 3 | This project encourages uses of up-to-date build tools and contemporary development environments. 4 | To build from sources, you need: 5 | 6 | - CMake 3.26 or higher 7 | - A C++ compiler that supports C++23 (or equivalent) which is just a bug fix of C++20 8 | - An implementation of the C++ Core Guidelines Support Library (GSL) 9 | 10 | ## Dependencies 11 | 12 | For a list of dependencies, please refer to [vcpkg.json](vcpkg.json). 13 | 14 | This project depends on the [Microsoft implementation of GSL](https://github.com/microsoft/GSL), which is available on all supported platforms. We recommend that you use your favorite package manager to install it prior to building and installing the IFC SDK from source. If you just want to build the project and try it without installing it, then there is no need to install the GSL, but in that case the build step is a bit more involved. 15 | 16 | On Windows platforms, we recommend using [vckpg](https://vcpkg.io/en/getting-started.html) as the C++ package manager. Issue 17 | ```sh 18 | $ ./vckpg install --triplet x64-windows ms-gsl 19 | ``` 20 | in the VCPKG directory, to install the GSL. The above command assumes you are building for the `x64` target. Replace that with the appropriate triplet if you are taregting a different platform like `arm64`. 21 | 22 | On linux platforms, there is no commonly agreed-upon name for that package. Here are a few common name for the Microsoft implementation of the GSL on some linux distributions if you're using the system package manager: 23 | 24 | - arch: `microsoft-gsl` 25 | - debian: `ms-gsl` 26 | - fedora: `guidelines-support-library` 27 | - gentoo: `dev-cpp/ms-gsl` 28 | - ubuntu: `ms-gsl` 29 | 30 | Please, consult your linux distribution manual to find the appropriate package name. 31 | 32 | ## Build 33 | 34 | Here are the steps for building in release mode with a single-configuration 35 | generator, as with Unix Makefiles: 36 | 37 | ```sh 38 | cmake -B build -D CMAKE_BUILD_TYPE=Release 39 | cmake --build build 40 | ``` 41 | 42 | Here are the steps for building in release mode with a multi-configuration 43 | generator, as with MSBuild projects: 44 | 45 | ```sh 46 | cmake -B build 47 | cmake --build build --config Release 48 | ``` 49 | 50 | If you are using vcpkg as your C++ package manager, you might need to add `-DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/vcpkg.cmake` to the CMake command in the configuration step. 51 | 52 | ### Unit Testing 53 | 54 | For configuring with testing enabled (only MSVC is enabled for now): 55 | 56 | ```sh 57 | cmake -B build --preset=test-msvc 58 | cmake --build build/test 59 | cd build 60 | ctest -C debug test 61 | ``` 62 | 63 | ### Using vcpkg (for testing) 64 | 65 | This project depends on [`doctest`](https://github.com/doctest/doctest) for validating the SDK. We recommend using [`vcpkg`](https://vcpkg.io) for managing this dependency. This project does not provide a [`builtin-baseline`](https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-json#builtin-baseline) in the `vcpkg.json` intentionally so that system dependencies can be relied on. If you are not using `vcpkg` in [classic mode](https://learn.microsoft.com/en-us/vcpkg/users/classic-mode) then you must introduce your own baseline (either through [`vcpkg x-update-baseline`](https://learn.microsoft.com/en-us/vcpkg/commands/update-baseline)) or add a custom [`vcpkg-configuration.json`](https://learn.microsoft.com/en-us/vcpkg/reference/vcpkg-configuration-json). Here's an example of using the `x-update-baseline` method: 66 | 67 | ```sh 68 | vcpkg x-update-baseline --add-initial-baseline 69 | cmake -B build -DCMAKE_TOOLCHAIN_FILE=/vcpkg.cmake --preset=test-msvc 70 | ``` 71 | 72 | Note: Using this method will locally modify `/vcpkg.json` so be sure to revert it or exclude it from your commit before submitting any change for PR. 73 | 74 | On Linux platforms please consult your package manager for alternative distributions of the C++ `doctest` framework. Some common alternatives: 75 | 76 | - arch: `doctest` 77 | - debian: `doctest-dev` 78 | - fedora: `doctest` 79 | - gentoo: `dev-cpp/doctest` 80 | - ubuntu: `doctest-dev` 81 | 82 | When using an alternative source for packages be sure to consult your distribution documentation on how to make that package available for CMake. 83 | 84 | ## Install 85 | 86 | Here is the command for installing the release mode artifacts with a 87 | single-configuration generator: 88 | 89 | ```sh 90 | cmake --install build 91 | ``` 92 | 93 | Here is the command for installing the release mode artifacts with a 94 | multi-configuration generator: 95 | 96 | ```sh 97 | cmake --install build --config Release 98 | ``` 99 | 100 | ### CMake package 101 | 102 | This project exports a CMake package to be used with the [`find_package`][2] 103 | command of CMake: 104 | 105 | * Package name: `Microsoft.IFC` 106 | * Target names: 107 | * `Microsoft.IFC::Core` 108 | * `Microsoft.IFC::DOM` 109 | * `Microsoft.IFC::SDK` - it's recommended to use this target 110 | 111 | Example usage: 112 | 113 | ```cmake 114 | find_package(Microsoft.IFC REQUIRED) 115 | # Declare the imported target as a build requirement using PRIVATE, where 116 | # project_target is a target created in the consuming project 117 | target_link_libraries( 118 | project_target PRIVATE 119 | Microsoft.IFC::SDK 120 | ) 121 | ``` 122 | 123 | [1]: https://cmake.org/cmake/help/latest/manual/cmake.1.html#install-a-project 124 | [2]: https://cmake.org/cmake/help/latest/command/find_package.html 125 | -------------------------------------------------------------------------------- /src/tools/ifc.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef WIN32 12 | # include 13 | #endif 14 | 15 | #include "ifc/file.hxx" 16 | #include "ifc/tooling.hxx" 17 | 18 | #ifdef WIN32 19 | # define STR(S) L ## S 20 | # define IFC_MAIN wmain 21 | # define IFC_OUT std::wcout 22 | # define IFC_ERR std::wcerr 23 | #else 24 | # define STR(S) S 25 | # define IFC_MAIN main 26 | # define IFC_OUT std::cout 27 | # define IFC_ERR std::cerr 28 | #endif 29 | 30 | namespace { 31 | using ProgramName = ifc::tool::StringView; 32 | 33 | // This predicate holds if the argument starts with a dash. 34 | bool resemble_option(const ifc::tool::StringView& s) 35 | { 36 | return s.starts_with(STR("-")); 37 | } 38 | 39 | // -- Print a brief message of how to invoke the ifc tool. 40 | void print_usage(const ifc::fs::path& prog) 41 | { 42 | auto name = prog.stem(); 43 | IFC_ERR << name << STR(" usage:\n\t") 44 | << name.native() << STR(" [options] \n"); 45 | } 46 | 47 | // -- Check that the input file has a valid IFC file header signature. 48 | bool validate_ifc_signature(std::ifstream& file) 49 | { 50 | constexpr auto sz = sizeof ifc::InterfaceSignature; 51 | std::array sig { }; 52 | if (not file.read(reinterpret_cast(sig.data()), sz)) 53 | return false; 54 | return std::memcmp(sig.data(), std::begin(ifc::InterfaceSignature), sz) == 0; 55 | } 56 | 57 | // -- Subcommand printing the Spec version from an IFC file. 58 | struct VersionCommand : ifc::tool::Extension { 59 | ifc::tool::Name name() const final { return STR("version"); } 60 | int run_with(const ifc::tool::Arguments& args) const final 61 | { 62 | int error_count = 0; 63 | for (auto& arg : args) 64 | { 65 | if (resemble_option(arg)) 66 | { 67 | IFC_ERR << STR("invalid option ") << arg 68 | << STR(" to ifc subcommand ") 69 | << name() << std::endl; 70 | ++error_count; 71 | continue; 72 | } 73 | ifc::fs::path path{arg}; 74 | std::ifstream file{path, std::ios_base::binary}; 75 | if (not file) 76 | { 77 | IFC_ERR << arg << STR(": couldn't open file") << std::endl; 78 | ++error_count; 79 | continue; 80 | } 81 | if (not validate_ifc_signature(file)) 82 | { 83 | IFC_ERR << arg << STR(" is not an IFC file") << std::endl; 84 | ++error_count; 85 | continue; 86 | } 87 | ifc::Header header {}; 88 | if (not file.read(reinterpret_cast(&header), sizeof header)) 89 | { 90 | IFC_ERR << arg << STR(" is truncated or corrupted") << std::endl; 91 | ++error_count; 92 | continue; 93 | } 94 | IFC_OUT << arg << STR(":\n\tversion: ") 95 | << static_cast(std::to_underlying(header.version.major)) 96 | << STR(".") 97 | << static_cast(std::to_underlying(header.version.minor)) 98 | << std::endl; 99 | } 100 | return error_count; 101 | } 102 | }; 103 | 104 | constexpr VersionCommand version_cmd { }; 105 | 106 | // -- List of all builtin subcommands, sorted by their name. 107 | constexpr const ifc::tool::Extension* builtin_extensions[] { 108 | &version_cmd, 109 | }; 110 | static_assert(std::ranges::is_sorted(builtin_extensions, { }, &ifc::tool::Extension::name)); 111 | 112 | const ifc::tool::Extension* builtin_operation(const ifc::tool::Name& cmd) 113 | { 114 | auto ext = std::ranges::lower_bound(builtin_extensions, cmd, { }, &ifc::tool::Extension::name); 115 | if (ext < std::end(builtin_extensions) and (*ext)->name() == cmd) 116 | return *ext; 117 | return nullptr; 118 | } 119 | 120 | // Enclose the argument in double quotes. 121 | ifc::tool::String quote(const ifc::tool::StringView& s) 122 | { 123 | static constexpr auto kwote_str = STR("\""); 124 | ifc::tool::String r = kwote_str; 125 | r += s; 126 | r += kwote_str; 127 | return r; 128 | } 129 | } 130 | 131 | 132 | int IFC_MAIN(int argc, ifc::tool::NativeChar* argv[]) 133 | { 134 | // The `ifc` tool itself does not accept any option. 135 | int idx = 1; 136 | while (idx < argc) 137 | { 138 | ifc::tool::StringView s { argv[idx] }; 139 | if (not s.starts_with(STR("-"))) 140 | break; 141 | ++idx; 142 | } 143 | 144 | if (argc < 2 or idx > 1 or idx >= argc) 145 | { 146 | print_usage(argv[0]); 147 | return 1; 148 | } 149 | 150 | // The subcommand name is next, after possibly invalid options. 151 | // It shall not contain any path separator character. 152 | ifc::tool::Name cmd { argv[idx] }; 153 | if (cmd.contains(STR("/")[0]) or cmd.contains(STR("\\")[0])) 154 | { 155 | IFC_ERR << STR("ifc subcommand cannot contain pathname separator") << std::endl; 156 | return 1; 157 | } 158 | 159 | ifc::tool::Arguments args { argv + idx + 1, argv + argc }; 160 | 161 | if (auto op = builtin_operation(cmd)) 162 | return op->run_with(args); 163 | 164 | ifc::tool::String tool = STR("ifc-"); 165 | tool += cmd; 166 | ifc::tool::String command = quote(tool); 167 | for (auto& arg : args) 168 | { 169 | command += STR(" "); 170 | command += quote(arg); 171 | } 172 | 173 | #ifdef WIN32 174 | auto status = _wsystem(command.c_str()); 175 | #else 176 | auto status = std::system(command.c_str()); 177 | #endif 178 | if (status != 0) 179 | IFC_ERR << STR("ifc: no subcommand named '") << cmd << STR("'") << std::endl; 180 | return status; 181 | } 182 | 183 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/types.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class FundamentalType { 5 | static partition_name = "type.fundamental"; 6 | 7 | constructor(reader) { 8 | this.basis = new TypeBasis(reader); 9 | this.precision = new TypePrecision(reader); 10 | this.sign = new TypeSign(reader); 11 | } 12 | } 13 | 14 | class DesignatedType { 15 | static partition_name = "type.designated"; 16 | 17 | constructor(reader) { 18 | this.decl = new DeclIndex(reader); 19 | } 20 | } 21 | 22 | class TorType { 23 | static partition_name = "type.tor"; 24 | 25 | constructor(reader) { 26 | this.source = new TypeIndex(reader); 27 | this.eh_spec = new NoexceptSpecification(reader); 28 | this.convention = new CallingConvention(reader); 29 | } 30 | } 31 | 32 | class SyntacticType { 33 | static partition_name = "type.syntactic"; 34 | 35 | constructor(reader) { 36 | this.expr = new ExprIndex(reader); 37 | } 38 | } 39 | 40 | class ExpansionType { 41 | static partition_name = "type.expansion"; 42 | 43 | constructor(reader) { 44 | this.operand = new TypeIndex(reader); 45 | this.mode = new ExpansionMode(reader); 46 | } 47 | } 48 | 49 | class PointerType { 50 | static partition_name = "type.pointer"; 51 | 52 | constructor(reader) { 53 | this.pointee = new TypeIndex(reader); 54 | } 55 | } 56 | 57 | class PointerToMemberType { 58 | static partition_name = "type.pointer-to-member"; 59 | 60 | constructor(reader) { 61 | this.scope = new TypeIndex(reader); 62 | this.type = new TypeIndex(reader); 63 | } 64 | } 65 | 66 | class LvalueReferenceType { 67 | static partition_name = "type.lvalue-reference"; 68 | 69 | constructor(reader) { 70 | this.referee = new TypeIndex(reader); 71 | } 72 | } 73 | 74 | class RvalueReferenceType { 75 | static partition_name = "type.rvalue-reference"; 76 | 77 | constructor(reader) { 78 | this.referee = new TypeIndex(reader); 79 | } 80 | } 81 | 82 | class FunctionType { 83 | static partition_name = "type.function"; 84 | 85 | constructor(reader) { 86 | this.target = new TypeIndex(reader); 87 | this.source = new TypeIndex(reader); 88 | this.eh_spec = new NoexceptSpecification(reader); 89 | this.convention = new CallingConvention(reader); 90 | this.traits = new FunctionTypeTraits(reader); 91 | } 92 | } 93 | 94 | class MethodType { 95 | static partition_name = "type.nonstatic-member-function"; 96 | 97 | constructor(reader) { 98 | this.target = new TypeIndex(reader); 99 | this.source = new TypeIndex(reader); 100 | this.class_type = new TypeIndex(reader); 101 | this.eh_spec = new NoexceptSpecification(reader); 102 | this.convention = new CallingConvention(reader); 103 | this.traits = new FunctionTypeTraits(reader); 104 | } 105 | } 106 | 107 | class ArrayType { 108 | static partition_name = "type.array"; 109 | 110 | constructor(reader) { 111 | this.element = new TypeIndex(reader); 112 | this.bound = new ExprIndex(reader); 113 | } 114 | } 115 | 116 | class TypenameType { 117 | static partition_name = "type.typename"; 118 | 119 | constructor(reader) { 120 | this.path = new ExprIndex(reader); 121 | } 122 | } 123 | 124 | class QualifiedType { 125 | static partition_name = "type.qualified"; 126 | 127 | constructor(reader) { 128 | this.unqualified_type = new TypeIndex(reader); 129 | this.qualifiers = new Qualifier(reader); 130 | } 131 | } 132 | 133 | class BaseType { 134 | static partition_name = "type.base"; 135 | 136 | constructor(reader) { 137 | this.type = new TypeIndex(reader); 138 | this.access = new Access(reader); 139 | this.traits = new BaseClassTraits(reader); 140 | } 141 | } 142 | 143 | class DecltypeType { 144 | static partition_name = "type.decltype"; 145 | 146 | constructor(reader) { 147 | this.expression = new SyntaxIndex(reader); 148 | } 149 | } 150 | 151 | class PlaceholderType { 152 | static partition_name = "type.placeholder"; 153 | 154 | constructor(reader) { 155 | this.constraint = new ExprIndex(reader); 156 | this.basis = new TypeBasis(reader); 157 | this.pad1 = new StructPadding(reader); 158 | this.pad2 = new StructPadding(reader); 159 | this.pad3 = new StructPadding(reader); 160 | this.elaboration = new TypeIndex(reader); 161 | } 162 | } 163 | 164 | class TupleType { 165 | static partition_name = "type.tuple"; 166 | 167 | constructor(reader) { 168 | this.seq = new HeapSequence(HeapSort.Values.Type, reader); 169 | } 170 | } 171 | 172 | class ForallType { 173 | static partition_name = "type.forall"; 174 | 175 | constructor(reader) { 176 | this.chart = new ChartIndex(reader); 177 | this.subject = new TypeIndex(reader); 178 | } 179 | } 180 | 181 | class UnalignedType { 182 | static partition_name = "type.unaligned"; 183 | 184 | constructor(reader) { 185 | this.type = new TypeIndex(reader); 186 | } 187 | } 188 | 189 | class SyntaxTreeType { 190 | static partition_name = "type.syntax-tree"; 191 | 192 | constructor(reader) { 193 | this.syntax = new SyntaxIndex(reader); 194 | } 195 | } 196 | 197 | function symbolic_for_type_sort(sort) { 198 | switch (sort) { 199 | case TypeIndex.Sort.Fundamental: 200 | return FundamentalType; 201 | case TypeIndex.Sort.Designated: 202 | return DesignatedType; 203 | case TypeIndex.Sort.Tor: 204 | return TorType; 205 | case TypeIndex.Sort.Syntactic: 206 | return SyntacticType; 207 | case TypeIndex.Sort.Expansion: 208 | return ExpansionType; 209 | case TypeIndex.Sort.Pointer: 210 | return PointerType; 211 | case TypeIndex.Sort.PointerToMember: 212 | return PointerToMemberType; 213 | case TypeIndex.Sort.LvalueReference: 214 | return LvalueReferenceType; 215 | case TypeIndex.Sort.RvalueReference: 216 | return RvalueReferenceType; 217 | case TypeIndex.Sort.Function: 218 | return FunctionType; 219 | case TypeIndex.Sort.Method: 220 | return MethodType; 221 | case TypeIndex.Sort.Array: 222 | return ArrayType; 223 | case TypeIndex.Sort.Typename: 224 | return TypenameType; 225 | case TypeIndex.Sort.Qualified: 226 | return QualifiedType; 227 | case TypeIndex.Sort.Base: 228 | return BaseType; 229 | case TypeIndex.Sort.Decltype: 230 | return DecltypeType; 231 | case TypeIndex.Sort.Placeholder: 232 | return PlaceholderType; 233 | case TypeIndex.Sort.Tuple: 234 | return TupleType; 235 | case TypeIndex.Sort.Forall: 236 | return ForallType; 237 | case TypeIndex.Sort.Unaligned: 238 | return UnalignedType; 239 | case TypeIndex.Sort.SyntaxTree: 240 | return SyntaxTreeType; 241 | case TypeIndex.Sort.VendorExtension: 242 | default: 243 | console.error(`Bad sort: ${sort}`); 244 | return null; 245 | } 246 | } -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/resolve.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class Resolver { 5 | constructor(reader, toc, string_table) { 6 | this.reader = reader; 7 | this.toc = toc; 8 | this.string_table = string_table; 9 | this.msvc_traits = this.associated_trait_table(AssociatedMsvcTraits); 10 | } 11 | 12 | decl_index_in_bounds(index) { 13 | const symbolic = symbolic_for_decl_sort(index.sort); 14 | const partition = this.toc.partition(symbolic); 15 | return { in_bounds: index.index < partition.cardinality, partition_size: partition.cardinality }; 16 | } 17 | 18 | type_index_in_bounds(index) { 19 | const symbolic = symbolic_for_type_sort(index.sort); 20 | const partition = this.toc.partition(symbolic); 21 | return { in_bounds: index.index < partition.cardinality, partition_size: partition.cardinality }; 22 | } 23 | 24 | expr_index_in_bounds(index) { 25 | const symbolic = symbolic_for_expr_sort(index.sort); 26 | const partition = this.toc.partition(symbolic); 27 | return { in_bounds: index.index < partition.cardinality, partition_size: partition.cardinality }; 28 | } 29 | 30 | read(T, index) { 31 | const partition = this.toc.partition(T); 32 | const offset = partition.tell(index); 33 | this.reader.seek(offset); 34 | return new T(this.reader); 35 | } 36 | 37 | offset_read(T, off) { 38 | this.reader.seek(off); 39 | return new T(this.reader); 40 | } 41 | 42 | resolve_line_index(index) { 43 | return this.read(FileAndLine, index); 44 | } 45 | 46 | resolve_source_file(index) { 47 | return this.read(SourceFileName, index.index); 48 | } 49 | 50 | resolve_operator_id(index) { 51 | return this.read(OperatorFunctionId, index.index); 52 | } 53 | 54 | resolve_conversion_id(index) { 55 | return this.read(ConversionFunctionId, index.index); 56 | } 57 | 58 | resolve_name_index(index) { 59 | switch (index.sort) { 60 | case NameIndex.Sort.Identifier: 61 | return this.string_table.get(index.index); 62 | case NameIndex.Sort.Operator: 63 | return this.resolve_text_offset(this.resolve_operator_id(index).name); 64 | case NameIndex.Sort.Conversion: 65 | return this.resolve_text_offset(this.resolve_conversion_id(index).name); 66 | case NameIndex.Sort.Literal: 67 | case NameIndex.Sort.Template: 68 | case NameIndex.Sort.Specialization: 69 | case NameIndex.Sort.SourceFile: 70 | return this.string_table.get(this.resolve_source_file(index).name); 71 | case NameIndex.Sort.Guide: 72 | } 73 | return ""; 74 | } 75 | 76 | resolve_text_offset(offset) { 77 | return this.string_table.get(offset.offset); 78 | } 79 | 80 | resolve_identity(identity) { 81 | if (identity instanceof IdentityTextOffset) { 82 | return { name: this.resolve_text_offset(identity.name), locus: new ResolvedLocus(identity.locus, this) }; 83 | } 84 | 85 | if (identity instanceof IdentityNameIndex) { 86 | return { name: this.resolve_name_index(identity.name), locus: new ResolvedLocus(identity.locus, this) }; 87 | } 88 | } 89 | 90 | resolve_lit_index(index) { 91 | if (index.sort == LitIndex.Sort.Immediate) 92 | return new ImmediateLiteral(index); 93 | const symbolic = symbolic_for_lit_sort(index.sort); 94 | const literal = this.read(symbolic, index.index); 95 | return literal; 96 | } 97 | 98 | resolve_chart_index(index) { 99 | const symbolic = symbolic_for_chart_sort(index.sort); 100 | const chart = this.read(symbolic, index.index); 101 | return chart; 102 | } 103 | 104 | decls_for_scope(index) { 105 | // Note: ScopeIndex is a 1-based index but stored in the IFC as 0-based. 106 | const scope = this.read(Scope, index.value - 1); 107 | const partition = this.toc.partition(Declaration); 108 | var offset = partition.tell(scope.seq.start); 109 | var decls = new Array(); 110 | for (var i = 0; i < scope.seq.cardinality; ++i) { 111 | decls.push(this.offset_read(Declaration, offset)); 112 | offset += partition.entry_size; 113 | } 114 | return decls; 115 | } 116 | 117 | decls_for_enumeration(seq) { 118 | const partition = this.toc.partition(EnumeratorDecl); 119 | var offset = partition.tell(seq.start); 120 | var decls = new Array(); 121 | for (var i = 0; i < seq.cardinality; ++i) { 122 | var rel_index = (offset - partition.offset) / partition.entry_size; 123 | var index = { sort: DeclIndex.Sort.Enumerator, index: rel_index }; 124 | var decl = { decl: index }; 125 | decls.push(decl); 126 | offset += partition.entry_size; 127 | } 128 | return decls; 129 | } 130 | 131 | fundamental_type_is_class(decl) { 132 | if (decl.type.sort != TypeIndex.Sort.Fundamental) 133 | return false; 134 | const fun_type = this.read(FundamentalType, decl.type.index); 135 | switch (fun_type.basis.value) { 136 | case TypeBasis.Values.Class: 137 | case TypeBasis.Values.Struct: 138 | case TypeBasis.Values.Union: 139 | return true; 140 | default: 141 | return false; 142 | } 143 | } 144 | 145 | resolve_heap(heap_seq) { 146 | if (!heap_seq.Heap.T) 147 | return undefined; 148 | const partition = this.toc.partition_by_name(heap_seq.Heap.heap); 149 | var offset = partition.tell(heap_seq.start); 150 | var elms = new Array(); 151 | for (var i = 0; i < heap_seq.cardinality; ++i) { 152 | var e = this.offset_read(heap_seq.Heap.T, offset); 153 | elms.push(e); 154 | offset += partition.entry_size; 155 | } 156 | return elms; 157 | } 158 | 159 | resolve_sequence(seq) { 160 | const partition = this.toc.partition_by_name(seq.T.partition_name); 161 | var offset = partition.tell(seq.start); 162 | var elms = new Array(); 163 | for (var i = 0; i < seq.cardinality; ++i) { 164 | var e = this.offset_read(seq.T, offset); 165 | elms.push(e); 166 | offset += partition.entry_size; 167 | } 168 | return elms; 169 | } 170 | 171 | resolve_spec_form(form) { 172 | const partition = this.toc.partition_by_name(form.T.partition_name); 173 | return this.read(form.T, form.value); 174 | } 175 | 176 | associated_trait_table(T) { 177 | const partition = this.toc.partition_by_name(T.partition_name); 178 | var elms = new Array(); 179 | if (partition == undefined) 180 | return elms; 181 | var offset = partition.tell(0); 182 | for (var i = 0; i < partition.cardinality; ++i) { 183 | var k = this.offset_read(T.K, offset); 184 | var v = new T.V(this.reader); 185 | elms.push({ key: k, value: v }); 186 | offset += partition.entry_size; 187 | } 188 | return elms; 189 | } 190 | 191 | associated_trait_index(key, table) { 192 | var idx = lower_bound(table, e => [...Object.values(key)] <= [...Object.values(e.key)]); 193 | if (idx < table.length && [...Object.values(key)].equal([...Object.values(table[idx].key)])) { 194 | return table[idx].value; 195 | } 196 | return undefined; 197 | } 198 | } 199 | 200 | class ResolvedLocus { 201 | constructor(locus, resolver) { 202 | var file_and_line = resolver.resolve_line_index(locus.line); 203 | this.file = resolver.resolve_name_index(file_and_line.file); 204 | this.line = file_and_line.line; 205 | this.column = locus.column; 206 | } 207 | } -------------------------------------------------------------------------------- /src/sha256.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | // Cross-platform Implementation of SHA256 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace { 16 | // Defined values of K for SHA-256 17 | constexpr uint32_t K[64] = { 18 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 19 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 20 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 21 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 22 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 23 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 24 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 25 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; 26 | 27 | // Initial hash values for SHA-256 28 | constexpr std::array initial_hash_values{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 29 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; 30 | 31 | // Various functions performing operations as defined by SHA-256. 32 | constexpr uint32_t ch(uint32_t x, uint32_t y, uint32_t z) 33 | { 34 | return (x & y) ^ ((~x) & z); 35 | } 36 | 37 | constexpr uint32_t maj(uint32_t x, uint32_t y, uint32_t z) 38 | { 39 | return (x & y) ^ (x & z) ^ (y & z); 40 | } 41 | 42 | uint32_t constexpr SIGMA0(uint32_t w) 43 | { 44 | return std::rotr(w, 2) xor std::rotr(w, 13) xor std::rotr(w, 22); 45 | } 46 | 47 | uint32_t constexpr SIGMA1(uint32_t w) 48 | { 49 | return std::rotr(w, 6) xor std::rotr(w, 11) xor std::rotr(w, 25); 50 | } 51 | 52 | uint32_t constexpr sigma0(uint32_t w) 53 | { 54 | return std::rotr(w, 7) xor std::rotr(w, 18) xor (w >> 3); 55 | } 56 | 57 | uint32_t constexpr sigma1(uint32_t w) 58 | { 59 | return std::rotr(w, 17) xor std::rotr(w, 19) xor (w >> 10); 60 | } 61 | 62 | constexpr void process_chunk(std::array& hash, const std::byte* p) 63 | { 64 | // w is the message schedule array. 65 | uint32_t w[64] = {0}; 66 | for (int i = 0, j = 0; i < 16; ++i, j += 4) 67 | { 68 | // Can't do a memcpy and then change_endianness as memcpy is not constexpr. 69 | uint32_t b0, b1, b2, b3; 70 | if constexpr (std::endian::native == std::endian::little) 71 | { 72 | b0 = static_cast(p[j]) << 24; 73 | b1 = static_cast(p[j + 1]) << 16; 74 | b2 = static_cast(p[j + 2]) << 8; 75 | b3 = static_cast(p[j + 3]); 76 | } 77 | else 78 | { 79 | b0 = static_cast(p[j]); 80 | b1 = static_cast(p[j + 1]) << 8; 81 | b2 = static_cast(p[j + 2]) << 16; 82 | b3 = static_cast(p[j + 3]) << 24; 83 | } 84 | w[i] = b0 | b1 | b2 | b3; 85 | } 86 | 87 | for (int i = 16; i < 64; ++i) 88 | w[i] = sigma1(w[i - 2]) + w[i - 7] + sigma0(w[i - 15]) + w[i - 16]; 89 | 90 | auto a = hash[0]; 91 | auto b = hash[1]; 92 | auto c = hash[2]; 93 | auto d = hash[3]; 94 | auto e = hash[4]; 95 | auto f = hash[5]; 96 | auto g = hash[6]; 97 | auto h = hash[7]; 98 | 99 | for (size_t i = 0; i < 64; ++i) 100 | { 101 | uint32_t T1 = h + SIGMA1(e) + ch(e, f, g) + K[i] + w[i]; 102 | uint32_t T2 = SIGMA0(a) + maj(a, b, c); 103 | 104 | h = g; 105 | g = f; 106 | f = e; 107 | e = d + T1; 108 | d = c; 109 | c = b; 110 | b = a; 111 | a = T1 + T2; 112 | } 113 | 114 | hash[0] += a; 115 | hash[1] += b; 116 | hash[2] += c; 117 | hash[3] += d; 118 | hash[4] += e; 119 | hash[5] += f; 120 | hash[6] += g; 121 | hash[7] += h; 122 | } 123 | 124 | // convert between little-endian/big-endian 125 | constexpr void change_endianness(uint32_t& v) 126 | { 127 | // TODO use std::byteswap when C++23 targeted. 128 | v = ((v & 0xff000000) >> 24) | ((v & 0xff0000) >> 8) | ((v & 0xff00) << 8) | ((v & 0xff) << 24); 129 | } 130 | 131 | constexpr ifc::SHA256Hash sha256(gsl::span span) 132 | { 133 | ifc::SHA256Hash hash = {initial_hash_values}; 134 | 135 | const auto length = span.size(); 136 | const auto base = span.data(); 137 | // Process whole chunks first 138 | const auto iterations = length / 64; 139 | for (size_t iter = 0; iter < iterations; ++iter) 140 | process_chunk(hash.value, base + iter * 64); 141 | 142 | /* 143 | append a single '1' bit 144 | append K '0' bits, where K is the minimum number >= 0 such that (L + 1 + K + 64) is a multiple of 512 145 | append L as a 64-bit big-endian integer, making the total post-processed length a multiple of 512 bits 146 | This requires at least 9 bytes. If the remainder is greater than 55, a second extra chunk is needed. 147 | */ 148 | 149 | // remainder is number of bytes in message past 64byte boundary (could be zero) 150 | const auto remainder = length % 64; 151 | 152 | // Room for one or two chunks (as needed) 153 | std::array v {}; // Fill with zeros 154 | std::copy(base + iterations * 64, base + iterations * 64 + remainder, v.begin()); 155 | v[remainder] = std::byte{0x80}; // Put 10000000 after data 156 | // The data, the "1" bit, and zero bits are now in place. 157 | 158 | // Put total number of bits in message at end. 159 | const uint64_t total_bits = length * 8; 160 | if (remainder > 55) // Need the second block 161 | { 162 | // Put number of bits at end of second block. 163 | constexpr auto bits_count_offset = v.size() - sizeof(uint64_t); 164 | std::byte* pbits = v.data() + bits_count_offset; 165 | for (int i = 0; i < 8; ++i) 166 | { 167 | uint8_t byte = static_cast(total_bits >> (7 - i) * 8); 168 | *pbits++ = std::byte{byte}; 169 | } 170 | process_chunk(hash.value, v.data()); 171 | process_chunk(hash.value, v.data() + 64); 172 | } 173 | else 174 | { 175 | // Put number of bits at end of first block. 176 | constexpr auto bits_count_offset = v.size()/2 - sizeof(uint64_t); 177 | std::byte* pbits = v.data() + bits_count_offset; 178 | for (int i = 0; i < 8; ++i) 179 | { 180 | uint8_t byte = static_cast(total_bits >> (7 - i) * 8); 181 | *pbits++ = std::byte{byte}; 182 | } 183 | process_chunk(hash.value, v.data()); 184 | } 185 | 186 | if constexpr (std::endian::native == std::endian::little) 187 | { 188 | // Convert hash bytes to proper endianness 189 | for (auto& i : hash.value) 190 | change_endianness(i); 191 | } 192 | 193 | return hash; 194 | } 195 | 196 | #ifndef NDEBUG 197 | // Statically verify the hash is correct with two simple tests. 198 | 199 | // "", "E3B0C442 98FC1C14 9AFBF4C8 996FB924 27AE41E4 649B934C A495991B 7852B855"); 200 | constexpr const std::byte test1[1] = {}; 201 | constexpr ifc::SHA256Hash hash1 = {0x42C4B0E3, 0x141CFC98, 0xC8F4FB9A, 0x24B96F99, 202 | 0xE441AE27, 0x4C939B64, 0x1B9995A4, 0x55B85278}; 203 | static_assert(sha256(gsl::span(test1, test1)).value == hash1.value); 204 | 205 | // "a" "CA978112 CA1BBDCA FAC231B3 9A23DC4D A786EFF8 147C4E72 B9807785 AFEE48BB"); 206 | constexpr const std::byte test2[1] = {std::byte{'a'}}; 207 | constexpr ifc::SHA256Hash hash2 = {0x128197CA, 0xCABD1BCA, 0xB331C2FA, 0x4DDC239A, 208 | 0xF8EF86A7, 0x724E7C14, 0x857780B9, 0xBB48EEAF}; 209 | static_assert(sha256(gsl::span(test2, 1)).value == hash2.value); 210 | #endif 211 | } // namespace 212 | 213 | namespace ifc { 214 | SHA256Hash hash_bytes(const std::byte* first, const std::byte* last) 215 | { 216 | gsl::span span(first, last); 217 | return sha256(span); 218 | } 219 | } // namespace ifc 220 | -------------------------------------------------------------------------------- /samples/sgraph-js/sgraph/io.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class RawByteReader { 5 | constructor(bytes) { 6 | this.bytes = bytes; 7 | this.offset = 0; 8 | } 9 | 10 | read_uint8() { 11 | const byte = this.bytes[this.offset]; 12 | this.offset += 1; 13 | return byte; 14 | } 15 | 16 | read_uint16() { 17 | const array8 = new Uint8Array([ this.read_uint8(), 18 | this.read_uint8() ]); 19 | const array16 = new Uint16Array(array8.buffer); 20 | const uint16 = array16[0]; 21 | return uint16; 22 | } 23 | 24 | read_uint32() { 25 | const array8 = new Uint8Array([ this.read_uint8(), 26 | this.read_uint8(), 27 | this.read_uint8(), 28 | this.read_uint8() ]); 29 | const array32 = new Uint32Array(array8.buffer); 30 | const uint32 = array32[0]; 31 | return uint32; 32 | } 33 | 34 | read_null_terminated_string() { 35 | var array = new Array(); 36 | for (var byte = this.read_uint8(); byte != 0; byte = this.read_uint8()) { 37 | array.push(byte); 38 | } 39 | return String.fromCharCode.apply(String, array); 40 | } 41 | 42 | static bit_width(n) { 43 | return n.toString(2).length; 44 | } 45 | 46 | read_index_bitfield(sort_count) { 47 | var sort_size = RawByteReader.bit_width(sort_count - 1); 48 | var index = this.read_uint32(); 49 | var mask = (1 << sort_size) - 1; 50 | var sort = (index & mask); 51 | index = (index & ~mask) >> sort_size; 52 | return { sort: sort, index: index }; 53 | } 54 | 55 | read_index16_bitfield(sort_count) { 56 | var sort_size = RawByteReader.bit_width(sort_count - 1); 57 | var index = this.read_uint16(); 58 | var mask = (1 << sort_size) - 1; 59 | var sort = (index & mask); 60 | index = (index & ~mask) >> sort_size; 61 | return { sort: sort, index: index }; 62 | } 63 | 64 | seek(off) { 65 | this.offset = off; 66 | } 67 | } 68 | 69 | class Signature { 70 | constructor(reader) { 71 | this.A = reader.read_uint8(); 72 | this.B = reader.read_uint8(); 73 | this.C = reader.read_uint8(); 74 | this.D = reader.read_uint8(); 75 | } 76 | 77 | valid() { 78 | const valid = [ 0x54, 0x51, 0x45, 0x1A ]; 79 | if (this.A != valid[0]) 80 | return false; 81 | if (this.B != valid[1]) 82 | return false; 83 | if (this.C != valid[2]) 84 | return false; 85 | if (this.D != valid[3]) 86 | return false; 87 | return true; 88 | } 89 | } 90 | 91 | class SHA256 { 92 | constructor(reader) { 93 | this.hash = new Uint32Array([ reader.read_uint32(), 94 | reader.read_uint32(), 95 | reader.read_uint32(), 96 | reader.read_uint32(), 97 | reader.read_uint32(), 98 | reader.read_uint32(), 99 | reader.read_uint32(), 100 | reader.read_uint32() ]); 101 | } 102 | 103 | as_string() { 104 | const uint8_data = new Uint8Array(this.hash.buffer); 105 | const str = uint8_data.map(x => x.toString(16).padStart(2, '0')).join(''); 106 | return str; 107 | } 108 | } 109 | 110 | class Version { 111 | constructor(major, minor) { 112 | this.major = major; 113 | this.minor = minor; 114 | } 115 | } 116 | 117 | function read_version(reader) { 118 | var major = reader.read_uint8(); 119 | var minor = reader.read_uint8(); 120 | return new Version(major, minor); 121 | } 122 | 123 | class Abi { 124 | constructor(reader) { 125 | this.abi = reader.read_uint8(); 126 | } 127 | } 128 | 129 | class Architecture { 130 | static Kind = { 131 | Unknown: 0, 132 | X86: 1, 133 | X64: 2, 134 | ARM32: 3, 135 | ARM64: 4, 136 | HybridX86ARM64 : 5 137 | }; 138 | 139 | constructor(reader) { 140 | this.arch = reader.read_uint8(); 141 | } 142 | 143 | kind() { 144 | var k = ""; 145 | switch (this.arch) { 146 | case Architecture.Kind.Unknown: 147 | k = "unknown"; 148 | break; 149 | case Architecture.Kind.X86: 150 | k = "x86"; 151 | break; 152 | case Architecture.Kind.X64: 153 | k = "x64"; 154 | break; 155 | case Architecture.Kind.ARM32: 156 | k = "arm"; 157 | break; 158 | case Architecture.Kind.ARM64: 159 | k = "arm64"; 160 | break; 161 | case Architecture.Kind.HybridX86ARM64: 162 | k = "chpe"; 163 | break; 164 | } 165 | return k; 166 | } 167 | } 168 | 169 | class CPlusPlus { 170 | constructor(reader) { 171 | this.cplusplus = reader.read_uint32(); 172 | } 173 | } 174 | 175 | class StringTable { 176 | constructor(reader, offset, size) { 177 | this.strings = new Map(); 178 | reader.seek(offset); 179 | while ((reader.offset - offset) < size) { 180 | var key = reader.offset - offset; 181 | var str = reader.read_null_terminated_string(); 182 | this.strings.set(key, str); 183 | //console.log("string_table[", key, "] =", str); // More logging if necessary. 184 | } 185 | //console.log("String table built. Byte offset =", offset, " size =", size, " iterated =", (reader.offset - offset)); // More logging if necessary. 186 | } 187 | 188 | get(offset) { 189 | return this.strings.get(offset); 190 | } 191 | } 192 | 193 | class UnitIndex { 194 | static Sort = { 195 | Source: 0, 196 | Primary: 1, 197 | Partition: 2, 198 | Header: 3, 199 | ExportedTU: 4, 200 | Count: 5 201 | }; 202 | 203 | constructor(reader) { 204 | var index = reader.read_index_bitfield(UnitIndex.Sort.Count); 205 | this.sort = index.sort; 206 | this.index = index.index; 207 | } 208 | 209 | ifc_designator() { 210 | return this.index; 211 | } 212 | } 213 | 214 | function null_scope(index) { 215 | return index.value == 0; 216 | } 217 | 218 | class ScopeIndex { 219 | constructor(reader) { 220 | this.value = reader.read_uint32(); 221 | } 222 | } 223 | 224 | class Header { 225 | constructor(reader) { 226 | this.signature = new Signature(reader); 227 | if (!this.signature.valid()) 228 | return; 229 | this.content_hash = new SHA256(reader); 230 | this.version = read_version(reader); 231 | this.abi = new Abi(reader); 232 | this.arch = new Architecture(reader); 233 | this.cplusplus = new CPlusPlus(reader); 234 | this.string_table_offset = reader.read_uint32(); 235 | this.string_table_size = reader.read_uint32(); 236 | this.unit_sort = new UnitIndex(reader); 237 | this.src_path = reader.read_uint32(); 238 | this.scope_index = new ScopeIndex(reader); 239 | this.toc = reader.read_uint32(); 240 | this.partition_count = reader.read_uint32(); 241 | this.internal_partition = reader.read_uint8(); 242 | } 243 | 244 | valid() { 245 | if (!this.signature.valid()) 246 | return false; 247 | return true; 248 | } 249 | } 250 | 251 | class Partition { 252 | constructor(reader) { 253 | this.name = reader.read_uint32(); 254 | this.offset = reader.read_uint32(); 255 | this.cardinality = reader.read_uint32(); 256 | this.entry_size = reader.read_uint32(); 257 | } 258 | 259 | tell(index) { 260 | return this.offset + index * this.entry_size; 261 | } 262 | 263 | byte_count() { 264 | return this.cardinality * this.entry_size; 265 | } 266 | } 267 | 268 | class ToC { 269 | constructor(reader, header, string_table) { 270 | this.partitions = new Array(); 271 | reader.seek(header.toc); 272 | for (var i = 0; i < header.partition_count; ++i) { 273 | this.partitions.push(new Partition(reader)); 274 | } 275 | this._classify_partitions(string_table); 276 | } 277 | 278 | _classify_partitions(string_table) { 279 | this.partition_by_name_map = new Map(); 280 | for (var i = 0; i < this.partitions.length; ++i) { 281 | this._classify_partition(this.partitions[i], string_table); 282 | } 283 | } 284 | 285 | _classify_partition(p, string_table) { 286 | this.partition_by_name_map.set(string_table.get(p.name), p); 287 | } 288 | 289 | partition_by_name(name) { 290 | return this.partition_by_name_map.get(name); 291 | } 292 | 293 | partition(T) { 294 | return this.partition_by_name(T.partition_name); 295 | } 296 | } -------------------------------------------------------------------------------- /include/ifc/dom/node.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | // We load the ifc into in memory "dom" format to allow simpler processing 5 | // for text printing, graphing in a dot format, etc. 6 | // 7 | // The main abstraction is a node that has a collection of child nodes 8 | // (where tree form if possible) and properties that are simple string. 9 | // 10 | // Loader tries to preserve the tree form where possible and keep track of references 11 | // to be resolved later. Given an index, loader can be asked for a node 12 | // via Node& get(index) which will load and populate the node and all of it children 13 | // and properties or via std::string ref(index) that will either produce 14 | // a reference string to a node (and remember that a node was referenced to 15 | // be dealt with later) or give a shorthand string when possible, 16 | // such as simple literal or a type 'int' or 'int*" . 17 | 18 | #ifndef IFC_UTIL_NODE_H 19 | #define IFC_UTIL_NODE_H 20 | 21 | #include "ifc/reader.hxx" 22 | #include "ifc/util.hxx" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace ifc::util { 34 | enum class SortKind : std::uint16_t { 35 | Expr, 36 | Decl, 37 | Type, 38 | Name, 39 | Scope, 40 | Sentence, 41 | Chart, 42 | Syntax, 43 | Stmt, 44 | }; 45 | 46 | inline std::string to_string(SortKind kind) 47 | { 48 | switch (kind) 49 | { 50 | case SortKind::Expr: 51 | return "expr"; 52 | case SortKind::Decl: 53 | return "decl"; 54 | case SortKind::Type: 55 | return "type"; 56 | case SortKind::Name: 57 | return "name"; 58 | case SortKind::Scope: 59 | return "scope"; 60 | case SortKind::Sentence: 61 | return "sentence"; 62 | case SortKind::Chart: 63 | return "chart"; 64 | case SortKind::Syntax: 65 | return "syntax"; 66 | case SortKind::Stmt: 67 | return "stmt"; 68 | default: 69 | return "unexpected-sort-kind-" + std::to_string(ifc::to_underlying(kind)); 70 | } 71 | } 72 | 73 | // clang-format off 74 | constexpr SortKind sort_kind(ExprIndex) { return SortKind::Expr;} 75 | constexpr SortKind sort_kind(DeclIndex) { return SortKind::Decl;} 76 | constexpr SortKind sort_kind(TypeIndex) { return SortKind::Type;} 77 | constexpr SortKind sort_kind(ScopeIndex) { return SortKind::Scope;} 78 | constexpr SortKind sort_kind(NameIndex) { return SortKind::Name;} 79 | constexpr SortKind sort_kind(SentenceIndex) { return SortKind::Sentence;} 80 | constexpr SortKind sort_kind(ChartIndex) { return SortKind::Chart;} 81 | constexpr SortKind sort_kind(StmtIndex) { return SortKind::Stmt;} 82 | constexpr SortKind sort_kind(SyntaxIndex) { return SortKind::Syntax;} 83 | constexpr SortKind sort_kind(symbolic::DefaultIndex) { return SortKind::Expr; } 84 | // clang-format on 85 | 86 | // Type-erased abstract index. 87 | struct NodeKey { 88 | // implicit construction intentional 89 | template 90 | NodeKey(T value) 91 | : index_kind(sort_kind(value)), index_sort(ifc::to_underlying(value.sort())), 92 | index_value(ifc::to_underlying(value.index())) 93 | {} 94 | 95 | // implicit construction intentional 96 | template 97 | NodeKey(T value) : index_kind(sort_kind(value)), index_sort(), index_value(ifc::to_underlying(value)) 98 | {} 99 | 100 | bool operator==(const NodeKey&) const = default; 101 | auto operator<=>(const NodeKey&) const = default; 102 | 103 | SortKind kind() const 104 | { 105 | return index_kind; 106 | } 107 | 108 | auto index() const 109 | { 110 | return index_value; 111 | } 112 | 113 | // convert back to abstract indices 114 | template 115 | decltype(auto) visit(F&& f); 116 | 117 | private: 118 | const SortKind index_kind; 119 | const std::uint16_t index_sort; 120 | const std::uint32_t index_value; 121 | }; 122 | 123 | template 124 | inline decltype(auto) NodeKey::visit(F&& f) 125 | { 126 | // clang-format off 127 | switch (index_kind) 128 | { 129 | case SortKind::Expr: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 130 | case SortKind::Decl: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 131 | case SortKind::Type: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 132 | case SortKind::Name: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 133 | case SortKind::Scope: return std::forward(f)(static_cast(index_value)); 134 | case SortKind::Sentence: return std::forward(f)(static_cast(index_value)); 135 | case SortKind::Chart: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 136 | case SortKind::Syntax: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 137 | case SortKind::Stmt: return std::forward(f)(index_like::make(static_cast(index_sort), index_value)); 138 | default: throw std::logic_error("unexpected SortKind-" + std::to_string(ifc::to_underlying(index_kind))); 139 | } 140 | // clang-format on 141 | } 142 | 143 | struct Node; 144 | 145 | using PropertyMap = std::map; 146 | using Nodes = std::vector; 147 | 148 | struct Node { 149 | const NodeKey key; 150 | std::string id; 151 | 152 | explicit Node(NodeKey key_) : key(key_) {} 153 | Node(const Node&) = delete; 154 | Node& operator=(const Node&) = delete; 155 | 156 | PropertyMap props; 157 | Nodes children; 158 | }; 159 | 160 | // Enumerators and parameters are represented in their sequences by 161 | // value, not by index, thus, the getter need to be aware of that. 162 | template 163 | concept EnumeratorOrParameterDecl = 164 | std::same_as or std::same_as; 165 | 166 | // Loader is responsible for loading the nodes and resolving references. 167 | // It is also hosts the storage for all the nodes. 168 | struct Loader { 169 | ifc::Reader& reader; 170 | 171 | explicit Loader(Reader& reader_) : reader(reader_) {} 172 | 173 | template 174 | const Node& get(Key abstract_index); 175 | template 176 | const Node& get(const T&); 177 | const Node& get(NodeKey); 178 | // Convenience helper that returns nullptr for ChartSort::None 179 | Node* try_get(ChartIndex); 180 | 181 | template 182 | std::string ref(T index); 183 | std::string ref(const symbolic::Identity& id); 184 | std::string ref(const symbolic::Identity& id); 185 | 186 | // Wrapper for reader.get() that returns empty string for null TextOffset 187 | std::string reader_get(TextOffset offset) const 188 | { 189 | return index_like::null(offset) ? "" : reader.get(offset); 190 | } 191 | 192 | std::set referenced_nodes; 193 | 194 | private: 195 | using NodeMap = std::map; 196 | NodeMap all_nodes; 197 | }; 198 | 199 | // implementation details 200 | 201 | void load(Loader& ctx, Node& node, ScopeIndex index); 202 | void load(Loader& ctx, Node& node, DeclIndex index); 203 | void load(Loader& ctx, Node& node, ExprIndex index); 204 | void load(Loader& ctx, Node& node, NameIndex index); 205 | void load(Loader& ctx, Node& node, TypeIndex index); 206 | void load(Loader& ctx, Node& node, SentenceIndex index); 207 | void load(Loader& ctx, Node& node, SyntaxIndex index); 208 | void load(Loader& ctx, Node& node, ChartIndex index); 209 | void load(Loader& ctx, Node& node, StmtIndex index); 210 | void load(Loader& ctx, Node& node, symbolic::DefaultIndex index); 211 | 212 | // Will return a nice short string for a type, such as "int*" if it can. 213 | // Empty string otherwise. 214 | std::string get_string_if_possible(Loader&, TypeIndex); 215 | std::string get_string_if_possible(Loader&, ExprIndex); 216 | 217 | template 218 | std::string get_string_if_possible(Loader&, Index) 219 | { 220 | return ""; 221 | } 222 | 223 | template 224 | std::string Loader::ref(T index) 225 | { 226 | if (null(index)) 227 | return "no-" + to_string(sort_kind(index)); 228 | 229 | if (auto str = get_string_if_possible(*this, index); not str.empty()) 230 | return str; 231 | 232 | if (not all_nodes.contains(index)) 233 | referenced_nodes.insert(index); 234 | 235 | return to_string(index); 236 | } 237 | 238 | template 239 | const Node& Loader::get(Key abstract_index) 240 | { 241 | NodeKey key(abstract_index); 242 | auto [it, inserted] = all_nodes.emplace(key, key); 243 | if (inserted) 244 | { 245 | load(*this, it->second, abstract_index); 246 | // if we referenced the node before we can remove it now. 247 | referenced_nodes.erase(key); 248 | } 249 | return it->second; 250 | } 251 | 252 | inline const Node& Loader::get(NodeKey key) 253 | { 254 | return key.visit([this](auto index) -> const Node& { return get(index); }); 255 | } 256 | 257 | template 258 | inline const Node& Loader::get(const T& decl) 259 | { 260 | const auto abstract_index = reader.index_of(decl); 261 | return get(abstract_index); 262 | } 263 | 264 | } // namespace ifc::util 265 | 266 | #endif // IFC_UTIL_NODE_H 267 | -------------------------------------------------------------------------------- /include/ifc/index-utils.hxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #ifndef IFC_INDEX_UTILS_INCLUDED 5 | #define IFC_INDEX_UTILS_INCLUDED 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // Provide support infrastructure for common operations for index-like types. 14 | // These are types that have the same representational characteristics as 15 | // the 32-bit unsigned integer type std::uint32_t. 16 | namespace index_like { 17 | // Useful for generating sentinel values for enumerations. Typically placeholder for null values. 18 | template 19 | inline constexpr auto sentinel_for = std::numeric_limits>::max(); 20 | 21 | // Check whether the type parameter is std::uint32_t. 22 | // In fact, it would be acceptable if values of that type 23 | // promotes to an integer type with rank less than std::uint32_t. This concept 24 | // is useful mainly to guard against the anarchic implicit conversions 25 | // between builtin types. It is a best-effort attempt to prevent value truncation. 26 | template 27 | concept Uint32 = std::same_as; 28 | 29 | template 30 | concept U32Enum = ifc::Enum and Uint32>; 31 | 32 | template 33 | inline constexpr std::uint32_t wilderness = sentinel_for; 34 | 35 | // Generic representational index value type. 36 | // Every other type is representationally isomorphic to this (or std::uint32_t). 37 | enum class Index : std::uint32_t {}; 38 | 39 | // Advance an index by a give amount. 40 | template 41 | constexpr Index operator+(Index x, T n) 42 | { 43 | return Index{ifc::to_underlying(x) + n}; 44 | } 45 | 46 | // -- An index-like type is either a unisorted algebra, typically implemented as 47 | // -- an enumeration isomorphic to Index, or a multi-sorted algebra, typically 48 | // -- implemented as an abstract reference class over a given set of sorts. 49 | 50 | // This predicate holds for well-behaved enums that are index-like types. 51 | template 52 | concept Unisorted = std::is_enum_v and std::same_as, std::underlying_type_t>; 53 | 54 | // This predicate holds for any multi-sorted abstract reference type. 55 | // clang-format off 56 | template 57 | concept MultiSorted = sizeof(T) == sizeof(Index) and requires(T t) { 58 | typename T::SortType; 59 | {t.sort()} -> std::same_as; 60 | {t.index()} -> std::same_as; 61 | }; 62 | // clang-format on 63 | 64 | // This predicates holds if the type parameter is an index-like algebra. 65 | // Note: well, this is a crude and simple sanity check. The full accurate 66 | // check is left as an exercise to the reader. 67 | template 68 | concept Algebra = Unisorted or MultiSorted; 69 | 70 | template 71 | inline bool null(T t) 72 | { 73 | return t == T{}; 74 | } 75 | 76 | // Use the appropriate specialization of this class template as a base class when providing the 77 | // fiber implementation for a given sort value. See also Fiber concept. 78 | template 79 | struct SortTag {}; 80 | 81 | // Return the sort value associated with fiber. 82 | // This is an internal/helper function. Don't use directly. Use algebra_sort instead. 83 | // Note: Although this declaration has the flavour of a variable template, it offers key properties 84 | // different from what a variable template provides: 85 | // (a) template argument deduction relying on derived->base conversion, so that 86 | // existential quantification can be expressed simply. E.g. Fiber concept below. 87 | // (b) if a data type fails to derive from SortTag, overload resolution fails, 88 | // leading to gracious concept-based overloading in the right context. 89 | template 90 | constexpr auto sort_value(SortTag) 91 | { 92 | return s; 93 | } 94 | 95 | // A fiber is a data type that, for a given sort value, provides a symbolic representation. 96 | // Note: it should be noted that the sort value is not explicitly provided. 97 | // It is computed/inferred from the data type itself. 98 | // In mathematical terms: T is a fiber iff exists s . T derives from SortTag 99 | template 100 | concept Fiber = std::derived_from>; 101 | 102 | // This predicate holds for a given data type that is a fiber at a specified sort value. 103 | template 104 | concept FiberAt = std::derived_from>; 105 | 106 | // When a data type is certified as a bona fide fiber, return the sort value derived from that certification 107 | template 108 | inline constexpr auto algebra_sort = sort_value(T{}); 109 | 110 | // This predicate holds for any fiber U (an implementation for a given sort) with index type embedded in 111 | // a multi-sorted algebra V. 112 | // Note: The value U::algebra_sort gives the sort at the base of the fiber U, i.e. the sort for which the fiber U 113 | // is an implementation. 114 | // clang-format off 115 | template 116 | concept FiberEmbedding = MultiSorted and requires { 117 | {U::algebra_sort} -> std::convertible_to; 118 | }; 119 | // clang-format on 120 | 121 | // -- The next two functions (rep and per) are conceptual inverse of each other. 122 | // The name 'rep' is purposefully kept short (for 'representation') because it 123 | // is supposed to be both lower-level (looking under the hood) and not directly 124 | // used in interfaces. 125 | // Finally, this operator has a well-established pedigree in algebraically- 126 | // (and more generally mathematically-) oriented programming languages for 127 | // exactly the same purpose. 128 | // 129 | // Convert a value from an index-like type to a canonical abstract representation. 130 | // Note: This enables a word read of the argument, even if it is of class type. 131 | template 132 | inline Index rep(T t) 133 | { 134 | return reinterpret_cast(t); 135 | } 136 | 137 | // Convert a concrete index value to an abstract index-like type. 138 | template 139 | inline T per(Index t) 140 | { 141 | return reinterpret_cast(t); 142 | } 143 | 144 | inline namespace operators { 145 | template 146 | constexpr auto operator<=>(T x, T y) 147 | { 148 | return rep(x) <=> rep(y); 149 | } 150 | template 151 | constexpr auto operator==(T x, T y) 152 | { 153 | return rep(x) == rep(y); 154 | } 155 | } // namespace operators 156 | 157 | // For an index-like type over a sort S, this constant holds the number of bits 158 | // necessary to represent the tags from S. 159 | // Note: There is no satisfactory concept expression of 'sort' at this point. 160 | // Just defining that concept as a check for 'Count' enumerator is not 161 | // desirable as that does not capture the notion of sort -- it would only 162 | // inappropriately elevate an implementation detail/trick to specification. 163 | // Note: suffices to subtract 1 from 'Count' since it is 1 greater than the actual largest value. 164 | template 165 | inline constexpr auto tag_precision = 166 | ifc::to_underlying(S::Count) == 0 ? 0u : ifc::bit_length(ifc::to_underlying(S::Count) - 1u); 167 | 168 | // For an index-like type T over a sort S, return the number of bits 169 | // available for representation of indicies over T. 170 | template 171 | inline constexpr auto index_precision = 32 - tag_precision; 172 | 173 | // Basic representation of an index-like type with specified bit precision 174 | // for the sort. This structure is parameterized directly by the sort type 175 | // parameter, instead of the bit precision parameter, so we can distinguish 176 | // multiple index-like instances with same value precision. 177 | template 178 | struct Over { 179 | using SortType = S; 180 | constexpr Over() : tag(), value() {} 181 | constexpr Over(S s, std::uint32_t v) : tag(ifc::to_underlying(s)), value(v) {} 182 | 183 | constexpr S sort() const 184 | { 185 | return S(tag); 186 | } 187 | 188 | constexpr Index index() const 189 | { 190 | return Index{value}; 191 | } 192 | 193 | private: 194 | std::underlying_type_t tag : tag_precision; 195 | std::underlying_type_t value : index_precision; 196 | }; 197 | 198 | template 199 | constexpr T make(typename T::SortType s, V v) 200 | { 201 | using S = typename T::SortType; 202 | IFCASSERT(ifc::bit_length(v) <= index_precision); 203 | return {s, v}; 204 | } 205 | 206 | // Sometimes, an index-like type conceptually represents a (nullable) pointer type. 207 | // Let's call it a pointed index-like type. These types have distinguished values 208 | // that are indicators of absence of values. The distinguished value does not 209 | // represent any material value. Consequently, the concretized value system starts 210 | // at index 0 while conceptually the materialization starts at 1. 211 | // So we have two operations: (a) inject(), and (b) retract(). 212 | // The inject() operation maps a concrete index value into the abstract index type space. 213 | // Conversely, the retract() operation pulls back a concrete index value out of an abstract index type space. 214 | template 215 | struct pointed { 216 | template 217 | static T inject(S s) 218 | { 219 | IFCASSERT(s < std::numeric_limits::max()); 220 | return T(s + 1); 221 | } 222 | 223 | static auto retract(T t) 224 | { 225 | IFCASSERT(t > T{}); 226 | auto n = ifc::to_underlying(t); 227 | return --n; 228 | } 229 | 230 | static auto index(T t) 231 | { 232 | return Index{retract(t)}; 233 | } 234 | }; 235 | } // namespace index_like 236 | 237 | #endif // IFC_INDEX_UTILS_INCLUDED 238 | -------------------------------------------------------------------------------- /src/ifc-printer/printer.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "printer.hxx" 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace ifc::util { 11 | namespace { 12 | // Values of enum refer to the escaped console color code '\u001b[m'. 13 | enum class ConsoleColor { 14 | None = 0, 15 | Black = 30, 16 | Red, 17 | Green, 18 | Yellow, 19 | Blue, 20 | Magenta, 21 | Cyan, 22 | White, 23 | }; 24 | 25 | std::ostream& operator<<(std::ostream& os, ConsoleColor color) 26 | { 27 | os << "\u001b[" << static_cast(color) << "m"; 28 | return os; 29 | } 30 | 31 | class ScopedConsoleColor { 32 | public: 33 | ScopedConsoleColor(std::ostream& os_, ConsoleColor color, bool enabled_ = true) : os(os_), enabled(enabled_) 34 | { 35 | if (enabled) 36 | { 37 | os << color; 38 | } 39 | } 40 | 41 | ~ScopedConsoleColor() 42 | { 43 | if (enabled) 44 | { 45 | os << ConsoleColor::None; 46 | } 47 | } 48 | 49 | private: 50 | std::ostream& os; 51 | bool enabled = true; 52 | }; 53 | 54 | ConsoleColor get_node_color(const SortKind kind) 55 | { 56 | switch (kind) 57 | { 58 | case SortKind::Scope: 59 | return ConsoleColor::Yellow; 60 | case SortKind::Chart: 61 | return ConsoleColor::Yellow; 62 | case SortKind::Expr: 63 | return ConsoleColor::Yellow; 64 | 65 | case SortKind::Decl: 66 | return ConsoleColor::Magenta; 67 | 68 | case SortKind::Name: 69 | return ConsoleColor::Cyan; 70 | case SortKind::Type: 71 | return ConsoleColor::Green; 72 | case SortKind::Stmt: 73 | return ConsoleColor::Red; 74 | default: 75 | return ConsoleColor::White; 76 | } 77 | } 78 | 79 | struct TreePrinter { 80 | enum class ChildType : char8_t { 81 | First, 82 | Last, 83 | Only_child, 84 | Regular, 85 | }; 86 | enum class IndentAction : size_t { 87 | Child, // '|-' mark this item as a child of its parent 88 | Last_child, // '`-' mark this item as the last child of its parent 89 | Recurse, // '| ' recurse for the elements of a single child 90 | Recurse_last, // ' ' recurse for the elements of the last child 91 | Undo, // remove the last indent action 92 | }; 93 | 94 | static ChildType compute_child_type(const size_t current, const size_t total) 95 | { 96 | if (total == 1) 97 | return ChildType::Only_child; 98 | if (current == 1) 99 | return ChildType::First; 100 | if (current == total) 101 | return ChildType::Last; 102 | return ChildType::Regular; 103 | } 104 | 105 | // We may switch values of the properties to be a variant in the future. 106 | // This useless (at the moment) function will be doing the translation to string from NodeRef then. 107 | const std::string& get_string_prop(const std::string& val) 108 | { 109 | return val; 110 | } 111 | 112 | void dump_node_header(const Node& n, ChildType child_type) 113 | { 114 | update_indent(depth, child_type); 115 | 116 | // Handle the well known properties first. 117 | // We put the interesting ones (name and type, etc) in a fixed order and in a pretty color. 118 | // Some known properties are too noisy (such as alignment), we don't print it at all. 119 | static std::set known = {"name", "type", "base", "source", 120 | "assort", "pack_size", "alignment", "home-scope"}; 121 | stream << indent.c_str(); 122 | { 123 | ColorSetter color(*this, get_node_color(n.key.kind())); 124 | stream << n.id; 125 | // Only declarations have an index embedded in the id, but, if a 126 | // use requested printing of index, we will print it as requested. 127 | if (depth == 0 and implies(options, PrintOptions::Top_level_index) 128 | and n.key.kind() != SortKind::Decl) 129 | stream << "-" << n.key.index(); 130 | } 131 | if (auto it = n.props.find("type"); it != n.props.end()) 132 | { 133 | ColorSetter color(*this, get_node_color(SortKind::Type)); 134 | stream << " '" << get_string_prop(it->second) << "'"; 135 | } 136 | if (auto it = n.props.find("name"); it != n.props.end()) 137 | { 138 | ColorSetter color(*this, get_node_color(SortKind::Name)); 139 | stream << " " << get_string_prop(it->second); 140 | } 141 | if (auto it = n.props.find("base"); it != n.props.end()) 142 | { 143 | ColorSetter color(*this, get_node_color(SortKind::Type)); 144 | stream << " :" << get_string_prop(it->second); 145 | } 146 | if (auto it = n.props.find("assort"); it != n.props.end()) 147 | { 148 | ColorSetter color(*this, get_node_color(SortKind::Name)); 149 | stream << " " << get_string_prop(it->second); 150 | } 151 | if (auto it = n.props.find("pack_size"); it != n.props.end()) 152 | { 153 | ColorSetter color(*this, get_node_color(SortKind::Name)); 154 | stream << " packed-" << get_string_prop(it->second); 155 | } 156 | if (auto it = n.props.find("home-scope"); it != n.props.end()) 157 | { 158 | ColorSetter color(*this, get_node_color(SortKind::Stmt)); 159 | stream << " home-scope(" << get_string_prop(it->second) << ")"; 160 | } 161 | 162 | // dump the rest of the properties in a boring white. 163 | // Color_setter color(*this, Console_color::White); 164 | 165 | for (auto& entry : n.props) 166 | if (not known.contains(entry.first)) 167 | if (std::string str = get_string_prop(entry.second); not str.empty()) 168 | stream << ' ' << str; 169 | 170 | stream << std::endl; 171 | } 172 | 173 | void visit(const Node& n) 174 | { 175 | // special case for the topmost node. 176 | if (depth == 0) 177 | dump_node_header(n, ChildType::Only_child); 178 | 179 | ++depth; 180 | const auto total = n.children.size(); 181 | size_t child_count = 0; 182 | for (auto* child : n.children) 183 | { 184 | dump_node_header(*child, compute_child_type(++child_count, total)); 185 | visit(*child); 186 | } 187 | --depth; 188 | } 189 | 190 | TreePrinter& operator<<(std::string_view str); 191 | TreePrinter& operator<<(IndentAction action); 192 | 193 | void update_indent(size_t, ChildType); 194 | 195 | explicit TreePrinter(std::ostream& stream_, PrintOptions options_ = {}) : stream(stream_), options(options_) {} 196 | 197 | struct ColorSetter : ScopedConsoleColor { 198 | ColorSetter(TreePrinter& pp, ConsoleColor color) 199 | : ScopedConsoleColor(pp.stream, color, implies(pp.options, PrintOptions::Use_color)) 200 | {} 201 | }; 202 | 203 | private: 204 | size_t depth{0}; 205 | 206 | std::ostream& stream; 207 | PrintOptions options; 208 | 209 | std::string indent; 210 | std::stack indents; 211 | std::string indent_strs[static_cast(IndentAction::Recurse_last) + 1]{"|-", "\\-", "| ", " "}; 212 | }; 213 | 214 | TreePrinter& TreePrinter::operator<<(IndentAction action) 215 | { 216 | using enum_type = std::underlying_type::type; 217 | if (action != IndentAction::Undo) 218 | { 219 | indents.push(action); 220 | indent += indent_strs[static_cast(action)]; 221 | } 222 | else if (not indents.empty()) 223 | { 224 | auto indent_size = indent_strs[static_cast(indents.top())].size(); 225 | indent = indent.substr(0, indent.size() - indent_size); 226 | indents.pop(); 227 | } 228 | 229 | return *this; 230 | } 231 | 232 | void TreePrinter::update_indent(size_t indent_depth, ChildType child_type) 233 | { 234 | if (indent_depth == 0) 235 | return; 236 | 237 | if (not indents.empty() and (child_type == ChildType::First or child_type == ChildType::Only_child)) 238 | { 239 | if (indents.top() == IndentAction::Child) 240 | { 241 | *this << IndentAction::Undo << IndentAction::Recurse; 242 | } 243 | else if (indents.top() == IndentAction::Last_child) 244 | { 245 | *this << IndentAction::Undo << IndentAction::Recurse_last; 246 | } 247 | } 248 | 249 | while (indents.size() >= indent_depth) 250 | { 251 | *this << IndentAction::Undo; 252 | } 253 | 254 | if (child_type == ChildType::First or child_type == ChildType::Regular) 255 | { 256 | *this << IndentAction::Child; 257 | } 258 | else 259 | { 260 | *this << IndentAction::Last_child; 261 | } 262 | } 263 | } // namespace 264 | 265 | void print(const Node& node, std::ostream& os, PrintOptions options) 266 | { 267 | TreePrinter tree(os, options); 268 | tree.visit(node); 269 | } 270 | 271 | } // namespace ifc::util 272 | -------------------------------------------------------------------------------- /src/ifc-dom/types.cxx: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | #include "common.hxx" 5 | 6 | namespace ifc::util { 7 | // Try to produce a short string for an type, if possible. 8 | 9 | namespace { 10 | struct TypeTranslator { 11 | Loader& ctx; 12 | 13 | std::string operator()(const symbolic::FundamentalType& type) 14 | { 15 | return to_string(type); 16 | } 17 | 18 | std::string operator()(const symbolic::DesignatedType& as_type) 19 | { 20 | std::string result("decl-type("); 21 | result.append(ctx.ref(as_type.decl)); 22 | result.push_back(')'); 23 | return result; 24 | } 25 | 26 | std::string operator()(const symbolic::SyntacticType& type) 27 | { 28 | std::string result("syntactic-type("); 29 | result.append(ctx.ref(type.expr)); 30 | result.push_back(')'); 31 | return result; 32 | } 33 | 34 | std::string operator()(const symbolic::ExpansionType& type) 35 | { 36 | return ctx.ref(type.pack) + to_string(type.mode); 37 | } 38 | 39 | std::string operator()(const symbolic::PointerType& ptr) 40 | { 41 | return ctx.ref(ptr.pointee) + "*"; 42 | } 43 | 44 | std::string operator()(const symbolic::PointerToMemberType& ptr) 45 | { 46 | return ctx.ref(ptr.type) + " " + ctx.ref(ptr.scope) + "::*"; 47 | } 48 | 49 | std::string operator()(const symbolic::LvalueReferenceType& type) 50 | { 51 | return ctx.ref(type.referee) + "&"; 52 | } 53 | std::string operator()(const symbolic::RvalueReferenceType& type) 54 | { 55 | return ctx.ref(type.referee) + "&&"; 56 | } 57 | std::string operator()(const symbolic::FunctionType& type) 58 | { 59 | // int(int,int) 60 | std::string result(ctx.ref(type.target)); 61 | result.push_back('('); 62 | result.append(ctx.ref(type.source)); 63 | result.push_back(')'); 64 | // TODO: util::append_misc(result, type.eh_spec, type.convention, type.traits); 65 | return result; 66 | } 67 | 68 | std::string operator()(const symbolic::MethodType& type) 69 | { 70 | // int(MyClass: int,int) 71 | std::string result(ctx.ref(type.target)); 72 | result.push_back('('); 73 | result.append(ctx.ref(type.class_type)); 74 | result.append(": "); 75 | result.append(ctx.ref(type.source)); 76 | result.push_back(')'); 77 | // TODO: util::append_misc(result, type.eh_spec, type.convention, type.traits); 78 | return result; 79 | } 80 | 81 | std::string operator()(const symbolic::ArrayType& arr) 82 | { 83 | return ctx.ref(arr.element) + '[' + ctx.ref(arr.bound) + ']'; 84 | } 85 | std::string operator()(const symbolic::TypenameType& unresolved_type) 86 | { 87 | return "typename " + ctx.ref(unresolved_type.path); 88 | } 89 | std::string operator()(const symbolic::QualifiedType& qual) 90 | { 91 | return ctx.ref(qual.unqualified_type) + " " + to_string(qual.qualifiers); 92 | } 93 | 94 | std::string operator()(const symbolic::BaseType& base) 95 | { 96 | std::string result = ctx.ref(base.type); 97 | if (base.traits == symbolic::BaseClassTraits::Shared) 98 | result = "virtual " + result; 99 | if (base.traits == symbolic::BaseClassTraits::Expanded) 100 | result = result + "..."; 101 | return to_string(base.access) + " " + result; 102 | } 103 | 104 | std::string operator()(const symbolic::DecltypeType& val) 105 | { 106 | return "decltype(" + ctx.ref(val.expression) + ")"; 107 | } 108 | 109 | std::string operator()(const symbolic::PlaceholderType& type) 110 | { 111 | std::string result = "type-placeholder(" + to_string(type.basis); 112 | if (not null(type.elaboration)) 113 | result += " " + ctx.ref(type.elaboration); 114 | if (not null(type.constraint)) 115 | result.append(" " + ctx.ref(type.constraint)); 116 | result += ')'; 117 | return result; 118 | } 119 | 120 | std::string operator()(const symbolic::TupleType& tuple) 121 | { 122 | std::string result; 123 | for (auto& index : ctx.reader.sequence(tuple)) 124 | { 125 | if (not result.empty()) 126 | result.push_back(','); 127 | result.append(ctx.ref(index)); 128 | } 129 | return result; 130 | } 131 | 132 | std::string operator()(const symbolic::ForallType&) 133 | { 134 | return ""; 135 | } 136 | 137 | std::string operator()(const symbolic::UnalignedType& type) 138 | { 139 | return "__unaligned " + ctx.ref(type.operand); 140 | } 141 | 142 | std::string operator()(const symbolic::SyntaxTreeType& syntax) 143 | { 144 | return "syntax-tree(" + ctx.ref(syntax.syntax) + ")"; 145 | } 146 | 147 | std::string operator()(const symbolic::TorType& type) 148 | { 149 | // #TOR(int, float) 150 | std::string result("#TOR("); 151 | result.append(ctx.ref(type.source)); 152 | result.push_back(')'); 153 | // TODO: util::append_misc(result, type.eh_spec, type.convention); 154 | return result; 155 | } 156 | }; 157 | 158 | } // namespace 159 | 160 | std::string get_string_if_possible(Loader& ctx, TypeIndex index) 161 | { 162 | if (not null(index)) 163 | return ctx.reader.visit(index, TypeTranslator{ctx}); 164 | return "no-type"; 165 | } 166 | 167 | // Load types as full blown nodes. 168 | 169 | namespace { 170 | struct TypeLoader : detail::LoaderVisitorBase { 171 | void operator()(const symbolic::FundamentalType& type) 172 | { 173 | node.props.emplace("type", to_string(type)); 174 | } 175 | 176 | void operator()(const symbolic::DesignatedType& as_type) 177 | { 178 | node.props.emplace("ref", ctx.ref(as_type.decl)); 179 | } 180 | 181 | void operator()(const symbolic::SyntacticType& type) 182 | { 183 | add_child(type.expr); 184 | } 185 | 186 | void operator()(const symbolic::ExpansionType& type) 187 | { 188 | node.props.emplace("mode", to_string(type.mode)); 189 | add_child(type.pack); 190 | } 191 | 192 | void operator()(const symbolic::PointerType& ptr) 193 | { 194 | add_child(ptr.pointee); 195 | } 196 | 197 | void operator()(const symbolic::PointerToMemberType& ptr) 198 | { 199 | add_child(ptr.type); 200 | add_child(ptr.scope); 201 | } 202 | 203 | void operator()(const symbolic::LvalueReferenceType& type) 204 | { 205 | add_child(type.referee); 206 | } 207 | 208 | void operator()(const symbolic::RvalueReferenceType& type) 209 | { 210 | add_child(type.referee); 211 | } 212 | 213 | void operator()(const symbolic::FunctionType& type) 214 | { 215 | add_child(type.target); 216 | add_child_if_not_null(type.source); 217 | } 218 | 219 | void operator()(const symbolic::MethodType& type) 220 | { 221 | add_child(type.class_type); 222 | add_child(type.target); 223 | add_child_if_not_null(type.source); 224 | } 225 | 226 | void operator()(const symbolic::ArrayType& arr) 227 | { 228 | add_child(arr.element); 229 | add_child(arr.bound); 230 | } 231 | 232 | void operator()(const symbolic::TypenameType& unresolved_type) 233 | { 234 | add_child(unresolved_type.path); 235 | } 236 | 237 | void operator()(const symbolic::QualifiedType& qual) 238 | { 239 | node.props.emplace("qualifiers", to_string(qual.qualifiers)); 240 | add_child(qual.unqualified_type); 241 | } 242 | 243 | void operator()(const symbolic::BaseType& base) 244 | { 245 | node.props.emplace("access", to_string(base.access)); 246 | node.props.emplace("traits", to_string(base.traits)); 247 | add_child(base.type); 248 | } 249 | 250 | void operator()(const symbolic::DecltypeType& val) 251 | { 252 | add_child(val.expression); 253 | } 254 | 255 | void operator()(const symbolic::PlaceholderType& type) 256 | { 257 | add_prop("basis", to_string(type.basis)); 258 | add_child_if_not_null("elaboration", type.elaboration); 259 | add_child_if_not_null("constraint", type.elaboration); 260 | } 261 | 262 | void operator()(const symbolic::TupleType& tuple) 263 | { 264 | for (auto& index : ctx.reader.sequence(tuple)) 265 | add_child(index); 266 | } 267 | 268 | void operator()(const symbolic::ForallType& type) 269 | { 270 | add_child(type.chart); 271 | add_child(type.subject); 272 | } 273 | 274 | void operator()(const symbolic::UnalignedType& type) 275 | { 276 | add_child(type.operand); 277 | } 278 | 279 | void operator()(const symbolic::SyntaxTreeType& type) 280 | { 281 | add_child(type.syntax); 282 | } 283 | 284 | void operator()(const symbolic::TorType& type) 285 | { 286 | add_child(type.source); 287 | // TODO: add props convention, eh_spec 288 | } 289 | }; 290 | 291 | } // namespace 292 | 293 | void load(Loader& ctx, Node& node, TypeIndex index) 294 | { 295 | IFCASSERT(not null(index)); 296 | node.id = sort_name(index.sort()); 297 | ctx.reader.visit(index, TypeLoader{ctx, node}); 298 | } 299 | 300 | } // namespace ifc::util 301 | -------------------------------------------------------------------------------- /samples/sgraph-js/ui/options.js: -------------------------------------------------------------------------------- 1 | // Copyright Microsoft Corporation. 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 3 | 4 | class Options { 5 | constructor() { 6 | this._populate_default_decl_colors(); 7 | this._populate_default_class_colors(); 8 | // By default, the view is icicle graph where the top-most node stretches the 9 | // width of the screen and each descendant sibling is layed out along the x-axis. 10 | // Transposing enabled will show the graph in the opposite form: each descendant 11 | // is layed out along the y-axis and each sibling is stacked along the y-axis. 12 | this.transpose = false; 13 | this.deselected_opacity = 0.1; 14 | this.show_fps = false; 15 | this.graph_animations = true; 16 | } 17 | 18 | color_for_index(index) { 19 | if (index.sort == DeclIndex.Sort.Scope) { 20 | const decl = sgraph.resolver.read(ScopeDecl, index.index); 21 | if (sgraph.resolver.fundamental_type_is_class(decl)) { 22 | const basis = sgraph.resolver.read(FundamentalType, decl.type.index).basis.value; 23 | return this.color_for_class(basis); 24 | } 25 | } 26 | return this.color_for_sort(index.sort); 27 | } 28 | 29 | color_for_sort(sort) { 30 | return this.decl_color_map.get(sort); 31 | } 32 | 33 | set_color_for_sort(sort, color) { 34 | this.decl_color_map.set(sort, color); 35 | } 36 | 37 | color_for_class(basis) { 38 | return this.class_color_map.get(basis); 39 | } 40 | 41 | set_color_for_class(basis, color) { 42 | this.class_color_map.set(basis, color); 43 | } 44 | 45 | reset_decl_colors_to_default() { 46 | const decl_sorts = Object.entries(DeclIndex.Sort); 47 | for (var i = 0; i < decl_sorts.length; ++i) { 48 | const sort = decl_sorts[i][1]; 49 | this.decl_color_map.set(sort, Options._intern_decl_sort_default(sort)); 50 | } 51 | } 52 | 53 | reset_class_colors_to_default() { 54 | this.class_color_map.set(TypeBasis.Values.Class, Options._intern_color_for_class_type(TypeBasis.Values.Class)); 55 | this.class_color_map.set(TypeBasis.Values.Struct, Options._intern_color_for_class_type(TypeBasis.Values.Struct)); 56 | this.class_color_map.set(TypeBasis.Values.Union, Options._intern_color_for_class_type(TypeBasis.Values.Union)); 57 | } 58 | 59 | _populate_default_decl_colors() { 60 | this.decl_color_map = new Map(); 61 | this.reset_decl_colors_to_default(); 62 | } 63 | 64 | _populate_default_class_colors() { 65 | this.class_color_map = new Map(); 66 | this.reset_class_colors_to_default(); 67 | } 68 | 69 | static _as_hex(c) { 70 | c = Math.max(0, Math.min(255, Math.round(c) || 0)); 71 | return (c < 16 ? "0" : "") + c.toString(16); 72 | } 73 | 74 | static _intern_color(color) { 75 | const d3_color = d3.color(color); 76 | const r = Options._as_hex(d3_color.r); 77 | const g = Options._as_hex(d3_color.g); 78 | const b = Options._as_hex(d3_color.b); 79 | return `#${r}${g}${b}`; 80 | } 81 | 82 | static _intern_decl_sort_default(sort) { 83 | const color = Options._default_color_for_decl_sort(sort); 84 | return Options._intern_color(color); 85 | } 86 | 87 | static _default_color_for_decl_sort(sort) { 88 | switch (sort) { 89 | case DeclIndex.Sort.Enumerator: 90 | return 'yellow'; 91 | case DeclIndex.Sort.Variable: 92 | return 'lightgreen'; 93 | case DeclIndex.Sort.Parameter: 94 | return 'lightgray'; 95 | case DeclIndex.Sort.Field: 96 | return 'lightskyblue'; 97 | case DeclIndex.Sort.Bitfield: 98 | return 'lightsalmon'; 99 | case DeclIndex.Sort.Scope: 100 | return 'darkcyan'; 101 | case DeclIndex.Sort.Enumeration: 102 | return 'darkgoldenrod'; 103 | case DeclIndex.Sort.Alias: 104 | return 'gold'; 105 | case DeclIndex.Sort.Temploid: 106 | return 'dodgerblue'; 107 | case DeclIndex.Sort.Template: 108 | return 'dodgerblue'; 109 | case DeclIndex.Sort.PartialSpecialization: 110 | case DeclIndex.Sort.Specialization: 111 | case DeclIndex.Sort.Concept: 112 | return 'violet'; 113 | case DeclIndex.Sort.Function: 114 | return 'plum'; 115 | case DeclIndex.Sort.Method: 116 | return 'pink'; 117 | case DeclIndex.Sort.Constructor: 118 | return 'olive'; 119 | case DeclIndex.Sort.InheritedConstructor: 120 | return 'olive'; 121 | case DeclIndex.Sort.Destructor: 122 | return 'firebrick'; 123 | case DeclIndex.Sort.Reference: 124 | case DeclIndex.Sort.Using: 125 | case DeclIndex.Sort.Friend: 126 | case DeclIndex.Sort.Expansion: 127 | case DeclIndex.Sort.DeductionGuide: 128 | case DeclIndex.Sort.Barren: 129 | case DeclIndex.Sort.Tuple: 130 | case DeclIndex.Sort.SyntaxTree: 131 | case DeclIndex.Sort.Intrinsic: 132 | case DeclIndex.Sort.Property: 133 | case DeclIndex.Sort.OutputSegment: 134 | return 'red'; 135 | case DeclIndex.Sort.VendorExtension: 136 | return 'tan'; 137 | case DeclIndex.Sort.DefaultArgument: 138 | case DeclIndex.Sort.Prolongation: 139 | case DeclIndex.Sort.Count: 140 | return 'black'; 141 | default: 142 | console.error(`Bad sort: ${sort}`); 143 | return 'black'; 144 | } 145 | } 146 | 147 | static _intern_color_for_class_type(basis) { 148 | const color = Options._default_color_for_class_type(basis); 149 | return Options._intern_color(color); 150 | } 151 | 152 | static _default_color_for_class_type(basis) { 153 | switch (basis) { 154 | case TypeBasis.Values.Class: 155 | return 'lightblue'; 156 | case TypeBasis.Values.Struct: 157 | return 'greenyellow'; 158 | case TypeBasis.Values.Union: 159 | return 'darkgrey'; 160 | default: 161 | console.error(`Bad basis for class: ${basis}`); 162 | return 'black'; 163 | } 164 | } 165 | } 166 | 167 | function populate_decl_color_dropdown() { 168 | Object.entries(DeclIndex.Sort) 169 | .sort(function(lhs, rhs) { 170 | if (lhs[0] < rhs[0]) 171 | return -1; 172 | if (lhs[0] > rhs[0]) 173 | return 1; 174 | return 0; 175 | }) 176 | .forEach(element => { 177 | if (element[1] != DeclIndex.Sort.Count) { 178 | var entry = document.createElement("option"); 179 | entry.textContent = element[0]; 180 | entry.value = element[1]; 181 | decl_color_dropdown.appendChild(entry); 182 | } 183 | }); 184 | } 185 | 186 | function select_decl_for_color(e, selector) { 187 | const sort = parseInt(selector.value); 188 | decl_color_selector.value = options.color_for_sort(sort); 189 | } 190 | 191 | function select_color_for_decl(e, selector) { 192 | const color = selector.value; 193 | const sort = parseInt(decl_color_dropdown.value); 194 | options.set_color_for_sort(sort, color); 195 | color_updated(); 196 | } 197 | 198 | function reset_decl_colors() { 199 | options.reset_decl_colors_to_default(); 200 | // Show the newly updated color for the currently selected decl. 201 | select_decl_for_color(null, decl_color_dropdown); 202 | color_updated(); 203 | } 204 | 205 | function populate_class_color_dropdown() { 206 | const class_types = { 207 | Class: TypeBasis.Values.Class, 208 | Struct: TypeBasis.Values.Struct, 209 | Union: TypeBasis.Values.Union 210 | }; 211 | Object.entries(class_types) 212 | .forEach(element => { 213 | var entry = document.createElement("option"); 214 | entry.textContent = element[0]; 215 | entry.value = element[1]; 216 | class_color_dropdown.appendChild(entry); 217 | }); 218 | } 219 | 220 | function select_class_for_color(e, selector) { 221 | const basis = parseInt(selector.value); 222 | class_color_selector.value = options.color_for_class(basis); 223 | } 224 | 225 | function select_color_for_class(e, selector) { 226 | const color = selector.value; 227 | const basis = parseInt(class_color_dropdown.value); 228 | options.set_color_for_class(basis, color); 229 | color_updated(); 230 | } 231 | 232 | function reset_class_colors() { 233 | options.reset_class_colors_to_default(); 234 | // Show the newly updated color for the currently selected class type. 235 | select_class_for_color(null, class_color_dropdown); 236 | color_updated(); 237 | } 238 | 239 | function toggle_graph_transpose(e, checkbox) { 240 | options.transpose = checkbox.checked; 241 | transpose_updated(); 242 | } 243 | 244 | function show_bad_opacity_value(str) { 245 | bad_filter_opacity_text.textContent = `Bad opacity value "${str}"`; 246 | bad_filter_opacity_text.hidden = false; 247 | } 248 | 249 | function hide_bad_filter_opacity_value() { 250 | bad_filter_opacity_text.hidden = true; 251 | } 252 | 253 | function valid_opacity_value(edit) { 254 | var str = edit.value; 255 | if (str == "") 256 | return false; 257 | if (isNaN(str)) 258 | return false; 259 | var percentage = parseInt(str); 260 | if (percentage < 0 || percentage > 100) 261 | return false; 262 | return true; 263 | } 264 | 265 | var filter_opacity_timeout_delay = 250; // 0.25s 266 | var filter_opacity_timeout; 267 | 268 | function filter_opacity_keyup(e, text_input) { 269 | hide_bad_filter_opacity_value(); 270 | clearInterval(filter_opacity_timeout); 271 | filter_opacity_timeout = setInterval(function() { 272 | if (valid_opacity_value(text_input)) { 273 | var percentage = parseInt(text_input.value); 274 | options.deselected_opacity = percentage * 0.01; 275 | color_updated(); 276 | } 277 | else { 278 | show_bad_opacity_value(text_input.value); 279 | } 280 | clearInterval(filter_opacity_timeout); 281 | }, filter_opacity_timeout_delay); 282 | } 283 | 284 | function toggle_graph_fps(e, checkbox) { 285 | options.show_fps = checkbox.checked; 286 | // No updated needed. It is checked in the draw() function. 287 | } 288 | 289 | function toggle_disable_graph_animations(e, checkbox) { 290 | // This is a 'is x disabled' so we propagate the inverse. 291 | options.graph_animations = !checkbox.checked; 292 | } 293 | 294 | function init_options_dialog() { 295 | options = new Options(); 296 | populate_decl_color_dropdown(); 297 | populate_class_color_dropdown(); 298 | 299 | decl_color_dropdown.addEventListener("change", event => select_decl_for_color(event, decl_color_dropdown)); 300 | decl_color_selector.addEventListener("change", event => select_color_for_decl(event, decl_color_selector)); 301 | reset_decl_colors_btn.addEventListener("click", e => reset_decl_colors()); 302 | transpose_graph_toggle.addEventListener("click", event => toggle_graph_transpose(event, transpose_graph_toggle)); 303 | class_color_dropdown.addEventListener("change", event => select_class_for_color(event, class_color_dropdown)); 304 | class_color_selector.addEventListener("change", event => select_color_for_class(event, class_color_selector)); 305 | reset_class_colors_btn.addEventListener("click", e => reset_class_colors()); 306 | filter_opacity_edit.addEventListener("keyup", event => filter_opacity_keyup(event, filter_opacity_edit)); 307 | graph_fps_toggle.addEventListener("click", event => toggle_graph_fps(event, graph_fps_toggle)); 308 | graph_animations.addEventListener("click", event => toggle_disable_graph_animations(event, graph_animations)); 309 | 310 | // Defaults 311 | // Populate the default color box. 312 | select_decl_for_color(null, decl_color_dropdown); 313 | transpose_graph_toggle.checked = options.transpose; 314 | select_class_for_color(null, class_color_dropdown); 315 | bad_filter_opacity_text.hidden = true; 316 | filter_opacity_edit.value = (100 * options.deselected_opacity).toString(); 317 | graph_fps_toggle.checked = options.show_fps; 318 | } -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The Microsoft IFC SDK is under the Apache License v2.0 with LLVM Exception: 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | 205 | ---- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. --------------------------------------------------------------------------------