├── .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 |
--------------------------------------------------------------------------------