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..e047a80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,366 @@ -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( + CVM + VERSION 2.0.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() +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 +# 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/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/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/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/SourcesAndHeaders.cmake b/cmake/SourcesAndHeaders.cmake new file mode 100644 index 0000000..2c03a99 --- /dev/null +++ b/cmake/SourcesAndHeaders.cmake @@ -0,0 +1,19 @@ +set(sources + src/classfile/classfile.cpp + src/log.cpp +) + +set(exe_sources + src/main.cpp + ${sources} +) + +set(headers + include/cvm/fmt_commons.hpp + include/cvm/classfile/classfile.hpp + ) + + +set(test_sources + src/tmp_test.cpp +) diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake new file mode 100644 index 0000000..58a6db0 --- /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." 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) + +# +# 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/docs/banner.jpg b/docs/banner.jpg deleted file mode 100644 index 3929966..0000000 Binary files a/docs/banner.jpg and /dev/null differ 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/cvm/classfile/classfile.hpp b/include/cvm/classfile/classfile.hpp new file mode 100644 index 0000000..dfe7f09 --- /dev/null +++ b/include/cvm/classfile/classfile.hpp @@ -0,0 +1,39 @@ +#ifndef __CLASSFILE_HPP__ +#define __CLASSFILE_HPP__ + +#include +#include +#include +#include + +#include "../fmt_commons.hpp" +#include "cp_info.hpp" + +class Classfile +{ + public: + uint32_t magic; + uint16_t minor_version; + uint16_t major_version; + uint16_t constant_pool_count; + std::vector constant_pool; + 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); + 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); +}; + +#endif //__CLASSFILE_HPP__ diff --git a/include/cvm/classfile/cp_info.hpp b/include/cvm/classfile/cp_info.hpp new file mode 100644 index 0000000..93d3d83 --- /dev/null +++ b/include/cvm/classfile/cp_info.hpp @@ -0,0 +1,48 @@ +#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 +#define CONSTANT_Double 6 +#define CONSTANT_Utf8 1 + +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; + struct{ + uint64_t bytes; + }Double; + struct { + uint16_t length; + uint8_t* bytes; + } Utf8; + }info; +} cp_info; + +#endif //__CP_INF0_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/include/cvm/fmt_commons.hpp b/include/cvm/fmt_commons.hpp new file mode 100644 index 0000000..c7a4760 --- /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/log.hpp b/include/cvm/log.hpp new file mode 100644 index 0000000..dba74c3 --- /dev/null +++ b/include/cvm/log.hpp @@ -0,0 +1,19 @@ +#ifndef __LOG_HPP__ +#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/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/sample/Add.class b/sample/Add.class new file mode 100644 index 0000000..f6c7455 Binary files /dev/null and b/sample/Add.class differ diff --git a/sample/Add.java b/sample/Add.java new file mode 100644 index 0000000..1299646 --- /dev/null +++ b/sample/Add.java @@ -0,0 +1,5 @@ +public class Add { + public static int add(int a, int b) { + return a + b; + } +} diff --git a/sample/Hello.class b/sample/Hello.class new file mode 100644 index 0000000..fdd0d0b Binary files /dev/null and b/sample/Hello.class differ diff --git a/sample/Hello.java b/sample/Hello.java new file mode 100644 index 0000000..65f3110 --- /dev/null +++ b/sample/Hello.java @@ -0,0 +1,5 @@ +class Hello { + public void hello() { + System.out.println("Hello, World!"); + } +} 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/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/classfile/classfile.cpp b/src/classfile/classfile.cpp new file mode 100644 index 0000000..cd5fb95 --- /dev/null +++ b/src/classfile/classfile.cpp @@ -0,0 +1,152 @@ +#include "../../include/cvm/classfile/classfile.hpp" + +#include + +#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. + */ +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); + 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++]; + + spdlog::info("Parsing constant pool entry {}, tag: {}, offset: {}", i, cp_entry.tag, 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_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; + + 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; + } + } +} + +/** + * Helper methods ends + */ + +Classfile Classfile::parseClassfile(const uint8_t* classBytes, size_t fileSize) +{ + size_t offset = 0; + Classfile cf; + + // 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); + fmt::print("Constant Pool has been parsed: {}", cf.constant_pool.at(0).tag); + return cf; +} 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 Log::getLogger() +{ + return logger; +} diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 6236955..0000000 --- a/src/main.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int main(int argc, char** argv) -{ - return 0; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d0ebc72 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,40 @@ +#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"); + + if (argc < 2) + { + throw std::runtime_error("Please specify .class file path as an argument."); + } + + 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(); + + return 0; +} 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..71c4d71 --- /dev/null +++ b/test/src/tmp_test.cpp @@ -0,0 +1,15 @@ +//#include "cvm/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(); +}