From 7e03a4f0b56a6eabe3781be0df8e054c547d591f Mon Sep 17 00:00:00 2001 From: lvntky Date: Fri, 7 Jun 2024 15:20:59 +0300 Subject: [PATCH 1/9] [initial] --- .clang-format | 24 + .clang-tidy | 6 + .github/ISSUE_TEMPLATE/bug_report.md | 33 ++ .github/ISSUE_TEMPLATE/custom.md | 10 + .github/ISSUE_TEMPLATE/feature_request.md | 25 + .github/workflows/macos.yml | 53 +++ .github/workflows/release.yml | 189 ++++++++ .github/workflows/ubuntu.yml | 55 +++ .github/workflows/windows.yml | 53 +++ .gitignore | 532 +++++++++++++++------- CMakeLists.txt | 367 ++++++++++++++- CONTRIBUTING.md | 203 +++++++++ Dockerfile | 52 +++ Makefile | 63 +++ PULL_REQUEST_TEMPLATE.md | 72 +++ cmake/CompilerWarnings.cmake | 99 ++++ cmake/Conan.cmake | 46 ++ cmake/Doxygen.cmake | 11 + cmake/ProjectConfig.cmake.in | 9 + cmake/SourcesAndHeaders.cmake | 16 + cmake/StandardSettings.cmake | 95 ++++ cmake/StaticAnalyzers.cmake | 20 + cmake/Utils.cmake | 40 ++ cmake/Vcpkg.cmake | 20 + cmake/version.hpp.in | 11 + codecov.yaml | 5 + include/attribute.h | 42 -- include/bigendian.h | 9 - include/class.h | 30 -- include/constant_pool.h | 125 ----- include/cvm.h | 10 - include/field.h | 29 -- include/jvm_heap.h | 3 - include/jvm_stack.h | 3 - include/method.h | 23 - include/project/tmp.hpp | 9 + src/attribute.c | 42 -- src/class.c | 103 ----- src/constant_pool.c | 71 --- src/cvm.c | 0 src/field.c | 28 -- src/main.c | 6 - src/tmp.cpp | 3 + test/Add.java | 5 - test/CMakeLists.txt | 84 ++++ test/Greet.java | 5 - test/constTest.java | 9 - test/greet_hexdump.txt | 18 - test/greet_javapdump.txt | 13 - test/hexdump.txt | 32 -- test/javapdump.txt | 30 -- test/src/tmp_test.cpp | 15 + 52 files changed, 2041 insertions(+), 815 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/custom.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/macos.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/ubuntu.yml create mode 100644 .github/workflows/windows.yml create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 PULL_REQUEST_TEMPLATE.md create mode 100644 cmake/CompilerWarnings.cmake create mode 100644 cmake/Conan.cmake create mode 100644 cmake/Doxygen.cmake create mode 100644 cmake/ProjectConfig.cmake.in create mode 100644 cmake/SourcesAndHeaders.cmake create mode 100644 cmake/StandardSettings.cmake create mode 100644 cmake/StaticAnalyzers.cmake create mode 100644 cmake/Utils.cmake create mode 100644 cmake/Vcpkg.cmake create mode 100644 cmake/version.hpp.in create mode 100644 codecov.yaml delete mode 100644 include/attribute.h delete mode 100644 include/bigendian.h delete mode 100644 include/class.h delete mode 100644 include/constant_pool.h delete mode 100644 include/cvm.h delete mode 100644 include/field.h delete mode 100644 include/jvm_heap.h delete mode 100644 include/jvm_stack.h delete mode 100644 include/method.h create mode 100644 include/project/tmp.hpp delete mode 100644 src/attribute.c delete mode 100644 src/class.c delete mode 100644 src/constant_pool.c delete mode 100644 src/cvm.c delete mode 100644 src/field.c delete mode 100644 src/main.c create mode 100644 src/tmp.cpp delete mode 100644 test/Add.java create mode 100644 test/CMakeLists.txt delete mode 100644 test/Greet.java delete mode 100644 test/constTest.java delete mode 100644 test/greet_hexdump.txt delete mode 100644 test/greet_javapdump.txt delete mode 100644 test/hexdump.txt delete mode 100644 test/javapdump.txt create mode 100644 test/src/tmp_test.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8d70915 --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +--- +BasedOnStyle: Google +AlignAfterOpenBracket: 'AlwaysBreak' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AlignConsecutiveMacros: 'true' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: 'None' +AllowShortIfStatementsOnASingleLine: 'Never' +AllowShortLoopsOnASingleLine: 'false' +BreakBeforeBraces: Allman +BinPackArguments: 'false' +BinPackParameters: 'false' +Cpp11BracedListStyle: 'false' +ColumnLimit: 125 +NamespaceIndentation: All +SpaceAfterTemplateKeyword: 'false' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyBlock: true +Standard: 'Latest' +... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..d85b250 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,6 @@ +--- +Checks: '*,-fuchsia-*,-google-*,-zircon-*,-abseil-*,-modernize-use-trailing-return-type,-llvm-*,-llvmlibc-*' +CheckOptions: [{ key: misc-non-private-member-variables-in-classes, value: IgnoreClassesWithAllMemberVariablesBeingPublic }] +WarningsAsErrors: '*' +HeaderFilterRegex: '' +FormatStyle: none diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..7532789 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a report to help me improve +title: "[BUG]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + +* OS: [e.g. Windows] +* Version [e.g. 10] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000..48d5f81 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e75a04b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,25 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated +when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Provide usage examples** +A few examples of how the feature should be used. Please make sure they are clear +and concise. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 0000000..e20fc40 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,53 @@ +name: MacOS + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + BUILD_TYPE: Release + INSTALL_LOCATION: .local + +jobs: + build: + + runs-on: macos-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + + steps: + - uses: actions/checkout@v2 + + - name: cache dependencies + uses: actions/cache@v2 + id: cache + with: + path: ${{ github.workspace }}/${{ env.INSTALL_LOCATION }} + key: ${{ runner.os }}-dependencies + + - name: install GoogleTest + if: ${{ steps.cache.output.cache-hit != 'true' }} + run: | + cd .. + git clone https://github.com/google/googletest.git --branch release-1.10.0 + cd googletest + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION + cmake --build build --config Release + cmake --build build --target install --config Release + cd ../modern-cpp-template + + - name: configure + run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION + + - name: build + run: cmake --build build --config $BUILD_TYPE -j4 + + - name: run tests + run: | + cd build + ctest -C $BUILD_TYPE -VV + + - name: install project + run: cmake --build build --target install --config Release + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..433c451 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,189 @@ +name: Release + +on: + push: + tags: + - 'v*' + +env: + PROJECT_NAME: "modern-cpp-template" + BUILD_TYPE: Release + +jobs: + build: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - { + name: "Windows Latest MSVC", + artifact_ext: '.zip', + os: windows-latest, + cc: "cl", + cxx: "cl", + environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", + } + - { + name: "Ubuntu Latest GCC", + artifact_ext: '.tar.gz', + os: ubuntu-latest, + cc: "gcc", + cxx: "g++", + } + - { + name: "macOS Latest Clang", + artifact_ext: '.tar.gz', + os: macos-latest, + cc: "clang", + cxx: "clang++", + } + + steps: + - name: set version name (Windows) + id: version_win + if: ${{ runner.os == 'Windows' }} + run: | + $TAG = (${env:GITHUB_REF} -replace 'refs/tags/', '') + echo "::set-output name=name::$TAG" + + - name: set version name + id: version + if: ${{ runner.os != 'Windows' }} + run: echo ::set-output name=name::${GITHUB_REF#refs/tags/} + + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: cache dependencies + uses: actions/cache@v2 + id: cache + with: + path: ${{ github.HOME }}/.local + key: ${{ runner.os }}-dependencies + + - name: install GoogleTest + if: ${{ steps.cache.output.cache-hit != 'true' }} + run: | + cd .. + git clone https://github.com/google/googletest.git --branch release-1.10.0 + cd googletest + cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/.local" -Dgtest_force_shared_crt=1 + cmake --build build --config Release + cmake --build build --target install --config Release + cd ../modern-cpp-template + + - name: configure + run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/.local" + + - name: build + run: cmake --build build --config "$env:BUILD_TYPE" -j4 + + - name: run tests + run: | + cd build + ctest -C "$env:BUILD_TYPE" -VV + + # for a release not containing directly the source code, replace the files archived + # with the actual files needed (i.e. *.lib/*.a and *.h(pp)) + + - name: generate archive (Windows) + if: ${{ runner.os == 'Windows' }} + run: | + rmdir -r -fo build + 7z a -tzip $HOME/artifact.zip * + + + - name: generate archive + if: ${{ runner.os != 'Windows' }} + run: | + rm -rf build + tar -cvzf $HOME/artifact.tar.gz . + + - name: upload artifacts + uses: actions/upload-artifact@v2 + if: ${{ runner.os == 'Windows' }} + with: + name: ${{ runner.os }}-${{ steps.version_win.outputs.name }} + path: '~/artifact.*' + + - name: upload artifacts + uses: actions/upload-artifact@v2 + if: ${{ runner.os != 'Windows' }} + with: + name: ${{ runner.os }}-${{ steps.version.outputs.name }} + path: '~/artifact.*' + + release: + if: contains(github.ref, 'tags/v') + runs-on: ubuntu-latest + needs: build + + steps: + - name: set version name + id: version + run: echo ::set-output name=name::${GITHUB_REF#refs/tags/} + + - name: create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ steps.version.outputs.name }} + # if needed, you can set the release body here + #body: "Release notes" + draft: false + prerelease: false + + - name: download artifact + uses: actions/download-artifact@v2 + with: + name: "Linux-${{ steps.version.outputs.name }}" + path: ./ + + - name: upload ubuntu release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: "artifact.tar.gz" + asset_name: "${{ env.PROJECT_NAME }}-Linux-${{ steps.version.outputs.name }}.tar.gz" + asset_content_type: application/x-tar + + - name: download artifact + uses: actions/download-artifact@v2 + with: + name: "Windows-${{ steps.version.outputs.name }}" + path: ./ + + - name: upload windows release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: "artifact.zip" + asset_name: "${{ env.PROJECT_NAME }}-Windows-${{ steps.version.outputs.name }}.zip" + asset_content_type: application/zip + + - name: download artifact + uses: actions/download-artifact@v2 + with: + name: "macOS-${{ steps.version.outputs.name }}" + path: ./ + + - name: upload macos release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: "./artifact.tar.gz" + asset_name: "${{ env.PROJECT_NAME }}-macOS-${{ steps.version.outputs.name }}.tar.gz" + asset_content_type: application/x-tar diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 0000000..2aa8542 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,55 @@ +name: Ubuntu + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + BUILD_TYPE: Release + INSTALL_LOCATION: .local + +jobs: + build: + + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + + steps: + - uses: actions/checkout@v2 + + - name: cache dependencies + uses: actions/cache@v2 + id: cache + with: + path: ${{ github.workspace }}/${{ env.INSTALL_LOCATION }} + key: ${{ runner.os }}-dependencies + + - name: install GoogleTest + if: ${{ steps.cache.output.cache-hit != 'true' }} + run: | + cd .. + git clone https://github.com/google/googletest.git --branch release-1.10.0 + cd googletest + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION + cmake --build build --config Release + cmake --build build --target install --config Release + + - name: configure + run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/$INSTALL_LOCATION -DProject_ENABLE_CODE_COVERAGE=1 + + - name: build + run: cmake --build build --config $BUILD_TYPE -j4 + + - name: run tests + run: | + cd build + ctest -C $BUILD_TYPE -VV + + - name: Code coverage using Codecov + run: bash <(curl -s https://codecov.io/bash) + + - name: install project + run: cmake --build build --target install --config Release + diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..d2da574 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,53 @@ +name: Windows + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + BUILD_TYPE: Release + INSTALL_LOCATION: ".local" + +jobs: + build: + + runs-on: windows-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + + steps: + - uses: actions/checkout@v2 + + - name: cache dependencies + uses: actions/cache@v2 + id: cache + with: + path: ${{env.INSTALL_LOCATION}} + key: ${{runner.os}}-dependencies + + - name: install GoogleTest + if: ${{steps.cache.output.cache-hit != 'true'}} + run: | + cd .. + git clone https://github.com/google/googletest.git --branch release-1.10.0 + cd googletest + cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/$env:INSTALL_LOCATION" -Dgtest_force_shared_crt=1 + cmake --build build --config Release + cmake --build build --target install --config Release + cd ../modern-cpp-template + + - name: configure + run: cmake -Bbuild -DCMAKE_INSTALL_PREFIX="$HOME/$env:INSTALL_LOCATION" + + - name: build + run: cmake --build build --config "$env:BUILD_TYPE" -j4 + + - name: run tests + run: | + cd build + ctest -C "$env:BUILD_TYPE" -VV + + - name: install project + run: cmake --build build --target install --config Release + diff --git a/.gitignore b/.gitignore index 265efa6..101c97f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,186 +1,368 @@ -### C ### -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output +[Bb]uild/* +[Dd]ocs/* +CMakeCache.* +CMakeFiles/* +[Tt]esting/* + +### VisualStudioCode ### +.vscode/* +.vscode/settings.json +.vscode/tasks.json +.vscode/launch.json +.vscode/extensions.json + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h *.ilk -*.map -*.exp - -# Precompiled Headers -*.gch +*.obj +*.iobj *.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb *.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -### C++ ### -# Prerequisites - -# Compiled Object files -*.slo - -# Precompiled Headers - -# Compiled Dynamic libraries - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai - -# Executables - -### Emacs ### -# -*- mode: gitignore; -*- +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* *~ -\#*\# -/.emacs.desktop -/.emacs.desktop.lock -*.elc -auto-save-list -tramp -.\#* - -# Org-mode -.org-id-locations -*_archive - -# flymake-mode -*_flymake.* - -# eshell files -/eshell/history -/eshell/lastdir - -# elpa packages -/elpa/ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +#KDevelop +*.kdev4 +.kdev4/* + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ -# reftex files -*.rel +# Azure Stream Analytics local run output +ASALocalRun/ -# AUCTeX auto folder -/auto/ +# MSBuild Binary and Structured Log +*.binlog -# cask packages -.cask/ -dist/ +# NVidia Nsight GPU debugger configuration file +*.nvuser -# Flycheck -flycheck_*.el +# MFractors (Xamarin productivity tool) working folder +.mfractor/ -# server auth directory -/server/ +# Local History for Visual Studio +.localhistory/ -# projectiles files -.projectile - -# directory configuration -.dir-locals.el - -# network security -/network-security.data - - -### Java ### -# Compiled class file -*.class - -# Log file -*.log +# BeatPulse healthcheck temp database +healthchecksdb -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* - -### Linux ### - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -/src/class -/src/constant -/src/hexdump - -### CMake ### -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles -CMakeScripts -Testing -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -_deps - -### CMake Patch ### -# External projects -*-prefix/ - -cvm +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 11d9942..777c9ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,363 @@ -cmake_minimum_required(VERSION 3.26.3) -project(CVM) -add_compile_options(-fno-stack-protector) -add_executable(cvm ./src/class.c ./include/class.h ./src/constant_pool.c ./include/constant_pool.h ./src/attribute.c ./include/attribute.h ./src/field.c ./include/field.h) +cmake_minimum_required(VERSION 3.15) + +# +# Project details +# + +project( + "Project" + VERSION 0.1.0 + LANGUAGES CXX +) + +# +# Set project options +# + +include(cmake/StandardSettings.cmake) +include(cmake/StaticAnalyzers.cmake) +include(cmake/Utils.cmake) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() +message(STATUS "Started CMake for ${PROJECT_NAME} v${PROJECT_VERSION}...\n") + +if (UNIX) + add_compile_options("$<$:-D_DEBUG>") #this will allow to use same _DEBUG macro available in both Linux as well as Windows - MSCV environment. Easy to put Debug specific code. +endif (UNIX) + + +# +# Setup alternative names +# + +if(${PROJECT_NAME}_USE_ALT_NAMES) + string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) + string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPERCASE) +else() + set(PROJECT_NAME_LOWERCASE ${PROJECT_NAME}) + set(PROJECT_NAME_UPPERCASE ${PROJECT_NAME}) +endif() + +# +# Prevent building in the source directory +# + +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\n") +endif() + +# +# Enable package managers +# + +include(cmake/Conan.cmake) +include(cmake/Vcpkg.cmake) + +# +# Create library, setup header and source files +# + +# Find all headers and implementation files +include(cmake/SourcesAndHeaders.cmake) + +if(${PROJECT_NAME}_BUILD_EXECUTABLE) + add_executable(${PROJECT_NAME} ${exe_sources}) + + if(${PROJECT_NAME}_VERBOSE_OUTPUT) + verbose_message("Found the following sources:") + foreach(source IN LISTS exe_sources) + verbose_message("* ${source}") + endforeach() + endif() + + if(${PROJECT_NAME}_ENABLE_UNIT_TESTING) + add_library(${PROJECT_NAME}_LIB ${headers} ${sources}) + + if(${PROJECT_NAME}_VERBOSE_OUTPUT) + verbose_message("Found the following headers:") + foreach(header IN LISTS headers) + verbose_message("* ${header}") + endforeach() + endif() + endif() +elseif(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + add_library(${PROJECT_NAME} INTERFACE) + + if(${PROJECT_NAME}_VERBOSE_OUTPUT) + verbose_message("Found the following headers:") + foreach(header IN LIST headers) + verbose_message("* ${header}") + endforeach() + endif() +else() + add_library( + ${PROJECT_NAME} + ${headers} + ${sources} + ) + + if(${PROJECT_NAME}_VERBOSE_OUTPUT) + verbose_message("Found the following sources:") + foreach(source IN LISTS sources) + verbose_message("* ${source}") + endforeach() + verbose_message("Found the following headers:") + foreach(header IN LISTS headers) + verbose_message("* ${header}") + endforeach() + endif() +endif() + +set_target_properties( + ${PROJECT_NAME} + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/${CMAKE_BUILD_TYPE}" +) +if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) + set_target_properties( + ${PROJECT_NAME}_LIB + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE}" + OUTPUT_NAME ${PROJECT_NAME} + ) +endif() + +message(STATUS "Added all header and implementation files.\n") + +# +# Set the project standard and warnings +# + +if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_17) +else() + target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17) + + if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) + target_compile_features(${PROJECT_NAME}_LIB PUBLIC cxx_std_17) + endif() +endif() +include(cmake/CompilerWarnings.cmake) +set_project_warnings(${PROJECT_NAME}) + +verbose_message("Applied compiler warnings. Using standard ${CMAKE_CXX_STANDARD}.\n") + +# +# Enable Doxygen +# + +include(cmake/Doxygen.cmake) + +# +# Model project dependencies +# + +# Identify and link with the specific "packages" the project uses +#find_package(package_name package_version REQUIRED package_type [other_options]) +#target_link_libraries( +# ${PROJECT_NAME} +# PUBLIC +# dependency1 ... +# PRIVATE +# dependency2 ... +# ${PROJECT_NAME}_PROJECT_OPTIONS +# ${PROJECT_NAME}_PROJECT_WARNINGS +#) +#if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) +# target_link_libraries( +# ${PROJECT_NAME}_LIB +# PUBLIC +# dependency1 ... +# ) +#endif() + +# For Windows, it is necessary to link with the MultiThreaded library. +# Depending on how the rest of the project's dependencies are linked, it might be necessary +# to change the line to statically link with the library. +# +# This is done as follows: +# +# set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +# +# On Linux and Mac this variable is ignored. If any issues rise from it, try commenting it out +# and letting CMake decide how to link with it. +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL") + +verbose_message("Successfully added all dependencies and linked against them.") + +# +# Set the build/user include directories +# + +# Allow usage of header files in the `src` directory, but only for utilities +if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + target_include_directories( + ${PROJECT_NAME} + INTERFACE + $ + $ + ) +else() + target_include_directories( + ${PROJECT_NAME} + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + if(${PROJECT_NAME}_BUILD_EXECUTABLE AND ${PROJECT_NAME}_ENABLE_UNIT_TESTING) + target_include_directories( + ${PROJECT_NAME}_LIB + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + endif() +endif() + +message(STATUS "Finished setting up include directories.") + +# +# Provide alias to library for +# + +if(${PROJECT_NAME}_BUILD_EXECUTABLE) + add_executable(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +else() + add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) +endif() + +verbose_message("Project is now aliased as ${PROJECT_NAME}::${PROJECT_NAME}.\n") + +# +# Format the project using the `clang-format` target (i.e: cmake --build build --target clang-format) +# + +add_clang_format_target() + +# +# Install library for easy downstream inclusion +# + +include(GNUInstallDirs) +install( + TARGETS + ${PROJECT_NAME} + EXPORT + ${PROJECT_NAME}Targets + LIBRARY DESTINATION + ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION + ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION + ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION + include + PUBLIC_HEADER DESTINATION + include +) + +install( + EXPORT + ${PROJECT_NAME}Targets + FILE + ${PROJECT_NAME}Targets.cmake + NAMESPACE + ${PROJECT_NAME}:: + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +# +# Add version header +# + +configure_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/version.hpp.in + include/${PROJECT_NAME_LOWERCASE}/version.hpp + @ONLY +) + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/include/${PROJECT_NAME_LOWERCASE}/version.hpp + DESTINATION + include/${PROJECT_NAME_LOWERCASE} +) + +# +# Install the `include` directory +# + +install( + DIRECTORY + include/${PROJECT_NAME_LOWERCASE} + DESTINATION + include +) + +verbose_message("Install targets successfully built. Install with `cmake --build --target install --config `.") + +# +# Quick `ConfigVersion.cmake` creation +# + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + ${PROJECT_NAME}ConfigVersion.cmake + VERSION + ${PROJECT_VERSION} + COMPATIBILITY + SameMajorVersion +) + +configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/${PROJECT_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} +) + +# +# Generate export header if specified +# + +if(${PROJECT_NAME}_GENERATE_EXPORT_HEADER) + include(GenerateExportHeader) + generate_export_header(${PROJECT_NAME}) + install( + FILES + ${PROJECT_BINARY_DIR}/${PROJECT_NAME_LOWERCASE}_export.h + DESTINATION + include + ) + + message(STATUS "Generated the export header `${PROJECT_NAME_LOWERCASE}_export.h` and installed it.") +endif() + +message(STATUS "Finished building requirements for installing the package.\n") + +# +# Unit testing setup +# + +if(${PROJECT_NAME}_ENABLE_UNIT_TESTING) + enable_testing() + message(STATUS "Build unit tests for the project. Tests should always be found in the test folder\n") + add_subdirectory(test) +endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8743dc3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,203 @@ +# Contributing to [INSERT PROJECT NAME] + +The [INSERT PROJECT NAME] team encourages community feedback and contributions. +Thank you for your interest in making [INSERT PROJECT NAME] better! There are several +ways you can get involved. + +If you are looking for a good way to contribute to the project, please: + +* have a look at the [available issue templates](https://github.com/filipdutescu/modern-cpp-template/issues/new/choose) +and checkout the [examples of good first issues](https://github.com/filipdutescu/modern-cpp-template/contribute) +(or [click here](https://github.com/filipdutescu/modern-cpp-template/labels/good%20first%20issue)). + +* look through the [issues that need help](https://github.com/filipdutescu/modern-cpp-template/labels/help%20wanted). + +* take a look at a [Pull Request template](PULL_REQUEST_TEMPLATE.md) to get yourself +started. + +## Reporting issues and suggesting new features + +If you find that the project is not working properly, please file a report using +the [Bug Report template](https://github.com/filipdutescu/modern-cpp-template/issues/new?assignees=&labels=bug&template=bug_report.md&title=[BUG]). +Should the template provided not suit your needs, feel free to make a +[custom Bug Report](https://github.com/filipdutescu/modern-cpp-template/issues/new/choose), +but please label it accordingly. + +We are happy to hear your ideas for how to further improve [INSERT PROJECT NAME], +ensuring it suits your needs. Check the [Issues](https://github.com/filipdutescu/modern-cpp-template/issues) +and see if others have submitted similar feedback. You can upvote existing feedback +(using the thumbs up reaction/by commenting) or [submit a new suggestion](https://github.com/filipdutescu/modern-cpp-template/labels/feature). + +We always look at upvoted items in [Issues](https://github.com/filipdutescu/modern-cpp-template/issues) +when we decide what to work on next. We read the comments and we look forward to +hearing your input. + +## Finding issues you can help with + +Looking for something to work on? +Issues marked [`good first issue`](https://github.com/filipdutescu/modern-cpp-template/labels/good%20first%20issue) +are a good place to start. + +You can also check the [`help wanted`](https://github.com/filipdutescu/modern-cpp-template/labels/help%20wanted) +tag to find other issues to help with. If you're interested in working on a fix, +leave a comment to let everyone know and to help avoid duplicated effort from others. + +## Contributions we accept + +We highly appreciate any contributions that help us improve the end product, with +a high emphasis being put on any bug fixes you can manage to create and direct +improvements which address the top issues reported by Calculator users. Some general +guidelines: + +### DOs + +* **DO** create one pull request per Issue, and ensure that the Issue is linked +in the pull request. You can follow the [Pull Request Template](PULL_REQUEST_TEMPLATE.md) +for this. + +* **DO** follow our [Coding and Style](#style-guidelines) guidelines, and keep code +changes as small as possible. + +* **DO** include corresponding tests whenever possible. + +* **DO** check for additional occurrences of the same problem in other parts of the +codebase before submitting your PR. + +* **DO** link the issue you are addressing in the pull request. + +* **DO** write a good description for your pull request. More detail is better. +Describe *why* the change is being made and *why* you have chosen a particular solution. +Describe any manual testing you performed to validate your change. + +### DO NOTs + +* **DO NOT** merge multiple changes into one PR unless they have the same root cause. +* **DO NOT** merge directly into the master branch. + +> Submitting a pull request for an approved Issue is not a guarantee it will be approved. +> The change must meet our high bar for code quality, architecture and performance. + +## Making changes to the code + +### Preparing your development environment + +To learn how to build the code and run tests, follow the instructions in the [README](README.md). + +### Style guidelines + +The code in this project uses several different coding styles, depending on the +age and history of the code. Please attempt to match the style of surrounding +code as much as possible. In new components, prefer the patterns described in the +[C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). + +### Code formatting + +***Run clang-format*** + +Use the following commands from the project's root directory to run clang-format +(must be installed on the host system). + +**1. Run the CMake target for `clang-format`:** + +```bash +cmake --build build --target clang-format +``` + +**2. Using clang-format:** + +```bash +# !!! clang-format does not run recursively in subdirectories !!! +# for each .cpp file modified +clang-format -i *.cpp + +# for each .h file modified +clang-format -i *.h + +# for each .hpp file modified +clang-format -i *.hpp +``` + +**3. Using TheLartians' Format.cmake:** + +```bash +cmake -Htest -Bbuild/test + +# view changes +cmake --build build/test --target format + +# apply changes +cmake --build build/test --target fix-format +``` + +See [Format.cmake](https://github.com/TheLartians/Format.cmake) for more options. + +### Testing + +Your change should include tests to verify new functionality wherever possible. +Code should be structured so that it can be unit tested independently of the UI. +Manual test cases should be used where automated testing is not feasible. + +### Git workflow + +The core principle of the project, when it comes to Git workflows is that the +`master` branch should always be in a healthy state which is ready for release. +Every commit on master should be deployable on push. To ensure this, pull request +**must not** be made directly on master. **Each change** should either be made in +the **development branch** (named a variation of development, i.e. `dev`) or in a +separate branch, named as a short summary of the change. + +If your change is complex, please clean up the branch history before submitting a +pull request. You can use [git rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) +to group your changes into a small number of commits which we can review one at a +time. + +When completing a pull request, we will generally squash your changes into a single +commit. After confirming that the change works as intended, the branch *might* be +deleted, in order to prevent branch polluting. Please let us know if your pull request +needs to be merged as separate commits. + +### Continuous Integration + +For this project, CI is provided by [GitHub Actions](https://github.com/features/actions), +with workflows found in the [`.github/workflows` folder](.github/workflows). Workflows +are run automatically on every commit made on the master branch, unless told to skip +for that particular commit. + +To skip CI runs on a particular commit, include either `[skip ci]` or `[ci skip]` +in the commit message. + +```bash +# an example of a commit message that would not trigger CI workflows +git commit -m "my normal commit message [skip ci]" +# or +git commit -m "my normal commit message [ci skip]" +``` + +## Review process + +After submitting a pull request, members of the team will review your code. We will +assign the request to an appropriate reviewer (if applicable). Any member of the +community may participate in the review, but at least one member of the project team +will ultimately approve the request. + +Often, multiple iterations or discussions will be needed to responding to feedback +from reviewers. Try looking at [past pull requests](https://github.com/filipdutescu/modern-cpp-template/pulls?q=is%3Apr+is%3Aclosed) +to see what the experience might be like. + +## Contributor License Agreement + +Before we can review and accept a pull request from you, you'll need to sign a +Contributor License Agreement (CLA). The CLA ensures that the community is free +to use your contributions. Signing the CLA is a manual process, and you need to +do it for each pull request made. This is done by checking the boxes in the +[Pull Request Readiness Checklist of a Pull Request](PULL_REQUEST_TEMPLATE.md#Pull-Request-Readiness-Checklist). + +### IMPORTANT + +***Checking the aforementioned boxes means that you agree to provide your change +and/or code FREE TO USE and SUBJECT TO CHANGES for the entire community!*** + +You don't need to sign a CLA until you're ready to create a pull request. When your +pull request is created, it is reviewed by a team member which, if the change is +trivial (i.e. you just fixed a typo) will be labelled as `cla-not-required`. +Otherwise, it's classified as `cla-required`, if not already signed. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..44076e5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +FROM ubuntu:18.04 + +RUN echo "Updating Ubuntu" +RUN apt-get update && apt-get upgrade -y + +RUN echo "Installing dependencies..." +RUN apt install -y \ + ccache \ + clang \ + clang-format \ + clang-tidy \ + cppcheck \ + curl \ + doxygen \ + gcc \ + git \ + graphviz \ + make \ + ninja-build \ + python3 \ + python3-pip \ + tar \ + unzip \ + vim + +RUN echo "Installing dependencies not found in the package repos..." + +RUN apt install -y wget tar build-essential libssl-dev && \ + wget https://github.com/Kitware/CMake/releases/download/v3.15.0/cmake-3.15.0.tar.gz && \ + tar -zxvf cmake-3.15.0.tar.gz && \ + cd cmake-3.15.0 && \ + ./bootstrap && \ + make && \ + make install + +RUN pip3 install conan + +RUN git clone https://github.com/catchorg/Catch2.git && \ + cd Catch2 && \ + cmake -Bbuild -H. -DBUILD_TESTING=OFF && \ + cmake --build build/ --target install + +# Disabled pthread support for GTest due to linking errors +RUN git clone https://github.com/google/googletest.git --branch release-1.10.0 && \ + cd googletest && \ + cmake -Bbuild -Dgtest_disable_pthreads=1 && \ + cmake --build build --config Release && \ + cmake --build build --target install --config Release + +RUN git clone https://github.com/microsoft/vcpkg -b 2020.06 && \ + cd vcpkg && \ + ./bootstrap-vcpkg.sh -disableMetrics -useSystemBinaries diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d2bdaef --- /dev/null +++ b/Makefile @@ -0,0 +1,63 @@ +.PHONY: install coverage test docs help +.DEFAULT_GOAL := help + +define BROWSER_PYSCRIPT +import os, webbrowser, sys + +try: + from urllib import pathname2url +except: + from urllib.request import pathname2url + +webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) +endef +export BROWSER_PYSCRIPT + +define PRINT_HELP_PYSCRIPT +import re, sys + +for line in sys.stdin: + match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) + if match: + target, help = match.groups() + print("%-20s %s" % (target, help)) +endef +export PRINT_HELP_PYSCRIPT + +BROWSER := python -c "$$BROWSER_PYSCRIPT" +INSTALL_LOCATION := ~/.local + +help: + @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) + +test: ## run tests quickly with ctest + rm -rf build/ + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dmodern-cpp-template_ENABLE_UNIT_TESTING=1 -DCMAKE_BUILD_TYPE="Release" + cmake --build build --config Release + cd build/ && ctest -C Release -VV + +coverage: ## check code coverage quickly GCC + rm -rf build/ + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dmodern-cpp-template_ENABLE_CODE_COVERAGE=1 + cmake --build build --config Release + cd build/ && ctest -C Release -VV + cd .. && (bash -c "find . -type f -name '*.gcno' -exec gcov -pb {} +" || true) + +docs: ## generate Doxygen HTML documentation, including API docs + rm -rf docs/ + rm -rf build/ + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -DProject_ENABLE_DOXYGEN=1 + cmake --build build --config Release + cmake --build build --target doxygen-docs + $(BROWSER) docs/html/index.html + +install: ## install the package to the `INSTALL_LOCATION` + rm -rf build/ + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) + cmake --build build --config Release + cmake --build build --target install --config Release + +format: ## format the project sources + rm -rf build/ + cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) + cmake --build build --target clang-format diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..85a2582 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,72 @@ +**IMPORTANT: Please do not create a Pull Request without creating an issue first.** + +*Any change needs to be discussed before proceeding. Failure to do so may result +in the rejection of the pull request.* + +Please provide enough information so that others can review your pull request. You +can skip this if you're fixing a typo or adding an app to the Showcase. + +Explain the **details** for making this change. What existing problem does the pull +request solve? + +Ex: + +1. If you "Added a/changed the function to do X", explain why: + + * it is necessary to have a way to do X. + * if there already exists a way, why is your implementation better + +2. If you "Fixed bug/error in X", explain: + + * what was the bug/error (if you already made an issue, please link to it here) + * how does your implementation fix the issue + +### Code style and formatting + +Check the [Contributors Style Guidelines section](CONTRIBUTING.md#Style-guidelines) +for how to write your code and the [Contributors Code Formatting section](CONTRIBUTING.md#Code-formatting) +for how to format your code. + +#### Closing Issues + +Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes +(if such). + +--- + +Fixes #XXXX + +## Proposed changes + +* +* +* + +## Motivation behind changes + +### Test plan + +Demonstrate the code is solid. Example: The exact commands you ran and their output, +screenshots / videos if the pull request changes UI. + +*Make sure tests pass on all of the [relevant CI workflows](https://github.com/filipdutescu/modern-cpp-template/actions).* + +### Pull Request Readiness Checklist + +See details at [CONTRIBUTING.md](https://github.com/filipdutescu/modern-cpp-template/blob/master/CONTRIBUTING.md). + +* [ ] I agree to contribute to the project under [INSERT PROJECT NAME] (Unlicense) +[License](LICENSE). + +* [ ] To the best of my knowledge, the proposed patch is not based on a code under +GPL or other license that is incompatible with [INSERT PROJECT NAME] + +* [ ] The PR is proposed to proper branch + +* [ ] There is reference to original bug report and related work + +* [ ] There is accuracy test, performance test and test data in the repository, +if applicable + +* [ ] The feature is well documented and sample code can be built with the project +CMake diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 0000000..2b5d58e --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,99 @@ +# from here: +# +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Avai +# lable.md +# Courtesy of Jason Turner + +function(set_project_warnings project_name) + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss + # of data + /w14254 # 'operator': conversion from 'type1:field_bits' to + # 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class + # virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not + # virtual instances of this class may not be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable + # declared in the for-loop is used outside the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing + # an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected + # operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend + # 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may + # cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined + # conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a + # parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to + # track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual + # function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output + # (ie printf) + ) + + if (${PROJECT_NAME}_WARNINGS_AS_ERRORS) + set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) + set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) + endif() + + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks + # do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were + # probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + + if(MSVC) + set(PROJECT_WARNINGS ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") + endif() + + if(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS}) + else() + target_compile_options(${project_name} PUBLIC ${PROJECT_WARNINGS}) + endif() + + if(NOT TARGET ${project_name}) + message(AUTHOR_WARNING "${project_name} is not a target, thus no compiler warning were added.") + endif() +endfunction() diff --git a/cmake/Conan.cmake b/cmake/Conan.cmake new file mode 100644 index 0000000..b844e2a --- /dev/null +++ b/cmake/Conan.cmake @@ -0,0 +1,46 @@ +if(${PROJECT_NAME}_ENABLE_CONAN) + # + # Setup Conan requires and options here: + # + + set(${PROJECT_NAME}_CONAN_REQUIRES "") + set(${PROJECT_NAME}_CONAN_OPTIONS "") + + # + # If `conan.cmake` (from https://github.com/conan-io/cmake-conan) does not exist, download it. + # + if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") + message( + STATUS + "Downloading conan.cmake from https://github.com/conan-io/cmake-conan..." + ) + file( + DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.15/conan.cmake" + "${CMAKE_BINARY_DIR}/conan.cmake" + ) + message(STATUS "Cmake-Conan downloaded succesfully.") + endif() + + include(${CMAKE_BINARY_DIR}/conan.cmake) + + conan_add_remote( + NAME bincrafters + URL + https://api.bintray.com/conan/bincrafters/public-conan + ) + + conan_cmake_run( + REQUIRES + ${${PROJECT_NAME}_CONAN_REQUIRES} + OPTIONS + ${${PROJECT_NAME}_CONAN_OPTIONS} + BASIC_SETUP + CMAKE_TARGETS # Individual targets to link to + BUILD + missing + ) + + conan_basic_setup() + + verbose_message("Conan is setup and all requires have been installed.") +endif() diff --git a/cmake/Doxygen.cmake b/cmake/Doxygen.cmake new file mode 100644 index 0000000..bd6fe45 --- /dev/null +++ b/cmake/Doxygen.cmake @@ -0,0 +1,11 @@ +if(${PROJECT_NAME}_ENABLE_DOXYGEN) + set(DOXYGEN_CALLER_GRAPH YES) + set(DOXYGEN_CALL_GRAPH YES) + set(DOXYGEN_EXTRACT_ALL YES) + set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/docs) + + find_package(Doxygen REQUIRED dot) + doxygen_add_docs(doxygen-docs ${PROJECT_SOURCE_DIR}) + + verbose_message("Doxygen has been setup and documentation is now available.") +endif() diff --git a/cmake/ProjectConfig.cmake.in b/cmake/ProjectConfig.cmake.in new file mode 100644 index 0000000..2ac739c --- /dev/null +++ b/cmake/ProjectConfig.cmake.in @@ -0,0 +1,9 @@ +set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) + +@PACKAGE_INIT@ + +set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + +check_required_components(@PROJECT_NAME@) diff --git a/cmake/SourcesAndHeaders.cmake b/cmake/SourcesAndHeaders.cmake new file mode 100644 index 0000000..b637967 --- /dev/null +++ b/cmake/SourcesAndHeaders.cmake @@ -0,0 +1,16 @@ +set(sources + src/tmp.cpp +) + +set(exe_sources + src/main.cpp + ${sources} +) + +set(headers + include/project/tmp.hpp +) + +set(test_sources + src/tmp_test.cpp +) diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake new file mode 100644 index 0000000..fd77929 --- /dev/null +++ b/cmake/StandardSettings.cmake @@ -0,0 +1,95 @@ +# +# Project settings +# + +option(${PROJECT_NAME}_BUILD_EXECUTABLE "Build the project as an executable, rather than a library." OFF) +option(${PROJECT_NAME}_BUILD_HEADERS_ONLY "Build the project as a header-only library." OFF) +option(${PROJECT_NAME}_USE_ALT_NAMES "Use alternative names for the project, such as naming the include directory all lowercase." ON) + +# +# Compiler options +# + +option(${PROJECT_NAME}_WARNINGS_AS_ERRORS "Treat compiler warnings as errors." OFF) + +# +# Package managers +# +# Currently supporting: Conan, Vcpkg. + +option(${PROJECT_NAME}_ENABLE_CONAN "Enable the Conan package manager for this project." OFF) +option(${PROJECT_NAME}_ENABLE_VCPKG "Enable the Vcpkg package manager for this project." OFF) + +# +# Unit testing +# +# Currently supporting: GoogleTest, Catch2. + +option(${PROJECT_NAME}_ENABLE_UNIT_TESTING "Enable unit tests for the projects (from the `test` subfolder)." ON) + +option(${PROJECT_NAME}_USE_GTEST "Use the GoogleTest project for creating unit tests." ON) +option(${PROJECT_NAME}_USE_GOOGLE_MOCK "Use the GoogleMock project for extending the unit tests." OFF) + +option(${PROJECT_NAME}_USE_CATCH2 "Use the Catch2 project for creating unit tests." OFF) + +# +# Static analyzers +# +# Currently supporting: Clang-Tidy, Cppcheck. + +option(${PROJECT_NAME}_ENABLE_CLANG_TIDY "Enable static analysis with Clang-Tidy." OFF) +option(${PROJECT_NAME}_ENABLE_CPPCHECK "Enable static analysis with Cppcheck." OFF) + +# +# Code coverage +# + +option(${PROJECT_NAME}_ENABLE_CODE_COVERAGE "Enable code coverage through GCC." OFF) + +# +# Doxygen +# + +option(${PROJECT_NAME}_ENABLE_DOXYGEN "Enable Doxygen documentation builds of source." OFF) + +# +# Miscelanious options +# + +# Generate compile_commands.json for clang based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(${PROJECT_NAME}_VERBOSE_OUTPUT "Enable verbose output, allowing for a better understanding of each step taken." ON) +option(${PROJECT_NAME}_GENERATE_EXPORT_HEADER "Create a `project_export.h` file containing all exported symbols." OFF) + +# Export all symbols when building a shared library +if(BUILD_SHARED_LIBS) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS OFF) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +endif() + +option(${PROJECT_NAME}_ENABLE_LTO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)." OFF) +if(${PROJECT_NAME}_ENABLE_LTO) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(SEND_ERROR "IPO is not supported: ${output}.") + endif() +endif() + + +option(${PROJECT_NAME}_ENABLE_CCACHE "Enable the usage of Ccache, in order to speed up rebuild times." ON) +find_program(CCACHE_FOUND ccache) +if(CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) +endif() + +option(${PROJECT_NAME}_ENABLE_ASAN "Enable Address Sanitize to detect memory error." OFF) +if(${PROJECT_NAME}_ENABLE_ASAN) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() diff --git a/cmake/StaticAnalyzers.cmake b/cmake/StaticAnalyzers.cmake new file mode 100644 index 0000000..10e4da4 --- /dev/null +++ b/cmake/StaticAnalyzers.cmake @@ -0,0 +1,20 @@ +if(${PROJECT_NAME}_ENABLE_CLANG_TIDY) + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option) + message("Clang-Tidy finished setting up.") + else() + message(SEND_ERROR "Clang-Tidy requested but executable not found.") + endif() +endif() + +if(${PROJECT_NAME}_ENABLE_CPPCHECK) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --suppress=missingInclude --enable=all + --inline-suppr --inconclusive -i ${CMAKE_SOURCE_DIR}/imgui/lib) + message("Cppcheck finished setting up.") + else() + message(SEND_ERROR "Cppcheck requested but executable not found.") + endif() +endif() diff --git a/cmake/Utils.cmake b/cmake/Utils.cmake new file mode 100644 index 0000000..754c7cd --- /dev/null +++ b/cmake/Utils.cmake @@ -0,0 +1,40 @@ +# +# Print a message only if the `VERBOSE_OUTPUT` option is on +# + +function(verbose_message content) + if(${PROJECT_NAME}_VERBOSE_OUTPUT) + message(STATUS ${content}) + endif() +endfunction() + +# +# Add a target for formating the project using `clang-format` (i.e: cmake --build build --target clang-format) +# + +function(add_clang_format_target) + if(NOT ${PROJECT_NAME}_CLANG_FORMAT_BINARY) + find_program(${PROJECT_NAME}_CLANG_FORMAT_BINARY clang-format) + endif() + + if(${PROJECT_NAME}_CLANG_FORMAT_BINARY) + if(${PROJECT_NAME}_BUILD_EXECUTABLE) + add_custom_target(clang-format + COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} + -i ${exe_sources} ${headers} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + elseif(${PROJECT_NAME}_BUILD_HEADERS_ONLY) + add_custom_target(clang-format + COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} + -i ${headers} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + else() + add_custom_target(clang-format + COMMAND ${${PROJECT_NAME}_CLANG_FORMAT_BINARY} + -i ${sources} ${headers} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + endif() + + message(STATUS "Format the project using the `clang-format` target (i.e: cmake --build build --target clang-format).\n") + endif() +endfunction() diff --git a/cmake/Vcpkg.cmake b/cmake/Vcpkg.cmake new file mode 100644 index 0000000..1c13e38 --- /dev/null +++ b/cmake/Vcpkg.cmake @@ -0,0 +1,20 @@ +if(${PROJECT_NAME}_ENABLE_VCPKG) + # + # If `vcpkg.cmake` (from https://github.com/microsoft/vcpkg) does not exist, download it. + # + if(NOT EXISTS "${CMAKE_BINARY_DIR}/vcpkg.cmake") + message( + STATUS + "Downloading `vcpkg.cmake` from https://github.com/microsoft/vcpkg..." + ) + file(DOWNLOAD "https://github.com/microsoft/vcpkg/raw/master/scripts/buildsystems/vcpkg.cmake" + "${CMAKE_BINARY_DIR}/vcpkg.cmake" + ) + message(STATUS "Vcpkg config downloaded succesfully.") + endif() + + if(${PROJECT_NAME}_VERBOSE_OUTPUT) + set(VCPKG_VERBOSE ON) + endif() + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" "${CMAKE_BINARY_DIR}/vcpkg.cmake") +endif() diff --git a/cmake/version.hpp.in b/cmake/version.hpp.in new file mode 100644 index 0000000..985bad6 --- /dev/null +++ b/cmake/version.hpp.in @@ -0,0 +1,11 @@ +#ifndef @PROJECT_NAME_UPPERCASE@_VERSION_H_ +#define @PROJECT_NAME_UPPERCASE@_VERSION_H_ + +#define @PROJECT_NAME_UPPERCASE@_VERSION "@PROJECT_VERSION@" + +#define @PROJECT_NAME_UPPERCASE@_MAJOR_VERSION @PROJECT_VERSION_MAJOR@ +#define @PROJECT_NAME_UPPERCASE@_MINOR_VERSION @PROJECT_VERSION_MINOR@ +#define @PROJECT_NAME_UPPERCASE@_PATCH_VERSION @PROJECT_VERSION_PATCH@ + +#endif // @PROJECT_NAME_UPPERCASE@_VERSION_H_ + diff --git a/codecov.yaml b/codecov.yaml new file mode 100644 index 0000000..b2c991f --- /dev/null +++ b/codecov.yaml @@ -0,0 +1,5 @@ +ignore: + - "test" + +comment: + require_changes: true diff --git a/include/attribute.h b/include/attribute.h deleted file mode 100644 index cc02fe7..0000000 --- a/include/attribute.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __ATTRIBUTE_H__ -#define __ATTRIBUTE_H__ -#include -#include - -typedef struct attribute_info -{ - uint16_t attribute_name_index; - uint32_t attribute_length; - uint8_t *info; -}attribute_info; - -// JVM SPEC REF: 4.7.5 -// for exception throwing methods -typedef struct exception_table -{ - uint16_t attribute_name_index; - uint32_t attribute_length; - uint16_t number_of_exceptions; - uint16_t *exception_index_table; -}exception_table; - -// A Code attribute contains the Java Virtual Machine instructions and auxiliary -// information for a method, including an instance initialization method and a class or -// interface initialization method -typedef struct Code_attribute -{ - uint16_t attribute_name_index; - uint32_t attribute_length; - uint16_t max_stack; - uint16_t max_locals; - uint32_t code_length; - uint8_t* code; - uint16_t exception_table_lenght; - exception_table* exceptionTable; - uint16_t attributes_count; - attribute_info* attributes; -} Code_attribute; - -attribute_info parse_attribute_info(FILE* file); -Code_attribute get_code_attribute(attribute_info response); -#endif diff --git a/include/bigendian.h b/include/bigendian.h deleted file mode 100644 index c544a21..0000000 --- a/include/bigendian.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __BIGENDIAN_H__ -#define __BIGENDIAN_H__ - -#define be8toh(x) __builtin_bswap8(x) -#define be16toh(x) __builtin_bswap16(x) -#define be32toh(x) __builtin_bswap32(x) -#define be64toh(x) __builtin_bswap64(x) - -#endif diff --git a/include/class.h b/include/class.h deleted file mode 100644 index 02dfe4c..0000000 --- a/include/class.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef __CLASS_H__ -#define __CLASS_H__ -#include -#include -#include "../include/constant_pool.h" -#include "./attribute.h" -#include "./method.h" -#include "./field.h" -typedef struct classfile -{ - uint32_t magic; - uint16_t minor_version; - uint16_t major_version; - uint16_t constant_pool_count; - cp_info *constant_pool; - uint16_t access_flags; - uint16_t this_class; - uint16_t super_class; - uint16_t interface_count; - uint16_t *interfaces; - uint16_t field_count; - field_info* fileds; - uint16_t method_count; - method_info *methods; - uint16_t attribute_count; - attribute_info *attributes; -}classfile; - -classfile read_classfile(const char* filename); -#endif diff --git a/include/constant_pool.h b/include/constant_pool.h deleted file mode 100644 index 9a1b64a..0000000 --- a/include/constant_pool.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef __CONSTANT_POOL_H__ -#define __CONSTANT_POOL_H__ - -#include -#include - -// Tag Identifier -typedef enum cp_tags{ - CONSTANT_Class=7, - CONSTANT_Fieldref=9, - CONSTANT_Methodref=10, - CONSTANT_InterfaceMethodref=11, - CONSTANT_String=8, - CONSTANT_Integer=3, - CONSTANT_Float=4, - CONSTANT_Long=5, - CONSTANT_Double=6, - CONSTANT_NameAndType=12, - CONSTANT_Utf8=1, - CONSTANT_MethodHandle=15, - CONSTANT_MethodType=16, - CONSTANT_InvokeDynamic=18 -}cp_tags; - -/* constants START */ -typedef struct CONSTANT_Class_Info -{ - uint16_t name_index; -}CONSTANT_Class_Info; - -typedef struct CONSTANT_Ref_Info -{ - uint16_t class_index; - uint16_t name_and_type_index; -}CONSTANT_Ref_Info; - -typedef struct CONSTANT_String_Info -{ - uint16_t string_index; -}CONSTANT_String_Info; - -typedef struct CONSTANT_4BYTES_Info -{ - uint32_t bytes; -}CONSTANT_4BYTES_Info; - -typedef struct CONSTANT_8BYTES_Info -{ - uint64_t bytes; -}CONSTANT_8BYTES_Info; - -typedef struct CONSTANT_NameAndType_Info -{ - uint16_t name_index; - uint16_t descriptor_index; -}CONSTANT_NameAndType_Info; - -typedef struct CONSTANT_Utf8_Info -{ - uint16_t length; - uint8_t* bytes; -}CONSTANT_Utf8_Info; - -typedef struct CONSTANT_MethodHandle_Info -{ - uint8_t reference_kind; - uint16_t reference_index; -}CONSTANT_MethodHandle_Info; - -typedef struct CONSTANT_MethodType_Info -{ - uint16_t descriptor_index; -}CONSTANT_MethodType_Info; - -typedef struct CONSTANT_InvokeDynamic_Info -{ - uint16_t boostrap; - uint16_t name_and_type_index; -}CONSTANT_InvokeDynamic_Info; - -typedef enum primitive_types_array - { - T_BOOLEAN = 4, - T_CHAR = 5, - T_FLOAT = 6, - T_BYTE = 8, - T_SHORT = 9, - T_INT = 10, - T_LONG = 11 - }primitive_types_array; - -//primitive types -typedef struct CONSTANT_Array_Info -{ - int32_t size; - primitive_types_array ptarray; - uint8_t* ref; -}CONSTANT_Array_Info; -/* constants END */ - -// constant pool -typedef struct constant_info -{ - CONSTANT_Class_Info class_info; - CONSTANT_Ref_Info ref_info; - CONSTANT_String_Info string_info; - CONSTANT_4BYTES_Info _4BYTES_info; - CONSTANT_8BYTES_Info _8BYTES_info; - CONSTANT_NameAndType_Info nameAndType_info; - CONSTANT_Utf8_Info utf8_info; - CONSTANT_MethodHandle_Info methodHandle_info; - CONSTANT_MethodType_Info methodType_info; - CONSTANT_InvokeDynamic_Info invokeDynamic_info; -}constant_info; - -// Meta-Information of constant pool -typedef struct cp_info -{ - uint8_t tag; - constant_info info; -}cp_info; - -cp_info parse_constant_pool(FILE *file); -void free_constant_pool(constant_info constant_info); -#endif diff --git a/include/cvm.h b/include/cvm.h deleted file mode 100644 index d63d09d..0000000 --- a/include/cvm.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - - -#ifdef _WIN32 - #define CVM_EXPORT __declspec(dllexport) -#else - #define CVM_EXPORT -#endif - -CVM_EXPORT void cvm(); diff --git a/include/field.h b/include/field.h deleted file mode 100644 index 2f8b366..0000000 --- a/include/field.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __FIELD_H__ -#define __FIELD_H__ -#include "./attribute.h" -#include - -typedef enum field_access_flags{ - F_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package. - F_ACC_PRIVATE = 0x0002, // Declared private; usable only within the defining class. - F_ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses. - F_ACC_STATIC = 0x0008, // Declared static. - F_ACC_FINAL = 0x0010, // Declared final; never directly assigned to after object construction (JLS §17.5). - F_ACC_VOLATILE = 0x0040, // Declared volatile; cannot be cached. - F_ACC_TRANSIENT = 0x0080, // Declared transient; not written or read by a persistent object manager. - F_ACC_SYNTHETIC = 0x1000, // Declared synthetic; not present in the source code. - F_ACC_ENUM = 0x4000 // Declared as an element of an enum. -}access_flags; - -typedef struct field_info { - uint16_t access_flags; - uint16_t name_index; - uint16_t descriptor_index; - uint16_t attributes_count; - attribute_info* attributes; - uint64_t value; -}field_info; - -field_info parse_filed_info(FILE* file); - -#endif diff --git a/include/jvm_heap.h b/include/jvm_heap.h deleted file mode 100644 index 0e00de2..0000000 --- a/include/jvm_heap.h +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef __JVM_HEAP_H__ -#define __JVM_HEAP_H__ -#endif diff --git a/include/jvm_stack.h b/include/jvm_stack.h deleted file mode 100644 index aba1edc..0000000 --- a/include/jvm_stack.h +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef __JVM_STACK_H__ -#define __JVM_STACK_H__ -#endif diff --git a/include/method.h b/include/method.h deleted file mode 100644 index 64f3546..0000000 --- a/include/method.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __METHOD_H__ -#define __METHOD_H__ -#include "./attribute.h" -#include "./field.h" -typedef struct field_info method_info; // same as field info - -typedef enum method_access_flags{ - M_ACC_PUBLIC = 0x0001, // Declared public; may be accessed from outside its package. - M_ACC_PRIVATE = 0x0002, // Declared private; accessible only within the defining class. - M_ACC_PROTECTED = 0x0004, // Declared protected; may be accessed within subclasses. - M_ACC_STATIC = 0x0008, // Declared static. - M_ACC_FINAL = 0x0010, // Declared final; must not be overridden (§5.4.5). - M_ACC_SYNCHRONIZED = 0x0020, // Declared synchronized; invocation is wrapped by a monitor use. - M_ACC_BRIDGE = 0x0040, // A bridge method, generated by the compiler. - M_ACC_VARARGS = 0x0080, // Declared with variable number of arguments. - M_ACC_NATIVE = 0x0100, // Declared native; implemented in a language other than Java. - M_ACC_ABSTRACT = 0x0400, // Declared abstract; no implementation is provided. - M_ACC_STRICT = 0x0800, // Declared strictfp; floating-point mode is FP-strict. - M_ACC_SYNTHETIC = 0x1000 // Declared synthetic; not present in the source code. -}method_access_flags; - -#define getMethod_Info(fd) parse_filed_info(fd) // same too, no need to reimplement -#endif diff --git a/include/project/tmp.hpp b/include/project/tmp.hpp new file mode 100644 index 0000000..a7af7ae --- /dev/null +++ b/include/project/tmp.hpp @@ -0,0 +1,9 @@ +#ifndef TMP_TMP_H_ +#define TMP_TMP_H_ + +namespace tmp +{ + int add(int, int); +} + +#endif // TMP_TMP_H_ diff --git a/src/attribute.c b/src/attribute.c deleted file mode 100644 index 443de3a..0000000 --- a/src/attribute.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "../include/attribute.h" -#include "../include/bigendian.h" -#include -#include - -attribute_info parse_attribute_info(FILE* fd) -{ - attribute_info attribute; - fread(&attribute.attribute_name_index,sizeof(attribute.attribute_name_index),1,fd); - attribute.attribute_name_index = be16toh(attribute.attribute_name_index); - - fread(&attribute.attribute_length,sizeof(attribute.attribute_length),1,fd); - attribute.attribute_length = be32toh(attribute.attribute_length); - - attribute.info = malloc(attribute.attribute_length); // leak - fread(attribute.info,attribute.attribute_length,1,fd); - return attribute; -} -Code_attribute get_code_attribute(attribute_info attributeInfo) -{ - Code_attribute attribute; - attribute.attribute_name_index = attributeInfo.attribute_name_index; - attribute.attribute_length = attributeInfo.attribute_length; - - int memOffset=0; - memcpy(&attribute.max_stack, attributeInfo.info+memOffset,sizeof(attribute.max_stack)); - attribute.max_stack = be16toh(attribute.max_stack); - memOffset +=sizeof(attribute.max_stack); - - memcpy(&attribute.max_locals, attributeInfo.info+memOffset,sizeof(attribute.max_locals)); - attribute.max_locals = be16toh(attribute.max_locals); - memOffset +=sizeof(attribute.max_locals); - - memcpy(&attribute.code_length, attributeInfo.info+memOffset,sizeof(attribute.code_length)); - attribute.code_length = be32toh(attribute.code_length); - memOffset +=sizeof(attribute.code_length); - - attribute.code = attributeInfo.info+memOffset; // leak - memOffset +=attribute.code_length; - - return attribute; -} diff --git a/src/class.c b/src/class.c deleted file mode 100644 index cb8f803..0000000 --- a/src/class.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "../include/class.h" -#include "../include/constant_pool.h" -#include "../include/method.h" -#include -#include - -classfile read_classfile(const char* filename) -{ - classfile classfile; - FILE *fileptr; - fileptr = fopen(filename, "rb"); - printf("(-) [CLASS INFO START]\n\n"); - fread(&classfile.magic, sizeof(classfile.magic), 1, fileptr); - classfile.magic = be32toh(classfile.magic); - printf("\tMAGIC: %X\n", classfile.magic); - - fread(&classfile.minor_version, sizeof(classfile.minor_version), 1, fileptr); - classfile.minor_version = be16toh(classfile.minor_version); - printf("\tMINOR: %d\n", classfile.minor_version); - - fread(&classfile.major_version, sizeof(classfile.major_version), 1, fileptr); - classfile.major_version = be16toh(classfile.major_version); - printf("\tMAJOR: %d\n", classfile.major_version); - - fread(&classfile.constant_pool_count, sizeof(classfile.constant_pool_count), 1, fileptr); - classfile.constant_pool_count = be16toh(classfile.constant_pool_count); - printf("\tCONSTANT POOL COUNT: %d\n", classfile.constant_pool_count - 1); - - classfile.constant_pool = malloc(sizeof(cp_info) * (classfile.constant_pool_count - 1)); - - printf("\n\t(-) [CONST POOL INFO START]\n"); - for (int i = 0; i < classfile.constant_pool_count - 1; i++) { - classfile.constant_pool[i] = parse_constant_pool(fileptr); - printf("\t\t%d: constant:\t%d\n", i, classfile.constant_pool[i].tag); - } - printf("\t(+) [CONST POOL INFO END]\n"); - fread(&classfile.access_flags, sizeof(classfile.access_flags), 1, fileptr); - classfile.access_flags = be16toh(classfile.access_flags); - printf("\n\tACCESS FLAGS:\t%d\n", classfile.access_flags); - fread(&classfile.this_class, sizeof(classfile.this_class), 1, fileptr); - classfile.this_class = be16toh(classfile.this_class); - printf("\tTHIS CLASS:\t%d\n", classfile.this_class); - fread(&classfile.super_class, sizeof(classfile.super_class), 1, fileptr); - classfile.super_class = be16toh(classfile.super_class); - printf("\tSUPER CLASS:\t%d\n", classfile.super_class); - fread(&classfile.interface_count, sizeof(classfile.interface_count), 1, fileptr); - printf("\tINTERFACE COUNT:\t%d\n", classfile.interface_count ); - - printf("\n\t(-) [INTERFACE INFO START]\n"); - classfile.interfaces = malloc(classfile.interface_count * sizeof(uint16_t)); - if(classfile.interface_count < 1) { - printf("\t\t***\tNo interface provided in bytecode\t***"); - } else { - for(int j = 0; j < classfile.interface_count; j++){ - uint16_t interface; - fread(&interface, sizeof(interface), 1, fileptr); - classfile.interfaces[j] = be16toh(interface); - printf("\t\t%d: interface:\t%d\n", j, classfile.interfaces[j]); - } - } - printf("\n\t(+) [INTERFACE INFO END]\n"); - - fread(&classfile.field_count, sizeof(classfile.field_count), 1, fileptr); - classfile.field_count = be16toh(classfile.field_count); - printf("\n\tFIELD COUNT:\t%d\n", classfile.field_count); - // TODO: fileds - fread(&classfile.method_count, sizeof(classfile.method_count), 1, fileptr); - classfile.method_count = be16toh(classfile.method_count); - printf("\tMETHOD COUNT:\t%d\n", classfile.method_count); - - printf("\n\t(-) [METHOD INFO START]\n"); - classfile.methods = malloc(sizeof(method_info) * classfile.method_count); - for (int method_counter = 0; method_counter < classfile.method_count; method_counter++) { - classfile.methods[method_counter] = getMethod_Info(fileptr); - printf("\t\tmethod:\t%x\n", classfile.methods[method_counter]); - } - printf("\t(+) [METHOD INFO END]\n"); - - fread(&classfile.attribute_count, sizeof(classfile.attribute_count), 1, fileptr); - classfile.attribute_count = be16toh(classfile.attribute_count); - printf("\n\tATTRIBUTE COUNT:\t%d\n", classfile.attribute_count); - - classfile.attributes = malloc((sizeof(attribute_info) * classfile.attribute_count)); - printf("\n\t(-) ATTRIBUTE INFO START\n"); - for(int att_iterator = 0; att_iterator < classfile.attribute_count; att_iterator++){ - classfile.attributes[att_iterator] = parse_attribute_info(fileptr); - printf("\t\t%d: attribute:\t%d\n", att_iterator, classfile.attributes[att_iterator]); - } - printf("\t(-) ATTRIBUTE INFO END\n\n"); - printf("(+) [CLASS INFO END]\n"); - //TODO - fclose(fileptr); - return classfile; -} - - -//driver -// TODO: DELETE -int main(int argc, char** argv) -{ - read_classfile(argv[1]); - return 0; -} diff --git a/src/constant_pool.c b/src/constant_pool.c deleted file mode 100644 index 0fe1af0..0000000 --- a/src/constant_pool.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "../include/constant_pool.h" -#include "../include/class.h" -#include -#include - - -cp_info parse_constant_pool(FILE* fd){ - cp_info info; - fread(&info.tag,sizeof(info.tag),1,fd); // tag len is 1 - switch (info.tag){ - case CONSTANT_Class: - fread(&info.info.class_info.name_index,sizeof(info.info.class_info.name_index),1,fd); - info.info.class_info.name_index = be16toh(info.info.class_info.name_index); // big order to host - break; - case CONSTANT_Fieldref: - case CONSTANT_Methodref: - case CONSTANT_InterfaceMethodref: - fread(&info.info.ref_info.class_index,sizeof(info.info.ref_info.class_index),1,fd); - fread(&info.info.ref_info.name_and_type_index,sizeof(info.info.ref_info.name_and_type_index),1,fd); - info.info.ref_info.class_index = be16toh(info.info.ref_info.class_index); - info.info.ref_info.name_and_type_index = be16toh(info.info.ref_info.name_and_type_index); - break; - case CONSTANT_String: - fread(&info.info.string_info.string_index,sizeof(info.info.string_info.string_index),1,fd); - info.info.string_info.string_index = be16toh(info.info.string_info.string_index); - break; - case CONSTANT_Integer: - case CONSTANT_Float: - fread(&info.info._4BYTES_info.bytes,sizeof(info.info._4BYTES_info.bytes),1,fd); - info.info._4BYTES_info.bytes = be32toh(info.info._4BYTES_info.bytes); - break; - case CONSTANT_Long: - case CONSTANT_Double: - fread(&info.info._8BYTES_info.bytes,sizeof(info.info._8BYTES_info.bytes),1,fd); - info.info._8BYTES_info.bytes = be64toh(info.info._8BYTES_info.bytes); - break; - case CONSTANT_NameAndType: - fread(&info.info.nameAndType_info.name_index,sizeof(info.info.nameAndType_info.name_index),1,fd); - fread(&info.info.nameAndType_info.descriptor_index,sizeof(info.info.nameAndType_info.descriptor_index),1,fd); - info.info.nameAndType_info.name_index = be16toh(info.info.nameAndType_info.name_index); - info.info.nameAndType_info.descriptor_index = be16toh(info.info.nameAndType_info.descriptor_index); - break; - case CONSTANT_Utf8:; // compiler wants a statement after label. - fread(&info.info.utf8_info.length,sizeof(info.info.utf8_info.length),1,fd); // len - info.info.utf8_info.length = be16toh(info.info.utf8_info.length); - info.info.utf8_info.bytes = malloc(info.info.utf8_info.length); //leak - fread(info.info.utf8_info.bytes,info.info.utf8_info.length,1,fd); // offsetting 2 bytes + reading utf8 - break; - case CONSTANT_MethodHandle:; // compiler wants a statement after label. - fread(&info.info.methodHandle_info.reference_kind,sizeof(info.info.methodHandle_info.reference_kind),1,fd); // reference kind - fread(&info.info.methodHandle_info.reference_index,sizeof(info.info.methodHandle_info.reference_index),1,fd); // ref index - info.info.methodHandle_info.reference_index = be16toh(info.info.methodHandle_info.reference_index); - break; - case CONSTANT_MethodType: - fread(&info.info.methodType_info.descriptor_index,sizeof(info.info.methodType_info.descriptor_index),1,fd); - info.info.methodType_info.descriptor_index = be16toh(info.info.methodType_info.descriptor_index); - break; - case CONSTANT_InvokeDynamic: - fread(&info.info.invokeDynamic_info.boostrap,sizeof(info.info.invokeDynamic_info.boostrap),1,fd); - fread(&info.info.invokeDynamic_info.name_and_type_index,sizeof(info.info.invokeDynamic_info.name_and_type_index),1,fd); - info.info.invokeDynamic_info.boostrap = be16toh(info.info.invokeDynamic_info.boostrap); - info.info.invokeDynamic_info.name_and_type_index = be16toh(info.info.invokeDynamic_info.name_and_type_index); - break; - - default: - printf("Unsupported tag: %d\n", info.tag); - break; - } - return info; - -} diff --git a/src/cvm.c b/src/cvm.c deleted file mode 100644 index e69de29..0000000 diff --git a/src/field.c b/src/field.c deleted file mode 100644 index ff42a06..0000000 --- a/src/field.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "../include/field.h" -#include "../include/bigendian.h" -#include "../include/constant_pool.h" -#include -#include - -field_info parse_filed_info(FILE* fd){ - field_info field; - fread(&field.access_flags,sizeof(field.access_flags),1,fd); - field.access_flags = be16toh(field.access_flags); - - fread(&field.name_index,sizeof(field.name_index),1,fd); - field.name_index = be16toh(field.name_index); - - fread(&field.descriptor_index,sizeof(field.descriptor_index),1,fd); - field.descriptor_index = be16toh(field.descriptor_index); - - fread(&field.attributes_count,sizeof(field.attributes_count),1,fd); - field.attributes_count = be16toh(field.attributes_count); - - field.attributes = malloc (sizeof(attribute_info) * field.attributes_count); - for (int attributeCount=0; attributeCount - -int main(int argc, char** argv) -{ - return 0; -} diff --git a/src/tmp.cpp b/src/tmp.cpp new file mode 100644 index 0000000..7a4f8fe --- /dev/null +++ b/src/tmp.cpp @@ -0,0 +1,3 @@ +#include "project/tmp.hpp" + +int tmp::add(int a, int b) { return a + b; } diff --git a/test/Add.java b/test/Add.java deleted file mode 100644 index 697621b..0000000 --- a/test/Add.java +++ /dev/null @@ -1,5 +0,0 @@ -public class Add { - public static int Add(int a, int b) { - return a + b; - } -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..71f07af --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 3.15) + +# +# Project details +# + +project( + ${CMAKE_PROJECT_NAME}Tests + LANGUAGES CXX +) + +verbose_message("Adding tests under ${CMAKE_PROJECT_NAME}Tests...") + +foreach(file ${test_sources}) + string(REGEX REPLACE "(.*/)([a-zA-Z0-9_ ]+)(\.cpp)" "\\2" test_name ${file}) + add_executable(${test_name}_Tests ${file}) + + # + # Set the compiler standard + # + + target_compile_features(${test_name}_Tests PUBLIC cxx_std_17) + + # + # Setup code coverage if enabled + # + + if (${CMAKE_PROJECT_NAME}_ENABLE_CODE_COVERAGE) + target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC -O0 -g -fprofile-arcs -ftest-coverage) + target_link_options(${CMAKE_PROJECT_NAME} PUBLIC -fprofile-arcs -ftest-coverage) + verbose_message("Code coverage is enabled and provided with GCC.") + endif() + + # + # Load the desired unit testing framework + # + # Currently supported: GoogleTest (and GoogleMock), Catch2. + + if(${CMAKE_PROJECT_NAME}_BUILD_EXECUTABLE) + set(${CMAKE_PROJECT_NAME}_TEST_LIB ${CMAKE_PROJECT_NAME}_LIB) + else() + set(${CMAKE_PROJECT_NAME}_TEST_LIB ${CMAKE_PROJECT_NAME}) + endif() + + if(${CMAKE_PROJECT_NAME}_USE_GTEST) + find_package(GTest REQUIRED) + + if(${CMAKE_PROJECT_NAME}_USE_GOOGLE_MOCK) + set(GOOGLE_MOCK_LIBRARIES GTest::gmock GTest::gmock_main) + endif() + + target_link_libraries( + ${test_name}_Tests + PUBLIC + GTest::GTest + GTest::Main + ${GOOGLE_MOCK_LIBRARIES} + ${${CMAKE_PROJECT_NAME}_TEST_LIB} + ) + elseif(${CMAKE_PROJECT_NAME}_USE_CATCH2) + find_package(Catch2 REQUIRED) + target_link_libraries( + ${test_name}_Tests + PUBLIC + Catch2::Catch2 + ${${CMAKE_PROJECT_NAME}_TEST_LIB} + ) + else() + message(FATAL_ERROR "Unknown testing library. Please setup your desired unit testing library by using `target_link_libraries`.") + endif() + + # + # Add the unit tests + # + + add_test( + NAME + ${test_name} + COMMAND + ${test_name}_Tests + ) +endforeach() + +verbose_message("Finished adding unit tests for ${CMAKE_PROJECT_NAME}.") diff --git a/test/Greet.java b/test/Greet.java deleted file mode 100644 index d9c297f..0000000 --- a/test/Greet.java +++ /dev/null @@ -1,5 +0,0 @@ -public class Greet { - public static String Greet() { - return "Hello, CVM!"; - } -} diff --git a/test/constTest.java b/test/constTest.java deleted file mode 100644 index e26604c..0000000 --- a/test/constTest.java +++ /dev/null @@ -1,9 +0,0 @@ -public class constTest{ - public static int a = 10; - public static int b = 10; - public static String c = " "; - public static int add(int x , int y) - { - return x + y; - } -} diff --git a/test/greet_hexdump.txt b/test/greet_hexdump.txt deleted file mode 100644 index 02948a8..0000000 --- a/test/greet_hexdump.txt +++ /dev/null @@ -1,18 +0,0 @@ -00000000 ca fe ba be 00 00 00 40 00 10 0a 00 02 00 03 07 |.......@........| -00000010 00 04 0c 00 05 00 06 01 00 10 6a 61 76 61 2f 6c |..........java/l| -00000020 61 6e 67 2f 4f 62 6a 65 63 74 01 00 06 3c 69 6e |ang/Object......()V......H| -00000040 65 6c 6c 6f 2c 20 43 56 4d 21 07 00 0a 01 00 05 |ello, CVM!......| -00000050 47 72 65 65 74 01 00 04 43 6f 64 65 01 00 0f 4c |Greet...Code...L| -00000060 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 |ineNumberTable..| -00000070 14 28 29 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 |.()Ljava/lang/St| -00000080 72 69 6e 67 3b 01 00 0a 53 6f 75 72 63 65 46 69 |ring;...SourceFi| -00000090 6c 65 01 00 0a 47 72 65 65 74 2e 6a 61 76 61 00 |le...Greet.java.| -000000a0 21 00 09 00 02 00 00 00 00 00 02 00 01 00 05 00 |!...............| -000000b0 06 00 01 00 0b 00 00 00 1d 00 01 00 01 00 00 00 |................| -000000c0 05 2a b7 00 01 b1 00 00 00 01 00 0c 00 00 00 06 |.*..............| -000000d0 00 01 00 00 00 01 00 09 00 0a 00 0d 00 01 00 0b |................| -000000e0 00 00 00 1b 00 01 00 00 00 00 00 03 12 07 b0 00 |................| -000000f0 00 00 01 00 0c 00 00 00 06 00 01 00 00 00 03 00 |................| -00000100 01 00 0e 00 00 00 02 00 0f |.........| -00000109 diff --git a/test/greet_javapdump.txt b/test/greet_javapdump.txt deleted file mode 100644 index 6450782..0000000 --- a/test/greet_javapdump.txt +++ /dev/null @@ -1,13 +0,0 @@ -Compiled from "Greet.java" -public class Greet { - public Greet(); - Code: - 0: aload_0 - 1: invokespecial #1 // Method java/lang/Object."":()V - 4: return - - public static java.lang.String Greet(); - Code: - 0: ldc #7 // String Hello, CVM! - 2: areturn -} diff --git a/test/hexdump.txt b/test/hexdump.txt deleted file mode 100644 index c365844..0000000 --- a/test/hexdump.txt +++ /dev/null @@ -1,32 +0,0 @@ -00000000 ca fe ba be 00 00 00 40 00 0e 0a 00 02 00 03 07 |.......@........| -00000010 00 04 0c 00 05 00 06 01 00 10 6a 61 76 61 2f 6c |..........java/l| -00000020 61 6e 67 2f 4f 62 6a 65 63 74 01 00 06 3c 69 6e |ang/Object......()V......A| -00000040 64 64 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 |dd...Code...Line| -00000050 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 05 28 49 |NumberTable...(I| -00000060 49 29 49 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 |I)I...SourceFile| -00000070 01 00 08 41 64 64 2e 6a 61 76 61 00 20 00 07 00 |...Add.java. ...| -00000080 02 00 00 00 00 00 02 00 00 00 05 00 06 00 01 00 |................| -00000090 09 00 00 00 1d 00 01 00 01 00 00 00 05 2a b7 00 |.............*..| -000000a0 01 b1 00 00 00 01 00 0a 00 00 00 06 00 01 00 00 |................| -000000b0 00 01 00 09 00 08 00 0b 00 01 00 09 00 00 00 1c |................| -000000c0 00 02 00 02 00 00 00 04 1a 1b 60 ac 00 00 00 01 |..........`.....| -000000d0 00 0a 00 00 00 06 00 01 00 00 00 03 00 01 00 0c |................| -000000e0 00 00 00 02 00 0d |......| -000000e6 -00000000 ca fe ba be 00 00 00 40 00 0e 0a 00 02 00 03 07 |.......@........| -00000010 00 04 0c 00 05 00 06 01 00 10 6a 61 76 61 2f 6c |..........java/l| -00000020 61 6e 67 2f 4f 62 6a 65 63 74 01 00 06 3c 69 6e |ang/Object......()V......A| -00000040 64 64 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 |dd...Code...Line| -00000050 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 05 28 49 |NumberTable...(I| -00000060 49 29 49 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 |I)I...SourceFile| -00000070 01 00 08 41 64 64 2e 6a 61 76 61 00 21 00 07 00 |...Add.java.!...| -00000080 02 00 00 00 00 00 02 00 01 00 05 00 06 00 01 00 |................| -00000090 09 00 00 00 1d 00 01 00 01 00 00 00 05 2a b7 00 |.............*..| -000000a0 01 b1 00 00 00 01 00 0a 00 00 00 06 00 01 00 00 |................| -000000b0 00 01 00 09 00 08 00 0b 00 01 00 09 00 00 00 1c |................| -000000c0 00 02 00 02 00 00 00 04 1a 1b 60 ac 00 00 00 01 |..........`.....| -000000d0 00 0a 00 00 00 06 00 01 00 00 00 03 00 01 00 0c |................| -000000e0 00 00 00 02 00 0d |......| -000000e6 diff --git a/test/javapdump.txt b/test/javapdump.txt deleted file mode 100644 index e5de1d5..0000000 --- a/test/javapdump.txt +++ /dev/null @@ -1,30 +0,0 @@ -Compiled from "Add.java" -class Add { - Add(); - Code: - 0: aload_0 - 1: invokespecial #1 // Method java/lang/Object."":()V - 4: return - - public static int Add(int, int); - Code: - 0: iload_0 - 1: iload_1 - 2: iadd - 3: ireturn -} -Compiled from "Add.java" -public class Add { - public Add(); - Code: - 0: aload_0 - 1: invokespecial #1 // Method java/lang/Object."":()V - 4: return - - public static int Add(int, int); - Code: - 0: iload_0 - 1: iload_1 - 2: iadd - 3: ireturn -} diff --git a/test/src/tmp_test.cpp b/test/src/tmp_test.cpp new file mode 100644 index 0000000..eb88881 --- /dev/null +++ b/test/src/tmp_test.cpp @@ -0,0 +1,15 @@ +#include "project/tmp.hpp" + +#include + +TEST(TmpAddTest, CheckValues) +{ + ASSERT_EQ(tmp::add(1, 2), 3); + EXPECT_TRUE(true); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 15e703cdd8680e20d56f10bca28f678f0a16fa93 Mon Sep 17 00:00:00 2001 From: lvntky Date: Fri, 7 Jun 2024 16:24:05 +0300 Subject: [PATCH 2/9] [conf] fmt integration --- CMakeLists.txt | 7 +++++-- cmake/ProjectConfig.cmake.in | 9 --------- cmake/SourcesAndHeaders.cmake | 6 ++++-- cmake/StandardSettings.cmake | 2 +- include/project/tmp.hpp | 9 --------- src/tmp.cpp | 2 +- test/src/tmp_test.cpp | 2 +- 7 files changed, 12 insertions(+), 25 deletions(-) delete mode 100644 cmake/ProjectConfig.cmake.in delete mode 100644 include/project/tmp.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 777c9ad..e047a80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 3.15) # project( - "Project" - VERSION 0.1.0 + CVM + VERSION 2.0.0 LANGUAGES CXX ) @@ -174,6 +174,9 @@ include(cmake/Doxygen.cmake) # dependency1 ... # ) #endif() +find_package(fmt REQUIRED) + +target_link_libraries(${PROJECT_NAME} fmt::fmt) # For Windows, it is necessary to link with the MultiThreaded library. # Depending on how the rest of the project's dependencies are linked, it might be necessary diff --git a/cmake/ProjectConfig.cmake.in b/cmake/ProjectConfig.cmake.in deleted file mode 100644 index 2ac739c..0000000 --- a/cmake/ProjectConfig.cmake.in +++ /dev/null @@ -1,9 +0,0 @@ -set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) - -@PACKAGE_INIT@ - -set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") - -include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") - -check_required_components(@PROJECT_NAME@) diff --git a/cmake/SourcesAndHeaders.cmake b/cmake/SourcesAndHeaders.cmake index b637967..9c9a1f1 100644 --- a/cmake/SourcesAndHeaders.cmake +++ b/cmake/SourcesAndHeaders.cmake @@ -8,8 +8,10 @@ set(exe_sources ) set(headers - include/project/tmp.hpp -) + include/cvm/tmp.hpp + include/cvm/fmt_commons.hpp + ) + set(test_sources src/tmp_test.cpp diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake index fd77929..58a6db0 100644 --- a/cmake/StandardSettings.cmake +++ b/cmake/StandardSettings.cmake @@ -2,7 +2,7 @@ # Project settings # -option(${PROJECT_NAME}_BUILD_EXECUTABLE "Build the project as an executable, rather than a library." OFF) +option(${PROJECT_NAME}_BUILD_EXECUTABLE "Build the project as an executable, rather than a library." ON) option(${PROJECT_NAME}_BUILD_HEADERS_ONLY "Build the project as a header-only library." OFF) option(${PROJECT_NAME}_USE_ALT_NAMES "Use alternative names for the project, such as naming the include directory all lowercase." ON) diff --git a/include/project/tmp.hpp b/include/project/tmp.hpp deleted file mode 100644 index a7af7ae..0000000 --- a/include/project/tmp.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef TMP_TMP_H_ -#define TMP_TMP_H_ - -namespace tmp -{ - int add(int, int); -} - -#endif // TMP_TMP_H_ diff --git a/src/tmp.cpp b/src/tmp.cpp index 7a4f8fe..92a6e42 100644 --- a/src/tmp.cpp +++ b/src/tmp.cpp @@ -1,3 +1,3 @@ -#include "project/tmp.hpp" +#include "cvm/tmp.hpp" int tmp::add(int a, int b) { return a + b; } diff --git a/test/src/tmp_test.cpp b/test/src/tmp_test.cpp index eb88881..afc5152 100644 --- a/test/src/tmp_test.cpp +++ b/test/src/tmp_test.cpp @@ -1,4 +1,4 @@ -#include "project/tmp.hpp" +#include "cvm/tmp.hpp" #include From 0e81635466d983ce7d22984aab0be9dbb2a3f890 Mon Sep 17 00:00:00 2001 From: lvntky Date: Fri, 7 Jun 2024 16:24:44 +0300 Subject: [PATCH 3/9] [conf] fmt integration --- cmake/CVMConfig.cmake.in | 9 +++++++++ include/cvm/fmt_commons.hpp | 21 +++++++++++++++++++++ include/cvm/tmp.hpp | 9 +++++++++ src/main.cpp | 8 ++++++++ 4 files changed, 47 insertions(+) create mode 100644 cmake/CVMConfig.cmake.in create mode 100644 include/cvm/fmt_commons.hpp create mode 100644 include/cvm/tmp.hpp create mode 100644 src/main.cpp diff --git a/cmake/CVMConfig.cmake.in b/cmake/CVMConfig.cmake.in new file mode 100644 index 0000000..2ac739c --- /dev/null +++ b/cmake/CVMConfig.cmake.in @@ -0,0 +1,9 @@ +set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) + +@PACKAGE_INIT@ + +set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") + +check_required_components(@PROJECT_NAME@) diff --git a/include/cvm/fmt_commons.hpp b/include/cvm/fmt_commons.hpp new file mode 100644 index 0000000..0fe9584 --- /dev/null +++ b/include/cvm/fmt_commons.hpp @@ -0,0 +1,21 @@ +#ifndef FMT_COMMONS_HPP_ +#define FMT_COMMONS_HPP_ + +/** + * + * A colleciton of fmt API libraries + * might not be a good idea from compiler view + * but handy when coding the CVMpp :3 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // FMT_COMMONS_HPP_ diff --git a/include/cvm/tmp.hpp b/include/cvm/tmp.hpp new file mode 100644 index 0000000..a7af7ae --- /dev/null +++ b/include/cvm/tmp.hpp @@ -0,0 +1,9 @@ +#ifndef TMP_TMP_H_ +#define TMP_TMP_H_ + +namespace tmp +{ + int add(int, int); +} + +#endif // TMP_TMP_H_ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..3bdf395 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,8 @@ +#include "cvm/fmt_commons.hpp" + +int main () +{ + + fmt::print("Hello {}, version: {}\n", "CVM", "2.0.0"); + return 0; +} \ No newline at end of file From 473f43b06b7af40f5cbe7e65ba4c4c8efe366a70 Mon Sep 17 00:00:00 2001 From: lvntky Date: Fri, 7 Jun 2024 16:31:57 +0300 Subject: [PATCH 4/9] [format] formatted code --- include/cvm/fmt_commons.hpp | 10 +++++----- src/main.cpp | 7 +++---- src/tmp.cpp | 5 ++++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/cvm/fmt_commons.hpp b/include/cvm/fmt_commons.hpp index 0fe9584..c7a4760 100644 --- a/include/cvm/fmt_commons.hpp +++ b/include/cvm/fmt_commons.hpp @@ -9,13 +9,13 @@ * */ +#include +#include #include #include -#include -#include -#include #include -#include #include +#include +#include -#endif // FMT_COMMONS_HPP_ +#endif // FMT_COMMONS_HPP_ diff --git a/src/main.cpp b/src/main.cpp index 3bdf395..6625a1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,7 @@ #include "cvm/fmt_commons.hpp" -int main () +int main() { - - fmt::print("Hello {}, version: {}\n", "CVM", "2.0.0"); - return 0; + fmt::print("Hello {}, version: {}\n", "CVM", "2.0.0"); + return 0; } \ No newline at end of file diff --git a/src/tmp.cpp b/src/tmp.cpp index 92a6e42..e1fb382 100644 --- a/src/tmp.cpp +++ b/src/tmp.cpp @@ -1,3 +1,6 @@ #include "cvm/tmp.hpp" -int tmp::add(int a, int b) { return a + b; } +int tmp::add(int a, int b) +{ + return a + b; +} From 87df7ca8954160fe3504d1eb0068bd756035635f Mon Sep 17 00:00:00 2001 From: lvntky Date: Fri, 7 Jun 2024 16:44:24 +0300 Subject: [PATCH 5/9] [test] added sample .class to run --- sample/Add.class | Bin 0 -> 236 bytes sample/Add.java | 5 +++++ 2 files changed, 5 insertions(+) create mode 100644 sample/Add.class create mode 100644 sample/Add.java diff --git a/sample/Add.class b/sample/Add.class new file mode 100644 index 0000000000000000000000000000000000000000..f6c7455a63bdac693ff4da409f8013596bceb399 GIT binary patch literal 236 zcmZ8aI|{;35S*8PVl>u%3cJL@TtEaBgn($FSo@MZBq1gcqNlR45-dD`hZ6T$y2Z}y z&dl!n^LznVz;mELHKDoCVK8)ae&u1o%Tu_GbCEO*#xyIlX2ziU!NG(@WS*uB+OkRo zgSXB~vALXMQSW(N&_?A_*8M07A_iwyUFt-vGAUV Date: Sat, 15 Jun 2024 01:09:35 +0300 Subject: [PATCH 6/9] [feature] added load class file method --- cmake/SourcesAndHeaders.cmake | 4 ++-- docs/banner.jpg | Bin 94309 -> 0 bytes include/cvm/class_loader.hpp | 23 +++++++++++++++++++++++ include/cvm/tmp.hpp | 9 --------- sample/javap_output.txt | 15 +++++++++++++++ src/class_loader.cpp | 22 ++++++++++++++++++++++ src/main.cpp | 18 +++++++++++++++--- src/tmp.cpp | 6 ------ test/src/tmp_test.cpp | 4 ++-- 9 files changed, 79 insertions(+), 22 deletions(-) delete mode 100644 docs/banner.jpg create mode 100644 include/cvm/class_loader.hpp delete mode 100644 include/cvm/tmp.hpp create mode 100644 sample/javap_output.txt create mode 100644 src/class_loader.cpp delete mode 100644 src/tmp.cpp diff --git a/cmake/SourcesAndHeaders.cmake b/cmake/SourcesAndHeaders.cmake index 9c9a1f1..48c8e44 100644 --- a/cmake/SourcesAndHeaders.cmake +++ b/cmake/SourcesAndHeaders.cmake @@ -1,5 +1,5 @@ set(sources - src/tmp.cpp + src/class_loader.cpp ) set(exe_sources @@ -8,8 +8,8 @@ set(exe_sources ) set(headers - include/cvm/tmp.hpp include/cvm/fmt_commons.hpp + include/cvm/class_loader.hpp ) diff --git a/docs/banner.jpg b/docs/banner.jpg deleted file mode 100644 index 3929966ccc4c0da445c4131b71bf8d285d6ea3a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94309 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.yBN`y!ZGpd8n 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 diff --git a/include/cvm/class_loader.hpp b/include/cvm/class_loader.hpp new file mode 100644 index 0000000..948e915 --- /dev/null +++ b/include/cvm/class_loader.hpp @@ -0,0 +1,23 @@ +#ifndef __CLASS_LOADER_HPP__ +#define __CLASS_LOADER_HPP__ + +#include +#include +#include +#include +#include + +#include "fmt_commons.hpp" + +class ClassLoader +{ + public: + std::vector loadClassFile(const std::string& filePath); + void parseClassFile(const std::vector& buffer); + + private: + void verifyMagicNumber(const std::vector& buffer); + void parseVersion(const std::vector& buffer); +}; + +#endif //__CLASS_LOADER_HPP__ diff --git a/include/cvm/tmp.hpp b/include/cvm/tmp.hpp deleted file mode 100644 index a7af7ae..0000000 --- a/include/cvm/tmp.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef TMP_TMP_H_ -#define TMP_TMP_H_ - -namespace tmp -{ - int add(int, int); -} - -#endif // TMP_TMP_H_ diff --git a/sample/javap_output.txt b/sample/javap_output.txt new file mode 100644 index 0000000..422a4d1 --- /dev/null +++ b/sample/javap_output.txt @@ -0,0 +1,15 @@ +Compiled from "Add.java" +public class Add { + public Add(); + Code: + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."":()V + 4: return + + public static int add(int, int); + Code: + 0: iload_0 + 1: iload_1 + 2: iadd + 3: ireturn +} diff --git a/src/class_loader.cpp b/src/class_loader.cpp new file mode 100644 index 0000000..438ddf6 --- /dev/null +++ b/src/class_loader.cpp @@ -0,0 +1,22 @@ +#include "../include/cvm/class_loader.hpp" + +std::vector ClassLoader::loadClassFile(const std::string& filePath) +{ + std::ifstream file(filePath, std::ios::binary | std::ios::ate); + + if (file.is_open()) + { + fmt::system_error(errno, "cannot open class file {}", filePath); + } + + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector buffer(size); + if (!file.read(reinterpret_cast(buffer.data()), size)) + { + fmt::system_error(errno, "Failed to load class file into memory", filePath); + } + + return buffer; +} diff --git a/src/main.cpp b/src/main.cpp index 6625a1d..8893264 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,19 @@ -#include "cvm/fmt_commons.hpp" +#include "../include/cvm/class_loader.hpp" +#include "../include/cvm/fmt_commons.hpp" -int main() +int main(int argc, char** argv) { fmt::print("Hello {}, version: {}\n", "CVM", "2.0.0"); + ClassLoader classLoader; + + if (argc < 2) + { + throw std::runtime_error("Please specify .class file path as an argument."); + } + + std::string filePath = argv[1]; + std::vector buffer = classLoader.loadClassFile(filePath); + + fmt::print("The class file buffer: {}\n", buffer); return 0; -} \ No newline at end of file +} diff --git a/src/tmp.cpp b/src/tmp.cpp deleted file mode 100644 index e1fb382..0000000 --- a/src/tmp.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "cvm/tmp.hpp" - -int tmp::add(int a, int b) -{ - return a + b; -} diff --git a/test/src/tmp_test.cpp b/test/src/tmp_test.cpp index afc5152..71c4d71 100644 --- a/test/src/tmp_test.cpp +++ b/test/src/tmp_test.cpp @@ -1,10 +1,10 @@ -#include "cvm/tmp.hpp" +//#include "cvm/tmp.hpp" #include TEST(TmpAddTest, CheckValues) { - ASSERT_EQ(tmp::add(1, 2), 3); + //ASSERT_EQ(tmp::add(1, 2), 3); EXPECT_TRUE(true); } From 00b507d96a87f14f938d565528545b8c922d6ee2 Mon Sep 17 00:00:00 2001 From: lvntky Date: Sat, 15 Jun 2024 23:59:04 +0300 Subject: [PATCH 7/9] [feature] base parsing methods and class file --- cmake/SourcesAndHeaders.cmake | 4 ++-- include/cvm/class_loader.hpp | 23 ------------------ include/cvm/classfile/classfile.hpp | 36 +++++++++++++++++++++++++++++ include/cvm/cvm_commons.hpp | 22 ++++++++++++++++++ src/class_loader.cpp | 22 ------------------ src/classfile/classfile.cpp | 26 +++++++++++++++++++++ src/main.cpp | 31 +++++++++++++++++++++---- 7 files changed, 112 insertions(+), 52 deletions(-) delete mode 100644 include/cvm/class_loader.hpp create mode 100644 include/cvm/classfile/classfile.hpp create mode 100644 include/cvm/cvm_commons.hpp delete mode 100644 src/class_loader.cpp create mode 100644 src/classfile/classfile.cpp diff --git a/cmake/SourcesAndHeaders.cmake b/cmake/SourcesAndHeaders.cmake index 48c8e44..1341ccc 100644 --- a/cmake/SourcesAndHeaders.cmake +++ b/cmake/SourcesAndHeaders.cmake @@ -1,5 +1,5 @@ set(sources - src/class_loader.cpp + src/classfile/classfile.cpp ) set(exe_sources @@ -9,7 +9,7 @@ set(exe_sources set(headers include/cvm/fmt_commons.hpp - include/cvm/class_loader.hpp + include/cvm/classfile/classfile.hpp ) diff --git a/include/cvm/class_loader.hpp b/include/cvm/class_loader.hpp deleted file mode 100644 index 948e915..0000000 --- a/include/cvm/class_loader.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __CLASS_LOADER_HPP__ -#define __CLASS_LOADER_HPP__ - -#include -#include -#include -#include -#include - -#include "fmt_commons.hpp" - -class ClassLoader -{ - public: - std::vector loadClassFile(const std::string& filePath); - void parseClassFile(const std::vector& buffer); - - private: - void verifyMagicNumber(const std::vector& buffer); - void parseVersion(const std::vector& buffer); -}; - -#endif //__CLASS_LOADER_HPP__ diff --git a/include/cvm/classfile/classfile.hpp b/include/cvm/classfile/classfile.hpp new file mode 100644 index 0000000..bf0b349 --- /dev/null +++ b/include/cvm/classfile/classfile.hpp @@ -0,0 +1,36 @@ +#ifndef __CLASSFILE_HPP__ +#define __CLASSFILE_HPP__ + +#include +#include +#include +#include + +#include "../fmt_commons.hpp" + +class Classfile +{ + public: + uint32_t magic; + uint16_t minor_version; + uint16_t major_version; + uint16_t constant_pool_count; + // TODO: cp_info constant_pool[constant_pool_count-1]; + uint16_t access_flags; + uint16_t this_class; + uint16_t super_class; + uint16_t interfaces_count; + std::vector interfaces; + uint16_t fields_coun; + // TODO: field_info fields[fields_count]; + uint16_t methods_count; + // method_info methods[methods_count]; + uint16_t attributes_count; + // TODO: attribute_info attributes[attributes_count]; + + uint16_t readShort(const uint8_t *bytes, size_t &offset); + uint32_t readInt(const uint8_t *bytes, size_t &offset); + Classfile parseClassfile(const uint8_t *classBytes, size_t fileSize); +}; + +#endif //__CLASSFILE_HPP__ diff --git a/include/cvm/cvm_commons.hpp b/include/cvm/cvm_commons.hpp new file mode 100644 index 0000000..4c6ca4a --- /dev/null +++ b/include/cvm/cvm_commons.hpp @@ -0,0 +1,22 @@ +#ifndef __CVM_COMMONS_HPP__ +#define __CVM_COMMONS_HPP__ + +/** + * This header mostly contains + * JVM spec to C++ convertions + * i.e Oracle's JVM spec uses uint32_t as u4 + * So i created this header to convert this type of values to C++. + * Not mandatory to use and maybe confusing sometimes + * But I liked to add it in order to be compatible with JVM spec + * Referance : https://docs.oracle.com/javase/specs/jvms/se8/html/ + */ + + +#include + +using u1 = uint8_t; // JVM unsigned byte (8 bits) +using u2 = uint16_t; // JVM unsigned short (16 bits) +using u4 = uint32_t; // JVM unsigned int (32 bits) +using u8 = uint64_t; // JVM unsigned long long (64 bits) + +#endif //__CVM_COMMONS_HPP__ diff --git a/src/class_loader.cpp b/src/class_loader.cpp deleted file mode 100644 index 438ddf6..0000000 --- a/src/class_loader.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "../include/cvm/class_loader.hpp" - -std::vector ClassLoader::loadClassFile(const std::string& filePath) -{ - std::ifstream file(filePath, std::ios::binary | std::ios::ate); - - if (file.is_open()) - { - fmt::system_error(errno, "cannot open class file {}", filePath); - } - - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - - std::vector buffer(size); - if (!file.read(reinterpret_cast(buffer.data()), size)) - { - fmt::system_error(errno, "Failed to load class file into memory", filePath); - } - - return buffer; -} diff --git a/src/classfile/classfile.cpp b/src/classfile/classfile.cpp new file mode 100644 index 0000000..4bc351a --- /dev/null +++ b/src/classfile/classfile.cpp @@ -0,0 +1,26 @@ +#include "../../include/cvm/classfile/classfile.hpp" + +uint16_t Classfile::readShort(const uint8_t* bytes, size_t& offset) +{ + uint16_t value = (bytes[offset] << 8 | bytes[offset + 1]); + offset += 2; + return value; +} + +uint32_t Classfile::readInt(const uint8_t* bytes, size_t& offset) +{ + uint32_t value = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; + offset += 4; + return value; +} + +Classfile Classfile::parseClassfile(const uint8_t* classBytes, size_t fileSize) +{ + size_t offset = 0; + Classfile cf; + + // Read magic number + cf.magic = readInt(classBytes, offset); + + return cf; +} diff --git a/src/main.cpp b/src/main.cpp index 8893264..d0ebc72 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,19 +1,40 @@ -#include "../include/cvm/class_loader.hpp" +#include "../include/cvm/classfile/classfile.hpp" #include "../include/cvm/fmt_commons.hpp" int main(int argc, char** argv) + { fmt::print("Hello {}, version: {}\n", "CVM", "2.0.0"); - ClassLoader classLoader; if (argc < 2) { throw std::runtime_error("Please specify .class file path as an argument."); } - std::string filePath = argv[1]; - std::vector buffer = classLoader.loadClassFile(filePath); + std::string classFilePath = argv[1]; + std::ifstream classFile(classFilePath, std::ios::binary); + + if (!classFile) + { + throw std::runtime_error("Cannot open class file at specified path."); + } + + classFile.seekg(0, std::ios::end); + size_t fileSize = classFile.tellg(); + classFile.seekg(0, std::ios::beg); + + std::vector buffer(fileSize); + if (!classFile.read(reinterpret_cast(buffer.data()), fileSize)) + { + throw std::runtime_error("Error while reading class data."); + return 1; + } + + Classfile cf; + cf = cf.parseClassfile(buffer.data(), fileSize); + + std::cout << "Magic: 0x" << std::hex << cf.magic << std::endl; + classFile.close(); - fmt::print("The class file buffer: {}\n", buffer); return 0; } From 096c8886af4b945d5159ed56a05fe8cc9b82384b Mon Sep 17 00:00:00 2001 From: lvntky Date: Sun, 16 Jun 2024 12:51:16 +0300 Subject: [PATCH 8/9] [feature] copnstant pool parsing --- include/cvm/classfile/classfile.hpp | 3 +- include/cvm/classfile/cp_info.hpp | 39 ++++++++++++++++++++ include/cvm/log.hpp | 9 +++++ src/classfile/classfile.cpp | 57 +++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 include/cvm/classfile/cp_info.hpp create mode 100644 include/cvm/log.hpp diff --git a/include/cvm/classfile/classfile.hpp b/include/cvm/classfile/classfile.hpp index bf0b349..4768f51 100644 --- a/include/cvm/classfile/classfile.hpp +++ b/include/cvm/classfile/classfile.hpp @@ -7,6 +7,7 @@ #include #include "../fmt_commons.hpp" +#include "cp_info.hpp" class Classfile { @@ -15,7 +16,7 @@ class Classfile uint16_t minor_version; uint16_t major_version; uint16_t constant_pool_count; - // TODO: cp_info constant_pool[constant_pool_count-1]; + std::vector constant_pool; uint16_t access_flags; uint16_t this_class; uint16_t super_class; diff --git a/include/cvm/classfile/cp_info.hpp b/include/cvm/classfile/cp_info.hpp new file mode 100644 index 0000000..d555208 --- /dev/null +++ b/include/cvm/classfile/cp_info.hpp @@ -0,0 +1,39 @@ +#ifndef __CP_INF0_HPP__ +#define __CP_INF0_HPP__ + +#include + +// Constants for constant pool tags +#define CONSTANT_Class 7 +#define CONSTANT_Fieldref 9 +#define CONSTANT_Methodref 10 +#define CONSTANT_InterfaceMethodref 11 +#define CONSTANT_String 8 +#define CONSTANT_Integer 3 +#define CONSTANT_NameAndType 12 + +typedef struct cpinfo { + + uint8_t tag; + union{ + struct{ + uint16_t name_index; + }Class; + struct { + uint16_t string_index; + }String; + struct { + uint16_t class_index; + uint16_t name_and_type_index; + } Fieldref, Methodref, InterfaceMethodref; + struct { + int32_t bytes; + } Integer; + struct { + uint16_t name_index; + uint16_t descriptor_index; + } NameAndType; + }info; +} cp_info; + +#endif //__CP_INF0_HPP__ diff --git a/include/cvm/log.hpp b/include/cvm/log.hpp new file mode 100644 index 0000000..a0858b2 --- /dev/null +++ b/include/cvm/log.hpp @@ -0,0 +1,9 @@ +#ifndef __LOG_HPP__ +#define __LOG_HPP__ + +#include + +#define LOG_OK "OK" +#define LOG_NOK "NOK" + +#endif //__LOG_HPP__ diff --git a/src/classfile/classfile.cpp b/src/classfile/classfile.cpp index 4bc351a..f5807c5 100644 --- a/src/classfile/classfile.cpp +++ b/src/classfile/classfile.cpp @@ -1,5 +1,7 @@ #include "../../include/cvm/classfile/classfile.hpp" +#include "../../include/cvm/log.hpp" + uint16_t Classfile::readShort(const uint8_t* bytes, size_t& offset) { uint16_t value = (bytes[offset] << 8 | bytes[offset + 1]); @@ -14,6 +16,49 @@ uint32_t Classfile::readInt(const uint8_t* bytes, size_t& offset) return value; } +/** + * Helper methods for validating etc. + */ +bool validateMagicNumber(uint32_t magic) +{ + return magic == 0xcafebabe; +} + +void parseConstantPool(const uint8_t* bytes, size_t& offset, Classfile& cf) +{ + cf.constant_pool.resize(cf.constant_pool_count - 1); + + for (uint16_t i = 1; i < cf.constant_pool_count; i++) + { + cp_info& cp_entry = cf.constant_pool[i - 1]; + cp_entry.tag = bytes[offset]; + + switch (cp_entry.tag) + { + case CONSTANT_Class: cp_entry.info.Class.name_index = cf.readShort(bytes, offset); break; + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + cp_entry.info.Fieldref.class_index = cf.readShort(bytes, offset); + cp_entry.info.Fieldref.name_and_type_index = cf.readShort(bytes, offset); + break; + case CONSTANT_String: cp_entry.info.String.string_index = cf.readShort(bytes, offset); break; + case CONSTANT_Integer: cp_entry.info.Integer.bytes = cf.readInt(bytes, offset); break; + case CONSTANT_NameAndType: + cp_entry.info.NameAndType.name_index = cf.readShort(bytes, offset); + cp_entry.info.NameAndType.descriptor_index = cf.readShort(bytes, offset); + break; + default: spdlog::error("Unsupported Constant Pool Tag: {}! Consider to implement it ? :^)", cp_entry.tag); break; + } + + cf.constant_pool.push_back(cp_entry); + } +} + +/** + * Helper methods ends + */ + Classfile Classfile::parseClassfile(const uint8_t* classBytes, size_t fileSize) { size_t offset = 0; @@ -21,6 +66,18 @@ Classfile Classfile::parseClassfile(const uint8_t* classBytes, size_t fileSize) // Read magic number cf.magic = readInt(classBytes, offset); + if (!validateMagicNumber(cf.magic)) + { + throw std::runtime_error("The magic number of classfile is not valid."); + } + else + { + spdlog::info("{} Magic number validated", LOG_OK); + } + cf.minor_version = readShort(classBytes, offset); + cf.major_version = readShort(classBytes, offset); + cf.constant_pool_count = readShort(classBytes, offset); + parseConstantPool(classBytes, offset, cf); return cf; } From 0f54dcff81e3f05f4dbb821a7a9919ba83643186 Mon Sep 17 00:00:00 2001 From: lvntky Date: Thu, 20 Jun 2024 00:41:13 +0300 Subject: [PATCH 9/9] [feature] more constant pool parsing --- build_and_run.sh | 3 ++ cmake/SourcesAndHeaders.cmake | 1 + include/cvm/classfile/classfile.hpp | 2 + include/cvm/classfile/cp_info.hpp | 9 ++++ include/cvm/log.hpp | 10 ++++ sample/Hello.class | Bin 0 -> 393 bytes sample/Hello.java | 5 ++ src/classfile/classfile.cpp | 81 +++++++++++++++++++++++++--- src/log.cpp | 22 ++++++++ 9 files changed, 127 insertions(+), 6 deletions(-) create mode 100755 build_and_run.sh create mode 100644 sample/Hello.class create mode 100644 sample/Hello.java create mode 100644 src/log.cpp diff --git a/build_and_run.sh b/build_and_run.sh new file mode 100755 index 0000000..fcc8cc0 --- /dev/null +++ b/build_and_run.sh @@ -0,0 +1,3 @@ +make format +make install +./build/bin/Debug/CVM sample/Add.class diff --git a/cmake/SourcesAndHeaders.cmake b/cmake/SourcesAndHeaders.cmake index 1341ccc..2c03a99 100644 --- a/cmake/SourcesAndHeaders.cmake +++ b/cmake/SourcesAndHeaders.cmake @@ -1,5 +1,6 @@ set(sources src/classfile/classfile.cpp + src/log.cpp ) set(exe_sources diff --git a/include/cvm/classfile/classfile.hpp b/include/cvm/classfile/classfile.hpp index 4768f51..dfe7f09 100644 --- a/include/cvm/classfile/classfile.hpp +++ b/include/cvm/classfile/classfile.hpp @@ -31,6 +31,8 @@ class Classfile uint16_t readShort(const uint8_t *bytes, size_t &offset); uint32_t readInt(const uint8_t *bytes, size_t &offset); + uint64_t readLong(const uint8_t *bytes, size_t &offset); + double readDouble(const uint8_t *bytes, size_t &offset); Classfile parseClassfile(const uint8_t *classBytes, size_t fileSize); }; diff --git a/include/cvm/classfile/cp_info.hpp b/include/cvm/classfile/cp_info.hpp index d555208..93d3d83 100644 --- a/include/cvm/classfile/cp_info.hpp +++ b/include/cvm/classfile/cp_info.hpp @@ -11,6 +11,8 @@ #define CONSTANT_String 8 #define CONSTANT_Integer 3 #define CONSTANT_NameAndType 12 +#define CONSTANT_Double 6 +#define CONSTANT_Utf8 1 typedef struct cpinfo { @@ -33,6 +35,13 @@ typedef struct cpinfo { uint16_t name_index; uint16_t descriptor_index; } NameAndType; + struct{ + uint64_t bytes; + }Double; + struct { + uint16_t length; + uint8_t* bytes; + } Utf8; }info; } cp_info; diff --git a/include/cvm/log.hpp b/include/cvm/log.hpp index a0858b2..dba74c3 100644 --- a/include/cvm/log.hpp +++ b/include/cvm/log.hpp @@ -2,8 +2,18 @@ #define __LOG_HPP__ #include +#include #define LOG_OK "OK" #define LOG_NOK "NOK" +#define DEBUG_ENABLED 1 +class Log{ +public: + static Log& getInstance(); + std::shared_ptr getLogger(); +private: + Log(); // Private constructor (singleton pattern) + std::shared_ptr logger; // The actual logger instance +}; #endif //__LOG_HPP__ diff --git a/sample/Hello.class b/sample/Hello.class new file mode 100644 index 0000000000000000000000000000000000000000..fdd0d0bfa8dc2a94254f7fba84e97c70ef87001d GIT binary patch literal 393 zcmZut%}T>S7@V(3x=l=NZLR-8dk|U?^8ghO3gV%t5UJ;+UFw!?A~6wsEIlZA@Bw@% z@!JTB;2ypmW@qMG=Hv729l!~8JXBx_*ghOo3C(%*5Cu9)X2E4Vmy?`OJyMCvj|o

MfTcP2z9oZJ5nE`P|WDwz!jV}Uh-Q|1!v({w5cjgd;^MX`uwb{)l< z8Ryn4CU{|5WD|L&4C$4wL$elLbhr(3%;Y{mSZB1$HyLLC74*b_u))(j8%)^zk?yh8 au + #include "../../include/cvm/log.hpp" +// The global logger for classfile.cpp +auto logger = Log::getInstance().getLogger(); + uint16_t Classfile::readShort(const uint8_t* bytes, size_t& offset) { + spdlog::info("Reading short at offset: {}", offset); uint16_t value = (bytes[offset] << 8 | bytes[offset + 1]); offset += 2; + spdlog::info("Read short: 0x{:04x}, new offset: {}", value, offset); return value; } uint32_t Classfile::readInt(const uint8_t* bytes, size_t& offset) { + spdlog::info("Reading int at offset: {}", offset); uint32_t value = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]; offset += 4; + spdlog::info("Read int: 0x{:08x}, new offset: {}", value, offset); + return value; +} + +uint64_t Classfile::readLong(const uint8_t* bytes, size_t& offset) +{ + spdlog::info("Reading long at offset: {}", offset); + uint64_t value = (static_cast(bytes[offset]) << 56) | (static_cast(bytes[offset + 1]) << 48) | + (static_cast(bytes[offset + 2]) << 40) | (static_cast(bytes[offset + 3]) << 32) | + (static_cast(bytes[offset + 4]) << 24) | (static_cast(bytes[offset + 5]) << 16) | + (static_cast(bytes[offset + 6]) << 8) | static_cast(bytes[offset + 7]); + offset += 8; + spdlog::info("Read long: 0x{:016x}, new offset: {}", value, offset); return value; } +double Classfile::readDouble(const uint8_t* bytes, size_t& offset) +{ + spdlog::info("Reading double at offset: {}", offset); + uint64_t longValue = readLong(bytes, offset); + double value; + std::memcpy(&value, &longValue, sizeof(value)); + spdlog::info("Read double: {}, new offset: {}", value, offset); + return value; +} /** * Helper methods for validating etc. */ @@ -27,11 +57,14 @@ bool validateMagicNumber(uint32_t magic) void parseConstantPool(const uint8_t* bytes, size_t& offset, Classfile& cf) { cf.constant_pool.resize(cf.constant_pool_count - 1); + spdlog::info("Constant pool size: {}", cf.constant_pool_count - 1); for (uint16_t i = 1; i < cf.constant_pool_count; i++) { cp_info& cp_entry = cf.constant_pool[i - 1]; - cp_entry.tag = bytes[offset]; + cp_entry.tag = bytes[offset++]; + + spdlog::info("Parsing constant pool entry {}, tag: {}, offset: {}", i, cp_entry.tag, offset); switch (cp_entry.tag) { @@ -43,15 +76,51 @@ void parseConstantPool(const uint8_t* bytes, size_t& offset, Classfile& cf) cp_entry.info.Fieldref.name_and_type_index = cf.readShort(bytes, offset); break; case CONSTANT_String: cp_entry.info.String.string_index = cf.readShort(bytes, offset); break; - case CONSTANT_Integer: cp_entry.info.Integer.bytes = cf.readInt(bytes, offset); break; + case CONSTANT_Integer: + cp_entry.info.Integer.bytes = cf.readInt(bytes, offset); + break; + /* + case CONSTANT_Float: + cp_entry.info.Float.bytes = cf.readInt(bytes, offset); + break; + case CONSTANT_Long: + cp_entry.info.Long.bytes = cf.readLong(bytes, offset); + i++; // Long occupies two entries in the constant pool + break; + */ + case CONSTANT_Double: + cp_entry.info.Double.bytes = cf.readDouble(bytes, offset); + i++; // Double occupies two entries in the constant pool + break; case CONSTANT_NameAndType: cp_entry.info.NameAndType.name_index = cf.readShort(bytes, offset); cp_entry.info.NameAndType.descriptor_index = cf.readShort(bytes, offset); break; - default: spdlog::error("Unsupported Constant Pool Tag: {}! Consider to implement it ? :^)", cp_entry.tag); break; - } - cf.constant_pool.push_back(cp_entry); + case CONSTANT_Utf8: + cp_entry.info.Utf8.length = cf.readShort(bytes, offset); + cp_entry.info.Utf8.bytes = new uint8_t[cp_entry.info.Utf8.length]; + std::memcpy(cp_entry.info.Utf8.bytes, &bytes[offset], cp_entry.info.Utf8.length); + offset += cp_entry.info.Utf8.length; + break; + /* + case CONSTANT_MethodHandle: + cp_entry.info.MethodHandle.reference_kind = bytes[offset++]; + cp_entry.info.MethodHandle.reference_index = cf.readShort(bytes, offset); + break; + case CONSTANT_MethodType: + cp_entry.info.MethodType.descriptor_index = cf.readShort(bytes, offset); + break; + case CONSTANT_InvokeDynamic: + cp_entry.info.InvokeDynamic.bootstrap_method_attr_index = cf.readShort(bytes, offset); + cp_entry.info.InvokeDynamic.name_and_type_index = cf.readShort(bytes, offset); + break; + */ + default: + spdlog::error("Unsupported Constant Pool Tag: {}! At location i: {})", cp_entry.tag, i); + offset += 2; + break; + } } } @@ -78,6 +147,6 @@ Classfile Classfile::parseClassfile(const uint8_t* classBytes, size_t fileSize) cf.major_version = readShort(classBytes, offset); cf.constant_pool_count = readShort(classBytes, offset); parseConstantPool(classBytes, offset, cf); - + fmt::print("Constant Pool has been parsed: {}", cf.constant_pool.at(0).tag); return cf; } diff --git a/src/log.cpp b/src/log.cpp new file mode 100644 index 0000000..03a9051 --- /dev/null +++ b/src/log.cpp @@ -0,0 +1,22 @@ +#include "../include/cvm/log.hpp" + +Log::Log() +{ + // Initialize the logger (stdout logger in this case) + // spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); + if (DEBUG_ENABLED) + { + spdlog::set_level(spdlog::level::debug); // Set global log level to debug + } +} + +Log& Log::getInstance() +{ + static Log instance; // Guaranteed to be destroyed and instantiated on first use + return instance; +} + +std::shared_ptr Log::getLogger() +{ + return logger; +}