├── .gitattributes ├── .gitignore ├── .gitmodules ├── .vscode ├── c_cpp_properties.json ├── launch.json └── settings.json ├── BUILDING-UNIX.md ├── BUILDING-WIN32.md ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── build.cmd ├── build.md ├── build.sh ├── lib └── README ├── linux-build.proj ├── nuget.config └── src ├── Microsoft.R.Host.sln ├── Microsoft.R.Host.vcxproj ├── Microsoft.R.Host.vcxproj.filters ├── R.Build.Version.targets ├── Resource.rc ├── Rgraphicsapi.h ├── blobs.cpp ├── blobs.h ├── detours.cpp ├── detours.h ├── eval.cpp ├── eval.h ├── exports.cpp ├── exports.h ├── grdevices.h ├── grdeviceside.cpp ├── grdeviceside.h ├── grdevicesxaml.cpp ├── grdevicesxaml.h ├── host.cpp ├── host.h ├── json.cpp ├── json.h ├── loadr.cpp ├── loadr.h ├── log.cpp ├── log.h ├── main.cpp ├── message.cpp ├── message.h ├── packages.config ├── project.cpp ├── project.h ├── r_api.h ├── r_gd_api.h ├── r_util.cpp ├── r_util.h ├── resource.h ├── rstrtmgr.cpp ├── rstrtmgr.h ├── stdafx.h ├── transport.cpp ├── transport.h ├── util.cpp ├── util.h └── xamlbuilder.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior (used when a rule below doesn't match) 2 | * text=auto 3 | 4 | *.dll -text 5 | *.lib -text 6 | *.sln -text 7 | *.ico -text 8 | *.bmp -text 9 | *.png -text 10 | *.snk -text 11 | *.mht -text 12 | *.pickle -text 13 | *.Rdata -text 14 | *.Rhistory -text 15 | 16 | # Some Windows-specific files should always be CRLF 17 | *.bat text eol=crlf 18 | *.cmd text eol=crlf 19 | *.rc text eol=crlf 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.sln.docstates 5 | src/.vs/ 6 | 7 | # Build results 8 | build/ 9 | bin/ 10 | obj/ 11 | # Visual Studio profiler 12 | *.psess 13 | *.vsp 14 | *.vspx 15 | *.tlog 16 | 17 | # Backup & report files from converting an old project file 18 | # to a newer Visual Studio version. Backup files are not needed, 19 | # because we have git ;-) 20 | _UpgradeReport_Files/ 21 | Backup*/ 22 | UpgradeLog*.XML 23 | UpgradeLog*.htm 24 | 25 | # C++ Intellisense 26 | *.sdf 27 | *.opensdf 28 | *.VC*db 29 | *.ipch 30 | 31 | # Packages 32 | NugetPackages/ 33 | 34 | # VSCode 35 | src/.vscode 36 | 37 | # Generated version file 38 | src/GeneratedVersion.h 39 | 40 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/picojson"] 2 | path = lib/picojson 3 | url = https://github.com/kazuho/picojson 4 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": [ 6 | "/usr/include", 7 | "/usr/local/include" 8 | ], 9 | "browse": { 10 | "limitSymbolsToIncludedHeaders": true, 11 | "databaseFilename": "", 12 | "path": [ 13 | "/usr/include", 14 | "/usr/local/include", 15 | "${workspaceRoot}" 16 | ] 17 | }, 18 | "intelliSenseMode": "clang-x64", 19 | "macFrameworkPath": [ 20 | "/System/Library/Frameworks", 21 | "/Library/Frameworks" 22 | ] 23 | }, 24 | { 25 | "name": "Linux", 26 | "includePath": [ 27 | "/usr/include", 28 | "/usr/local/include" 29 | ], 30 | "browse": { 31 | "limitSymbolsToIncludedHeaders": true, 32 | "databaseFilename": "", 33 | "path": [ 34 | "/usr/include", 35 | "/usr/local/include", 36 | "${workspaceRoot}" 37 | ] 38 | }, 39 | "intelliSenseMode": "clang-x64" 40 | }, 41 | { 42 | "name": "Win32", 43 | "includePath": [ 44 | "C:/msys64/mingw64/include", 45 | "C:/msys64/mingw64/x86_64-w64-mingw32/include", 46 | "C:/msys64/mingw64/include/c++/6.2.0" 47 | ], 48 | "browse": { 49 | "limitSymbolsToIncludedHeaders": true, 50 | "databaseFilename": "", 51 | "path": [ 52 | "C:/msys64/mingw64/include", 53 | "C:/msys64/mingw64/x86_64-w64-mingw32/include", 54 | "C:/msys64/mingw64/include/c++/6.2.0", 55 | "${workspaceRoot}" 56 | ] 57 | }, 58 | "intelliSenseMode": "msvc-x64" 59 | } 60 | ], 61 | "version": 3 62 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "C++ Launch", 6 | "type": "cppdbg", 7 | "request": "launch", 8 | 9 | "stopAtEntry": false, 10 | "cwd": "${workspaceRoot}", 11 | "environment": [], 12 | "externalConsole": true, 13 | "linux": { 14 | "MIMode": "gdb", 15 | "setupCommands": [ 16 | { 17 | "description": "Enable pretty-printing for gdb", 18 | "text": "-enable-pretty-printing", 19 | "ignoreFailures": true 20 | } 21 | ] 22 | }, 23 | "osx": { 24 | "program": "${workspaceRoot}/bin/Debug/Microsoft.R.Host", 25 | "args": ["--rhost-r-dir", "/Library/Frameworks/R.framework/Resources"], 26 | "MIMode": "lldb" 27 | }, 28 | "windows": { 29 | "program": "${workspaceRoot}/bin/Debug/Microsoft.R.Host.exe", 30 | "args": ["--rhost-r-dir", "C:\\Program Files\\R\\R-3.4.0\\bin\\x64"], 31 | "MIMode": "gdb", 32 | "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", 33 | "setupCommands": [ 34 | { 35 | "description": "Enable pretty-printing for gdb", 36 | "text": "-enable-pretty-printing", 37 | "ignoreFailures": true 38 | } 39 | ] 40 | } 41 | }, 42 | { 43 | "name": "C++ Attach", 44 | "type": "cppdbg", 45 | "request": "attach", 46 | "program": "${workspaceRoot}/../../../bin/Debug/Microsoft.R.Host.exe", 47 | "processId": "${command:pickProcess}", 48 | "stopAtEntry": true, 49 | "linux": { 50 | "MIMode": "gdb", 51 | "setupCommands": [ 52 | { 53 | "description": "Enable pretty-printing for gdb", 54 | "text": "-enable-pretty-printing", 55 | "ignoreFailures": true 56 | } 57 | ] 58 | }, 59 | "osx": { 60 | "MIMode": "lldb" 61 | }, 62 | "windows": { 63 | "MIMode": "gdb", 64 | "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", 65 | "setupCommands": [ 66 | { 67 | "description": "Enable pretty-printing for gdb", 68 | "text": "-enable-pretty-printing", 69 | "ignoreFailures": true 70 | } 71 | ] 72 | } 73 | } 74 | ] 75 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "atomic": "cpp", 4 | "*.ipp": "cpp" 5 | } 6 | } -------------------------------------------------------------------------------- /BUILDING-UNIX.md: -------------------------------------------------------------------------------- 1 | - Install R 3.4+ 2 | - Mac: install XCode 3 | - Ubuntu: install CLANG and CMake via apt. 4 | 5 | Recommended: VS Code with C++ extension and CMake tools. It helps you locate and configure kits. You can also build directly from VS Code. 6 | -------------------------------------------------------------------------------- /BUILDING-WIN32.md: -------------------------------------------------------------------------------- 1 | - Install [MSYS2](http://www.msys2.org/). 2 | 3 | - Open MSYS2 MSYS prompt (*not* MinGW 32-bit or 64-bit prompt!). 4 | 5 | - Refresh package database, and update packages (might need to close and restart shell after this) 6 | ```sh 7 | pacman -Syu 8 | ``` 9 | 10 | - Install build tools from repo: 11 | ```sh 12 | pacman -S base-devel make cmake mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain mingw-w64-i686-cmake mingw-w64-x86_64-cmake 13 | ``` 14 | 15 | - Install library dependencies from repo: 16 | ```sh 17 | pacman -S mingw-w64-x86_64-libzip mingw-w64-x86_64-boost mingw-w64-x86_64-MinHook 18 | ``` 19 | 20 | - Fix missing libzip `zipconf.h` header: 21 | ```sh 22 | cp /mingw64/lib/libzip/include/zipconf.h /mingw64/include/ 23 | ``` 24 | 25 | - Close MSYS prompt, and open regular Windows command prompt. Go to root directory of the repo, and run `build.cmd`. 26 | 27 | The resulting binary will be in `bin\Release`, along with its runtime dependencies. 28 | To produce a debug build, do `build.cmd -t Debug`, and output will be in `bin\Debug`. 29 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.7) 2 | project(R-Host) 3 | 4 | if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) 5 | message(FATAL_ERROR "In-source builds are not allowed. Please use the ./build.sh helper script.") 6 | endif() 7 | 8 | if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_HOME_DIRECTORY}/bin/${CMAKE_BUILD_TYPE}) 10 | endif() 11 | 12 | # TODO: add -fstack-protector 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") 14 | 15 | file(GLOB src "src/*.h" "src/*.cpp" "src/*.rc") 16 | 17 | # Workaround for https://sourceforge.net/p/mingw-w64/bugs/622/ 18 | set_property(SOURCE "src/detours.cpp" APPEND_STRING PROPERTY COMPILE_FLAGS "-O") 19 | 20 | add_executable(Microsoft.R.Host ${src}) 21 | 22 | if(NOT APPLE) 23 | set_target_properties(Microsoft.R.Host PROPERTIES LINK_FLAGS "-static-libgcc -static-libstdc++") 24 | else() 25 | set_target_properties(Microsoft.R.Host PROPERTIES COMPILE_DEFINITIONS _APPLE ) 26 | endif() 27 | 28 | if("${TARGET_ARCH}" STREQUAL "x86") 29 | set_target_properties(Microsoft.R.Host PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-m32") 30 | endif() 31 | 32 | include_directories("${CMAKE_SOURCE_DIR}/lib/picojson" "${CMAKE_SOURCE_DIR}/lib/picojson") 33 | 34 | set(Boost_USE_STATIC_LIBS ON) 35 | find_package(Boost 1.58.0 REQUIRED COMPONENTS date_time filesystem locale program_options regex system) 36 | include_directories(${Boost_INCLUDE_DIRS}) 37 | target_link_libraries(Microsoft.R.Host ${Boost_LIBRARIES}) 38 | 39 | find_library(libzip_LIBRARY NAMES zip) 40 | if(NOT libzip_LIBRARY) 41 | message(FATAL_ERROR "libzip not found") 42 | endif() 43 | include_directories(${libzip_INCLUDE_DIRS}) 44 | target_link_libraries(Microsoft.R.Host ${zlib_LIBRARY} ${libzip_LIBRARY}) 45 | 46 | if(WIN32) 47 | # TODO: enable -dynamicbase 48 | set_property(TARGET Microsoft.R.Host APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-high-entropy-va -Wl,-nxcompat") 49 | 50 | add_definitions(-DUNICODE -D_UNICODE) 51 | 52 | target_link_libraries(Microsoft.R.Host "rstrmgr") 53 | 54 | find_library(MinHook_LIBRARY NAMES libMinHook.a) 55 | if(NOT MinHook_LIBRARY) 56 | message(FATAL_ERROR "MinHook not found") 57 | endif() 58 | include_directories(${MinHook_INCLUDE_DIRS}) 59 | target_link_libraries(Microsoft.R.Host ${MinHook_LIBRARY}) 60 | 61 | # Find R in registry, trying various keys from 3.4.20 to 3.4.0 down. 62 | 63 | set(R_PATH "/registry") 64 | foreach(i RANGE 20 0 -1) 65 | if("${R_PATH}" STREQUAL "/registry") 66 | GET_FILENAME_COMPONENT(R_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\R-core\\R64\\3.4.${i};InstallPath]" ABSOLUTE) 67 | endif() 68 | endforeach() 69 | 70 | if("${R_PATH}" STREQUAL "/registry") 71 | message(FATAL_ERROR "R 3.4.x not found") 72 | endif() 73 | 74 | message(STATUS "Using R at ${R_PATH}") 75 | 76 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I\"${R_PATH}/include\"") 77 | 78 | # Copy MinGW dependencies to output directory alongside .exe. 79 | set(DLL_dependencies "libwinpthread-1.dll" "libzip-5.dll" "zlib1.dll") 80 | foreach(dep ${DLL_dependencies}) 81 | configure_file("$ENV{MSYSTEM_PREFIX}/bin/${dep}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${dep}" COPYONLY) 82 | endforeach() 83 | else() 84 | include_directories("/usr/share/R/include;/Library/Frameworks/R.framework/Resources/include") 85 | set_target_properties(Microsoft.R.Host PROPERTIES COMPILE_FLAGS "`pkg-config --cflags libzip`") 86 | target_link_libraries(Microsoft.R.Host pthread ${CMAKE_DL_LIBS}) 87 | endif() 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Microsoft R Host Process is a wrapper for [R](https://www.r-project.org/) (GNU S) that provides access to various R functionality, such as expression evaluation and the main interpreter loop, from outside of the process via a WebSocket-based protocol. 2 | 3 | See LICENSE for license and third-party code attributions. 4 | 5 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 6 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact 7 | [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 8 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | set MSYSPATH=c:\msys64 3 | 4 | rem set MSYSTEM=MINGW32 5 | rem %MSYSPATH%\usr\bin\bash.exe --login '%~dp0\build.sh' %* 6 | 7 | set MSYSTEM=MINGW64 8 | %MSYSPATH%\usr\bin\bash.exe --login '%~dp0\build.sh' %* 9 | -------------------------------------------------------------------------------- /build.md: -------------------------------------------------------------------------------- 1 | Create a folder ./bin under R-Host directory 2 | 3 | Compiling: 4 | g++ -std=c++14 -fexceptions -fpermissive -O0 -ggdb -I../src -I../lib/picojson -I/usr/share/R/include -c ../src/*.c* &> build.log 5 | 6 | Linking: 7 | g++ -g -o Microsoft.R.Host.out ./*.o -lpthread -L/usr/lib/x86_64-linux-gnu -lboost_filesystem -lboost_atomic -lboost_chrono -lboost_system -lboost_program_options -lzip -L/usr/lib/R/lib -lR 8 | g++ -g -o Microsoft.R.Host.out ./*.o -lpthread -L/usr/lib/x86_64-linux-gnu -lboost_filesystem -lboost_atomic -lboost_chrono -lboost_system -lboost_program_options -lzip -L/usr/lib64/microsoft-r/3.3/lib64/R/lib -lR -lRblas -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | usage() 4 | { 5 | cat << EOF 6 | Usage: $0 [options] 7 | OPTIONS: 8 | -h Print this message. 9 | -t type Set build type (Debug or Release). 10 | -a arch Set target architecture (x86 or x64). 11 | -o dir Use the specified directory for build output. 12 | -i dir Use the specified directory for build artifacts. 13 | -m Don't colorize build output. 14 | EOF 15 | } 16 | 17 | ROOT_DIR=$(dirname "$0") 18 | BUILD_TYPE=Release 19 | COLORIZE=yes 20 | 21 | # Detect 32-bit MinGW. On other platforms x86 builds have to be specifically requested. 22 | if [ "$MSYSTEM" = "MINGW32" ]; then 23 | TARGET_ARCH=x86 24 | else 25 | TARGET_ARCH=x64 26 | fi 27 | 28 | OPTIND=1 29 | 30 | while getopts "h?t:a:o:i:m" opt; do 31 | case "$opt" in 32 | h|\?) 33 | usage 34 | exit 0 35 | ;; 36 | t) 37 | BUILD_TYPE=$OPTARG 38 | ;; 39 | a) 40 | TARGET_ARCH=$OPTARG 41 | ;; 42 | o) 43 | OUT_DIR=$OPTARG 44 | ;; 45 | i) 46 | INT_DIR=$OPTARG 47 | ;; 48 | m) 49 | COLORIZE=no 50 | ;; 51 | esac 52 | done 53 | 54 | shift $((OPTIND-1)) 55 | [ "$1" = "--" ] && shift 56 | 57 | if [ "$INT_DIR" = "" ]; then 58 | INT_DIR=$(dirname "$0")/obj/$BUILD_TYPE/$TARGET_ARCH 59 | fi 60 | 61 | pushd $ROOT_DIR >/dev/null 62 | ROOT_DIR=$(pwd) 63 | 64 | mkdir -p "$INT_DIR" && \ 65 | cd "$INT_DIR" && \ 66 | cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DTARGET_ARCH=$TARGET_ARCH -DCMAKE_COLOR_MAKEFILE=$COLORIZE "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=$OUT_DIR" "$ROOT_DIR" && \ 67 | make 68 | 69 | popd >/dev/null 70 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | Generating R lib files 2 | --------------------------------------------------------------------------------- 3 | 4 | 1. Open Visual Studio Native Command line specific to the architecture x86 or x64 5 | 2. Go to x86 or x64 directory. 6 | 3. Run the following commands: 7 | lib /DEF:R.def 8 | lib /DEF:Rgraphapp.def 9 | 10 | This should generate R.lib, R.exp, Rgraphapp.lib, Rgraphapp.exp 11 | 12 | 13 | Generating Def files 14 | --------------------------------------------------------------------------------- 15 | Def files are checked in for convenience. If they have to updated then follow 16 | these steps. 17 | 18 | 1. Open Visual Studio Native Command line 19 | 2. Find the DLL's you want to export. 20 | 3. Run this command to extract the exports: 21 | link /dump /exports R.dll > Rexports.txt 22 | link /dump /exports Rgraphapp.dll > RGAexports.txt 23 | 4. Edit the generated files to follow the DEF file format and save as .DEF file. -------------------------------------------------------------------------------- /linux-build.proj: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | Microsoft.R.Host 8 | $(SolutionDir)..\ 9 | $(RootDirectory)bin\$(Configuration)\ 10 | $(RootDirectory)obj\$(Configuration)\$(RHostName)\$(Platform)\ 11 | $(MSBuildThisFileDirectory)src\ 12 | 13 | $([System.DateTime]::Now.Year) 14 | $([System.DateTime]::Now.ToString("MMdd")) 15 | 16 | 17 | $(MSBuildThisFileDirectory) 18 | $(RHostDirectory)build.sh 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/Microsoft.R.Host.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.6 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.R.Host", "Microsoft.R.Host.vcxproj", "{DBBED91F-BF32-4C77-9445-8666532225BC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Debug|x64.ActiveCfg = Debug|x64 17 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Debug|x64.Build.0 = Debug|x64 18 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Debug|x86.ActiveCfg = Debug|Win32 19 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Debug|x86.Build.0 = Debug|Win32 20 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Release|x64.ActiveCfg = Release|x64 21 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Release|x64.Build.0 = Release|x64 22 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Release|x86.ActiveCfg = Release|Win32 23 | {DBBED91F-BF32-4C77-9445-8666532225BC}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/Microsoft.R.Host.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | $(UserProfile)\.nuget\packages\ 23 | 24 | 25 | ~\.nuget\packages\ 26 | 27 | 28 | Windows 29 | 30 | 31 | OSX 32 | 33 | 34 | Linux 35 | 36 | 37 | $(SolutionDir)..\bin\ 38 | $(SolutionDir)..\obj\ 39 | $(BinDirectory)$(Configuration)\Host\$(TargetOSName) 40 | $(ObjDirectory)$(Configuration)\$(TargetName)\$(Platform)\Microsoft.R.Host 41 | 42 | 43 | 15.0 44 | {DBBED91F-BF32-4C77-9445-8666532225BC} 45 | MakeFileProj 46 | c:\msys64 47 | 48 | 49 | 50 | 51 | Makefile 52 | true 53 | v141 54 | 55 | 56 | Makefile 57 | false 58 | v141 59 | 60 | 61 | Makefile 62 | true 63 | v141 64 | 65 | 66 | Makefile 67 | false 68 | v141 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | MINGW32 88 | $(MSYSPath)\mingw32\i686-w64-mingw32\include;$(MSYSPath)\mingw32\include;$(MSYSPath)\mingw32\include\c++\6.3.0 89 | 90 | 91 | MINGW64 92 | $(MSYSPath)\mingw64\x86_64-w64-mingw32\include;$(MSYSPath)\mingw64\include;$(MSYSPath)\mingw64\include\c++\6.3.0 93 | 94 | 95 | $(OutDir)\Microsoft.R.Host.exe 96 | set "MSYSTEM=$(MSystem)" & $(MSYSPath)\usr\bin\bash.exe --login '$(MSBuildProjectDirectory)\..\build.sh' -m -t $(Configuration) -i $(IntDir) -o $(OutDir) 97 | rmdir /s /q "$(OutDir)" "$(IntDir)" 98 | $(NMakeCleanCommandLine) & $(NMakeBuildCommandLine) 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | Microsoft 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /src/Microsoft.R.Host.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/R.Build.Version.targets: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 28 | $([System.DateTime]::Now.Year) 29 | $([MSBuild]::Subtract($(Year), 4)) 30 | $(YearMinus4.ToString()) 31 | $(YearMinus4String.Substring(3,1)) 32 | $([System.DateTime]::Now.ToString("MMdd")) 33 | 34 | 35 | 1 36 | 0 37 | $(LastDigitOfYear)$(Date) 38 | $(Time) 39 | $(Build).$(Revision) 40 | 41 | $(MajorVersion).$(MinorVersion).$(BuildNumber) 42 | $(MajorVersion).$(MinorVersion).$(Build).$(Revision) 43 | 44 | 45 | 46 | 47 | <_Parameter1>$(AssemblyVersion) 48 | 49 | 50 | <_Parameter1>$(BuildVersion) 51 | 52 | 53 | <_Parameter1>$(BuildVersion) 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | DeleteVersionInfoFile;$(CompileDependsOn) 75 | GenerateAssemblyInfoFile;$(CompileDependsOn) 76 | 77 | 78 | 79 | 83 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/Resource.rc: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | #include "resource.h" 23 | #include "winresrc.h" 24 | 25 | 1 TEXTINCLUDE 26 | BEGIN 27 | "resource.h\0" 28 | END 29 | 30 | 2 TEXTINCLUDE 31 | BEGIN 32 | "#include ""winres.h""\r\n" 33 | "\0" 34 | END 35 | 36 | 3 TEXTINCLUDE 37 | BEGIN 38 | "\r\n" 39 | "\0" 40 | END 41 | /////////////////////////////////////////////////////////////////////// 42 | // 43 | // Version 44 | // 45 | #include "GeneratedVersion.h" 46 | VS_VERSION_INFO VERSIONINFO 47 | FILEVERSION VER_FILEVERSION 48 | PRODUCTVERSION VER_PRODUCTVERSION 49 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 50 | FILEFLAGS 0 51 | FILEOS VOS__WINDOWS32 52 | FILETYPE VFT_APP 53 | FILESUBTYPE VFT2_UNKNOWN 54 | BEGIN 55 | BLOCK "StringFileInfo" 56 | BEGIN 57 | BLOCK "040904E4" 58 | BEGIN 59 | VALUE "CompanyName", "Microsoft Corporation\0" 60 | VALUE "FileDescription", "Microsoft R Host\0" 61 | VALUE "FileVersion", VER_FILEVERSION_STR 62 | VALUE "InternalName", "Microsoft R Host\0" 63 | VALUE "LegalCopyright", "Copyright (c) 2015-2016 Microsoft Corporation\0" 64 | VALUE "OriginalFilename", "Microsoft.R.Host.exe\0" 65 | VALUE "ProductName", "Microsoft R Host\0" 66 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 67 | END 68 | END 69 | 70 | BLOCK "VarFileInfo" 71 | BEGIN 72 | /* The following line should only be modified for localized versions. */ 73 | /* It consists of any number of WORD,WORD pairs, with each pair */ 74 | /* describing a language,codepage combination supported by the file. */ 75 | /* */ 76 | /* For example, a file might have values "0x409,1252" indicating that it */ 77 | /* supports English language (0x409) in the Windows ANSI codepage (1252). */ 78 | VALUE "Translation", 0x409, 1252 79 | END 80 | END -------------------------------------------------------------------------------- /src/Rgraphicsapi.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | // This file combines various bits and pieces of R API in a single header file. 24 | // 25 | // The main reason for its existence is that R headers themselves are not immediately usable 26 | // after checkout: some parts expect R to be at least partially built to generate config.h 27 | // etc, or rely on various macros defined in R makefiles. By moving all this code here and 28 | // cleaning it up, it is readily usable. 29 | // 30 | // The following R header files were used as a basis to produce this file: 31 | // - R_ext/Arith.h 32 | // - R_ext/GraphicsDevice.h 33 | // - R_ext/GraphicsEngine.h 34 | 35 | #pragma once 36 | 37 | #include 38 | #include 39 | 40 | #include "r_api.h" 41 | 42 | #define R_32_GE_version 10 43 | #define R_33_GE_version 11 44 | #define R_34_GE_version 12 45 | 46 | #define R_RGB(r,g,b) ((r)|((g)<<8)|((b)<<16)|0xFF000000) 47 | #define R_RGBA(r,g,b,a) ((r)|((g)<<8)|((b)<<16)|((a)<<24)) 48 | #define R_RED(col) (((col))&255) 49 | #define R_GREEN(col) (((col)>>8)&255) 50 | #define R_BLUE(col) (((col)>>16)&255) 51 | #define R_ALPHA(col) (((col)>>24)&255) 52 | #define R_OPAQUE(col) (R_ALPHA(col) == 255) 53 | #define R_TRANSPARENT(col) (R_ALPHA(col) == 0) 54 | 55 | #define LTY_BLANK -1 56 | #define LTY_SOLID 0 57 | #define LTY_DASHED 4 + (4<<4) 58 | #define LTY_DOTTED 1 + (3<<4) 59 | #define LTY_DOTDASH 1 + (3<<4) + (4<<8) + (3<<12) 60 | #define LTY_LONGDASH 7 + (3<<4) 61 | #define LTY_TWODASH 2 + (2<<4) + (6<<8) + (2<<12) 62 | 63 | #define NA_INTEGER R_NaInt 64 | 65 | extern "C" { 66 | int R_GE_getVersion(void); 67 | 68 | void R_GE_checkVersionOrDie(int version); 69 | 70 | typedef enum { 71 | GE_ROUND_CAP = 1, 72 | GE_BUTT_CAP = 2, 73 | GE_SQUARE_CAP = 3 74 | } R_GE_lineend; 75 | 76 | typedef enum { 77 | GE_ROUND_JOIN = 1, 78 | GE_MITRE_JOIN = 2, 79 | GE_BEVEL_JOIN = 3 80 | } R_GE_linejoin; 81 | 82 | typedef struct { 83 | int col; 84 | int fill; 85 | double gamma; 86 | double lwd; 87 | int lty; 88 | R_GE_lineend lend; 89 | R_GE_linejoin ljoin; 90 | double lmitre; 91 | double cex; 92 | double ps; 93 | double lineheight; 94 | int fontface; 95 | char fontfamily[201]; 96 | } R_GE_gcontext; 97 | 98 | typedef R_GE_gcontext* pGEcontext; 99 | 100 | typedef struct _DevDesc11 DevDesc10; // V11 is compatible with V10 101 | typedef struct _DevDesc11 DevDesc11; 102 | typedef struct _DevDesc12 DevDesc12; 103 | typedef DevDesc11* pDevDesc10; // V11 is compatible with V10 104 | typedef DevDesc11* pDevDesc11; 105 | typedef DevDesc12* pDevDesc12; 106 | typedef DevDesc12* pDevDesc; 107 | 108 | struct _DevDesc11 { 109 | double left; 110 | double right; 111 | double bottom; 112 | double top; 113 | double clipLeft; 114 | double clipRight; 115 | double clipBottom; 116 | double clipTop; 117 | double xCharOffset; 118 | double yCharOffset; 119 | double yLineBias; 120 | double ipr[2]; 121 | double cra[2]; 122 | double gamma; 123 | Rboolean canClip; 124 | Rboolean canChangeGamma; 125 | int canHAdj; 126 | double startps; 127 | int startcol; 128 | int startfill; 129 | int startlty; 130 | int startfont; 131 | double startgamma; 132 | void *deviceSpecific; 133 | Rboolean displayListOn; 134 | Rboolean canGenMouseDown; 135 | Rboolean canGenMouseMove; 136 | Rboolean canGenMouseUp; 137 | Rboolean canGenKeybd; 138 | Rboolean gettingEvent; 139 | void(*activate)(const pDevDesc); 140 | void(*circle)(double x, double y, double r, const pGEcontext gc, pDevDesc dd); 141 | void(*clip)(double x0, double x1, double y0, double y1, pDevDesc dd); 142 | void(*close)(pDevDesc dd); 143 | void(*deactivate)(pDevDesc); 144 | Rboolean(*locator)(double *x, double *y, pDevDesc dd); 145 | void(*line)(double x1, double y1, double x2, double y2, const pGEcontext gc, pDevDesc dd); 146 | void(*metricInfo)(int c, const pGEcontext gc, double* ascent, double* descent, double* width, pDevDesc dd); 147 | void(*mode)(int mode, pDevDesc dd); 148 | void(*newPage)(const pGEcontext gc, pDevDesc dd); 149 | void(*polygon)(int n, double *x, double *y, const pGEcontext gc, pDevDesc dd); 150 | void(*polyline)(int n, double *x, double *y, const pGEcontext gc, pDevDesc dd); 151 | void(*rect)(double x0, double y0, double x1, double y1, const pGEcontext gc, pDevDesc dd); 152 | void(*path)(double *x, double *y, int npoly, int *nper, Rboolean winding, const pGEcontext gc, pDevDesc dd); 153 | void(*raster)(unsigned int *raster, int w, int h, double x, double y, double width, double height, double rot, Rboolean interpolate, const pGEcontext gc, pDevDesc dd); 154 | SEXP(*cap)(pDevDesc dd); 155 | void(*size)(double *left, double *right, double *bottom, double *top, pDevDesc dd); 156 | double(*strWidth)(const char *str, const pGEcontext gc, pDevDesc dd); 157 | void(*text)(double x, double y, const char *str, double rot, double hadj, const pGEcontext gc, pDevDesc dd); 158 | void(*onExit)(pDevDesc dd); 159 | SEXP(*getEvent)(SEXP, const char *); 160 | Rboolean(*newFrameConfirm)(pDevDesc dd); 161 | Rboolean hasTextUTF8; 162 | void(*textUTF8)(double x, double y, const char *str, double rot, double hadj, const pGEcontext gc, pDevDesc dd); 163 | double(*strWidthUTF8)(const char *str, const pGEcontext gc, pDevDesc dd); 164 | Rboolean wantSymbolUTF8; 165 | Rboolean useRotatedTextInContour; 166 | SEXP eventEnv; 167 | void(*eventHelper)(pDevDesc dd, int code); 168 | int(*holdflush)(pDevDesc dd, int level); 169 | int haveTransparency; 170 | int haveTransparentBg; 171 | int haveRaster; 172 | int haveCapture, haveLocator; 173 | char reserved[64]; 174 | }; 175 | 176 | struct _DevDesc12 { 177 | double left; 178 | double right; 179 | double bottom; 180 | double top; 181 | double clipLeft; 182 | double clipRight; 183 | double clipBottom; 184 | double clipTop; 185 | double xCharOffset; 186 | double yCharOffset; 187 | double yLineBias; 188 | double ipr[2]; 189 | double cra[2]; 190 | double gamma; 191 | Rboolean canClip; 192 | Rboolean canChangeGamma; 193 | int canHAdj; 194 | double startps; 195 | int startcol; 196 | int startfill; 197 | int startlty; 198 | int startfont; 199 | double startgamma; 200 | void *deviceSpecific; 201 | Rboolean displayListOn; 202 | Rboolean canGenMouseDown; 203 | Rboolean canGenMouseMove; 204 | Rboolean canGenMouseUp; 205 | Rboolean canGenKeybd; 206 | Rboolean canGenIdle; 207 | Rboolean gettingEvent; 208 | void(*activate)(const pDevDesc); 209 | void(*circle)(double x, double y, double r, const pGEcontext gc, pDevDesc dd); 210 | void(*clip)(double x0, double x1, double y0, double y1, pDevDesc dd); 211 | void(*close)(pDevDesc dd); 212 | void(*deactivate)(pDevDesc); 213 | Rboolean(*locator)(double *x, double *y, pDevDesc dd); 214 | void(*line)(double x1, double y1, double x2, double y2, const pGEcontext gc, pDevDesc dd); 215 | void(*metricInfo)(int c, const pGEcontext gc, double* ascent, double* descent, double* width, pDevDesc dd); 216 | void(*mode)(int mode, pDevDesc dd); 217 | void(*newPage)(const pGEcontext gc, pDevDesc dd); 218 | void(*polygon)(int n, double *x, double *y, const pGEcontext gc, pDevDesc dd); 219 | void(*polyline)(int n, double *x, double *y, const pGEcontext gc, pDevDesc dd); 220 | void(*rect)(double x0, double y0, double x1, double y1, const pGEcontext gc, pDevDesc dd); 221 | void(*path)(double *x, double *y, int npoly, int *nper, Rboolean winding, const pGEcontext gc, pDevDesc dd); 222 | void(*raster)(unsigned int *raster, int w, int h, double x, double y, double width, double height, double rot, Rboolean interpolate, const pGEcontext gc, pDevDesc dd); 223 | SEXP(*cap)(pDevDesc dd); 224 | void(*size)(double *left, double *right, double *bottom, double *top, pDevDesc dd); 225 | double(*strWidth)(const char *str, const pGEcontext gc, pDevDesc dd); 226 | void(*text)(double x, double y, const char *str, double rot, double hadj, const pGEcontext gc, pDevDesc dd); 227 | void(*onExit)(pDevDesc dd); 228 | SEXP(*getEvent)(SEXP, const char *); 229 | Rboolean(*newFrameConfirm)(pDevDesc dd); 230 | Rboolean hasTextUTF8; 231 | void(*textUTF8)(double x, double y, const char *str, double rot, double hadj, const pGEcontext gc, pDevDesc dd); 232 | double(*strWidthUTF8)(const char *str, const pGEcontext gc, pDevDesc dd); 233 | Rboolean wantSymbolUTF8; 234 | Rboolean useRotatedTextInContour; 235 | SEXP eventEnv; 236 | void(*eventHelper)(pDevDesc dd, int code); 237 | int(*holdflush)(pDevDesc dd, int level); 238 | int haveTransparency; 239 | int haveTransparentBg; 240 | int haveRaster; 241 | int haveCapture, haveLocator; 242 | char reserved[64]; 243 | }; 244 | 245 | #ifndef BEGIN_SUSPEND_INTERRUPTS 246 | #define BEGIN_SUSPEND_INTERRUPTS do { \ 247 | Rboolean __oldsusp__ = R_interrupts_suspended; \ 248 | R_interrupts_suspended = R_TRUE; 249 | #define END_SUSPEND_INTERRUPTS R_interrupts_suspended = __oldsusp__; \ 250 | if (R_interrupts_pending && ! R_interrupts_suspended) \ 251 | Rf_onintr(); \ 252 | } while(0) 253 | extern __declspec(dllimport) Rboolean R_interrupts_suspended; 254 | extern __declspec(dllimport) int R_interrupts_pending; 255 | extern void Rf_onintr(void); 256 | extern __declspec(dllimport) Rboolean mbcslocale; 257 | #endif 258 | 259 | #define MAX_GRAPHICS_SYSTEMS 24 260 | 261 | typedef enum { 262 | GE_InitState = 0, 263 | GE_FinaliseState = 1, 264 | GE_SaveState = 2, 265 | GE_RestoreState = 6, 266 | GE_CopyState = 3, 267 | GE_SaveSnapshotState = 4, 268 | GE_RestoreSnapshotState = 5, 269 | GE_CheckPlot = 7, 270 | GE_ScalePS = 8 271 | } GEevent; 272 | 273 | typedef struct _GEDevDesc GEDevDesc; 274 | 275 | typedef SEXP(*GEcallback)(GEevent, GEDevDesc *, SEXP); 276 | 277 | typedef struct { 278 | void *systemSpecific; 279 | GEcallback callback; 280 | } GESystemDesc; 281 | 282 | struct _GEDevDesc { 283 | pDevDesc dev; 284 | Rboolean displayListOn; 285 | SEXP displayList; 286 | SEXP DLlastElt; 287 | SEXP savedSnapshot; 288 | Rboolean dirty; 289 | Rboolean recordGraphics; 290 | GESystemDesc *gesd[MAX_GRAPHICS_SYSTEMS]; 291 | Rboolean ask; 292 | }; 293 | 294 | typedef GEDevDesc* pGEDevDesc; 295 | 296 | extern void GEaddDevice2(pGEDevDesc, const char *); 297 | extern void GEaddDevice2f(pGEDevDesc, const char *, const char *); 298 | extern void GEkillDevice(pGEDevDesc); 299 | extern pGEDevDesc GEcreateDevDesc(pDevDesc dev); 300 | extern pGEDevDesc GEgetDevice(int); 301 | 302 | extern void R_CheckDeviceAvailable(void); 303 | extern Rboolean R_CheckDeviceAvailableBool(void); 304 | 305 | extern pGEDevDesc GEcurrentDevice(void); 306 | extern Rboolean GEdeviceDirty(pGEDevDesc dd); 307 | extern void GEdirtyDevice(pGEDevDesc dd); 308 | extern Rboolean GEcheckState(pGEDevDesc dd); 309 | extern Rboolean GErecording(SEXP call, pGEDevDesc dd); 310 | extern void GErecordGraphicOperation(SEXP op, SEXP args, pGEDevDesc dd); 311 | extern void GEinitDisplayList(pGEDevDesc dd); 312 | extern void GEplayDisplayList(pGEDevDesc dd); 313 | extern void GEcopyDisplayList(int fromDevice); 314 | extern SEXP GEcreateSnapshot(pGEDevDesc dd); 315 | extern void GEplaySnapshot(SEXP snapshot, pGEDevDesc dd); 316 | extern void GEonExit(void); 317 | extern void GEnullDevice(void); 318 | 319 | extern int Rf_curDevice(void); 320 | extern int Rf_selectDevice(int); 321 | extern int Rf_ndevNumber(pDevDesc); 322 | extern int Rf_NumDevices(void); 323 | extern pGEDevDesc Rf_desc2GEDesc(pDevDesc dd); 324 | } 325 | -------------------------------------------------------------------------------- /src/blobs.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "blobs.h" 24 | #include "log.h" 25 | #include "host.h" 26 | 27 | using namespace rhost::util; 28 | using namespace rhost::log; 29 | namespace js = picojson; 30 | 31 | namespace rhost { 32 | namespace blobs { 33 | namespace { 34 | void blob_error(SEXP sexp, const char* format, ...) { 35 | SEXP repr_sexp = STRING_ELT(Rf_deparse1line(sexp, R_FALSE), 0); 36 | Rf_protect(repr_sexp); 37 | const char* repr = R_CHAR(repr_sexp); 38 | 39 | char buf[0x10000] = {}; 40 | snprintf(buf, sizeof buf, "BLOB serialization failed for input:\n\n%s\n\n", repr); 41 | 42 | size_t len = strlen(buf); 43 | va_list va; 44 | va_start(va, format); 45 | vsnprintf(buf + len, sizeof buf - len, format, va); 46 | va_end(va); 47 | 48 | Rf_error("%s", buf); 49 | } 50 | 51 | bool to_blob_internal(SEXP sexp, std::vector& blob) { 52 | int type = TYPEOF(sexp); 53 | size_t length = Rf_length(sexp); 54 | 55 | if (type == NILSXP) { 56 | return false; 57 | } 58 | 59 | if (type == RAWSXP) { 60 | Rbyte* data = RAW(sexp); 61 | blob.assign(data, data + length); 62 | return true; 63 | } 64 | 65 | blob_error(sexp, "Unsupported type for blob - must be one of: NULL; RAW."); 66 | return false; 67 | } 68 | } 69 | 70 | bool to_blob(SEXP sexp, std::vector& blob) { 71 | bool result = false; 72 | rhost::util::errors_to_exceptions([&] {result = to_blob_internal(sexp, blob); }); 73 | return result; 74 | } 75 | 76 | void append_from_file(blob& blob, const char* path) { 77 | FILE* fp = nullptr; 78 | SCOPE_WARDEN(close_file, { 79 | if (fp) { 80 | fclose(fp); 81 | } 82 | }); 83 | 84 | fp = fopen(path, "rb"); 85 | if (fp) { 86 | fseek(fp, 0, SEEK_END); 87 | size_t len = ftell(fp); 88 | 89 | if (len) { 90 | size_t offset = blob.size(); 91 | blob.resize(offset + len); 92 | 93 | fseek(fp, 0, SEEK_SET); 94 | size_t read = fread(&blob[offset], 1, len, fp); 95 | if (read != len) { 96 | throw std::runtime_error("Error reading file"); 97 | } 98 | } 99 | } 100 | } 101 | 102 | void save_to_file(blob_id id, fs::path& file_path) { 103 | auto data = rhost::host::get_blob(id); 104 | 105 | fs::path parent = file_path.parent_path(); 106 | if (!fs::exists(parent)) { 107 | fs::create_directories(parent); 108 | } 109 | 110 | FILE *f = std::fopen(file_path.make_preferred().string().c_str(), "wb"); 111 | if (!f) { 112 | throw std::runtime_error("Error saving blob to file."); 113 | } 114 | 115 | SCOPE_WARDEN(blob_file, { 116 | std::fclose(f); 117 | }); 118 | 119 | size_t sz = std::fwrite(data.data(), sizeof(char), data.size(), f); 120 | if (sz != data.size()) { 121 | throw std::runtime_error("Error while writing blob to file."); 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/blobs.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "util.h" 26 | 27 | namespace rhost { 28 | namespace blobs { 29 | // Converts a RAWSXP or NILSXP object to a vector of bytes. For NILSXP, the vector is empty, and 30 | // return value is false. Otherwise, the vector contains the same bytes, and return value is true. 31 | bool to_blob(SEXP sexp, std::vector& blob); 32 | 33 | typedef std::vector blob; 34 | typedef uint64_t blob_id; // range of values constrained to always fit a double 35 | 36 | void append_from_file(blob& blob, const char* path); 37 | 38 | inline void append_from_file(blob& blob, const std::string& path) { 39 | return append_from_file(blob, path.c_str()); 40 | } 41 | 42 | inline void append_from_file(blob& blob, const fs::path& path) { 43 | return append_from_file(blob, path.string().c_str()); 44 | } 45 | 46 | void save_to_file(blob_id id, fs::path& file_path); 47 | } 48 | } -------------------------------------------------------------------------------- /src/detours.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "stdafx.h" 24 | #include "host.h" 25 | 26 | namespace rhost { 27 | namespace detours { 28 | #ifdef _WIN32 29 | // Converts host response back to MessageBox codes 30 | int ToMessageBoxCodes(int result, UINT mbType) { 31 | if (mbType == MB_YESNO) { 32 | return result == -1 ? IDNO : IDYES; 33 | } else if (mbType == MB_YESNOCANCEL) { 34 | return result == 0 ? IDCANCEL : (result == -1 ? IDNO : IDYES); 35 | } 36 | return result == 0 ? IDCANCEL : IDOK; 37 | } 38 | 39 | // Displays host message box. Host provides title and the parent window. 40 | // Communication with the host is in UTF-8 and single-byte characters 41 | // are converted to UTF-8 JSON in the rhost::host::ShowMessageBox(). 42 | int HostMessageBox(LPCSTR lpText, UINT uType) { 43 | UINT mbType = uType & 0x0F; 44 | if (mbType == MB_OKCANCEL) { 45 | return ToMessageBoxCodes(rhost::host::OkCancel(lpText), mbType); 46 | } else if (mbType == MB_YESNO) { 47 | return ToMessageBoxCodes(rhost::host::YesNo(lpText), mbType); 48 | } else if (mbType == MB_YESNOCANCEL) { 49 | return ToMessageBoxCodes(rhost::host::YesNoCancel(lpText), mbType); 50 | } 51 | rhost::host::ShowMessage(lpText); 52 | return IDOK; 53 | } 54 | 55 | // MessageBoxW 56 | decltype(MessageBoxW) *pMessageBoxW = nullptr; 57 | int WINAPI DetourMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) { 58 | char ch[1000]; 59 | wcstombs(ch, lpText, _countof(ch)); 60 | return HostMessageBox(ch, uType); 61 | } 62 | 63 | // MessageBoxA 64 | decltype(MessageBoxA) *pMessageBoxA = nullptr; 65 | int WINAPI DetourMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { 66 | return HostMessageBox(lpText, uType); 67 | } 68 | 69 | std::unordered_map hWndTitleMap; 70 | decltype(CreateWindowExA) *pCreateWindowExA = nullptr; 71 | HWND WINAPI DetourCreateWindowExA(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) { 72 | HWND hWnd = pCreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); 73 | 74 | // A window without parent or a window created to show message 75 | size_t len = 0; 76 | if (hWnd != nullptr && hWndParent == nullptr && 77 | hWndTitleMap.find(hWnd) == hWndTitleMap.end() && 78 | SUCCEEDED(StringCbLengthA(lpWindowName, STRSAFE_MAX_CCH, &len)) && len > 0) { 79 | 80 | int size = MultiByteToWideChar(CP_ACP, 0, lpWindowName, static_cast(len), NULL, 0); 81 | if (size == 0) { 82 | return hWnd; 83 | } 84 | 85 | std::unique_ptr wstr(new wchar_t[size + 1]); 86 | MultiByteToWideChar(CP_ACP, 0, lpWindowName, static_cast(len), wstr.get(), size); 87 | wstr[size] = '\0'; 88 | 89 | std::wstring windowTitle = std::wstring(wstr.get()); 90 | std::wstring_convert, wchar_t> strConverter; 91 | 92 | hWndTitleMap[hWnd] = strConverter.to_bytes(windowTitle); 93 | std::string text = "Created window - " + hWndTitleMap[hWnd]; 94 | 95 | rhost::host::send_notification("!!", text); 96 | } 97 | 98 | return hWnd; 99 | } 100 | 101 | decltype(CreateWindowExW) *pCreateWindowExW = nullptr; 102 | HWND WINAPI DetourCreateWindowExW(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) { 103 | HWND hWnd = pCreateWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); 104 | 105 | // A window without parent or a window created to show message 106 | size_t len = 0; 107 | if (hWnd != nullptr && hWndParent == nullptr && 108 | hWndTitleMap.find(hWnd) == hWndTitleMap.end() && 109 | SUCCEEDED(StringCchLengthW(lpWindowName, STRSAFE_MAX_CCH, &len) && len > 0)) { 110 | 111 | std::wstring windowTitle = std::wstring(lpWindowName); 112 | std::wstring_convert, wchar_t> strConverter; 113 | 114 | hWndTitleMap[hWnd] = strConverter.to_bytes(windowTitle); 115 | std::string text = "Created window - " + hWndTitleMap[hWnd]; 116 | 117 | rhost::host::send_notification("!!", text); 118 | } 119 | 120 | return hWnd; 121 | } 122 | 123 | decltype(DestroyWindow) *pDestroyWindow = nullptr; 124 | BOOL WINAPI DetourDestroyWindow(HWND hWnd) { 125 | BOOL stat = pDestroyWindow(hWnd); 126 | std::unordered_map::iterator iter = hWndTitleMap.find(hWnd); 127 | if (iter != hWndTitleMap.end()) { 128 | std::string text = "Closing window - " + iter->second; 129 | hWndTitleMap.erase(iter); 130 | rhost::host::send_notification("!!", text); 131 | } 132 | 133 | return stat; 134 | } 135 | #endif 136 | 137 | void init_ui_detours(bool is_remote) { 138 | #ifdef _WIN32 139 | MH_Initialize(); 140 | 141 | MH_CreateHook(reinterpret_cast(&MessageBoxW), reinterpret_cast(&DetourMessageBoxW), reinterpret_cast(&pMessageBoxW)); 142 | MH_CreateHook(reinterpret_cast(&MessageBoxA), reinterpret_cast(&DetourMessageBoxA), reinterpret_cast(&pMessageBoxA)); 143 | 144 | if (is_remote) { 145 | // Apply hooks for Remote REPL or any other session irrespective of local or remote case. 146 | MH_CreateHookApi(L"User32.dll", "CreateWindowExW", reinterpret_cast(&DetourCreateWindowExW), reinterpret_cast(&pCreateWindowExW)); 147 | MH_CreateHookApi(L"User32.dll", "CreateWindowExA", reinterpret_cast(&DetourCreateWindowExA), reinterpret_cast(&pCreateWindowExA)); 148 | MH_CreateHookApi(L"User32.dll", "DestroyWindow", reinterpret_cast(&DetourDestroyWindow), reinterpret_cast(&pDestroyWindow)); 149 | } 150 | 151 | MH_EnableHook(MH_ALL_HOOKS); 152 | #endif 153 | } 154 | 155 | void terminate_ui_detours() { 156 | #ifdef _WIN32 157 | MH_DisableHook(MH_ALL_HOOKS); 158 | MH_Uninitialize(); 159 | #endif 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /src/detours.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | namespace rhost { 25 | namespace detours { 26 | void init_ui_detours(bool is_remote); 27 | void terminate_ui_detours(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/eval.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "eval.h" 24 | 25 | namespace rhost { 26 | namespace eval { 27 | bool was_eval_canceled; 28 | 29 | void interrupt_eval() { 30 | was_eval_canceled = true; 31 | Rf_onintr(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/eval.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "util.h" 26 | #include "r_api.h" 27 | 28 | namespace rhost { 29 | namespace eval { 30 | extern bool was_eval_canceled; 31 | 32 | template 33 | struct r_eval_result { 34 | bool has_value; 35 | T value; 36 | bool has_error; 37 | std::string error; 38 | bool is_canceled; 39 | }; 40 | 41 | template 42 | inline std::vector> r_try_eval(const std::string& expr, SEXP env, ParseStatus& parse_status, FBefore before = [] {}, FAfter after = [] {}) { 43 | using namespace rhost::util; 44 | 45 | std::vector> results; 46 | 47 | protected_sexp sexp_expr(Rf_allocVector3(STRSXP, 1, nullptr)); 48 | SET_STRING_ELT(sexp_expr.get(), 0, Rf_mkChar(expr.c_str())); 49 | 50 | protected_sexp sexp_parsed(R_ParseVector(sexp_expr.get(), -1, &parse_status, R_NilValue)); 51 | if (parse_status == PARSE_OK) { 52 | results.resize(Rf_length(sexp_parsed.get())); 53 | for (size_t i = 0; i < results.size(); ++i) { 54 | auto& result = results[i]; 55 | 56 | struct eval_data_t { 57 | SEXP expr; 58 | SEXP env; 59 | decltype(result)& result_ref; 60 | FBefore& before; 61 | FAfter& after; 62 | } eval_data = { VECTOR_ELT(sexp_parsed.get(), i), env, result, before, after }; 63 | 64 | // Reset debug flag to avoid eval entering Browse mode. 65 | int rdebug = RDEBUG(env); 66 | SET_RDEBUG(env, 0); 67 | 68 | result.has_error = !rhost::util::r_top_level_exec([&] { 69 | eval_data.before(); 70 | was_eval_canceled = false; 71 | eval_data.result_ref.value.reset(Rf_eval(eval_data.expr, eval_data.env)); 72 | eval_data.after(); 73 | }); 74 | result.is_canceled = was_eval_canceled; 75 | was_eval_canceled = false; 76 | 77 | // Restore debug flag. 78 | SET_RDEBUG(env, rdebug); 79 | 80 | if (result.value) { 81 | result.has_value = true; 82 | } 83 | if (result.has_error) { 84 | if (result.is_canceled) { 85 | // R_curErrorBuf will be bogus in this case. 86 | result.error = "Evaluation canceled."; 87 | } else { 88 | result.error = R_curErrorBuf(); 89 | } 90 | } 91 | } 92 | } 93 | 94 | return results; 95 | } 96 | 97 | template 98 | inline r_eval_result r_try_eval_str(const std::string& expr, SEXP env, ParseStatus& parse_status, FBefore before, FAfter after) { 99 | using namespace rhost::util; 100 | 101 | r_eval_result result = {}; 102 | 103 | auto results = r_try_eval(expr, env, parse_status, before, after); 104 | if (!results.empty()) { 105 | auto& last = *(results.end() - 1); 106 | 107 | result.is_canceled = last.is_canceled; 108 | result.has_error = last.has_error; 109 | result.error = last.error; 110 | 111 | if (last.has_value) { 112 | protected_sexp sexp_char(Rf_asChar(last.value.get())); 113 | const char* s = R_CHAR(sexp_char.get()); 114 | if (s) { 115 | result.has_value = true; 116 | result.value = s; 117 | } else { 118 | result.has_value = false; 119 | } 120 | } else { 121 | result.has_value = false; 122 | } 123 | } 124 | 125 | return result; 126 | } 127 | 128 | inline r_eval_result r_try_eval_str(const std::string& expr, SEXP env, ParseStatus& parse_status) { 129 | auto before = [&] {}; 130 | auto after = [&] {}; 131 | return r_try_eval_str(expr, env, parse_status, before, after); 132 | } 133 | 134 | void interrupt_eval(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/exports.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "stdafx.h" 24 | #include "exports.h" 25 | 26 | namespace rhost { 27 | namespace exports { 28 | static std::vector external_methods; 29 | static std::vector call_methods; 30 | static std::vector c_methods; 31 | 32 | void add_external_methods(R_ExternalMethodDef *methods) { 33 | for (int i = 0; methods[i].name != nullptr; i++) { 34 | external_methods.push_back(methods[i]); 35 | } 36 | } 37 | 38 | void add_call_methods(R_CallMethodDef *methods) { 39 | for (int i = 0; methods[i].name != nullptr; i++) { 40 | call_methods.push_back(methods[i]); 41 | } 42 | } 43 | 44 | void add_c_methods(R_CMethodDef *methods) { 45 | for (int i = 0; methods[i].name != nullptr; i++) { 46 | c_methods.push_back(methods[i]); 47 | } 48 | } 49 | 50 | void register_all(DllInfo *dll) { 51 | external_methods.push_back({}); 52 | call_methods.push_back({}); 53 | c_methods.push_back({}); 54 | R_registerRoutines(dll, c_methods.data(), call_methods.data(), nullptr, external_methods.data()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/exports.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include "stdafx.h" 26 | #include "r_api.h" 27 | 28 | namespace rhost { 29 | namespace exports { 30 | void add_external_methods(R_ExternalMethodDef *call_method); 31 | void add_call_methods(R_CallMethodDef *call_method); 32 | void add_c_methods(R_CMethodDef *c_method); 33 | void register_all(DllInfo *dll); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/grdevices.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include "stdafx.h" 26 | #include "util.h" 27 | #include "r_api.h" 28 | 29 | namespace rhost { 30 | namespace grdevices { 31 | /////////////////////////////////////////////////////////////////// 32 | // Base class for graphics devices 33 | /////////////////////////////////////////////////////////////////// 34 | template 35 | class graphics_device { 36 | typedef rapi::gd_api gd_api; 37 | typedef typename gd_api::DevDesc DevDesc; 38 | 39 | public: 40 | DevDesc* device_desc; 41 | 42 | virtual ~graphics_device() {} 43 | virtual void activate() = 0; 44 | virtual void circle(double x, double y, double r, pGEcontext gc) = 0; 45 | virtual void clip(double x0, double x1, double y0, double y1) = 0; 46 | virtual void close() = 0; 47 | virtual void deactivate() = 0; 48 | virtual Rboolean locator(double *x, double *y) = 0; 49 | virtual void line(double x1, double y1, double x2, double y2, const pGEcontext gc) = 0; 50 | virtual void metric_info(int c, const pGEcontext gc, double* ascent, double* descent, double* width) = 0; 51 | virtual void mode(int mode) = 0; 52 | virtual void new_page(const pGEcontext gc) = 0; 53 | virtual void polygon(int n, double *x, double *y, const pGEcontext gc) = 0; 54 | virtual void polyline(int n, double *x, double *y, const pGEcontext gc) = 0; 55 | virtual void rect(double x0, double y0, double x1, double y1, const pGEcontext gc) = 0; 56 | virtual void path(double *x, double *y, int npoly, int *nper, Rboolean winding, const pGEcontext gc) = 0; 57 | virtual void raster(unsigned int *raster, int w, int h, double x, double y, double width, double height, double rot, Rboolean interpolate, const pGEcontext gc) = 0; 58 | virtual SEXP cap() = 0; 59 | virtual void size(double *left, double *right, double *bottom, double *top) = 0; 60 | virtual double str_width(const char *str, const pGEcontext gc) = 0; 61 | virtual void text(double x, double y, const char *str, double rot, double hadj, const pGEcontext gc) = 0; 62 | virtual void on_exit() = 0; 63 | virtual Rboolean new_frame_confirm() = 0; 64 | virtual void text_utf8(double x, double y, const char *str, double rot, double hadj, const pGEcontext gc) = 0; 65 | virtual double str_width_utf8(const char *str, const pGEcontext gc) = 0; 66 | virtual void event_helper(int code) = 0; 67 | virtual int hold_flush(int level) = 0; 68 | 69 | protected: 70 | graphics_device(DevDesc* dd) : 71 | device_desc(dd) 72 | { 73 | device_desc->activate = [](DevDesc* dd) { 74 | return rhost::util::exceptions_to_errors([&] { 75 | return reinterpret_cast(dd->deviceSpecific)->activate(); 76 | }); 77 | }; 78 | device_desc->circle = [](double x, double y, double r, pGEcontext gc, DevDesc* dd) { 79 | return rhost::util::exceptions_to_errors([&] { 80 | return reinterpret_cast(dd->deviceSpecific)->circle(x, y, r, gc); 81 | }); 82 | }; 83 | device_desc->clip = [](double x0, double x1, double y0, double y1, DevDesc* dd) { 84 | return rhost::util::exceptions_to_errors([&] { 85 | return reinterpret_cast(dd->deviceSpecific)->clip(x0, x1, y0, y1); 86 | }); 87 | }; 88 | device_desc->close = [](DevDesc* dd) { 89 | return rhost::util::exceptions_to_errors([&] { 90 | reinterpret_cast(dd->deviceSpecific)->close(); 91 | }); 92 | }; 93 | device_desc->deactivate = [](DevDesc* dd) { 94 | return rhost::util::exceptions_to_errors([&] { 95 | return reinterpret_cast(dd->deviceSpecific)->deactivate(); 96 | }); 97 | }; 98 | device_desc->locator = [](double *x, double *y, DevDesc* dd) { 99 | return rhost::util::exceptions_to_errors([&] { 100 | return reinterpret_cast(dd->deviceSpecific)->locator(x, y); 101 | }); 102 | }; 103 | device_desc->line = [](double x1, double y1, double x2, double y2, const pGEcontext gc, DevDesc* dd) { 104 | return rhost::util::exceptions_to_errors([&] { 105 | return reinterpret_cast(dd->deviceSpecific)->line(x1, y1, x2, y2, gc); 106 | }); 107 | }; 108 | device_desc->metricInfo = [](int c, const pGEcontext gc, double* ascent, double* descent, double* width, DevDesc* dd) { 109 | return rhost::util::exceptions_to_errors([&] { 110 | return reinterpret_cast(dd->deviceSpecific)->metric_info(c, gc, ascent, descent, width); 111 | }); 112 | }; 113 | device_desc->mode = [](int mode, DevDesc* dd) { 114 | return rhost::util::exceptions_to_errors([&] { 115 | return reinterpret_cast(dd->deviceSpecific)->mode(mode); 116 | }); 117 | }; 118 | device_desc->newPage = [](const pGEcontext gc, DevDesc* dd) { 119 | return rhost::util::exceptions_to_errors([&] { 120 | return reinterpret_cast(dd->deviceSpecific)->new_page(gc); 121 | }); 122 | }; 123 | device_desc->polygon = [](int n, double *x, double *y, const pGEcontext gc, DevDesc* dd) { 124 | return rhost::util::exceptions_to_errors([&] { 125 | return reinterpret_cast(dd->deviceSpecific)->polygon(n, x, y, gc); 126 | }); 127 | }; 128 | device_desc->polyline = [](int n, double *x, double *y, const pGEcontext gc, DevDesc* dd) { 129 | return rhost::util::exceptions_to_errors([&] { 130 | return reinterpret_cast(dd->deviceSpecific)->polyline(n, x, y, gc); 131 | }); 132 | }; 133 | device_desc->rect = [](double x0, double y0, double x1, double y1, const pGEcontext gc, DevDesc* dd) { 134 | return rhost::util::exceptions_to_errors([&] { 135 | return reinterpret_cast(dd->deviceSpecific)->rect(x0, y0, x1, y1, gc); 136 | }); 137 | }; 138 | device_desc->path = [](double *x, double *y, int npoly, int *nper, Rboolean winding, const pGEcontext gc, DevDesc* dd) { 139 | return rhost::util::exceptions_to_errors([&] { 140 | return reinterpret_cast(dd->deviceSpecific)->path(x, y, npoly, nper, winding, gc); 141 | }); 142 | }; 143 | device_desc->raster = [](unsigned int *raster, int w, int h, double x, double y, double width, double height, double rot, Rboolean interpolate, const pGEcontext gc, DevDesc* dd) { 144 | return rhost::util::exceptions_to_errors([&] { 145 | return reinterpret_cast(dd->deviceSpecific)->raster(raster, w, h, x, y, width, height, rot, interpolate, gc); 146 | }); 147 | }; 148 | device_desc->cap = [](DevDesc* dd) { 149 | return rhost::util::exceptions_to_errors([&] { 150 | return reinterpret_cast(dd->deviceSpecific)->cap(); 151 | }); 152 | }; 153 | device_desc->size = [](double *left, double *right, double *bottom, double *top, DevDesc* dd) { 154 | return rhost::util::exceptions_to_errors([&] { 155 | return reinterpret_cast(dd->deviceSpecific)->size(left, right, bottom, top); 156 | }); 157 | }; 158 | device_desc->strWidth = [](const char *str, const pGEcontext gc, DevDesc* dd) { 159 | return rhost::util::exceptions_to_errors([&] { 160 | return reinterpret_cast(dd->deviceSpecific)->str_width(str, gc); 161 | }); 162 | }; 163 | device_desc->text = [](double x, double y, const char *str, double rot, double hadj, const pGEcontext gc, DevDesc* dd) { 164 | return rhost::util::exceptions_to_errors([&] { 165 | return reinterpret_cast(dd->deviceSpecific)->text(x, y, str, rot, hadj, gc); 166 | }); 167 | }; 168 | device_desc->onExit = [](DevDesc* dd) { 169 | return rhost::util::exceptions_to_errors([&] { 170 | return reinterpret_cast(dd->deviceSpecific)->on_exit(); 171 | }); 172 | }; 173 | device_desc->newFrameConfirm = [](DevDesc* dd) { 174 | return rhost::util::exceptions_to_errors([&] { 175 | return reinterpret_cast(dd->deviceSpecific)->new_frame_confirm(); 176 | }); 177 | }; 178 | device_desc->textUTF8 = [](double x, double y, const char *str, double rot, double hadj, const pGEcontext gc, DevDesc* dd) { 179 | return rhost::util::exceptions_to_errors([&] { 180 | return reinterpret_cast(dd->deviceSpecific)->text_utf8(x, y, str, rot, hadj, gc); 181 | }); 182 | }; 183 | device_desc->strWidthUTF8 = [](const char *str, const pGEcontext gc, DevDesc* dd) { 184 | return rhost::util::exceptions_to_errors([&] { 185 | return reinterpret_cast(dd->deviceSpecific)->str_width_utf8(str, gc); 186 | }); 187 | }; 188 | device_desc->eventHelper = [](DevDesc* dd, int code) { 189 | return rhost::util::exceptions_to_errors([&] { 190 | return reinterpret_cast(dd->deviceSpecific)->event_helper(code); 191 | }); 192 | }; 193 | device_desc->holdflush = [](DevDesc* dd, int level) { 194 | return rhost::util::exceptions_to_errors([&] { 195 | return reinterpret_cast(dd->deviceSpecific)->hold_flush(level); 196 | }); 197 | }; 198 | } 199 | }; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/grdeviceside.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include "stdafx.h" 26 | #include "r_api.h" 27 | 28 | namespace rhost { 29 | namespace grdevices { 30 | namespace ide { 31 | void init(DllInfo *dll); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/grdevicesxaml.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include "stdafx.h" 26 | #include "r_api.h" 27 | 28 | namespace rhost { 29 | namespace grdevices { 30 | namespace xaml { 31 | void init(DllInfo *dll); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/host.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "util.h" 26 | #include "blobs.h" 27 | #include "message.h" 28 | #include "log.h" 29 | #include "r_api.h" 30 | 31 | namespace rhost { 32 | namespace host { 33 | class eval_cancel_error : std::exception { 34 | }; 35 | 36 | void initialize(structRstart& rp, const fs::path& rdata, std::chrono::seconds idle_timeout); 37 | void set_callbacks_windows(structRstart& rp); 38 | void set_callbacks_posix(); 39 | void shutdown_if_requested(); 40 | void do_r_callback(bool allow_eval_interrupt); 41 | 42 | extern "C" void ShowMessage(const char* s); 43 | extern "C" int YesNoCancel(const char* s); 44 | extern "C" int OkCancel(const char* s); 45 | extern "C" int YesNo(const char* s); 46 | 47 | extern boost::signals2::signal callback_started; 48 | extern boost::signals2::signal readconsole_done; 49 | extern boost::signals2::signal disconnected; 50 | 51 | RHOST_NORETURN void propagate_cancellation(); 52 | 53 | template 54 | auto with_cancellation(F body) -> decltype(body()) { 55 | shutdown_if_requested(); 56 | try { 57 | return body(); 58 | } catch (const eval_cancel_error&) { 59 | propagate_cancellation(); 60 | } 61 | } 62 | 63 | protocol::message_id send_notification(const std::string& name, const picojson::array& args, const blobs::blob& blob = blobs::blob()); 64 | 65 | template 66 | inline protocol::message_id send_notification(const std::string& name, const Args&... args) { 67 | picojson::array args_array; 68 | rhost::util::append(args_array, args...); 69 | return send_notification(name, args_array); 70 | } 71 | 72 | template 73 | inline protocol::message_id send_notification(const std::string& name, const blobs::blob& blob, const Args&... args) { 74 | picojson::array args_array; 75 | rhost::util::append(args_array, args...); 76 | return send_notification(name, args_array, blob); 77 | } 78 | 79 | protocol::message send_request_and_get_response(const std::string&, const picojson::array& args); 80 | 81 | template 82 | inline protocol::message send_request_and_get_response(const std::string& name, const Args&... args) { 83 | picojson::array args_array; 84 | rhost::util::append(args_array, args...); 85 | return send_request_and_get_response(name, args_array); 86 | } 87 | 88 | blobs::blob_id create_blob(blobs::blob&& blob); 89 | 90 | inline blobs::blob_id create_blob(const blobs::blob& blob) { 91 | auto copy = blob; 92 | return create_blob(copy); 93 | } 94 | 95 | blobs::blob_id create_compressed_blob(blobs::blob&& blob); 96 | 97 | bool get_blob(blobs::blob_id id, blobs::blob& blob); 98 | 99 | inline blobs::blob get_blob(blobs::blob_id id) { 100 | blobs::blob blob; 101 | if (!get_blob(id, blob)) { 102 | log::fatal_error("GetBlob: no blob with ID %lld", id); 103 | } 104 | return blob; 105 | } 106 | 107 | void destroy_blob(blobs::blob_id id); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/json.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "json.h" 24 | #include "log.h" 25 | 26 | using namespace rhost::util; 27 | using namespace rhost::log; 28 | namespace js = picojson; 29 | 30 | namespace rhost { 31 | namespace json { 32 | namespace { 33 | bool strsxp_to_utf8(SEXP sexp, std::string& result) { 34 | const void* vmax = vmaxget(); 35 | const char* s = Rf_translateCharUTF8(sexp); 36 | if (s) { 37 | result = s; 38 | } else { 39 | result.clear(); 40 | } 41 | vmaxset(vmax); 42 | return s != nullptr; 43 | } 44 | 45 | void json_error(SEXP sexp, const char* format, ...) { 46 | SEXP repr_sexp = STRING_ELT(Rf_deparse1line(sexp, R_FALSE), 0); 47 | Rf_protect(repr_sexp); 48 | const char* repr = R_CHAR(repr_sexp); 49 | 50 | char buf[0x10000] = {}; 51 | snprintf(buf, sizeof buf, "JSON serialization failed for input:\n\n%s\n\n", repr); 52 | 53 | size_t len = strlen(buf); 54 | va_list va; 55 | va_start(va, format); 56 | vsnprintf(buf + len, sizeof buf - len, format, va); 57 | va_end(va); 58 | 59 | Rf_error("%s", buf); 60 | } 61 | 62 | void at_most_one(SEXP sexp) { 63 | if (Rf_length(sexp) != 1) { 64 | json_error(sexp, "Vector must have 0 or 1 elements."); 65 | } 66 | } 67 | 68 | void list_to_array(SEXP sexp, js::array& result) { 69 | R_len_t count = Rf_length(sexp); 70 | result.reserve(count); 71 | 72 | for (R_len_t i = 0; i < count; ++i) { 73 | result.push_back(js::value()); 74 | auto& elem = result.back(); 75 | 76 | SEXP elem_sexp = VECTOR_ELT(sexp, i); 77 | if (!to_json(elem_sexp, elem)) { 78 | result.pop_back(); 79 | } 80 | } 81 | } 82 | 83 | void list_to_object(SEXP sexp, SEXP names, js::object& result) { 84 | R_len_t count = Rf_length(sexp); 85 | if (Rf_length(names) != count) { 86 | json_error(sexp, "All elements in list must be named, but there are fewer names than elements."); 87 | } 88 | 89 | for (R_len_t i = 0; i < count; ++i) { 90 | SEXP name_sexp = STRING_ELT(names, i); 91 | if (name_sexp == R_NaString) { 92 | json_error(sexp, "All elements in list must be named, but [[%d]] is not.", i + 1); 93 | } 94 | 95 | std::string name; 96 | if (!strsxp_to_utf8(name_sexp, name)) { 97 | json_error(sexp, "Name of [[%d]] could not be converted to UTF-8.", i + 1); 98 | } 99 | 100 | auto insert_result = result.insert(std::make_pair(name, js::value())); 101 | if (!insert_result.second) { 102 | json_error(sexp, "Duplicate name '%s' in list.", name.c_str()); 103 | } 104 | 105 | auto iter = insert_result.first; 106 | auto& elem = iter->second; 107 | 108 | SEXP elem_sexp = VECTOR_ELT(sexp, i); 109 | if (!to_json(elem_sexp, elem)) { 110 | result.erase(iter); 111 | } 112 | } 113 | } 114 | 115 | void env_to_object(SEXP sexp, js::object& result) { 116 | SEXP names = R_lsInternal3(sexp, R_TRUE, R_FALSE); 117 | R_len_t count = Rf_length(names); 118 | 119 | for (R_len_t i = 0; i < count; ++i) { 120 | SEXP name_sexp = STRING_ELT(names, i); 121 | std::string name; 122 | if (!strsxp_to_utf8(name_sexp, name)) { 123 | json_error(sexp, "Name of [[%d]] could not be converted to UTF-8.", i + 1); 124 | } 125 | 126 | auto insert_result = result.insert(std::make_pair(name, js::value())); 127 | if (!insert_result.second) { 128 | json_error(sexp, "Duplicate name '%s' in environment.", name.c_str()); 129 | } 130 | 131 | auto iter = insert_result.first; 132 | auto& elem = iter->second; 133 | 134 | SEXP sym_sexp = Rf_installChar(name_sexp); 135 | SEXP elem_sexp = Rf_findVar(sym_sexp, sexp); 136 | if (!to_json(elem_sexp, elem)) { 137 | result.erase(iter); 138 | } 139 | } 140 | } 141 | } 142 | 143 | bool to_json(SEXP sexp, js::value& result) { 144 | int type = TYPEOF(sexp); 145 | 146 | switch (type) { 147 | case NILSXP: 148 | result = js::value(); 149 | return true; 150 | 151 | case VECSXP: { 152 | SEXP names = Rf_getAttrib(sexp, R_NamesSymbol); 153 | if (Rf_isNull(names)) { 154 | result = js::value(js::array()); 155 | list_to_array(sexp, result.get()); 156 | } else { 157 | result = js::value(js::object()); 158 | list_to_object(sexp, names, result.get()); 159 | } 160 | return true; 161 | } 162 | 163 | case ENVSXP: 164 | result = js::value(js::object()); 165 | env_to_object(sexp, result.get()); 166 | return true; 167 | } 168 | 169 | if (Rf_length(sexp) == 0) { 170 | result = js::value(); 171 | return true; 172 | } 173 | 174 | switch (type) { 175 | case LGLSXP: { 176 | at_most_one(sexp); 177 | int x = *LOGICAL(sexp); 178 | result = x == R_NaInt ? js::value() : js::value(x != 0); 179 | break; 180 | } 181 | 182 | case INTSXP: { 183 | at_most_one(sexp); 184 | int x = *INTEGER(sexp); 185 | result = x == R_NaInt ? js::value() : js::value(static_cast(x)); 186 | break; 187 | } 188 | 189 | case REALSXP: { 190 | at_most_one(sexp); 191 | double x = *REAL(sexp); 192 | if (R_IsNA(x)) { 193 | result = js::value(); 194 | } else { 195 | if (std::isinf(x) || std::isnan(x)) { 196 | json_error(sexp, "+Inf, -Inf and NaN cannot be serialized."); 197 | } 198 | result = js::value(x); 199 | } 200 | break; 201 | } 202 | 203 | case STRSXP: { 204 | at_most_one(sexp); 205 | SEXP x = STRING_ELT(sexp, 0); 206 | if (x == R_NaString) { 207 | result = js::value(); 208 | } else { 209 | std::string s; 210 | result = strsxp_to_utf8(x, s) ? js::value(s) : js::value(); 211 | } 212 | break; 213 | } 214 | 215 | default: 216 | json_error(sexp, "Unsupported type - must be one of: NULL; logical, integer, real, character vector; list; environment."); 217 | } 218 | 219 | return result != js::value(); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/json.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "util.h" 26 | 27 | namespace rhost { 28 | namespace json { 29 | // Produces JSON from an R object according to the following mappings: 30 | // 31 | // NULL, NA, empty vector -> null 32 | // TRUE -> true 33 | // FALSE -> false 34 | // Vector of a single non-NA integer or double -> numeric literal 35 | // Vector of a single non-NA string -> string literal 36 | // List with all elements unnamed -> array (recursively) 37 | // List with all elements named, or environment -> object (recursively) 38 | // 39 | // If any element of a list or environment is NA, that element is skipped. 40 | // Any input not covered by the rules above is considered invalid. 41 | // 42 | // Returns true if serialized value was NA, and false otherwise (even if it contains NA somewhere inside). 43 | // Errors are reported via Rf_error. 44 | bool to_json(SEXP sexp, picojson::value& result); 45 | 46 | // Same as above, but value is returned directly instead of being constructed in the provided location, 47 | // and errors are reported as C++ exceptions. 48 | inline picojson::value to_json(SEXP sexp) { 49 | picojson::value result; 50 | rhost::util::errors_to_exceptions([&] { to_json(sexp, result); }); 51 | return result; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/loadr.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #define RHOST_NO_API_REDIRECT 24 | #include "stdafx.h" 25 | #include "r_api.h" 26 | #include "log.h" 27 | 28 | #define RHOST_RAPI_DEFINE(api) decltype(api) *RHOST_RAPI_PTR(api) 29 | #define RHOST_RAPI_DEFINE_NULLPTR(api) RHOST_RAPI_DEFINE(api) = nullptr; 30 | 31 | #define RHOST_GD_DEFINE(api) \ 32 | decltype(gd_api<10>::api) gd_api<10>::api; \ 33 | decltype(gd_api<12>::api) gd_api<12>::api; 34 | 35 | #define RHOST_GET_PROC(m, api) RHOST_RAPI_PTR(api) = get_proc(m, RHOST_RAPI_STR(api)); 36 | #define RHOST_RAPI_GET_PROC(api) RHOST_GET_PROC(r_module, api) 37 | #define RHOST_RAPI_UNLOAD(api) RHOST_RAPI_PTR(api) = nullptr; 38 | 39 | #define RHOST_GD_GET_PROC(api) api = get_proc(r_module, RHOST_RAPI_STR(api)); 40 | #define RHOST_GD_UNLOAD(api) api = nullptr; 41 | 42 | #ifdef _WIN32 43 | #define RHOST_RGRAPHAPPAPI_GET_PROC(api) RHOST_GET_PROC(rgraphapp_module, api) 44 | #endif 45 | 46 | #ifdef _WIN32 47 | typedef HMODULE rhost_module_t; 48 | #else 49 | typedef void* rhost_module_t; 50 | #endif 51 | 52 | namespace rhost { 53 | namespace rapi { 54 | RHOST_RAPI_SET(RHOST_RAPI_DEFINE_NULLPTR); 55 | RHOST_GD_SET(RHOST_GD_DEFINE); 56 | #ifdef _WIN32 57 | RHOST_RGRAPHAPPAPI_SET(RHOST_RAPI_DEFINE_NULLPTR); 58 | #endif 59 | 60 | namespace { 61 | rhost_module_t r_module = nullptr; 62 | rhost_module_t rgraphapp_module = nullptr; 63 | 64 | template 65 | T get_proc(rhost_module_t module, const char* proc_name) { 66 | T ptr = nullptr; 67 | #ifdef _WIN32 68 | ptr = reinterpret_cast(GetProcAddress(module, proc_name)); 69 | if (!ptr) { 70 | log::fatal_error("Error failed to get address of %s: %d", proc_name, GetLastError()); 71 | } 72 | #else 73 | ptr = reinterpret_cast(dlsym(module, proc_name)); 74 | if (!ptr) { 75 | log::fatal_error("Error failed to get address of %s: %s", proc_name, dlerror()); 76 | } 77 | #endif 78 | return ptr; 79 | } 80 | 81 | void internal_load_r_apis() { 82 | RHOST_RAPI_SET(RHOST_RAPI_GET_PROC); 83 | } 84 | 85 | void internal_unload_r_apis() { 86 | RHOST_RAPI_SET(RHOST_RAPI_UNLOAD); 87 | } 88 | 89 | #ifdef _WIN32 90 | void internal_load_rgraphapp_apis() { 91 | RHOST_RGRAPHAPPAPI_SET(RHOST_RGRAPHAPPAPI_GET_PROC); 92 | } 93 | 94 | void internal_unload_rgraphapp_apis() { 95 | RHOST_RGRAPHAPPAPI_SET(RHOST_RAPI_UNLOAD); 96 | } 97 | #endif 98 | } 99 | 100 | void gd_api<10>::load() { 101 | RHOST_GD_SET(RHOST_GD_GET_PROC); 102 | } 103 | 104 | void gd_api<10>::unload() { 105 | RHOST_GD_SET(RHOST_GD_UNLOAD); 106 | } 107 | 108 | void gd_api<12>::load() { 109 | RHOST_GD_SET(RHOST_GD_GET_PROC); 110 | } 111 | 112 | void gd_api<12>::unload() { 113 | RHOST_GD_SET(RHOST_GD_UNLOAD); 114 | } 115 | 116 | void load_r_apis(fs::path& r_dll_dir) { 117 | #ifdef _WIN32 118 | fs::path r_path = r_dll_dir / "r.dll"; 119 | fs::path rgraphapp_path = r_dll_dir / "rgraphapp.dll"; 120 | 121 | r_module = LoadLibraryEx(r_path.make_preferred().wstring().c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); 122 | if (!r_module) { 123 | log::fatal_error("Error r module failed to load: %d", GetLastError()); 124 | } 125 | 126 | rgraphapp_module = LoadLibraryEx(rgraphapp_path.make_preferred().wstring().c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); 127 | if (!rgraphapp_module) { 128 | log::fatal_error("Error rgraphapp module failed to load: %d", GetLastError()); 129 | } 130 | 131 | internal_load_rgraphapp_apis(); 132 | #else // POSIX 133 | // Try Linux first 134 | fs::path r_path = r_dll_dir / "lib/libR.so"; 135 | r_module = dlopen(r_path.make_preferred().string().c_str(), RTLD_LOCAL | RTLD_LAZY); 136 | if (!r_module) { 137 | // Try Mac 138 | r_path = r_dll_dir / "lib/libR.dylib"; 139 | r_module = dlopen(r_path.make_preferred().string().c_str(), RTLD_LOCAL | RTLD_LAZY); 140 | if (!r_module) { 141 | log::fatal_error("Error r module failed to load: %s", dlerror()); 142 | } 143 | } 144 | #endif 145 | internal_load_r_apis(); 146 | 147 | switch (int ver = fp_R_GE_getVersion()) { 148 | case 10: 149 | gd_api<10>::load(); 150 | break; 151 | case 11: 152 | gd_api<11>::load(); 153 | break; 154 | case 12: 155 | gd_api<12>::load(); 156 | break; 157 | default: 158 | log::fatal_error("Unsupported GD API version %d", ver); 159 | } 160 | } 161 | 162 | void unload_r_apis() { 163 | switch (int ver = fp_R_GE_getVersion()) { 164 | case 10: 165 | gd_api<10>::unload(); 166 | break; 167 | case 11: 168 | gd_api<11>::unload(); 169 | break; 170 | case 12: 171 | gd_api<12>::unload(); 172 | break; 173 | default: 174 | log::fatal_error("Unsupported GD API version %d", ver); 175 | } 176 | 177 | internal_unload_r_apis(); 178 | #ifdef _WIN32 179 | internal_unload_rgraphapp_apis(); 180 | FreeLibrary(r_module); 181 | FreeLibrary(rgraphapp_module); 182 | #else // POSIX 183 | dlclose(r_module); 184 | #endif 185 | } 186 | } 187 | } 188 | 189 | -------------------------------------------------------------------------------- /src/loadr.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | 26 | #define RHOST_RAPI_PTR(api) fp_##api 27 | #define RHOST_RAPI_DECL(api) extern decltype(api) *RHOST_RAPI_PTR(api) 28 | #define RHOST_RAPI_DECL_END(api) RHOST_RAPI_DECL(api); 29 | #define RHOST_RAPI_STR(api) #api 30 | #define RAPI(api) rhost::rapi::RHOST_RAPI_PTR(api) 31 | 32 | #define RHOST_RAPI_SET_COMMON(macro) \ 33 | macro(CAR) \ 34 | macro(CDR) \ 35 | macro(INTEGER) \ 36 | macro(LOGICAL) \ 37 | macro(PRCODE) \ 38 | macro(PRVALUE) \ 39 | macro(R_BaseEnv) \ 40 | macro(R_CHAR) \ 41 | macro(R_CleanUp) \ 42 | macro(R_common_command_line) \ 43 | macro(R_curErrorBuf) \ 44 | macro(R_DefParams) \ 45 | macro(R_EmptyEnv) \ 46 | macro(R_FunTab) \ 47 | macro(R_getEmbeddingDllInfo) \ 48 | macro(R_GlobalContext) \ 49 | macro(R_GlobalEnv) \ 50 | macro(R_IsNA) \ 51 | macro(R_lsInternal3) \ 52 | macro(R_NaInt) \ 53 | macro(R_NamesSymbol) \ 54 | macro(R_NaString) \ 55 | macro(R_new_custom_connection) \ 56 | macro(R_NilValue) \ 57 | macro(R_ParseVector) \ 58 | macro(R_PreserveObject) \ 59 | macro(R_ProcessEvents) \ 60 | macro(R_registerRoutines) \ 61 | macro(R_ReleaseObject) \ 62 | macro(R_RestoreGlobalEnvFromFile) \ 63 | macro(R_SaveGlobalEnvToFile) \ 64 | macro(R_set_command_line_arguments) \ 65 | macro(R_SetParams) \ 66 | macro(R_Srcref) \ 67 | macro(R_ToplevelExec) \ 68 | macro(R_UnboundValue) \ 69 | macro(RAW) \ 70 | macro(RDEBUG) \ 71 | macro(REAL) \ 72 | macro(Rf_allocList) \ 73 | macro(Rf_allocVector) \ 74 | macro(Rf_allocVector3) \ 75 | macro(Rf_asChar) \ 76 | macro(Rf_asInteger) \ 77 | macro(Rf_asLogical) \ 78 | macro(Rf_asReal) \ 79 | macro(Rf_classgets) \ 80 | macro(Rf_deparse1line) \ 81 | macro(Rf_duplicate) \ 82 | macro(Rf_error) \ 83 | macro(Rf_eval) \ 84 | macro(Rf_findVar) \ 85 | macro(Rf_getAttrib) \ 86 | macro(Rf_install) \ 87 | macro(Rf_installChar) \ 88 | macro(Rf_isEnvironment) \ 89 | macro(Rf_isNull) \ 90 | macro(Rf_isString) \ 91 | macro(Rf_length) \ 92 | macro(Rf_mkChar) \ 93 | macro(Rf_mkCharCE) \ 94 | macro(Rf_mkString) \ 95 | macro(Rf_NewEnvironment) \ 96 | macro(Rf_protect) \ 97 | macro(Rf_ScalarInteger) \ 98 | macro(Rf_ScalarLogical) \ 99 | macro(Rf_ScalarReal) \ 100 | macro(Rf_ScalarString) \ 101 | macro(Rf_translateCharUTF8) \ 102 | macro(Rf_unprotect) \ 103 | macro(SET_RDEBUG) \ 104 | macro(SET_STRING_ELT) \ 105 | macro(SET_TYPEOF) \ 106 | macro(SET_VECTOR_ELT) \ 107 | macro(SETCAR) \ 108 | macro(STRING_ELT) \ 109 | macro(TYPEOF) \ 110 | macro(VECTOR_ELT) \ 111 | macro(vmaxget) \ 112 | macro(vmaxset) \ 113 | macro(Rf_onintr) \ 114 | macro(R_GE_getVersion) \ 115 | macro(Rf_curDevice) \ 116 | macro(Rf_selectDevice) \ 117 | macro(R_interrupts_suspended) \ 118 | macro(R_interrupts_pending) \ 119 | macro(R_CheckDeviceAvailable) \ 120 | macro(GEcopyDisplayList) \ 121 | macro(run_Rmainloop) \ 122 | macro(setup_Rmainloop) 123 | 124 | #define RHOST_GD_SET(macro) \ 125 | macro(Rf_desc2GEDesc) \ 126 | macro(GEplayDisplayList) \ 127 | macro(GEplaySnapshot) \ 128 | macro(GEcreateSnapshot) \ 129 | macro(Rf_ndevNumber) \ 130 | macro(GEgetDevice) \ 131 | macro(GEkillDevice) \ 132 | macro(GEcreateDevDesc) \ 133 | macro(GEaddDevice2) 134 | 135 | #ifdef _WIN32 136 | 137 | #define RHOST_RAPI_SET_WINDOWS(macro) \ 138 | macro(CharacterMode) \ 139 | macro(get_R_HOME) \ 140 | macro(getDLLVersion) \ 141 | macro(getRUser) \ 142 | macro(in_memsize) \ 143 | macro(R_setStartTime) \ 144 | macro(R_WaitEvent) \ 145 | macro(readconsolecfg) \ 146 | macro(Rf_utf8towcs) \ 147 | macro(Rf_wtransChar) 148 | 149 | #define RHOST_RGRAPHAPPAPI_SET(macro) \ 150 | macro(GA_initapp) 151 | 152 | #define RHOST_RAPI_SET(macro) \ 153 | RHOST_RAPI_SET_COMMON(macro) \ 154 | RHOST_RAPI_SET_WINDOWS(macro) 155 | 156 | #else // POSIX 157 | 158 | #define RHOST_RAPI_SET_POSIX(macro) \ 159 | macro(ptr_R_Busy) \ 160 | macro(ptr_R_ProcessEvents) \ 161 | macro(ptr_R_ReadConsole) \ 162 | macro(ptr_R_ShowMessage) \ 163 | macro(ptr_R_WriteConsole) \ 164 | macro(ptr_R_WriteConsoleEx) \ 165 | macro(R_running_as_main_program) \ 166 | macro(R_checkActivity) \ 167 | macro(R_Consolefile) \ 168 | macro(R_InputHandlers) \ 169 | macro(R_Interactive) \ 170 | macro(R_Outputfile) \ 171 | macro(R_runHandlers) \ 172 | macro(Rf_initialize_R) 173 | 174 | #define RHOST_RAPI_SET(macro) \ 175 | RHOST_RAPI_SET_COMMON(macro) \ 176 | RHOST_RAPI_SET_POSIX(macro) 177 | 178 | #endif 179 | 180 | namespace rhost { 181 | namespace rapi { 182 | 183 | // R.dll/R.so APIs 184 | RHOST_RAPI_SET(RHOST_RAPI_DECL_END); 185 | 186 | #ifdef _WIN32 187 | // Rgraphapp.dll/Rgraphapp.so APIs 188 | RHOST_RGRAPHAPPAPI_SET(RHOST_RAPI_DECL_END); 189 | #endif 190 | void load_r_apis(fs::path& r_dll_dir); 191 | void unload_r_apis(); 192 | } 193 | } 194 | 195 | #ifndef RHOST_NO_API_REDIRECT 196 | #define CAR rhost::rapi::RHOST_RAPI_PTR(CAR) 197 | #define CDR rhost::rapi::RHOST_RAPI_PTR(CDR) 198 | //#define GEaddDevice2 rhost::rapi::RHOST_RAPI_PTR(GEaddDevice2) 199 | //#define GEaddDevice2f rhost::rapi::RHOST_RAPI_PTR(GEaddDevice2f) 200 | #define GEcopyDisplayList rhost::rapi::RHOST_RAPI_PTR(GEcopyDisplayList) 201 | //#define GEcreateDevDesc rhost::rapi::RHOST_RAPI_PTR(GEcreateDevDesc) 202 | //#define GEcreateSnapshot rhost::rapi::RHOST_RAPI_PTR(GEcreateSnapshot) 203 | //#define GEgetDevice rhost::rapi::RHOST_RAPI_PTR(GEgetDevice) 204 | //#define GEkillDevice rhost::rapi::RHOST_RAPI_PTR(GEkillDevice) 205 | //#define GEplayDisplayList rhost::rapi::RHOST_RAPI_PTR(GEplayDisplayList) 206 | //#define GEplaySnapshot rhost::rapi::RHOST_RAPI_PTR(GEplaySnapshot) 207 | #define INTEGER rhost::rapi::RHOST_RAPI_PTR(INTEGER) 208 | #define LOGICAL rhost::rapi::RHOST_RAPI_PTR(LOGICAL) 209 | #define PRCODE rhost::rapi::RHOST_RAPI_PTR(PRCODE) 210 | #define PRVALUE rhost::rapi::RHOST_RAPI_PTR(PRVALUE) 211 | #define R_BaseEnv (*rhost::rapi::RHOST_RAPI_PTR(R_BaseEnv)) 212 | #define R_CHAR rhost::rapi::RHOST_RAPI_PTR(R_CHAR) 213 | #define R_CheckDeviceAvailable rhost::rapi::RHOST_RAPI_PTR(R_CheckDeviceAvailable) 214 | #define R_CleanUp rhost::rapi::RHOST_RAPI_PTR(R_CleanUp) 215 | #define R_common_command_line rhost::rapi::RHOST_RAPI_PTR(R_common_command_line) 216 | #define R_curErrorBuf rhost::rapi::RHOST_RAPI_PTR(R_curErrorBuf) 217 | #define R_DefParams rhost::rapi::RHOST_RAPI_PTR(R_DefParams) 218 | #define R_EmptyEnv (*rhost::rapi::RHOST_RAPI_PTR(R_EmptyEnv)) 219 | #define R_FunTab (*rhost::rapi::RHOST_RAPI_PTR(R_FunTab)) 220 | #define R_GE_getVersion rhost::rapi::RHOST_RAPI_PTR(R_GE_getVersion) 221 | #define R_getEmbeddingDllInfo rhost::rapi::RHOST_RAPI_PTR(R_getEmbeddingDllInfo) 222 | #define R_GlobalContext (*rhost::rapi::RHOST_RAPI_PTR(R_GlobalContext)) 223 | #define R_GlobalEnv (*rhost::rapi::RHOST_RAPI_PTR(R_GlobalEnv)) 224 | #define R_interrupts_pending (*rhost::rapi::RHOST_RAPI_PTR(R_interrupts_pending)) 225 | #define R_interrupts_suspended (*rhost::rapi::RHOST_RAPI_PTR(R_interrupts_suspended)) 226 | #define R_IsNA rhost::rapi::RHOST_RAPI_PTR(R_IsNA) 227 | #define R_lsInternal3 rhost::rapi::RHOST_RAPI_PTR(R_lsInternal3) 228 | #define R_NaInt (*rhost::rapi::RHOST_RAPI_PTR(R_NaInt)) 229 | #define R_NamesSymbol (*rhost::rapi::RHOST_RAPI_PTR(R_NamesSymbol)) 230 | #define R_NaString (*rhost::rapi::RHOST_RAPI_PTR(R_NaString)) 231 | #define R_new_custom_connection rhost::rapi::RHOST_RAPI_PTR(R_new_custom_connection) 232 | #define R_NilValue (*rhost::rapi::RHOST_RAPI_PTR(R_NilValue)) 233 | #define R_ParseVector rhost::rapi::RHOST_RAPI_PTR(R_ParseVector) 234 | #define R_PreserveObject rhost::rapi::RHOST_RAPI_PTR(R_PreserveObject) 235 | #define R_ProcessEvents rhost::rapi::RHOST_RAPI_PTR(R_ProcessEvents) 236 | #define R_registerRoutines rhost::rapi::RHOST_RAPI_PTR(R_registerRoutines) 237 | #define R_ReleaseObject rhost::rapi::RHOST_RAPI_PTR(R_ReleaseObject) 238 | #define R_RestoreGlobalEnvFromFile rhost::rapi::RHOST_RAPI_PTR(R_RestoreGlobalEnvFromFile) 239 | #define R_SaveGlobalEnvToFile rhost::rapi::RHOST_RAPI_PTR(R_SaveGlobalEnvToFile) 240 | #define R_set_command_line_arguments rhost::rapi::RHOST_RAPI_PTR(R_set_command_line_arguments) 241 | #define R_SetParams rhost::rapi::RHOST_RAPI_PTR(R_SetParams) 242 | #define R_Srcref (*rhost::rapi::RHOST_RAPI_PTR(R_Srcref)) 243 | #define R_ToplevelExec rhost::rapi::RHOST_RAPI_PTR(R_ToplevelExec) 244 | #define R_UnboundValue (*rhost::rapi::RHOST_RAPI_PTR(R_UnboundValue)) 245 | #define RAW rhost::rapi::RHOST_RAPI_PTR(RAW) 246 | #define RDEBUG rhost::rapi::RHOST_RAPI_PTR(RDEBUG) 247 | #define REAL rhost::rapi::RHOST_RAPI_PTR(REAL) 248 | #define Rf_allocList rhost::rapi::RHOST_RAPI_PTR(Rf_allocList) 249 | #define Rf_allocVector rhost::rapi::RHOST_RAPI_PTR(Rf_allocVector) 250 | #define Rf_allocVector3 rhost::rapi::RHOST_RAPI_PTR(Rf_allocVector3) 251 | #define Rf_asChar rhost::rapi::RHOST_RAPI_PTR(Rf_asChar) 252 | #define Rf_asInteger rhost::rapi::RHOST_RAPI_PTR(Rf_asInteger) 253 | #define Rf_asLogical rhost::rapi::RHOST_RAPI_PTR(Rf_asLogical) 254 | #define Rf_asReal rhost::rapi::RHOST_RAPI_PTR(Rf_asReal) 255 | #define Rf_classgets rhost::rapi::RHOST_RAPI_PTR(Rf_classgets) 256 | #define Rf_curDevice rhost::rapi::RHOST_RAPI_PTR(Rf_curDevice) 257 | #define Rf_deparse1line rhost::rapi::RHOST_RAPI_PTR(Rf_deparse1line) 258 | //#define Rf_desc2GEDesc rhost::rapi::RHOST_RAPI_PTR(Rf_desc2GEDesc) 259 | #define Rf_duplicate rhost::rapi::RHOST_RAPI_PTR(Rf_duplicate) 260 | #define Rf_error rhost::rapi::RHOST_RAPI_PTR(Rf_error) 261 | #define Rf_eval rhost::rapi::RHOST_RAPI_PTR(Rf_eval) 262 | #define Rf_findVar rhost::rapi::RHOST_RAPI_PTR(Rf_findVar) 263 | #define Rf_getAttrib rhost::rapi::RHOST_RAPI_PTR(Rf_getAttrib) 264 | #define Rf_install rhost::rapi::RHOST_RAPI_PTR(Rf_install) 265 | #define Rf_installChar rhost::rapi::RHOST_RAPI_PTR(Rf_installChar) 266 | #define Rf_isEnvironment rhost::rapi::RHOST_RAPI_PTR(Rf_isEnvironment) 267 | #define Rf_isNull rhost::rapi::RHOST_RAPI_PTR(Rf_isNull) 268 | #define Rf_isString rhost::rapi::RHOST_RAPI_PTR(Rf_isString) 269 | #define Rf_length rhost::rapi::RHOST_RAPI_PTR(Rf_length) 270 | #define Rf_mkChar rhost::rapi::RHOST_RAPI_PTR(Rf_mkChar) 271 | #define Rf_mkCharCE rhost::rapi::RHOST_RAPI_PTR(Rf_mkCharCE) 272 | #define Rf_mkString rhost::rapi::RHOST_RAPI_PTR(Rf_mkString) 273 | //#define Rf_ndevNumber rhost::rapi::RHOST_RAPI_PTR(Rf_ndevNumber) 274 | #define Rf_NewEnvironment rhost::rapi::RHOST_RAPI_PTR(Rf_NewEnvironment) 275 | #define Rf_onintr rhost::rapi::RHOST_RAPI_PTR(Rf_onintr) 276 | #define Rf_protect rhost::rapi::RHOST_RAPI_PTR(Rf_protect) 277 | #define Rf_ScalarInteger rhost::rapi::RHOST_RAPI_PTR(Rf_ScalarInteger) 278 | #define Rf_ScalarLogical rhost::rapi::RHOST_RAPI_PTR(Rf_ScalarLogical) 279 | #define Rf_ScalarReal rhost::rapi::RHOST_RAPI_PTR(Rf_ScalarReal) 280 | #define Rf_ScalarString rhost::rapi::RHOST_RAPI_PTR(Rf_ScalarString) 281 | #define Rf_selectDevice rhost::rapi::RHOST_RAPI_PTR(Rf_selectDevice) 282 | #define Rf_translateCharUTF8 rhost::rapi::RHOST_RAPI_PTR(Rf_translateCharUTF8) 283 | #define Rf_unprotect rhost::rapi::RHOST_RAPI_PTR(Rf_unprotect) 284 | #define run_Rmainloop rhost::rapi::RHOST_RAPI_PTR(run_Rmainloop) 285 | #define SET_RDEBUG rhost::rapi::RHOST_RAPI_PTR(SET_RDEBUG) 286 | #define SET_STRING_ELT rhost::rapi::RHOST_RAPI_PTR(SET_STRING_ELT) 287 | #define SET_TYPEOF rhost::rapi::RHOST_RAPI_PTR(SET_TYPEOF) 288 | #define SET_VECTOR_ELT rhost::rapi::RHOST_RAPI_PTR(SET_VECTOR_ELT) 289 | #define SETCAR rhost::rapi::RHOST_RAPI_PTR(SETCAR) 290 | #define setup_Rmainloop rhost::rapi::RHOST_RAPI_PTR(setup_Rmainloop) 291 | #define STRING_ELT rhost::rapi::RHOST_RAPI_PTR(STRING_ELT) 292 | #define TYPEOF rhost::rapi::RHOST_RAPI_PTR(TYPEOF) 293 | #define VECTOR_ELT rhost::rapi::RHOST_RAPI_PTR(VECTOR_ELT) 294 | #define vmaxget rhost::rapi::RHOST_RAPI_PTR(vmaxget) 295 | #define vmaxset rhost::rapi::RHOST_RAPI_PTR(vmaxset) 296 | 297 | #ifdef _WIN32 298 | 299 | #define get_R_HOME rhost::rapi::RHOST_RAPI_PTR(get_R_HOME) 300 | #define getDLLVersion rhost::rapi::RHOST_RAPI_PTR(getDLLVersion) 301 | #define getRUser rhost::rapi::RHOST_RAPI_PTR(getRUser) 302 | #define in_memsize rhost::rapi::RHOST_RAPI_PTR(in_memsize) 303 | #define R_setStartTime rhost::rapi::RHOST_RAPI_PTR(R_setStartTime) 304 | #define R_WaitEvent rhost::rapi::RHOST_RAPI_PTR(R_WaitEvent) 305 | #define readconsolecfg rhost::rapi::RHOST_RAPI_PTR(readconsolecfg) 306 | #define Rf_utf8towcs rhost::rapi::RHOST_RAPI_PTR(Rf_utf8towcs) 307 | #define Rf_wtransChar rhost::rapi::RHOST_RAPI_PTR(Rf_wtransChar) 308 | 309 | #define GA_initapp rhost::rapi::RHOST_RAPI_PTR(GA_initapp) 310 | 311 | #else // POSIX 312 | 313 | #define ptr_R_Busy (*rhost::rapi::RHOST_RAPI_PTR(ptr_R_Busy)) 314 | #define ptr_R_ProcessEvents (*rhost::rapi::RHOST_RAPI_PTR(ptr_R_ProcessEvents)) 315 | #define ptr_R_ReadConsole (*rhost::rapi::RHOST_RAPI_PTR(ptr_R_ReadConsole)) 316 | #define ptr_R_ShowMessage (*rhost::rapi::RHOST_RAPI_PTR(ptr_R_ShowMessage)) 317 | #define ptr_R_WriteConsole (*rhost::rapi::RHOST_RAPI_PTR(ptr_R_WriteConsole)) 318 | #define ptr_R_WriteConsoleEx (*rhost::rapi::RHOST_RAPI_PTR(ptr_R_WriteConsoleEx)) 319 | #define R_running_as_main_program (*rhost::rapi::RHOST_RAPI_PTR(R_running_as_main_program)) 320 | #define R_checkActivity rhost::rapi::RHOST_RAPI_PTR(R_checkActivity) 321 | #define R_Consolefile (*rhost::rapi::RHOST_RAPI_PTR(R_Consolefile)) 322 | #define R_InputHandlers (*rhost::rapi::RHOST_RAPI_PTR(R_InputHandlers)) 323 | #define R_Interactive_ (*rhost::rapi::RHOST_RAPI_PTR(R_Interactive)) 324 | #define R_Outputfile (*rhost::rapi::RHOST_RAPI_PTR(R_Outputfile)) 325 | #define R_runHandlers rhost::rapi::RHOST_RAPI_PTR(R_runHandlers) 326 | #define Rf_initialize_R rhost::rapi::RHOST_RAPI_PTR(Rf_initialize_R) 327 | 328 | #endif 329 | 330 | #endif // RHOST_NO_API_REDIRECT 331 | -------------------------------------------------------------------------------- /src/log.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "stdafx.h" 24 | #include "log.h" 25 | #include "r_api.h" 26 | 27 | using namespace std::literals; 28 | using namespace rhost::rapi; 29 | 30 | namespace rhost { 31 | namespace log { 32 | namespace { 33 | #ifdef _MSC_VER 34 | const MINIDUMP_TYPE fulldump_type = MINIDUMP_TYPE( 35 | MiniDumpWithFullMemory | 36 | MiniDumpWithDataSegs | 37 | MiniDumpWithHandleData | 38 | MiniDumpWithProcessThreadData | 39 | MiniDumpWithFullMemoryInfo | 40 | MiniDumpWithThreadInfo | 41 | MiniDumpIgnoreInaccessibleMemory | 42 | MiniDumpWithTokenInformation | 43 | MiniDumpWithModuleHeaders); 44 | 45 | const DWORD fatal_error_exception_code = 0xE0000001; 46 | #endif 47 | 48 | std::mutex log_mutex, terminate_mutex; 49 | fs::path log_filename, stackdump_filename, fulldump_filename; 50 | FILE* logfile; 51 | int indent; 52 | log::log_verbosity current_verbosity; 53 | 54 | void log_flush_thread() { 55 | for (;;) { 56 | std::this_thread::sleep_for(1s); 57 | flush_log(); 58 | } 59 | } 60 | } 61 | 62 | #ifdef _MSC_VER 63 | void create_minidump(_EXCEPTION_POINTERS* ei) { 64 | // Don't let another thread interrupt us by terminating while we're doing this. 65 | std::lock_guard terminate_lock(terminate_mutex); 66 | 67 | MINIDUMP_EXCEPTION_INFORMATION mei; 68 | mei.ThreadId = GetCurrentThreadId(); 69 | mei.ExceptionPointers = ei; 70 | mei.ClientPointers = FALSE; 71 | 72 | // Create a regular minidump. 73 | HANDLE dump_file = CreateFileA(stackdump_filename.make_preferred().string().c_str(), GENERIC_ALL, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 74 | if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dump_file, MiniDumpNormal, &mei, nullptr, nullptr)) { 75 | logf(log_verbosity::minimal, "Stack-only minidump written out to %s\n", stackdump_filename.c_str()); 76 | } else { 77 | logf(log_verbosity::minimal, "Failed to write stack-only minidump to %s\n", stackdump_filename.c_str()); 78 | } 79 | CloseHandle(dump_file); 80 | flush_log(); 81 | 82 | // Create a full heap minidump with as much data as possible. 83 | dump_file = CreateFileA(fulldump_filename.make_preferred().string().c_str(), GENERIC_ALL, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 84 | if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dump_file, fulldump_type, &mei, nullptr, nullptr)) { 85 | logf(log_verbosity::minimal, "Full minidump written out to %s\n", fulldump_filename.c_str()); 86 | } else { 87 | logf(log_verbosity::minimal, "Failed to write full minidump to %s\n", fulldump_filename.c_str()); 88 | } 89 | CloseHandle(dump_file); 90 | flush_log(); 91 | } 92 | 93 | LONG WINAPI unhandled_exception_filter(_EXCEPTION_POINTERS* ei) { 94 | // Prevent recursion if an unhandled exception happens inside the filter itself. 95 | static bool in_unhandled_exception_filter; 96 | if (in_unhandled_exception_filter) { 97 | return EXCEPTION_CONTINUE_SEARCH; 98 | } 99 | in_unhandled_exception_filter = true; 100 | 101 | // Flush log, so that if anything below fails (e.g. if heap is corrupted too badly, or 102 | // if we're out of memory), at least the stuff that's already in the log gets written 103 | flush_log(); 104 | 105 | logf(log_verbosity::minimal, "Terminating process due to unhandled Win32 exception 0x%x\n", ei->ExceptionRecord->ExceptionCode); 106 | flush_log(); 107 | create_minidump(ei); 108 | 109 | in_unhandled_exception_filter = false; 110 | return EXCEPTION_CONTINUE_SEARCH; 111 | } 112 | #endif 113 | 114 | void init_log(const std::string& log_suffix, const fs::path& log_dir, log::log_verbosity verbosity, bool suppress_ui) { 115 | { 116 | current_verbosity = verbosity; 117 | 118 | std::string filename = "Microsoft.R.Host_"; 119 | if (!log_suffix.empty()) { 120 | filename += log_suffix + "_"; 121 | } 122 | 123 | time_t t; 124 | time(&t); 125 | 126 | tm tm; 127 | #ifdef _WIN32 128 | localtime_s(&tm, &t); 129 | #else 130 | localtime_r(&t, &tm); 131 | #endif 132 | size_t len = filename.size(); 133 | filename.resize(len + 1 + RHOST_MAX_PATH); 134 | auto it = filename.begin() + len; 135 | strftime(&*it, filename.end() - it, "%Y%m%d_%H%M%S", &tm); 136 | filename.resize(strlen(filename.c_str())); 137 | 138 | // Add PID to prevent conflicts in case two hosts with the same suffix 139 | // get started at the same time. 140 | filename += "_pid" + std::to_string(getpid()); 141 | 142 | log_filename = log_dir / (filename + ".log"); 143 | stackdump_filename = log_dir / (filename + ".stack.dmp"); 144 | fulldump_filename = log_dir / (filename + ".full.dmp"); 145 | } 146 | 147 | #ifdef _MSC_VER 148 | logfile = _fsopen(log_filename.make_preferred().string().c_str(), "wc", _SH_DENYWR); 149 | #else 150 | logfile = fopen(log_filename.make_preferred().string().c_str(), "w"); 151 | #endif 152 | if (logfile) { 153 | // Logging happens often, so use a large buffer to avoid hitting the disk all the time. 154 | setvbuf(logfile, nullptr, _IOFBF, 0x100000); 155 | 156 | // Start a thread that will flush the buffer periodically. 157 | std::thread(log_flush_thread).detach(); 158 | } else { 159 | std::string error = "Error creating logfile: " + log_filename.make_preferred().string() + "\r\n"; 160 | fprintf(stderr, "Error: %d\r\n", errno); 161 | fputs(error.c_str(), stderr); 162 | 163 | if (!suppress_ui) { 164 | #ifdef _WIN32 165 | MessageBoxA(HWND_DESKTOP, error.c_str(), "Microsoft R Host", MB_OK | MB_ICONWARNING); 166 | #endif 167 | } 168 | } 169 | 170 | #ifdef _MSC_VER 171 | SetUnhandledExceptionFilter(unhandled_exception_filter); 172 | #endif 173 | } 174 | 175 | void vlogf(log_verbosity verbosity, log_level message_type, const char* format, va_list va) { 176 | if (verbosity > current_verbosity) { 177 | return; 178 | } 179 | 180 | std::lock_guard lock(log_mutex); 181 | 182 | va_list va2; 183 | va_copy(va2, va); 184 | 185 | if (logfile) { 186 | for (int i = 0; i < indent; ++i) { 187 | fputc('\t', logfile); 188 | } 189 | vfprintf(logfile, format, va); 190 | 191 | #ifndef NDEBUG 192 | // In Debug builds, flush on every write so that log is always up-to-date. 193 | // In Release builds, we rely on flush_log being called on process shutdown. 194 | fflush(logfile); 195 | #endif 196 | } 197 | 198 | // Don't log trace level messages to stderr by default. 199 | if (message_type != log_level::trace) { 200 | for (int i = 0; i < indent; ++i) { 201 | fputc('\t', stderr); 202 | } 203 | vfprintf(stderr, format, va2); 204 | } 205 | 206 | va_end(va2); 207 | } 208 | 209 | void indent_log(int n) { 210 | indent += n; 211 | if (indent < 0) { 212 | indent = 0; 213 | } 214 | } 215 | 216 | void flush_log() { 217 | std::lock_guard lock(log_mutex); 218 | if (logfile) { 219 | fflush(logfile); 220 | } 221 | } 222 | 223 | 224 | RHOST_NORETURN void terminate(bool unexpected, const char* format, va_list va) { 225 | std::lock_guard terminate_lock(terminate_mutex); 226 | 227 | char message[0xFFFF]; 228 | vsprintf_s(message, format, va); 229 | if (unexpected) { 230 | logf(log_verbosity::minimal, "Fatal error: "); 231 | } 232 | logf(log_verbosity::minimal, unexpected ? log_level::error : log_level::information, "%s\n", message); 233 | flush_log(); 234 | 235 | if (unexpected) { 236 | std::string msgbox_text; 237 | for (size_t i = 0; i < strlen(message); ++i) { 238 | char c = message[i]; 239 | if (c == '\n') { 240 | msgbox_text += '\r'; 241 | } 242 | msgbox_text += c; 243 | } 244 | 245 | #ifdef _MSC_VER 246 | // Raise and catch an exception so that minidump with a stacktrace can be produced from it. 247 | [&] { 248 | terminate_mutex.unlock(); 249 | __try { 250 | RaiseException(fatal_error_exception_code, 0, 0, nullptr); 251 | } __except(unhandled_exception_filter(GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION) { 252 | } 253 | terminate_mutex.lock(); 254 | }(); 255 | #endif 256 | } 257 | 258 | R_CleanUp(SA_NOSAVE, (unexpected ? EXIT_FAILURE : EXIT_SUCCESS), 0); 259 | 260 | // We should never get here, but R_CleanUp is not marked as noreturn, so make compiler happy. 261 | std::terminate(); 262 | } 263 | 264 | void terminate(const char* format, ...) { 265 | va_list va; 266 | va_start(va, format); 267 | terminate(false, format, va); 268 | va_end(va); 269 | } 270 | 271 | void fatal_error(const char* format, ...) { 272 | va_list va; 273 | va_start(va, format); 274 | terminate(true, format, va); 275 | va_end(va); 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | 25 | namespace rhost { 26 | namespace log { 27 | enum class log_verbosity { 28 | none, 29 | minimal, 30 | normal, 31 | traffic 32 | }; 33 | 34 | enum class log_level { 35 | trace, 36 | information, 37 | warning, 38 | error 39 | }; 40 | 41 | void init_log(const std::string& log_suffix, const fs::path& log_dir, log_verbosity log_level, bool suppress_ui); 42 | 43 | void vlogf(log_verbosity level, log_level message_type, const char* format, va_list va); 44 | 45 | inline void logf(log_verbosity verbosity, log_level message_type, const char* format, ...) { 46 | va_list va; 47 | va_start(va, format); 48 | vlogf(verbosity, message_type, format, va); 49 | va_end(va); 50 | } 51 | 52 | inline void vlogf(log_verbosity verbosity, const char* format, va_list va) { 53 | vlogf(verbosity, log_level::trace, format, va); 54 | } 55 | 56 | inline void logf(log_verbosity verbosity, const char* format, ...) { 57 | va_list va; 58 | va_start(va, format); 59 | vlogf(verbosity, format, va); 60 | va_end(va); 61 | } 62 | 63 | void indent_log(int n); 64 | 65 | void flush_log(); 66 | 67 | RHOST_NORETURN void terminate(const char* format, ...); 68 | 69 | RHOST_NORETURN void fatal_error(const char* format, ...); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/message.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "message.h" 24 | #include "log.h" 25 | 26 | namespace rhost { 27 | namespace protocol { 28 | namespace { 29 | std::atomic last_message_id(-1); 30 | 31 | void log_payload(const std::string& payload) { 32 | std::ostringstream str; 33 | str << "\n\n"; 38 | 39 | log::logf(log::log_verbosity::traffic, "%s\n\n", str.str().c_str()); 40 | log::flush_log(); 41 | } 42 | } 43 | 44 | message::message(message_id request_id, const std::string& name, const std::string& json, const std::vector& blob) : 45 | _id(last_message_id += 2), 46 | _request_id(request_id), 47 | _payload(sizeof _id + sizeof _request_id + name.size() + 1 + json.size() + 1 + blob.size(), '\0') { 48 | 49 | auto& repr = *reinterpret_cast(&_payload[0]); 50 | repr.id = _id; 51 | repr.request_id = _request_id; 52 | 53 | const char* start = &_payload.front(); 54 | char* p = repr.data; 55 | 56 | _name = p - start; 57 | strcpy(p, name.c_str()); 58 | p += name.size() + 1; 59 | 60 | _json = p - start; 61 | strcpy(p, json.c_str()); 62 | p += json.size() + 1; 63 | 64 | _blob = p - start; 65 | memcpy(p, blob.data(), blob.size()); 66 | } 67 | 68 | message message::parse(std::string&& payload) { 69 | using namespace boost::endian; 70 | using namespace rhost::log; 71 | 72 | if (payload.size() < sizeof(message_repr)) { 73 | log_payload(payload); 74 | fatal_error("Malformed message header - missing IDs"); 75 | } 76 | auto& repr = *reinterpret_cast(&payload[0]); 77 | 78 | const char* start = &payload.front(); 79 | const char* end = &payload.back() + 1; 80 | const char* p = repr.data; 81 | 82 | if (p >= end) { 83 | log_payload(payload); 84 | fatal_error("Malformed message header - missing name"); 85 | } 86 | const char* name = p; 87 | p = reinterpret_cast(memchr(p, '\0', end - p)); 88 | if (!p) { 89 | log_payload(payload); 90 | fatal_error("Malformed message header - missing name terminator"); 91 | } 92 | 93 | if (++p >= end) { 94 | log_payload(payload); 95 | fatal_error("Malformed message body - missing JSON"); 96 | } 97 | const char* json = p; 98 | p = reinterpret_cast(memchr(p, '\0', end - p)); 99 | if (!p) { 100 | log_payload(payload); 101 | fatal_error("Malformed message body - missing JSON terminator"); 102 | } 103 | 104 | const char* blob = ++p; 105 | 106 | return message(repr.id.value(), repr.request_id.value(), std::move(payload), name - start, json - start, blob - start); 107 | } 108 | 109 | picojson::array message::json() const { 110 | picojson::value result; 111 | 112 | std::string err = picojson::parse(result, json_text()); 113 | if (!err.empty()) { 114 | log_payload(_payload); 115 | log::fatal_error("Malformed JSON payload - %s: %s", err.c_str(), _json); 116 | } 117 | if (!result.is()) { 118 | log_payload(_payload); 119 | log::fatal_error("JSON payload must be an array, but got %s", _json); 120 | } 121 | 122 | return result.get(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/message.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "blobs.h" 26 | 27 | namespace rhost { 28 | namespace protocol { 29 | typedef uint64_t message_id; 30 | 31 | struct message_repr { 32 | boost::endian::little_uint64_buf_t id, request_id; 33 | char data[]; 34 | }; 35 | 36 | class message { 37 | public: 38 | static const message_id request_marker = std::numeric_limits::max(); 39 | 40 | message() : 41 | _id(0), _request_id(0), _name(0), _json(0), _blob(0) { 42 | } 43 | 44 | message(message_id request_id, const std::string& name, const picojson::array& json, const std::vector& blob) : 45 | message(request_id, name, picojson::value(json).serialize(), blob) { 46 | } 47 | 48 | message(message_id request_id, const std::string& name, const std::string& json, const std::vector& blob); 49 | 50 | static message parse(std::string&& payload); 51 | 52 | static message parse(const std::string& payload) { 53 | return parse(std::string(payload)); 54 | } 55 | 56 | const std::string& payload() const { 57 | return _payload; 58 | } 59 | 60 | message_id id() const { 61 | return _id; 62 | } 63 | 64 | message_id request_id() const { 65 | return _request_id; 66 | } 67 | 68 | bool is_notification() const { 69 | return request_id() == 0; 70 | } 71 | 72 | bool is_request() const { 73 | return request_id() == request_marker; 74 | } 75 | 76 | bool is_response() const { 77 | return !is_notification() && !is_request(); 78 | } 79 | 80 | const char* name() const { 81 | return &_payload[_name]; 82 | } 83 | 84 | const char* blob_data() const { 85 | return &_payload[_blob]; 86 | } 87 | 88 | size_t blob_size() const { 89 | return _payload.size() - _blob; 90 | } 91 | 92 | blobs::blob blob() const { 93 | return blobs::blob(blob_data(), blob_data() + blob_size()); 94 | } 95 | 96 | const char* json_text() const { 97 | return &_payload[_json]; 98 | } 99 | 100 | picojson::array json() const; 101 | 102 | private: 103 | 104 | message_id _id; 105 | message_id _request_id; 106 | std::string _payload; 107 | 108 | // The following all point inside _payload. _name and _json are guaranteed 109 | // to be null-terminated. Blob spans from from _blob to end of _payload. 110 | ptrdiff_t _name; 111 | ptrdiff_t _json; 112 | ptrdiff_t _blob; 113 | 114 | message(message_id id, message_id request_id, std::string&& payload, ptrdiff_t name, ptrdiff_t json, ptrdiff_t blob) : 115 | _id(id), _request_id(request_id), _payload(std::move(payload)), 116 | _name(name), _json(json), _blob(blob) { 117 | } 118 | }; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/project.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "blobs.h" 24 | #include "project.h" 25 | #include "util.h" 26 | 27 | namespace rhost { 28 | namespace rproj { 29 | namespace { 30 | enum class dir_copy_options { 31 | none = 0, 32 | skip_existing = 1, overwrite_existing = 2, update_existing = 4, 33 | recursive = 8, 34 | copy_symlinks = 16, skip_symlinks = 32, 35 | directories_only = 64, create_symlinks = 128, create_hard_links = 256, 36 | _Unspecified_recursion_prevention_tag = 512 37 | }; 38 | RHOST_BITMASK_OPS(dir_copy_options); 39 | 40 | bool is_recursive_copy(dir_copy_options opts) 41 | { 42 | // Checks a _Copy_options for whether copy should call itself recursively 43 | if (opts == dir_copy_options::none) 44 | // This supports "copying a directory" as copying the directory and 45 | // files therein but not subdirectories. 46 | return (true); 47 | if ((opts & dir_copy_options::recursive) != dir_copy_options::none) 48 | return (true); 49 | return (false); 50 | } 51 | 52 | void copy_file(const fs::path& source_path, const fs::path& dest_path, dir_copy_options options) { 53 | if ((options & dir_copy_options::overwrite_existing) != dir_copy_options::none 54 | || (options & dir_copy_options::update_existing) != dir_copy_options::none) { 55 | fs::copy_file(source_path, dest_path, fs::copy_option::overwrite_if_exists); 56 | } else if ((options & dir_copy_options::skip_existing) != dir_copy_options::none 57 | && fs::exists(dest_path)) { 58 | ; // NO OP 59 | } else { 60 | fs::copy_file(source_path, dest_path, fs::copy_option::none); 61 | } 62 | } 63 | 64 | void copy(const fs::path& source_path, const fs::path& dest_path, dir_copy_options options) { 65 | // copy source_path to dest_path, general 66 | fs::file_status oldStat; 67 | fs::file_status newStat; 68 | 69 | if ((options & dir_copy_options::create_symlinks) != dir_copy_options::none 70 | || (options & dir_copy_options::skip_symlinks) != dir_copy_options::none) { 71 | // get symlink status 72 | oldStat = fs::symlink_status(source_path); 73 | newStat = fs::symlink_status(dest_path); 74 | } else { 75 | // get file status 76 | oldStat = fs::status(source_path); 77 | newStat = fs::status(dest_path); 78 | } 79 | 80 | if (!fs::exists(oldStat) 81 | || fs::equivalent(source_path, dest_path) 82 | || fs::is_other(oldStat) 83 | || fs::is_other(newStat) 84 | || (fs::is_directory(oldStat) && fs::is_regular_file(newStat))) { 85 | throw std::runtime_error("Copy operation not permitted."); 86 | } else if (fs::is_symlink(oldStat)) { 87 | if ((options & dir_copy_options::skip_symlinks) != dir_copy_options::none) { 88 | ; // NO OP 89 | } else if (!exists(newStat) && (options & dir_copy_options::copy_symlinks) != dir_copy_options::none) { 90 | copy_symlink(source_path, dest_path); 91 | } else { 92 | throw std::runtime_error("Copy operation not terminated."); 93 | } 94 | } else if (fs::is_regular_file(oldStat)) { 95 | if ((options & dir_copy_options::directories_only) != dir_copy_options::none) { 96 | ; // NO OP 97 | } else if ((options & dir_copy_options::create_symlinks) != dir_copy_options::none) { 98 | fs::create_symlink(source_path, dest_path); 99 | } else if ((options & dir_copy_options::create_hard_links) != dir_copy_options::none) { 100 | fs::create_hard_link(source_path, dest_path); 101 | } else if (fs::is_directory(newStat)) { 102 | copy_file(source_path, dest_path / source_path.filename(), options); 103 | } else { 104 | copy_file(source_path, dest_path, options); 105 | } 106 | } else if (fs::is_directory(oldStat) && is_recursive_copy(options)) { 107 | // copy directory recursively 108 | if (!fs::exists(dest_path) && !fs::create_directory(dest_path)) { 109 | throw std::runtime_error("Operation not terminated."); 110 | } 111 | for (fs::directory_iterator next(source_path), end; next != end; ++next) { 112 | copy(next->path(), dest_path / next->path().filename(), 113 | options | dir_copy_options::_Unspecified_recursion_prevention_tag); 114 | } 115 | } 116 | } 117 | 118 | 119 | 120 | void extract_project(fs::path& zip_file, fs::path& dest_dir, fs::path& temp_dir) { 121 | // Open ZIP archive file 122 | int zip_err = ZIP_ER_OK; 123 | zip_t* archive = zip_open(zip_file.make_preferred().string().c_str(), ZIP_RDONLY, &zip_err); 124 | SCOPE_WARDEN(zip_close, { 125 | if (archive) { 126 | zip_close(archive); 127 | } 128 | }); 129 | 130 | if (zip_err != ZIP_ER_OK) { 131 | throw std::runtime_error("Error while opening compressed project file."); 132 | } 133 | 134 | // Get the number of entries in the archive 135 | zip_int64_t nfiles = zip_get_num_entries(archive, ZIP_FL_UNCHANGED); 136 | 137 | // 10 MB buffer to extract files 138 | size_t buff_size = 10 * 1024 * 1024; 139 | std::unique_ptr buffer(new char[buff_size]); 140 | 141 | for (zip_int64_t i = 0; i < nfiles; ++i) { 142 | // Follow the ZIP specification and expect CP-437 encoded names in the ZIP archive(except if they are explicitly marked as UTF-8). 143 | fs::path file_name(zip_get_name(archive, i, ZIP_FL_ENC_STRICT)); 144 | 145 | zip_file_t* zf = nullptr; 146 | SCOPE_WARDEN(zf_close, { 147 | if (zf) { 148 | zip_fclose(zf); 149 | } 150 | }); 151 | 152 | // Open a file in the archive 153 | zf = zip_fopen_index(archive, i, ZIP_FL_UNCHANGED); 154 | if (!zf) { 155 | std::string errmsg("Error while reading compressed project file: "); 156 | errmsg.append(file_name.string()); 157 | throw std::runtime_error(errmsg.c_str()); 158 | } 159 | 160 | // Create a temp file path to extract above file to. 161 | fs::path temp_file_name = temp_dir / file_name; 162 | fs::path parent = temp_file_name.parent_path(); 163 | if (!fs::exists(parent)) { 164 | fs::create_directories(parent); 165 | } 166 | 167 | FILE* f = std::fopen(temp_file_name.make_preferred().string().c_str(), "wb"); 168 | if (!f) { 169 | std::string errmsg("Error while openings file: "); 170 | errmsg.append(temp_file_name.string()); 171 | throw std::runtime_error(errmsg.c_str()); 172 | } 173 | SCOPE_WARDEN(f_close, { 174 | if (f) { 175 | std::fclose(f); 176 | } 177 | }); 178 | 179 | // Decompress the file 180 | zip_int64_t bytes_read = 0; 181 | do { 182 | bytes_read = zip_fread(zf, buffer.get(), buff_size); 183 | if (bytes_read < 0) { 184 | std::string errmsg("Error while reading compressed project file: "); 185 | errmsg.append(file_name.string()); 186 | throw std::runtime_error(errmsg.c_str()); 187 | } 188 | 189 | if (bytes_read > 0) { 190 | size_t elements = std::fwrite(buffer.get(), static_cast(bytes_read), 1, f); 191 | if ((elements != 1) || (std::fflush(f) != 0)) { 192 | std::string errmsg("Error while writing de-compressed project file: "); 193 | errmsg.append(file_name.string()); 194 | throw std::runtime_error(errmsg.c_str()); 195 | } 196 | } 197 | } while (bytes_read == buff_size); 198 | } 199 | 200 | if (!fs::exists(dest_dir)) { 201 | fs::create_directories(dest_dir); 202 | } 203 | 204 | // Copy all decompressed files to their destination 205 | copy(temp_dir, dest_dir, dir_copy_options::recursive | dir_copy_options::overwrite_existing); 206 | 207 | // Delete all temp files created during decompression phase 208 | fs::remove_all(temp_dir); 209 | } 210 | } 211 | 212 | void save_to_project_folder_worker(blobs::blob_id blob_id, fs::path& project_name, fs::path& dest_dir, fs::path& temp_dir) { 213 | fs::path zip_file = temp_dir / project_name; 214 | zip_file.replace_extension(".zip"); 215 | 216 | fs::remove(zip_file); 217 | 218 | blobs::save_to_file(blob_id, zip_file); 219 | 220 | fs::path temp_proj_dir = temp_dir / project_name; 221 | fs::remove_all(temp_proj_dir); 222 | 223 | fs::path dest_proj_dir = dest_dir / project_name; 224 | 225 | extract_project(zip_file, dest_proj_dir, temp_proj_dir); 226 | } 227 | } 228 | } -------------------------------------------------------------------------------- /src/project.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | 26 | namespace rhost { 27 | namespace rproj { 28 | void save_to_project_folder_worker(blobs::blob_id blob_id, fs::path& project_name, fs::path& dest_dir, fs::path& temp_dir); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/r_api.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #ifdef _WIN32 26 | #define Win32 27 | #endif // _WIN32 28 | 29 | #define R_INTERFACE_PTRS 30 | #define R_NO_REMAP 31 | #undef ERROR 32 | 33 | #include "stdafx.h" 34 | 35 | #include "R.h" 36 | #include "Rinternals.h" 37 | 38 | #ifndef _WIN32 39 | #include "Rinterface.h" 40 | #endif 41 | 42 | #include "Rembedded.h" 43 | #include "Rversion.h" 44 | 45 | #define class class_ 46 | #define private private_ 47 | #include "R_ext/Connections.h" 48 | #undef class 49 | #undef private 50 | 51 | #include "R_ext/Rdynload.h" 52 | #include "R_ext/RStartup.h" 53 | #include "R_ext/Parse.h" 54 | #include "R_ext/GraphicsEngine.h" 55 | #include "R_ext/GraphicsDevice.h" 56 | 57 | #ifndef _WIN32 58 | #include "R_ext/eventloop.h" 59 | #endif 60 | 61 | #define R_FALSE Rboolean::FALSE 62 | #define R_TRUE Rboolean::TRUE 63 | 64 | extern "C" { 65 | extern SEXP Rf_deparse1line(SEXP, Rboolean); 66 | extern SEXP Rf_NewEnvironment(SEXP, SEXP, SEXP); 67 | extern void R_CleanUp(SA_TYPE, int, int); 68 | extern void run_Rmainloop(void); 69 | typedef SEXP(*CCODE)(SEXP, SEXP, SEXP, SEXP); 70 | 71 | enum { 72 | R_32_GE_version = 10, 73 | R_33_GE_version = 11, 74 | R_34_GE_version = 12, 75 | }; 76 | 77 | enum { 78 | CTXT_TOPLEVEL = 0, 79 | CTXT_NEXT = 1, 80 | CTXT_BREAK = 2, 81 | CTXT_LOOP = 3, 82 | CTXT_FUNCTION = 4, 83 | CTXT_CCODE = 8, 84 | CTXT_RETURN = 12, 85 | CTXT_BROWSER = 16, 86 | CTXT_GENERIC = 20, 87 | CTXT_RESTART = 32, 88 | CTXT_BUILTIN = 64 89 | }; 90 | 91 | #ifdef _WIN32 92 | typedef struct _sigjmp_buf { 93 | jmp_buf buf; 94 | int sigmask; 95 | int savedmask; 96 | } sigjmp_buf[1]; 97 | #endif 98 | 99 | typedef struct RCNTXT { 100 | struct RCNTXT *nextcontext; 101 | int callflag; 102 | sigjmp_buf cjmpbuf; 103 | int cstacktop; 104 | int evaldepth; 105 | SEXP promargs; 106 | SEXP callfun; 107 | SEXP sysparent; 108 | SEXP call; 109 | SEXP cloenv; 110 | SEXP conexit; 111 | void(*cend)(void *); 112 | void *cenddata; 113 | void *vmax; 114 | int intsusp; 115 | SEXP handlerstack; 116 | SEXP restartstack; 117 | struct RPRSTACK *prstack; 118 | SEXP *nodestack; 119 | #ifdef BC_INT_STACK 120 | IStackval *intstack; 121 | #endif 122 | SEXP srcref; 123 | } RCNTXT, *context; 124 | 125 | typedef enum { 126 | PP_INVALID = 0, 127 | PP_ASSIGN = 1, 128 | PP_ASSIGN2 = 2, 129 | PP_BINARY = 3, 130 | PP_BINARY2 = 4, 131 | PP_BREAK = 5, 132 | PP_CURLY = 6, 133 | PP_FOR = 7, 134 | PP_FUNCALL = 8, 135 | PP_FUNCTION = 9, 136 | PP_IF = 10, 137 | PP_NEXT = 11, 138 | PP_PAREN = 12, 139 | PP_RETURN = 13, 140 | PP_SUBASS = 14, 141 | PP_SUBSET = 15, 142 | PP_WHILE = 16, 143 | PP_UNARY = 17, 144 | PP_DOLLAR = 18, 145 | PP_FOREIGN = 19, 146 | PP_REPEAT = 20 147 | } PPkind; 148 | 149 | typedef enum { 150 | PREC_FN = 0, 151 | PREC_EQ = 1, 152 | PREC_LEFT = 2, 153 | PREC_RIGHT = 3, 154 | PREC_TILDE = 4, 155 | PREC_OR = 5, 156 | PREC_AND = 6, 157 | PREC_NOT = 7, 158 | PREC_COMPARE = 8, 159 | PREC_SUM = 9, 160 | PREC_PROD = 10, 161 | PREC_PERCENT = 11, 162 | PREC_COLON = 12, 163 | PREC_SIGN = 13, 164 | PREC_POWER = 14, 165 | PREC_SUBSET = 15, 166 | PREC_DOLLAR = 16, 167 | PREC_NS = 17 168 | } PPprec; 169 | 170 | typedef struct { 171 | PPkind kind; /* deparse kind */ 172 | PPprec precedence; /* operator precedence */ 173 | unsigned int rightassoc; /* right associative? */ 174 | } PPinfo; 175 | 176 | typedef struct { 177 | char *name; /* print name */ 178 | CCODE cfun; /* c-code address */ 179 | int code; /* offset within c-code */ 180 | int eval; /* evaluate args? */ 181 | int arity; /* function arity */ 182 | PPinfo gram; /* pretty-print info */ 183 | } FUNTAB; 184 | 185 | RHOST_IMPORT extern FUNTAB R_FunTab[]; 186 | RHOST_IMPORT extern int R_running_as_main_program; 187 | 188 | #ifdef _WIN32 189 | RHOST_IMPORT extern RCNTXT* R_GlobalContext; 190 | RHOST_IMPORT extern void R_SaveGlobalEnvToFile(const char *); 191 | RHOST_IMPORT extern void R_RestoreGlobalEnvFromFile(const char *, Rboolean); 192 | RHOST_IMPORT extern SEXP in_memsize(SEXP); 193 | RHOST_IMPORT extern void run_Rmainloop(void); 194 | RHOST_IMPORT extern UImode CharacterMode; 195 | RHOST_IMPORT extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n); 196 | RHOST_IMPORT extern const wchar_t* Rf_wtransChar(SEXP s); 197 | #endif 198 | 199 | #ifdef _WIN32 200 | #undef Win32 201 | #endif // _WIN32 202 | } 203 | 204 | 205 | #include "loadr.h" 206 | #include "r_gd_api.h" 207 | -------------------------------------------------------------------------------- /src/r_gd_api.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "r_api.h" 25 | 26 | namespace rhost { 27 | namespace rapi { 28 | // Takes type T, and returns the same type, except that any references to type 29 | // From are replaced with type To recursively. E.g. if the input type is: 30 | // From (*)(int, From*); 31 | // it becomes: 32 | // To (*)(int, To*); 33 | template 34 | struct subst { 35 | typedef T type; 36 | }; 37 | 38 | template 39 | struct subst { 40 | typedef To type; 41 | }; 42 | 43 | template 44 | struct subst { 45 | typedef const typename subst::type type; 46 | }; 47 | 48 | template 49 | struct subst { 50 | typedef typename subst::type* type; 51 | }; 52 | 53 | template 54 | struct subst { 55 | typedef typename subst::type type[N]; 56 | }; 57 | 58 | template 59 | struct subst { 60 | typedef typename subst::type(*type)(); 61 | }; 62 | 63 | template 64 | class subst { 65 | template 66 | static auto dummy(Result(*f)(SubbedArgs...))->Result(*)(typename subst::type, SubbedArgs...); 67 | public: 68 | typedef decltype(dummy(static_cast::type>(nullptr))) type; 69 | }; 70 | 71 | #define RHOST_GD_MEMBER_DECL(x) static subst<::DevDesc, DevDesc, decltype(::x)*>::type x; 72 | 73 | #define RHOST_DEVDESC_MEMBER(x) subst<::DevDesc, DevDesc, decltype(::DevDesc::x)>::type x; 74 | 75 | #define RHOST_DEVDESC struct DevDesc { \ 76 | RHOST_DEVDESC_V10_MEMBER(left) \ 77 | RHOST_DEVDESC_V10_MEMBER(right) \ 78 | RHOST_DEVDESC_V10_MEMBER(bottom) \ 79 | RHOST_DEVDESC_V10_MEMBER(top) \ 80 | RHOST_DEVDESC_V10_MEMBER(clipLeft) \ 81 | RHOST_DEVDESC_V10_MEMBER(clipRight) \ 82 | RHOST_DEVDESC_V10_MEMBER(clipBottom) \ 83 | RHOST_DEVDESC_V10_MEMBER(clipTop) \ 84 | RHOST_DEVDESC_V10_MEMBER(xCharOffset) \ 85 | RHOST_DEVDESC_V10_MEMBER(yCharOffset) \ 86 | RHOST_DEVDESC_V10_MEMBER(yLineBias) \ 87 | RHOST_DEVDESC_V10_MEMBER(ipr) \ 88 | RHOST_DEVDESC_V10_MEMBER(cra) \ 89 | RHOST_DEVDESC_V10_MEMBER(gamma) \ 90 | RHOST_DEVDESC_V10_MEMBER(canClip) \ 91 | RHOST_DEVDESC_V10_MEMBER(canChangeGamma) \ 92 | RHOST_DEVDESC_V10_MEMBER(canHAdj) \ 93 | RHOST_DEVDESC_V10_MEMBER(startps) \ 94 | RHOST_DEVDESC_V10_MEMBER(startcol) \ 95 | RHOST_DEVDESC_V10_MEMBER(startfill) \ 96 | RHOST_DEVDESC_V10_MEMBER(startlty) \ 97 | RHOST_DEVDESC_V10_MEMBER(startfont) \ 98 | RHOST_DEVDESC_V10_MEMBER(startgamma) \ 99 | RHOST_DEVDESC_V10_MEMBER(deviceSpecific) \ 100 | RHOST_DEVDESC_V10_MEMBER(displayListOn) \ 101 | RHOST_DEVDESC_V10_MEMBER(canGenMouseDown) \ 102 | RHOST_DEVDESC_V10_MEMBER(canGenMouseMove) \ 103 | RHOST_DEVDESC_V10_MEMBER(canGenMouseUp) \ 104 | RHOST_DEVDESC_V10_MEMBER(canGenKeybd) \ 105 | RHOST_DEVDESC_V12_MEMBER(canGenIdle) \ 106 | RHOST_DEVDESC_V10_MEMBER(gettingEvent) \ 107 | RHOST_DEVDESC_V10_MEMBER(activate) \ 108 | RHOST_DEVDESC_V10_MEMBER(circle) \ 109 | RHOST_DEVDESC_V10_MEMBER(clip) \ 110 | RHOST_DEVDESC_V10_MEMBER(close) \ 111 | RHOST_DEVDESC_V10_MEMBER(deactivate) \ 112 | RHOST_DEVDESC_V10_MEMBER(locator) \ 113 | RHOST_DEVDESC_V10_MEMBER(line) \ 114 | RHOST_DEVDESC_V10_MEMBER(metricInfo) \ 115 | RHOST_DEVDESC_V10_MEMBER(mode) \ 116 | RHOST_DEVDESC_V10_MEMBER(newPage) \ 117 | RHOST_DEVDESC_V10_MEMBER(polygon) \ 118 | RHOST_DEVDESC_V10_MEMBER(polyline) \ 119 | RHOST_DEVDESC_V10_MEMBER(rect) \ 120 | RHOST_DEVDESC_V10_MEMBER(path) \ 121 | RHOST_DEVDESC_V10_MEMBER(raster) \ 122 | RHOST_DEVDESC_V10_MEMBER(cap) \ 123 | RHOST_DEVDESC_V10_MEMBER(size) \ 124 | RHOST_DEVDESC_V10_MEMBER(strWidth) \ 125 | RHOST_DEVDESC_V10_MEMBER(text) \ 126 | RHOST_DEVDESC_V10_MEMBER(onExit) \ 127 | RHOST_DEVDESC_V10_MEMBER(getEvent) \ 128 | RHOST_DEVDESC_V10_MEMBER(newFrameConfirm) \ 129 | RHOST_DEVDESC_V10_MEMBER(hasTextUTF8) \ 130 | RHOST_DEVDESC_V10_MEMBER(textUTF8) \ 131 | RHOST_DEVDESC_V10_MEMBER(strWidthUTF8) \ 132 | RHOST_DEVDESC_V10_MEMBER(wantSymbolUTF8) \ 133 | RHOST_DEVDESC_V10_MEMBER(useRotatedTextInContour) \ 134 | RHOST_DEVDESC_V10_MEMBER(eventEnv) \ 135 | RHOST_DEVDESC_V10_MEMBER(eventHelper) \ 136 | RHOST_DEVDESC_V10_MEMBER(holdflush) \ 137 | RHOST_DEVDESC_V10_MEMBER(haveTransparency) \ 138 | RHOST_DEVDESC_V10_MEMBER(haveTransparentBg) \ 139 | RHOST_DEVDESC_V10_MEMBER(haveRaster) \ 140 | RHOST_DEVDESC_V10_MEMBER(haveCapture) \ 141 | RHOST_DEVDESC_V10_MEMBER(haveLocator) \ 142 | RHOST_DEVDESC_V10_MEMBER(reserved) \ 143 | }; 144 | 145 | #define RHOST_DEVDESC_V10_MEMBER(x) RHOST_DEVDESC_MEMBER(x) 146 | #define RHOST_DEVDESC_V12_MEMBER(x) RHOST_DEVDESC_MEMBER(x) 147 | 148 | template 149 | struct gd_api; 150 | 151 | template<> 152 | struct gd_api<12> { 153 | struct DevDesc; 154 | typedef DevDesc* pDevDesc; 155 | 156 | RHOST_DEVDESC; 157 | RHOST_GD_SET(RHOST_GD_MEMBER_DECL); 158 | 159 | static void load(); 160 | static void unload(); 161 | }; 162 | 163 | #undef RHOST_DEVDESC_V12_MEMBER 164 | #define RHOST_DEVDESC_V12_MEMBER(x) 165 | 166 | template<> 167 | struct gd_api<10> { 168 | struct DevDesc; 169 | typedef DevDesc* pDevDesc; 170 | RHOST_DEVDESC; 171 | RHOST_GD_SET(RHOST_GD_MEMBER_DECL); 172 | 173 | static void load(); 174 | static void unload(); 175 | }; 176 | 177 | template<> 178 | struct gd_api<11> : gd_api<10> { 179 | }; 180 | } 181 | } -------------------------------------------------------------------------------- /src/r_util.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "r_api.h" 25 | 26 | namespace rhost { 27 | namespace r_util { 28 | void init(DllInfo *dll); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Resource.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /src/rstrtmgr.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "rstrtmgr.h" 24 | #include "util.h" 25 | 26 | #ifdef _WIN32 27 | #include 28 | #endif 29 | 30 | namespace rhost { 31 | namespace util { 32 | file_lock_state lock_state_by_file(std::vector& wpaths) { 33 | #ifdef _WIN32 34 | std::vector wfilepaths(wpaths.size()); 35 | std::transform(wpaths.begin(), wpaths.end(), wfilepaths.begin(), [](std::wstring& wpath) { return wpath.data(); }); 36 | 37 | std::unique_ptr session_uuid_str(new WCHAR[128]); 38 | DWORD session_handle = 0; 39 | SCOPE_WARDEN(rstrtmgr_end, { 40 | if (session_handle) { 41 | RmEndSession(session_handle); 42 | } 43 | }); 44 | 45 | DWORD error = RmStartSession(&session_handle, 0, session_uuid_str.get()); 46 | if (error != ERROR_SUCCESS) { 47 | return file_lock_state::unlocked; 48 | } 49 | 50 | error = RmRegisterResources(session_handle, static_cast(wfilepaths.size()), wfilepaths.data(), 0, nullptr, 0, nullptr); 51 | if (error != ERROR_SUCCESS) { 52 | return file_lock_state::unlocked; 53 | } 54 | 55 | UINT process_info_needed = 0; 56 | UINT process_info_count = 0; 57 | DWORD reboot_reason = RmRebootReasonNone; 58 | error = RmGetList(session_handle, &process_info_needed, &process_info_count, nullptr, &reboot_reason); 59 | if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA) { 60 | return file_lock_state::unlocked; 61 | } 62 | 63 | std::vector process_info_data; 64 | while (error == ERROR_MORE_DATA) { 65 | process_info_data.resize(process_info_needed); 66 | process_info_count = process_info_needed; 67 | error = RmGetList(session_handle, &process_info_needed, &process_info_count, process_info_data.data(), &reboot_reason); 68 | } 69 | 70 | if (error != ERROR_SUCCESS) { 71 | return file_lock_state::unlocked; 72 | } 73 | 74 | std::vector process_ids(process_info_data.size()); 75 | std::transform(process_info_data.begin(), process_info_data.end(), process_ids.begin(), [](RM_PROCESS_INFO p) { return p.Process.dwProcessId; }); 76 | 77 | DWORD rhost_pid = GetCurrentProcessId(); 78 | if (process_ids.size() == 1 && process_ids[0] == rhost_pid) { 79 | return file_lock_state::locked_by_r_session; 80 | } else if (process_ids.size() > 0) { 81 | return file_lock_state::locked_by_other; 82 | } 83 | #endif 84 | return file_lock_state::unlocked; 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/rstrtmgr.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | 26 | namespace rhost { 27 | namespace util { 28 | 29 | enum file_lock_state { 30 | unlocked, 31 | locked_by_r_session, 32 | locked_by_other, 33 | }; 34 | 35 | file_lock_state lock_state_by_file(std::vector& paths); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/stdafx.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #pragma warning(disable: 4996) 25 | 26 | #ifndef NOMINMAX 27 | #define NOMINMAX 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #include "boost/algorithm/string.hpp" 55 | #include "boost/date_time/posix_time/posix_time.hpp" 56 | #include "boost/endian/buffers.hpp" 57 | #include "boost/format.hpp" 58 | #include "boost/program_options/cmdline.hpp" 59 | #include "boost/program_options/options_description.hpp" 60 | #include "boost/program_options/value_semantic.hpp" 61 | #include "boost/program_options/variables_map.hpp" 62 | #include "boost/program_options/parsers.hpp" 63 | #include "boost/optional.hpp" 64 | #include "boost/locale.hpp" 65 | #include "boost/signals2/signal.hpp" 66 | #include "boost/uuid/uuid.hpp" 67 | #include "boost/uuid/uuid_io.hpp" 68 | #include "boost/uuid/uuid_generators.hpp" 69 | #include "boost/filesystem.hpp" 70 | 71 | #include "picojson.h" 72 | #include "zip.h" 73 | 74 | #if defined(_MSC_VER) 75 | #define RHOST_EXPORT __declspec(dllexport) 76 | #define RHOST_IMPORT __declspec(dllimport) 77 | #define RHOST_NORETURN __declspec(noreturn) 78 | #elif defined(__GNUC__) 79 | #define RHOST_EXPORT __attribute__((visibility("default"))) 80 | #define RHOST_IMPORT 81 | #define RHOST_NORETURN __attribute__((noreturn)) 82 | #else 83 | #define RHOST_EXPORT 84 | #define RHOST_IMPORT 85 | #define RHOST_NORETURN 86 | #pragma warning Unknown DLL import/export. 87 | #endif 88 | 89 | #ifdef _WIN32 90 | #include 91 | #include 92 | #include 93 | #include 94 | 95 | #define STRSAFE_NO_DEPRECATE 96 | #include 97 | #pragma warning(push) 98 | #pragma warning(disable:4091) 99 | #include 100 | #pragma warning(pop) 101 | 102 | #include "minhook.h" 103 | #else // linux 104 | #include 105 | #include 106 | #include 107 | #endif 108 | 109 | namespace fs = boost::filesystem; 110 | 111 | #ifdef _WIN32 112 | #define RHOST_MAX_PATH MAX_PATH 113 | #else 114 | #define RHOST_MAX_PATH PATH_MAX 115 | 116 | #define vsprintf_s vsprintf 117 | void strcpy_s(char* dest, size_t n, char const* source) ; 118 | void memcpy_s(void* const dest, size_t const destSize, void const* const source, size_t const sourceSize); 119 | #endif 120 | 121 | 122 | 123 | #define RHOST_BITMASK_OPS(Ty) \ 124 | inline Ty& operator&=(Ty& _Left, Ty _Right) { _Left = (Ty)((int)_Left & (int)_Right); return (_Left); } \ 125 | inline Ty& operator|=(Ty& _Left, Ty _Right) { _Left = (Ty)((int)_Left | (int)_Right); return (_Left); } \ 126 | inline Ty& operator^=(Ty& _Left, Ty _Right) { _Left = (Ty)((int)_Left ^ (int)_Right); return (_Left); } \ 127 | inline constexpr Ty operator&(Ty _Left, Ty _Right) { return ((Ty)((int)_Left & (int)_Right)); } \ 128 | inline constexpr Ty operator|(Ty _Left, Ty _Right) { return ((Ty)((int)_Left | (int)_Right)); } \ 129 | inline constexpr Ty operator^(Ty _Left, Ty _Right) { return ((Ty)((int)_Left ^ (int)_Right)); } \ 130 | inline constexpr Ty operator~(Ty _Left) { return ((Ty)~(int)_Left); } 131 | -------------------------------------------------------------------------------- /src/transport.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "blobs.h" 24 | #include "transport.h" 25 | 26 | using namespace rhost::protocol; 27 | 28 | namespace rhost { 29 | namespace transport { 30 | namespace { 31 | const int pipeSize = 4096; 32 | #ifdef _WIN32 33 | typedef int ssize_t; 34 | const char devNull[] = "NUL"; 35 | #else 36 | const char devNull[] = "/dev/null"; 37 | #endif 38 | 39 | std::atomic connected; 40 | FILE *input, *output; 41 | std::mutex output_lock; 42 | 43 | void log_message(const char* prefix, message_id id, message_id request_id, const char* name, const char* json, const blobs::blob& blob) { 44 | #ifdef TRACE_JSON 45 | std::ostringstream str; 46 | str << prefix << " #" << id << "# " << name; 47 | 48 | if (request_id > 0 && request_id < std::numeric_limits::max()) { 49 | str << " #" << request_id << "#"; 50 | } 51 | 52 | str << " " << json; 53 | 54 | if (!blob.empty()) { 55 | str << " "; 56 | } 57 | 58 | log::logf(log::log_verbosity::traffic, "%s\n\n", str.str().c_str()); 59 | #endif 60 | } 61 | 62 | void disconnect() { 63 | if (connected.exchange(false)) { 64 | disconnected(); 65 | } 66 | } 67 | 68 | void receive_worker() { 69 | for (;;) { 70 | boost::endian::little_uint32_buf_t msg_size; 71 | if (fread(&msg_size, sizeof msg_size, 1, input) != 1) { 72 | break; 73 | } 74 | 75 | std::string payload(msg_size.value(), '\0'); 76 | if (!payload.empty()) { 77 | if (fread(&payload[0], payload.size(), 1, input) != 1) { 78 | break; 79 | } 80 | } 81 | 82 | auto msg = message::parse(payload); 83 | log_message("==>", msg.id(), msg.request_id(), msg.name(), msg.json_text(), msg.blob()); 84 | message_received(msg); 85 | } 86 | 87 | disconnect(); 88 | } 89 | 90 | void read_stream_to_message(int fdr, const std::string& message_name) { 91 | char line[pipeSize]; 92 | size_t len = pipeSize; 93 | ssize_t nread; 94 | 95 | for(;;) { 96 | #ifdef _WIN32 97 | nread = _read(fdr, line, pipeSize); 98 | #else 99 | nread = read(fdr, line, pipeSize); 100 | #endif 101 | 102 | if (nread == -1) { 103 | break; 104 | } 105 | 106 | if (nread > 0) { 107 | picojson::array json; 108 | json.push_back(picojson::value(std::string(line, nread))); 109 | message msg(0, message_name, json, blobs::blob()); 110 | send_message(msg); 111 | } 112 | } 113 | } 114 | 115 | void stdouterr_to_message() { 116 | int stdout_fd[2] = {}, stderr_fd[2] = {}; 117 | #ifdef _WIN32 118 | _pipe(stdout_fd, pipeSize, _O_TEXT); 119 | _pipe(stderr_fd, pipeSize, _O_TEXT); 120 | #else 121 | pipe(stdout_fd); 122 | pipe(stderr_fd); 123 | #endif 124 | 125 | dup2(stdout_fd[1], fileno(stdout)); 126 | dup2(stderr_fd[1], fileno(stderr)); 127 | 128 | std::thread([stdout_fd]() { 129 | read_stream_to_message(stdout_fd[0], "!"); 130 | }).detach(); 131 | 132 | std::thread([stderr_fd]() { 133 | read_stream_to_message(stderr_fd[0], "!!"); 134 | }).detach(); 135 | } 136 | } 137 | 138 | boost::signals2::signal message_received; 139 | 140 | boost::signals2::signal disconnected; 141 | 142 | void initialize() { 143 | assert(!input && !output); 144 | 145 | #ifdef _WIN32 146 | setmode(fileno(stdin), _O_BINARY); 147 | setmode(fileno(stdout), _O_BINARY); 148 | #endif 149 | 150 | // Duplicate and stash away handles for original stdin & stdout. 151 | input = fdopen(dup(fileno(stdin)), "rb"); 152 | setvbuf(input, NULL, _IONBF, 0); 153 | output = fdopen(dup(fileno(stdout)), "wb"); 154 | setvbuf(output, NULL, _IONBF, 0); 155 | 156 | // Redirect stdin null device, so that it will not interfere with the protocol. 157 | freopen(devNull, "rb", stdin); 158 | 159 | // This is needed to redirect any code that writes to stdout or stderr directly (instead of 160 | // via R_WriteConsole) will be sent to client as a message. 161 | stdouterr_to_message(); 162 | 163 | connected = true; 164 | std::thread(receive_worker).detach(); 165 | } 166 | 167 | void send_message(const message& msg) { 168 | assert(output); 169 | 170 | log_message("<==", msg.id(), msg.request_id(), msg.name(), msg.json_text(), msg.blob()); 171 | 172 | if (!connected) { 173 | return; 174 | } 175 | 176 | auto& payload = msg.payload(); 177 | boost::endian::little_uint32_buf_t msg_size(static_cast(payload.size())); 178 | 179 | std::lock_guard lock(output_lock); 180 | if (fwrite(&msg_size, sizeof msg_size, 1, output) == 1) { 181 | if (fwrite(payload.data(), payload.size(), 1, output) == 1) { 182 | fflush(output); 183 | return; 184 | } 185 | } 186 | 187 | disconnect(); 188 | } 189 | 190 | bool is_connected() { 191 | return connected; 192 | } 193 | } 194 | } -------------------------------------------------------------------------------- /src/transport.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "message.h" 26 | 27 | namespace rhost { 28 | namespace transport { 29 | extern boost::signals2::signal message_received; 30 | 31 | extern boost::signals2::signal disconnected; 32 | 33 | void initialize(); 34 | 35 | void send_message(const protocol::message& msg); 36 | 37 | bool is_connected(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/util.cpp: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #include "stdafx.h" 24 | #include "util.h" 25 | 26 | namespace po = boost::program_options; 27 | 28 | #ifndef _WIN32 29 | void strcpy_s(char* dest, size_t n, char const* source) { 30 | strcpy(dest, source); 31 | } 32 | 33 | void memcpy_s(void* const dest, size_t const destSize, void const* const source, size_t const sourceSize) { 34 | memcpy(dest, source, sourceSize); 35 | } 36 | #endif 37 | 38 | namespace rhost { 39 | namespace util { 40 | #ifdef _WIN32 41 | // Taken from R gnuwin32\console.c. Converts string that is partially 42 | // ANSI and partially UTF-8 to Unicode. UTF-8 fragment is bounded by 43 | // 02 FF FE at the start and by 03 FF FE at the end. 44 | size_t RString2Unicode(wchar_t *wc, char *s, size_t n) { 45 | char UTF8in[4] = "\002\377\376"; 46 | char UTF8out[4] = "\003\377\376"; 47 | 48 | size_t nc = 0; 49 | char *pb, *pe; 50 | 51 | if ((pb = strchr(s, UTF8in[0])) && *(pb + 1) == UTF8in[1] && *(pb + 2) == UTF8in[2]) { 52 | *pb = '\0'; 53 | 54 | nc += mbstowcs(wc, s, n); 55 | pb += 3; pe = pb; 56 | 57 | while (*pe && 58 | !((pe = strchr(pe, UTF8out[0])) && *(pe + 1) == UTF8out[1] && 59 | *(pe + 2) == UTF8out[2])) { 60 | pe++; 61 | } 62 | if (!*pe) { 63 | return nc; 64 | } 65 | 66 | *pe = '\0'; 67 | /* convert string starting at pb from UTF-8 */ 68 | nc += Rf_utf8towcs(wc + nc, pb, (pe - pb)); 69 | pe += 3; 70 | nc += RString2Unicode(wc + nc, pe, n - nc); 71 | } else { 72 | nc = mbstowcs(wc, s, n); 73 | } 74 | return nc; 75 | } 76 | 77 | std::string Rchar_to_utf8(const char* buf, size_t len) { 78 | // Convert 8-bit characters to Unicode via Windows CP. This guarantees 79 | // if locale for non-Unicode programs is set correctly, user can type in 80 | // their language. This does NOT guarantee that all languages can be used 81 | // since R is not Unicode app. If host app is Unicode, it must perform 82 | // checks if text being passed here is convertible to Unicode. 83 | std::wstring ws; 84 | size_t cch = strlen(buf); 85 | if (cch > 0) { 86 | ws.resize(cch); 87 | int nc = RString2Unicode(&ws[0], (char*)buf, len); 88 | ws.resize(nc); 89 | } 90 | // Now convert Unicode to UTF-8 for passing over to the host. 91 | return boost::locale::conv::utf_to_utf(ws); 92 | } 93 | 94 | std::string from_utf8(const std::string& u8s) { 95 | // Convert UTF-8 string that is coming from the host to Unicode. 96 | auto ws = boost::locale::conv::utf_to_utf(u8s); 97 | 98 | // Now convert to MBCS. Do it manually since WideCharToMultiByte 99 | // requires specific code page and fails if character 100 | // cannot be converted. Instead, we are going to encode 101 | // unconvertible characters into "\uABCD" form. This allows 102 | // us to preserve characters written in non-default OS CP. 103 | // Max 8 bytes per character which should fit both UTF-8 and \uABCD 104 | std::string converted(8 * ws.length(), '\0'); 105 | size_t j = 0; 106 | for (size_t i = 0; i < ws.length(); i++) 107 | { 108 | char mbcharbuf[8]; 109 | int mbcch = wctomb(mbcharbuf, ws[i]); 110 | 111 | bool escape; 112 | if (mbcch == -1) { 113 | escape = true; 114 | } else { 115 | // wctomb will try to do an inexact mapping if exact is unavailable - for example, 116 | // if the native encoding is Latin-1, and input is "Δ" (Greek delta), it will be 117 | // converted to Latin "D", and success will be reported. For such cases, we want to 118 | // do "\u..." escaping instead, to preserve the original letter exactly. To detect 119 | // that, convert the result back, and see if it matches the original. 120 | wchar_t wc; 121 | escape = mbtowc(&wc, mbcharbuf, mbcch) == -1 || wc != ws[i]; 122 | } 123 | 124 | if (escape) { 125 | // Character could not be converted, encode it 126 | sprintf(&converted[j], "\\u%04x", ws[i]); 127 | j += 6; 128 | } else { 129 | memcpy(&converted[j], mbcharbuf, mbcch); 130 | j += mbcch; 131 | } 132 | } 133 | converted[j] = '\0'; 134 | converted.resize(j); 135 | return converted; 136 | } 137 | #endif 138 | 139 | fs::path path_from_string_elt(SEXP string_elt) { 140 | #ifdef _WIN32 141 | return fs::path(Rf_wtransChar(string_elt)); 142 | #else 143 | return fs::path(Rf_translateCharUTF8(string_elt)); 144 | #endif 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* **************************************************************************** 2 | * 3 | * Copyright (c) Microsoft Corporation. All rights reserved. 4 | * 5 | * 6 | * This file is part of Microsoft R Host. 7 | * 8 | * Microsoft R Host is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * Microsoft R Host is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with Microsoft R Host. If not, see . 20 | * 21 | * ***************************************************************************/ 22 | 23 | #pragma once 24 | #include "stdafx.h" 25 | #include "log.h" 26 | #include "r_api.h" 27 | 28 | #define SCOPE_WARDEN(NAME, ...) \ 29 | auto xx##NAME##xx = [&]() { __VA_ARGS__ }; \ 30 | ::rhost::util::scope_warden NAME(xx##NAME##xx) 31 | 32 | #define SCOPE_WARDEN_RESTORE(NAME) \ 33 | auto NAME##_old_value = (NAME); \ 34 | SCOPE_WARDEN(restore_##NAME, (NAME) = NAME##_old_value;) 35 | 36 | namespace rhost { 37 | namespace util { 38 | template 39 | class scope_warden { 40 | public: 41 | scope_warden(F& f) noexcept 42 | : _p(std::addressof(f)) { 43 | } 44 | 45 | void dismiss() noexcept { 46 | _p = nullptr; 47 | } 48 | 49 | void run() noexcept { 50 | if (_p) { 51 | (*_p)(); 52 | } 53 | dismiss(); 54 | } 55 | 56 | ~scope_warden() noexcept { 57 | if (_p) { 58 | try { 59 | (*_p)(); 60 | } catch (...) { 61 | std::terminate(); 62 | } 63 | } 64 | } 65 | 66 | private: 67 | F* _p; 68 | 69 | scope_warden(F&&) = delete; 70 | scope_warden(const scope_warden&) = delete; 71 | scope_warden& operator=(const scope_warden&) = delete; 72 | }; 73 | 74 | 75 | struct SEXP_delete { 76 | typedef SEXP pointer; 77 | 78 | void operator() (SEXP sexp) { 79 | if (sexp) { 80 | R_ReleaseObject(sexp); 81 | } 82 | } 83 | }; 84 | 85 | class protected_sexp : public std::unique_ptr { 86 | public: 87 | using unique_ptr::unique_ptr; 88 | 89 | protected_sexp() {} 90 | 91 | protected_sexp(SEXP other) : 92 | unique_ptr(other) 93 | { 94 | R_PreserveObject(other); 95 | } 96 | 97 | protected_sexp(const protected_sexp& other) : 98 | protected_sexp(other.get()) {} 99 | 100 | protected_sexp(protected_sexp&& other) : 101 | unique_ptr(std::move(other)) {} 102 | 103 | using unique_ptr::operator=; 104 | 105 | protected_sexp& operator= (SEXP other) { 106 | protected_sexp o(other); 107 | swap(o); 108 | return *this; 109 | } 110 | 111 | protected_sexp& operator= (const protected_sexp& other) { 112 | return *this = other.get(); 113 | } 114 | 115 | protected_sexp& operator= (protected_sexp&& other) { 116 | swap(other); 117 | return *this; 118 | } 119 | }; 120 | 121 | #ifdef _WIN32 122 | std::string Rchar_to_utf8(const char* buf, size_t len); 123 | 124 | inline std::string Rchar_to_utf8(const std::string& s) { 125 | return Rchar_to_utf8(s.data(), s.size()); 126 | } 127 | std::string from_utf8(const std::string& u8s); 128 | #else 129 | inline std::string Rchar_to_utf8(const char* buf, size_t len) { 130 | return std::string(buf, len); 131 | } 132 | 133 | inline std::string Rchar_to_utf8(const std::string& s){ 134 | return s; 135 | } 136 | 137 | inline std::string from_utf8(const std::string& u8s){ 138 | return u8s; 139 | } 140 | #endif 141 | 142 | inline picojson::value to_utf8_json(const char* buf) { 143 | return buf ? picojson::value(Rchar_to_utf8(buf)) : picojson::value(); 144 | } 145 | 146 | const std::locale& single_byte_locale(); 147 | 148 | fs::path path_from_string_elt(SEXP string_elt); 149 | 150 | inline void append(picojson::array& msg) { 151 | } 152 | 153 | template 154 | inline void append(picojson::array& msg, Arg&& arg) { 155 | msg.push_back(picojson::value(std::forward(arg))); 156 | } 157 | 158 | template 159 | inline void append(picojson::array& msg, Arg&& arg, Args&&... args) { 160 | msg.push_back(picojson::value(std::forward(arg))); 161 | append(msg, std::forward(args)...); 162 | } 163 | 164 | // A C++-friendly helper for Rf_error. Invoking Rf_error directly is not a good idea, because 165 | // it performs a longjmp, which will skip all C++ destructors when unwinding stack frames - so 166 | // the only way to perform it safely is right at the boundary. This helper function will catch 167 | // any exception type derived from std::exception, and invoke Rf_error with what() as message. 168 | template 169 | inline auto exceptions_to_errors(F f) -> decltype(f()) { 170 | try { 171 | return f(); 172 | } catch (std::exception& ex) { 173 | Rf_error(ex.what()); 174 | } 175 | } 176 | 177 | // Executes the callback in its own context, protecting the caller from 178 | // any call to Rf_error. 179 | // Do not use C++ objects that rely on their destructor running in the callback. 180 | // Returns true on if there are no errors, false otherwise (Rf_error was called). 181 | // Note that if there are any errors, they have been displayed already. 182 | // Meaning, there's no need to fetch the error message, turn it into a 183 | // std::exception, only to have exceptions_to_error report the same 184 | // error a second time! 185 | template 186 | inline bool r_top_level_exec(FExecute protected_eval, const char* log_error_prefix = nullptr) { 187 | if (!R_ToplevelExec([](void* arg) { (*reinterpret_cast(arg))(); }, &protected_eval)) { 188 | const char* err = R_curErrorBuf(); 189 | if (log_error_prefix != nullptr) { 190 | log::logf(log::log_verbosity::normal, "%s: error: %s\n", log_error_prefix, err); 191 | } 192 | return false; 193 | } 194 | return true; 195 | } 196 | 197 | class r_error : public std::runtime_error { 198 | public: 199 | explicit r_error(const std::string& msg) 200 | : std::runtime_error(msg) { 201 | } 202 | 203 | explicit r_error(const char* msg) 204 | : std::runtime_error(msg) { 205 | } 206 | }; 207 | 208 | template 209 | inline void errors_to_exceptions(FExecute protected_eval) { 210 | if (!r_top_level_exec(protected_eval)) { 211 | const char* err = R_curErrorBuf(); 212 | throw r_error(err); 213 | } 214 | } 215 | 216 | inline std::string deparse(SEXP sexp) { 217 | return R_CHAR(STRING_ELT(Rf_deparse1line(sexp, R_FALSE), 0)); 218 | } 219 | 220 | inline double ensure_fits_double(size_t size) { 221 | double dsize = static_cast(size); 222 | size_t tsize = static_cast(dsize); 223 | if (size != tsize) { 224 | log::fatal_error("Overflow during cast"); 225 | } 226 | 227 | return dsize; 228 | } 229 | } 230 | } 231 | --------------------------------------------------------------------------------