mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-01 11:23:40 +00:00
Compare commits
190 Commits
v1.2.4
...
version-1-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
772b62cdeb | ||
|
|
82e9ec3b5c | ||
|
|
7e3e04c469 | ||
|
|
f2bbff989f | ||
|
|
de715892ac | ||
|
|
24fb72c8ad | ||
|
|
7e5686a828 | ||
|
|
843c479593 | ||
|
|
7b35ce9812 | ||
|
|
0798bd020b | ||
|
|
c956aeae7f | ||
|
|
af3d1342c0 | ||
|
|
aa0dbaf3a1 | ||
|
|
8a5c8d3e22 | ||
|
|
315a286d7c | ||
|
|
1d92e79dac | ||
|
|
01c8262426 | ||
|
|
5e102df04d | ||
|
|
57c9de181a | ||
|
|
eeea72060c | ||
|
|
0b1fee5d66 | ||
|
|
594831620b | ||
|
|
2eb0442a9a | ||
|
|
002621b61d | ||
|
|
8ea5475dd9 | ||
|
|
3e6d708175 | ||
|
|
e3fb2c21d7 | ||
|
|
c4ab020f0f | ||
|
|
6e4fdbc4a9 | ||
|
|
497d061cd1 | ||
|
|
4cb522fb8a | ||
|
|
84c1b1af0e | ||
|
|
a683ca3fac | ||
|
|
f7ea7b3c4f | ||
|
|
a34845b66c | ||
|
|
79f95a2efc | ||
|
|
8a3f542532 | ||
|
|
2009df72cb | ||
|
|
f4216f12b3 | ||
|
|
d327eaacbf | ||
|
|
735e3eea78 | ||
|
|
d8cc349ac6 | ||
|
|
e05daba73e | ||
|
|
2a72eeafdf | ||
|
|
2d06867388 | ||
|
|
9b47614ba1 | ||
|
|
8d5065453f | ||
|
|
1802f94868 | ||
|
|
5a42d393a4 | ||
|
|
76560576d2 | ||
|
|
2684b04ab1 | ||
|
|
6dde797752 | ||
|
|
74f34c81e8 | ||
|
|
34563f86c2 | ||
|
|
7a9e5ba68f | ||
|
|
d2af421089 | ||
|
|
ba02bf50b9 | ||
|
|
424a00db2b | ||
|
|
fb03c4b937 | ||
|
|
ba8f657635 | ||
|
|
6238f2ee1e | ||
|
|
7aff6ded84 | ||
|
|
fe8b84496f | ||
|
|
7f834c69fb | ||
|
|
9a58bddc55 | ||
|
|
9ebf537341 | ||
|
|
5ea5d82507 | ||
|
|
d85b7dde7b | ||
|
|
1d301e354f | ||
|
|
a7570ff0c2 | ||
|
|
86c8977726 | ||
|
|
2ee078cdf8 | ||
|
|
ebc114c526 | ||
|
|
6da0bf80c3 | ||
|
|
2e896e3360 | ||
|
|
d922751d98 | ||
|
|
ca1f97951c | ||
|
|
a48abc262c | ||
|
|
c18726f4a1 | ||
|
|
965b37ab4a | ||
|
|
7deedd3601 | ||
|
|
2db37e2e0e | ||
|
|
aa98aee18d | ||
|
|
3fb7979211 | ||
|
|
5ffa88e7e0 | ||
|
|
5788d288ad | ||
|
|
793bd0a977 | ||
|
|
90cf7177ca | ||
|
|
98894e5e07 | ||
|
|
040adf415b | ||
|
|
bdb3cc3a8a | ||
|
|
435eca6232 | ||
|
|
6d75a9c996 | ||
|
|
6e0c052509 | ||
|
|
c5bf0d6fa9 | ||
|
|
2b9c458692 | ||
|
|
28049053e8 | ||
|
|
a82cfb2d54 | ||
|
|
e3810646eb | ||
|
|
0888ac4097 | ||
|
|
c2982d5a9d | ||
|
|
212dcafcaa | ||
|
|
f04c8f0ba4 | ||
|
|
67bdbb676e | ||
|
|
93552e2e31 | ||
|
|
1ee94744e3 | ||
|
|
db70fec7ca | ||
|
|
252bebd16e | ||
|
|
a27459fc08 | ||
|
|
8c3f500338 | ||
|
|
9af27994c9 | ||
|
|
ee6b9d37c0 | ||
|
|
71c0b6e76c | ||
|
|
b3a12b4e3f | ||
|
|
a832fa65c4 | ||
|
|
6c5eaa955d | ||
|
|
e9e93bd0ca | ||
|
|
dde13f38c9 | ||
|
|
fabbb7ea3e | ||
|
|
76971596ad | ||
|
|
d777a8f86c | ||
|
|
00cd7b8ca4 | ||
|
|
714fd81199 | ||
|
|
59b5bdc2c6 | ||
|
|
4642a3485c | ||
|
|
58d711ec57 | ||
|
|
8f3f264160 | ||
|
|
2ce9352285 | ||
|
|
825f358c8b | ||
|
|
17bed3c966 | ||
|
|
eb51fa5ead | ||
|
|
dc1c251a2c | ||
|
|
d6c8a91b72 | ||
|
|
991902e286 | ||
|
|
5534c56d05 | ||
|
|
777c28cf45 | ||
|
|
3f0432bf8d | ||
|
|
bc42ec515e | ||
|
|
60783ccc11 | ||
|
|
9599d95caa | ||
|
|
e3c85f5885 | ||
|
|
da03b5ca8b | ||
|
|
736e1dd0a0 | ||
|
|
c6d51805be | ||
|
|
c53c6458b0 | ||
|
|
eefb13d638 | ||
|
|
7ef706fef9 | ||
|
|
4827bf5e88 | ||
|
|
2624de0588 | ||
|
|
183f876bd1 | ||
|
|
6eb656a75b | ||
|
|
0e457ae92c | ||
|
|
5254e0693e | ||
|
|
abe72ba1b3 | ||
|
|
e980256794 | ||
|
|
7bba0deb1d | ||
|
|
0845e88110 | ||
|
|
040ac6cd2b | ||
|
|
1c8ed1de0e | ||
|
|
b5c90a3431 | ||
|
|
53f3bd9376 | ||
|
|
ae688aa7f5 | ||
|
|
4e428c5a18 | ||
|
|
dc3f74f1c2 | ||
|
|
98e73512d1 | ||
|
|
a41ece8d86 | ||
|
|
67d729aa2e | ||
|
|
0ddf7153b8 | ||
|
|
0aec43e850 | ||
|
|
f94a3fa671 | ||
|
|
bf320ed172 | ||
|
|
e9d6d2b961 | ||
|
|
9d11fc026b | ||
|
|
896c3c41d3 | ||
|
|
f2844ba2d3 | ||
|
|
a3f00b87e1 | ||
|
|
df490bd59f | ||
|
|
b4ccdf870b | ||
|
|
c0e1ee965a | ||
|
|
c804e559ad | ||
|
|
378dc7c9fb | ||
|
|
d0a2857675 | ||
|
|
170f5b6229 | ||
|
|
e71395f571 | ||
|
|
ddcbc1a58f | ||
|
|
28bc815d3e | ||
|
|
e854d385f5 | ||
|
|
27acb43e69 | ||
|
|
1b093d7ec7 | ||
|
|
4d6cb1ff21 |
@@ -1,27 +1,21 @@
|
||||
# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd
|
||||
image: freebsd/latest
|
||||
|
||||
# packages:
|
||||
# - databases/sqlite3
|
||||
# - devel/boehm-gc-threaded
|
||||
# - devel/pcre
|
||||
# - devel/sdl20
|
||||
# - devel/sfml
|
||||
# - www/node
|
||||
# - devel/gmake
|
||||
# - devel/git
|
||||
packages:
|
||||
- databases/sqlite3
|
||||
- devel/boehm-gc-threaded
|
||||
- devel/pcre
|
||||
- devel/sdl20
|
||||
- devel/sfml
|
||||
- www/node
|
||||
- devel/gmake
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
# workaround https://github.com/timotheecour/Nim/issues/76
|
||||
sudo pkg update -q -f
|
||||
sudo pkg install -y -q databases/sqlite3 devel/boehm-gc-threaded devel/pcre \
|
||||
devel/sdl20 devel/sfml www/node devel/gmake devel/git
|
||||
cd Nim
|
||||
git clone --depth 1 -q https://github.com/nim-lang/csources.git
|
||||
git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources
|
||||
gmake -C csources -j $(sysctl -n hw.ncpu)
|
||||
bin/nim c --skipUserCfg --skipParentCfg koch
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
|
||||
4
.github/workflows/ci.yml.disabled
vendored
4
.github/workflows/ci.yml.disabled
vendored
@@ -85,11 +85,11 @@ jobs:
|
||||
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
7z x dist/mingw64.7z -odist
|
||||
7z x dist/dlls.zip -obin
|
||||
echo "::add-path::${{ github.workspace }}/dist/mingw64/bin"
|
||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "::add-path::${{ github.workspace }}/bin"
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Build csources'
|
||||
shell: bash
|
||||
|
||||
10
.github/workflows/ci_docs.yml
vendored
10
.github/workflows/ci_docs.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-10.15
|
||||
os: macos-11
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -49,17 +49,17 @@ jobs:
|
||||
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
7z x dist/mingw64.7z -odist
|
||||
7z x dist/dlls.zip -obin
|
||||
echo "::add-path::${{ github.workspace }}/dist/mingw64/bin"
|
||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "::add-path::${{ github.workspace }}/bin"
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Get current csources version'
|
||||
id: csources-version
|
||||
shell: bash
|
||||
run: |
|
||||
sha=$(git ls-remote https://github.com/nim-lang/csources master | cut -f 1)
|
||||
sha=$(git ls-remote https://github.com/nim-lang/csources_v1 master | cut -f 1)
|
||||
echo "::set-output name=sha::$sha"
|
||||
|
||||
- name: 'Get prebuilt csources from cache'
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
if: steps.csources-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: nim-lang/csources
|
||||
repository: nim-lang/csources_v1
|
||||
path: csources
|
||||
|
||||
- name: 'Build 1-stage compiler from csources'
|
||||
|
||||
4
.github/workflows/ci_ssl.yml
vendored
4
.github/workflows/ci_ssl.yml
vendored
@@ -47,11 +47,11 @@ jobs:
|
||||
curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
||||
7z x dist/mingw64.7z -odist
|
||||
7z x dist/dlls.zip -obin
|
||||
echo "::add-path::${{ github.workspace }}/dist/mingw64/bin"
|
||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "::add-path::${{ github.workspace }}/bin"
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Build 1-stage compiler from csources'
|
||||
shell: bash
|
||||
|
||||
@@ -15,23 +15,23 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
vmImage: 'ubuntu-18.04'
|
||||
CPU: amd64
|
||||
Linux_i386:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
vmImage: 'ubuntu-18.04'
|
||||
CPU: i386
|
||||
OSX_amd64:
|
||||
vmImage: 'macOS-10.15'
|
||||
vmImage: 'macOS-11'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-10.15'
|
||||
vmImage: 'macOS-11'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64:
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
Linux_amd64_pkg:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
vmImage: 'ubuntu-18.04'
|
||||
CPU: amd64
|
||||
NIM_TEST_PACKAGES: true
|
||||
OSX_amd64_pkg:
|
||||
@@ -51,9 +51,10 @@ jobs:
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
- checkout: self
|
||||
fetchDepth: 1
|
||||
|
||||
- bash: git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
displayName: 'Checkout csources'
|
||||
- bash: git clone --depth 1 https://github.com/nim-lang/csources_v1 csources
|
||||
displayName: 'Checkout Nim csources'
|
||||
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
@@ -71,19 +72,12 @@ jobs:
|
||||
- bash: |
|
||||
sudo dpkg --add-architecture i386
|
||||
|
||||
# Downgrade llvm, libgcc and libstdc++:
|
||||
# Downgrade llvm:
|
||||
# - llvm has to be downgraded to have 32bit version installed for sfml.
|
||||
# - libgcc and libstdc++ have to be downgraded as an optimization to
|
||||
# prevent the use of the toolchain ppa, which has a terrible download
|
||||
# speed.
|
||||
cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
|
||||
Package: libllvm6.0 libgcc1 libstdc++6
|
||||
Package: libllvm6.0
|
||||
Pin: origin "azure.archive.ubuntu.com"
|
||||
Pin-Priority: 1001
|
||||
|
||||
Package: *
|
||||
Pin: release o=LP-PPA-ubuntu-toolchain-r-test
|
||||
Pin-Priority: 100
|
||||
EOF
|
||||
|
||||
sudo apt-fast update -qq
|
||||
@@ -139,8 +133,18 @@ jobs:
|
||||
make -v
|
||||
displayName: 'System information'
|
||||
|
||||
- bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)"
|
||||
displayName: 'Get csources version'
|
||||
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: 'csources | "$(Agent.OS)" | $(CPU) | $(csources_version)'
|
||||
path: csources/bin
|
||||
displayName: 'Restore built csources'
|
||||
|
||||
- bash: |
|
||||
ncpu=
|
||||
ext=
|
||||
case '$(Agent.OS)' in
|
||||
'Linux')
|
||||
ncpu=$(nproc)
|
||||
@@ -150,19 +154,25 @@ jobs:
|
||||
;;
|
||||
'Windows_NT')
|
||||
ncpu=$NUMBER_OF_PROCESSORS
|
||||
ext=.exe
|
||||
;;
|
||||
esac
|
||||
[[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1
|
||||
|
||||
make -C csources -j $ncpu CC=gcc ucpu=$(CPU)
|
||||
displayName: 'Build csources'
|
||||
if [[ -x csources/bin/nim$ext ]]; then
|
||||
echo "Found cached compiler, skipping build"
|
||||
else
|
||||
make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no
|
||||
fi
|
||||
|
||||
cp csources/bin/nim$ext bin
|
||||
displayName: 'Build 1-stage compiler from csources'
|
||||
|
||||
- bash: nim c koch
|
||||
displayName: 'Build koch'
|
||||
|
||||
# set result to omit the "bash exited with error code '1'" message
|
||||
- bash: |
|
||||
./koch runCI || echo '##vso[task.complete result=Failed]'
|
||||
- bash: ./koch runCI || echo '##vso[task.complete result=Failed]'
|
||||
displayName: 'Run CI'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@echo off
|
||||
rem build development version of the compiler; can be rerun safely
|
||||
if not exist csources (
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources
|
||||
)
|
||||
if not exist bin\nim.exe (
|
||||
cd csources
|
||||
|
||||
@@ -11,7 +11,7 @@ echo_run(){
|
||||
"$@"
|
||||
}
|
||||
|
||||
[ -d csources ] || echo_run git clone -q --depth 1 https://github.com/nim-lang/csources.git
|
||||
[ -d csources ] || echo_run git clone -q --depth 1 https://github.com/nim-lang/csources_v1.git csources
|
||||
|
||||
nim_csources=bin/nim_csources
|
||||
|
||||
|
||||
243
changelog.md
243
changelog.md
@@ -3,6 +3,13 @@
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
- Added support for parenthesized expressions in `strformat`
|
||||
|
||||
- Fixed buffer overflow bugs in `net`
|
||||
|
||||
- Added `sections` iterator in `parsecfg`.
|
||||
|
||||
- Make custom op in macros.quote work for all statements.
|
||||
|
||||
For `net` and `nativesockets`, an `inheritable` flag has been added to all
|
||||
`proc`s that create sockets, allowing the user to control whether the
|
||||
@@ -78,8 +85,239 @@
|
||||
- Fix a bug where calling `close` on io streams in osproc.startProcess was a noop and led to
|
||||
hangs if a process had both reads from stdin and writes (eg to stdout).
|
||||
|
||||
- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
|
||||
- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.
|
||||
|
||||
- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
|
||||
parameters.
|
||||
|
||||
- Added a `ssl_config` module containing lists of secure ciphers as recommended by
|
||||
[Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS)
|
||||
|
||||
- `net.newContext` now defaults to the list of ciphers targeting
|
||||
["Intermediate compatibility"](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29)
|
||||
per Mozilla's recommendation instead of `ALL`. This change should protect
|
||||
users from the use of weak and insecure ciphers while still provides
|
||||
adequate compatibility with the majority of the Internet.
|
||||
|
||||
- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json
|
||||
serialization/deserialization of custom types was added.
|
||||
|
||||
- A new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``
|
||||
was added.
|
||||
- Added `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator`
|
||||
with `outLatex` output.
|
||||
- Added `os.normalizeExe`, e.g.: `koch` => `./koch`.
|
||||
- `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720`
|
||||
to keep old behavior.
|
||||
- Added `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
|
||||
- Added `typetraits.elementType` to get element type of an iterable.
|
||||
- `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`;
|
||||
`$tuple[]` is now `"tuple[]"` instead of `"tuple"`;
|
||||
`$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"`
|
||||
- Added `macros.extractDocCommentsAndRunnables` helper
|
||||
|
||||
- `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now
|
||||
expands to `fmt"expr={expr}"`.
|
||||
- deprecations: `os.existsDir` => `dirExists`, `os.existsFile` => `fileExists`
|
||||
|
||||
- Added `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
|
||||
- Made `maxLines` argument `Positive` in `logging.newRollingFileLogger`,
|
||||
because negative values will result in a new file being created for each logged
|
||||
line which doesn't make sense.
|
||||
- Changed `log` in `logging` to use proper log level on JavaScript target,
|
||||
e.g. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc.
|
||||
- Tables, HashSets, SharedTables and deques don't require anymore that the passed
|
||||
initial size must be a power of two - this is done internally.
|
||||
Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore.
|
||||
`CountTable.inc` takes `val: int` again not `val: Positive`; I.e. it can "count down" again.
|
||||
- Removed deprecated symbols from `macros` module, deprecated as far back as `0.15`.
|
||||
- On Windows the SSL library now checks for valid certificates.
|
||||
It uses the `cacert.pem` file for this purpose which was extracted
|
||||
from `https://curl.se/ca/cacert.pem`. Besides
|
||||
the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you
|
||||
now also need to ship `cacert.pem` with your `.exe` file.
|
||||
|
||||
|
||||
- Make `{.requiresInit.}` pragma to work for `distinct` types.
|
||||
|
||||
|
||||
- Added `asyncdispatch.activeDescriptors` that returns the number of currently
|
||||
active async event handles/file descriptors
|
||||
- Added `asyncdispatch.maxDescriptors` that returns the maximum number of
|
||||
active async event handles/file descriptors.
|
||||
|
||||
- Added `getPort` to `asynchttpserver`.
|
||||
|
||||
- `--gc:orc` is now 10% faster than previously for common workloads. If
|
||||
you have trouble with its changed behavior, compile with `-d:nimOldOrc`.
|
||||
|
||||
|
||||
- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`,
|
||||
determining preferred I/O block size for this file object.
|
||||
|
||||
- Added a simpler to use `io.readChars` overload.
|
||||
|
||||
- Added `**` to jsffi.
|
||||
|
||||
- `writeStackTrace` is available in JS backend now.
|
||||
|
||||
- Added `decodeQuery` to `std/uri`.
|
||||
|
||||
- `strscans.scanf` now supports parsing single characters.
|
||||
|
||||
- `strscans.scanTuple` added which uses `strscans.scanf` internally,
|
||||
returning a tuple which can be unpacked for easier usage of `scanf`.
|
||||
|
||||
- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`,
|
||||
if the iterable yields a built-in settable type.
|
||||
|
||||
- Added `setutils.fullSet` which returns a full built-in `set` for a valid type.
|
||||
|
||||
- Added `setutils.complement` which returns the complement of a built-in `set`.
|
||||
|
||||
- Added `setutils.[]=`.
|
||||
|
||||
- Added `math.isNaN`.
|
||||
|
||||
- Added `jsbigints` module, arbitrary precision integers for JavaScript target.
|
||||
|
||||
- Added `math.copySign`.
|
||||
|
||||
- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList`
|
||||
and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
|
||||
shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes
|
||||
its argument, `addMoved`, is also supplied.
|
||||
|
||||
- Added `euclDiv` and `euclMod` to `math`.
|
||||
|
||||
- Added `httpcore.is1xx` and missing HTTP codes.
|
||||
|
||||
- Added `jsconsole.jsAssert` for JavaScript target.
|
||||
|
||||
- Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs.
|
||||
https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||
|
||||
- Added `socketstream` module that wraps sockets in the stream interface
|
||||
|
||||
- Added `sugar.dumpToString` which improves on `sugar.dump`.
|
||||
|
||||
- Added `math.signbit`.
|
||||
|
||||
- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably)
|
||||
|
||||
- In `lists`: renamed `append` to `add` and retained `append` as an alias;
|
||||
added `prepend` and `prependMoved` analogously to `add` and `addMoved`;
|
||||
added `remove` for `SinglyLinkedList`s.
|
||||
|
||||
- Deprecated `any`. See https://github.com/nim-lang/RFCs/issues/281
|
||||
|
||||
- Added optional `options` argument to `copyFile`, `copyFileToDir`, and
|
||||
`copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are
|
||||
followed (copy files symlinks point to); on Windows, `options` argument is
|
||||
ignored and symlinks are skipped.
|
||||
|
||||
- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as
|
||||
symlinks (instead of skipping them as it was before); on Windows symlinks are
|
||||
skipped.
|
||||
|
||||
- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks
|
||||
(instead of skipping them sometimes as it was before).
|
||||
|
||||
- Added optional `followSymlinks` argument to `setFilePermissions`.
|
||||
|
||||
- Added `os.isAdmin` to tell whether the caller's process is a member of the
|
||||
Administrators local group (on Windows) or a root (on POSIX).
|
||||
|
||||
- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
|
||||
|
||||
- Added `compilesettings.SingleValueSetting.libPath`.
|
||||
|
||||
- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding
|
||||
issues like https://github.com/nim-lang/Nim/issues/13063 (which affected error messages)
|
||||
for modules importing `std/wrapnils`.
|
||||
Added `??.` macro which returns an `Option`.
|
||||
|
||||
- Added `math.frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead.
|
||||
|
||||
- `parseopt.initOptParser` has been made available and `parseopt` has been
|
||||
added back to `prelude` for all backends. Previously `initOptParser` was
|
||||
unavailable if the `os` module did not have `paramCount` or `paramStr`,
|
||||
but the use of these in `initOptParser` were conditionally to the runtime
|
||||
arguments passed to it, so `initOptParser` has been changed to raise
|
||||
`ValueError` when the real command line is not available. `parseopt` was
|
||||
previously excluded from `prelude` for JS, as it could not be imported.
|
||||
|
||||
- Added `system.prepareStrMutation` for better support of low
|
||||
level `moveMem`, `copyMem` operations for Orc's copy-on-write string
|
||||
implementation.
|
||||
|
||||
- Added `std/strbasics` for high performance string operations.
|
||||
Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`.
|
||||
|
||||
|
||||
- Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`.
|
||||
|
||||
- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
|
||||
and `$none(int)` to `"none(int)"` instead of `"None[int]"`.
|
||||
|
||||
- Added `algorithm.merge`.
|
||||
|
||||
|
||||
- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) wrapper for JavaScript target.
|
||||
|
||||
- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) wrapper for JavaScript target.
|
||||
|
||||
- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) wrapper for JavaScript target.
|
||||
|
||||
- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible
|
||||
with most other languages.
|
||||
|
||||
- Removed support for named procs in `sugar.=>`.
|
||||
|
||||
- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger).
|
||||
|
||||
- Added `std/channels`.
|
||||
|
||||
- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals).
|
||||
|
||||
- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time
|
||||
zone offsets without colons, e.g. `UTC+7 -> +0700`.
|
||||
|
||||
- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
|
||||
|
||||
- Added dollar `$` and `len` for `jsre.RegExp`.
|
||||
|
||||
- Added `std/tasks`.
|
||||
|
||||
- Added `hasDataBuffered` to `asyncnet`.
|
||||
|
||||
- Added `hasClosure` to `std/typetraits`.
|
||||
|
||||
- Added `std/tempfiles`.
|
||||
|
||||
- Added `genasts.genAst` that avoids the problems inherent with `quote do` and can
|
||||
be used as a replacement.
|
||||
|
||||
- Added `copyWithin` [for `seq` and `array` for JavaScript targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin).
|
||||
|
||||
- Fixed premature garbage collection in asyncdispatch, when a stack trace override is in place.
|
||||
|
||||
## Language changes
|
||||
- In newruntime it is now allowed to assign discriminator field without restrictions as long as case object doesn't have custom destructor. Discriminator value doesn't have to be a constant either. If you have custom destructor for case object and you do want to freely assign discriminator fields, it is recommended to refactor object into 2 objects like this:
|
||||
|
||||
- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
|
||||
`wasMoved` calls where needed.
|
||||
- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there
|
||||
is no need to update your code. This change was backported to 1.2 too so you can use the
|
||||
more readability `=copy` without loss of compatibility.
|
||||
|
||||
- In the newruntime it is now allowed to assign to the discriminator field
|
||||
without restrictions as long as case object doesn't have custom destructor.
|
||||
The discriminator value doesn't have to be a constant either. If you have a
|
||||
custom destructor for a case object and you do want to freely assign discriminator
|
||||
fields, it is recommended to refactor object into 2 objects like this:
|
||||
|
||||
```nim
|
||||
type
|
||||
MyObj = object
|
||||
@@ -111,5 +349,10 @@
|
||||
## Compiler changes
|
||||
|
||||
|
||||
- The style checking of the compiler now supports a `--styleCheck:usages` switch. This switch
|
||||
enforces that every symbol is written as it was declared, not enforcing
|
||||
the official Nim style guide. To be enabled, this has to be combined either
|
||||
with `--styleCheck:error` or `--styleCheck:hint`.
|
||||
|
||||
## Tool changes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ REM Some debug info
|
||||
echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%."
|
||||
gcc -v
|
||||
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources
|
||||
cd csources
|
||||
call build64.bat
|
||||
cd ..
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
sh ci/deps.sh
|
||||
|
||||
# Build from C sources.
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources
|
||||
cd csources
|
||||
sh build.sh
|
||||
cd ..
|
||||
|
||||
@@ -25,7 +25,7 @@ Rem Build csources
|
||||
koch csources -d:release || exit /b
|
||||
|
||||
rem Grab C sources and nimsuggest
|
||||
git clone --depth 1 https://github.com/nim-lang/csources.git
|
||||
git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources
|
||||
|
||||
set PATH=%CD%\bin;%PATH%
|
||||
|
||||
|
||||
@@ -1085,7 +1085,7 @@ proc safeLen*(n: PNode): int {.inline.} =
|
||||
|
||||
proc safeArrLen*(n: PNode): int {.inline.} =
|
||||
## works for array-like objects (strings passed as openArray in VM).
|
||||
if n.kind in {nkStrLit..nkTripleStrLit}:result = n.strVal.len
|
||||
if n.kind in {nkStrLit..nkTripleStrLit}: result = n.strVal.len
|
||||
elif n.kind in {nkNone..nkFloat128Lit}: result = 0
|
||||
else: result = n.len
|
||||
|
||||
@@ -1327,7 +1327,7 @@ const
|
||||
MaxLockLevel* = 1000'i16
|
||||
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||
"=destroy", "=", "=sink", "=trace", "=dispose", "=deepcopy"]
|
||||
"=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"]
|
||||
|
||||
proc `$`*(x: TLockLevel): string =
|
||||
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||
|
||||
@@ -189,7 +189,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
case skipTypes(a.t, abstractVar).kind
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = "$1, $1Len_0" % [rdLoc(a)]
|
||||
of tyString, tySequence:
|
||||
|
||||
@@ -833,6 +833,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
|
||||
[rdLoc(test), strLit, raiseInstr(p)])
|
||||
|
||||
proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
|
||||
assert e[0].kind == nkDotExpr
|
||||
if optFieldCheck in p.options:
|
||||
var a: TLoc
|
||||
genRecordFieldAux(p, e[0], d, a)
|
||||
@@ -867,7 +868,7 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
|
||||
if optBoundsCheck in p.options and ty.kind != tyUncheckedArray:
|
||||
if not isConstExpr(y):
|
||||
# semantic pass has already checked for const index expressions
|
||||
if firstOrd(p.config, ty) == 0:
|
||||
if firstOrd(p.config, ty) == 0 and lastOrd(p.config, ty) >= 0:
|
||||
if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)):
|
||||
linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n",
|
||||
[rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)])
|
||||
@@ -1781,9 +1782,9 @@ template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) =
|
||||
|
||||
proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
|
||||
case int(getSize(p.config, skipTypes(e[1].typ, abstractVar)))
|
||||
of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)")
|
||||
of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)")
|
||||
of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)")
|
||||
of 1: binaryExprIn(p, e, a, b, d, "(($1 &((NU8)1<<((NU)($2)&7U)))!=0)")
|
||||
of 2: binaryExprIn(p, e, a, b, d, "(($1 &((NU16)1<<((NU)($2)&15U)))!=0)")
|
||||
of 4: binaryExprIn(p, e, a, b, d, "(($1 &((NU32)1<<((NU)($2)&31U)))!=0)")
|
||||
of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)")
|
||||
else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
|
||||
|
||||
@@ -2740,6 +2741,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
inc p.splitDecls
|
||||
genGotoState(p, n)
|
||||
of nkBreakState: genBreakState(p, n, d)
|
||||
of nkMixinStmt, nkBindStmt: discard
|
||||
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||
|
||||
proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope =
|
||||
@@ -2941,7 +2943,23 @@ proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope =
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope =
|
||||
case n.kind
|
||||
of nkHiddenStdConv, nkHiddenSubConv:
|
||||
result = genBracedInit(p, n[1], isConst)
|
||||
when false:
|
||||
# XXX The frontend doesn't keep conversions to openArray for us. :-(
|
||||
# We need to change 'transformConv' first, but that is hard.
|
||||
if n.typ.kind == tyOpenArray:
|
||||
assert n[1].kind == nkBracket
|
||||
let data = genBracedInit(p, n[1], isConst)
|
||||
|
||||
let payload = getTempName(p.module)
|
||||
let ctype = getTypeDesc(p.module, n.typ.skipTypes(abstractInst)[0])
|
||||
let arrLen = n[1].len
|
||||
appcg(p.module, cfsData,
|
||||
"static $5 $1 $3[$2] = $4;$n", [
|
||||
ctype, arrLen, payload, data,
|
||||
if isConst: "const" else: ""])
|
||||
result = "{($1*)&$2, $3}" % [ctype, payload, rope arrLen]
|
||||
else:
|
||||
result = genBracedInit(p, n[1], isConst)
|
||||
else:
|
||||
var ty = tyNone
|
||||
if n.typ == nil:
|
||||
|
||||
@@ -25,10 +25,10 @@ proc getTraverseProc(p: BProc, v: PSym): Rope =
|
||||
|
||||
proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) =
|
||||
if sfThread in v.flags:
|
||||
appcg(p.module, p.module.initProc.procSec(cpsInit),
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
|
||||
else:
|
||||
appcg(p.module, p.module.initProc.procSec(cpsInit),
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
|
||||
|
||||
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
|
||||
@@ -1330,8 +1330,19 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||
elif isDefined(p.config, "nimSigSetjmp"):
|
||||
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint])
|
||||
elif isDefined(p.config, "nimBuiltinSetjmp"):
|
||||
linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint])
|
||||
elif isDefined(p.config, "nimRawSetjmp"):
|
||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
||||
if isDefined(p.config, "mswindows"):
|
||||
# The Windows `_setjmp()` takes two arguments, with the second being an
|
||||
# undocumented buffer used by the SEH mechanism for stack unwinding.
|
||||
# Mingw-w64 has been trying to get it right for years, but it's still
|
||||
# prone to stack corruption during unwinding, so we disable that by setting
|
||||
# it to NULL.
|
||||
# More details: https://github.com/status-im/nimbus-eth2/issues/3121
|
||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
||||
|
||||
@@ -220,7 +220,8 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool =
|
||||
# such a poor programming language.
|
||||
# We exclude records with refs too. This enhances efficiency and
|
||||
# is necessary for proper code generation of assignments.
|
||||
if rettype == nil: result = true
|
||||
if rettype == nil or (tfByCopy notin rettype.flags and getSize(conf, rettype) > conf.target.floatSize*3):
|
||||
result = true
|
||||
else:
|
||||
case mapType(conf, rettype)
|
||||
of ctArray:
|
||||
|
||||
@@ -244,7 +244,7 @@ proc safeLineNm(info: TLineInfo): int =
|
||||
|
||||
proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
|
||||
assert line >= 0
|
||||
if optLineDir in conf.options:
|
||||
if optLineDir in conf.options and line > 0:
|
||||
r.addf("$N#line $2 $1$N",
|
||||
[rope(makeSingleLineCString(filename)), rope(line)])
|
||||
|
||||
@@ -432,8 +432,14 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
|
||||
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
elif not isComplexValueType(typ):
|
||||
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
|
||||
getTypeDesc(p.module, typ)])
|
||||
if containsGarbageCollectedRef(loc.t):
|
||||
var nilLoc: TLoc
|
||||
initLoc(nilLoc, locTemp, loc.lode, OnStack)
|
||||
nilLoc.r = rope("NIM_NIL")
|
||||
genRefAssign(p, loc, nilLoc)
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
|
||||
getTypeDesc(p.module, typ)])
|
||||
else:
|
||||
if not isTemp or containsGarbageCollectedRef(loc.t):
|
||||
# don't use nimZeroMem for temporary values for performance if we can
|
||||
@@ -838,7 +844,8 @@ proc containsResult(n: PNode): bool =
|
||||
for i in 0..<n.safeLen:
|
||||
if containsResult(n[i]): return true
|
||||
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, nkMacroDef} +
|
||||
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
|
||||
nkMacroDef, nkMixinStmt, nkBindStmt} +
|
||||
declarativeDefs
|
||||
|
||||
proc easyResultAsgn(n: PNode): PNode =
|
||||
@@ -1645,7 +1652,7 @@ proc genInitCode(m: BModule) =
|
||||
writeSection(preInitProc, cpsLocals)
|
||||
writeSection(preInitProc, cpsInit, m.hcrOn)
|
||||
writeSection(preInitProc, cpsStmts)
|
||||
prc.addf("}$N", [])
|
||||
prc.addf("}/* preInitProc end */$N", [])
|
||||
|
||||
# add new scope for following code, because old vcc compiler need variable
|
||||
# be defined at the top of the block
|
||||
|
||||
@@ -121,7 +121,10 @@
|
||||
# yield 2
|
||||
# if :unrollFinally: # This node is created by `newEndFinallyNode`
|
||||
# if :curExc.isNil:
|
||||
# return :tmpResult
|
||||
# if nearestFinally == 0:
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# :state = nearestFinally # bubble up
|
||||
# else:
|
||||
# closureIterSetupExc(nil)
|
||||
# raise
|
||||
@@ -130,7 +133,8 @@
|
||||
|
||||
import
|
||||
ast, msgs, idents,
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
|
||||
tables, options
|
||||
|
||||
type
|
||||
Ctx = object
|
||||
@@ -155,7 +159,7 @@ type
|
||||
|
||||
const
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt} + procDefs
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
|
||||
|
||||
proc newStateAccess(ctx: var Ctx): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
@@ -792,7 +796,10 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
# Generate the following code:
|
||||
# if :unrollFinally:
|
||||
# if :curExc.isNil:
|
||||
# return :tmpResult
|
||||
# if nearestFinally == 0:
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# :state = nearestFinally # bubble up
|
||||
# else:
|
||||
# raise
|
||||
let curExc = ctx.newCurExcAccess()
|
||||
@@ -801,11 +808,17 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
|
||||
cmp.typ = ctx.g.getSysType(info, tyBool)
|
||||
|
||||
let asgn = newTree(nkFastAsgn,
|
||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
|
||||
ctx.newTmpResultAccess())
|
||||
let retStmt =
|
||||
if ctx.nearestFinally == 0:
|
||||
# last finally, we can return
|
||||
let asgn = newTree(nkFastAsgn,
|
||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn), info),
|
||||
ctx.newTmpResultAccess())
|
||||
newTree(nkReturnStmt, asgn)
|
||||
else:
|
||||
# bubble up to next finally
|
||||
newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally))
|
||||
|
||||
let retStmt = newTree(nkReturnStmt, asgn)
|
||||
let branch = newTree(nkElifBranch, cmp, retStmt)
|
||||
|
||||
let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
|
||||
@@ -849,6 +862,13 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
|
||||
of nkSkip:
|
||||
discard
|
||||
of nkTryStmt:
|
||||
if n.hasYields:
|
||||
# the inner try will handle these transformations
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
n[i] = ctx.transformReturnsInTry(n[i])
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
n[i] = ctx.transformReturnsInTry(n[i])
|
||||
@@ -1102,7 +1122,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
|
||||
|
||||
result = ctx.states[stateIdx][0].intVal.int
|
||||
|
||||
proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode =
|
||||
proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
|
||||
result = n
|
||||
case n.kind
|
||||
of nkSkip:
|
||||
@@ -1282,6 +1302,101 @@ proc deleteEmptyStates(ctx: var Ctx) =
|
||||
else:
|
||||
inc i
|
||||
|
||||
type
|
||||
PreprocessContext = object
|
||||
finallys: seq[PNode]
|
||||
config: ConfigRef
|
||||
blocks: seq[(PNode, int)]
|
||||
FreshVarsContext = object
|
||||
tab: Table[int, PSym]
|
||||
config: ConfigRef
|
||||
info: TLineInfo
|
||||
|
||||
proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
let x = c.tab.getOrDefault(n.sym.id)
|
||||
if x == nil:
|
||||
result = n
|
||||
else:
|
||||
result = newSymNode(x, n.info)
|
||||
of nkSkip - {nkSym}:
|
||||
result = n
|
||||
of nkLetSection, nkVarSection:
|
||||
result = copyNode(n)
|
||||
for it in n:
|
||||
if it.kind in {nkIdentDefs, nkVarTuple}:
|
||||
let idefs = copyNode(it)
|
||||
for v in 0..it.len-3:
|
||||
if it[v].kind == nkSym:
|
||||
let x = copySym(it[v].sym)
|
||||
c.tab[it[v].sym.id] = x
|
||||
idefs.add newSymNode(x)
|
||||
else:
|
||||
idefs.add it[v]
|
||||
|
||||
for rest in it.len-2 ..< it.len: idefs.add it[rest]
|
||||
result.add idefs
|
||||
else:
|
||||
result.add it
|
||||
of nkRaiseStmt:
|
||||
localError(c.config, c.info, "unsupported control flow: 'finally: ... raise' duplicated because of 'break'")
|
||||
else:
|
||||
result = n
|
||||
for i in 0..<n.safeLen:
|
||||
result[i] = freshVars(n[i], c)
|
||||
|
||||
proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||
# in order to fix bug #15243 without risking regressions, we preprocess
|
||||
# the AST so that 'break' statements inside a 'try finally' also have the
|
||||
# finally section. We need to duplicate local variables here and also
|
||||
# detect: 'finally: raises X' which is currently not supported. We produce
|
||||
# an error for this case for now. All this will be done properly with Yuriy's
|
||||
# patch.
|
||||
result = n
|
||||
case n.kind
|
||||
of nkTryStmt:
|
||||
let f = n.lastSon
|
||||
if f.kind == nkFinally:
|
||||
c.finallys.add f.lastSon
|
||||
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
|
||||
if f.kind == nkFinally:
|
||||
discard c.finallys.pop()
|
||||
|
||||
of nkWhileStmt, nkBlockStmt:
|
||||
c.blocks.add((n, c.finallys.len))
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
discard c.blocks.pop()
|
||||
|
||||
of nkBreakStmt:
|
||||
if c.blocks.len == 0:
|
||||
discard
|
||||
else:
|
||||
var fin = -1
|
||||
if n[0].kind == nkEmpty:
|
||||
fin = c.blocks[^1][1]
|
||||
elif n[0].kind == nkSym:
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if c.blocks[i][0].kind == nkBlockStmt and c.blocks[i][0][0].kind == nkSym and
|
||||
c.blocks[i][0][0].sym == n[0].sym:
|
||||
fin = c.blocks[i][1]
|
||||
break
|
||||
|
||||
if fin >= 0:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
for i in countdown(c.finallys.high, fin):
|
||||
var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info)
|
||||
result.add freshVars(preprocess(c, c.finallys[i]), vars)
|
||||
result.add n
|
||||
of nkSkip: discard
|
||||
else:
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
|
||||
proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
|
||||
var ctx: Ctx
|
||||
ctx.g = g
|
||||
@@ -1294,7 +1409,10 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode =
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info)
|
||||
var n = n.toStmtList
|
||||
var pc = PreprocessContext(finallys: @[], config: g.config)
|
||||
var n = preprocess(pc, n.toStmtList)
|
||||
#echo "transformed into ", n
|
||||
#var n = n.toStmtList
|
||||
|
||||
discard ctx.newState(n, nil)
|
||||
let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1))
|
||||
|
||||
@@ -225,7 +225,7 @@ const
|
||||
errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found"
|
||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
|
||||
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
||||
case switch.normalize
|
||||
@@ -851,6 +851,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
|
||||
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
|
||||
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
|
||||
of "usages": conf.globalOptions.incl optStyleUsages
|
||||
else: localError(conf, info, errOffHintsError % arg)
|
||||
of "showallmismatches":
|
||||
processOnOffSwitchG(conf, {optShowAllMismatches}, arg, pass, info)
|
||||
|
||||
@@ -116,3 +116,5 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimNewIntegerOps")
|
||||
defineSymbol("nimHasInvariant")
|
||||
defineSymbol("nimHasStacktraceMsgs")
|
||||
defineSymbol("nimHasStacktracesModule")
|
||||
defineSymbol("nimHasJsBigIntBackend")
|
||||
|
||||
@@ -651,6 +651,10 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||
if useCpp(conf, cfile.cname):
|
||||
options.add(' ' & CC[c].cppXsupport)
|
||||
|
||||
if cfile.customArgs != "":
|
||||
options.add ' '
|
||||
options.add cfile.customArgs
|
||||
|
||||
var compilePattern: string
|
||||
# compute include paths:
|
||||
var includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
|
||||
|
||||
@@ -212,7 +212,7 @@ template isUnpackedTuple(n: PNode): bool =
|
||||
|
||||
proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
||||
var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
|
||||
if opname == "=" and ri != nil:
|
||||
if (opname == "=" or opname == "=copy") and ri != nil:
|
||||
m.add "; requires a copy because it's not the last read of '"
|
||||
m.add renderTree(ri)
|
||||
m.add '\''
|
||||
@@ -301,7 +301,7 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode =
|
||||
if tfHasOwned in t.flags and ri.kind != nkNilLit:
|
||||
# try to improve the error message here:
|
||||
if c.otherRead == nil: discard isLastRead(ri, c)
|
||||
checkForErrorPragma(c, t, ri, "=")
|
||||
checkForErrorPragma(c, t, ri, "=copy")
|
||||
result = genCopyNoCheck(c, dest, ri)
|
||||
|
||||
proc addTopVar(c: var Con; v: PNode) =
|
||||
@@ -363,6 +363,7 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
|
||||
proc destructiveMoveVar(n: PNode; c: var Con): PNode =
|
||||
# generate: (let tmp = v; reset(v); tmp)
|
||||
if not hasDestructor(n.typ):
|
||||
assert n.kind != nkSym or not hasDestructor(n.sym.typ)
|
||||
result = copyTree(n)
|
||||
else:
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
@@ -944,7 +945,8 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode =
|
||||
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState:
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
||||
nkMixinStmt, nkBindStmt:
|
||||
result = n
|
||||
of nkBreakStmt:
|
||||
inc c.hasUnstructuredCf
|
||||
|
||||
@@ -6,12 +6,12 @@ Name: "Nim"
|
||||
Version: "$version"
|
||||
Platforms: """
|
||||
windows: i386;amd64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64
|
||||
macosx: i386;amd64;powerpc64
|
||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
netbsd: i386;amd64
|
||||
openbsd: i386;amd64
|
||||
openbsd: i386;amd64;arm;arm64
|
||||
dragonfly: i386;amd64
|
||||
haiku: i386;amd64
|
||||
android: i386;arm;arm64
|
||||
@@ -77,6 +77,7 @@ Files: "lib"
|
||||
[Other]
|
||||
Files: "examples"
|
||||
Files: "dist/nimble"
|
||||
Files: "dist/fusion"
|
||||
|
||||
Files: "tests"
|
||||
|
||||
@@ -110,6 +111,7 @@ Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/downlo
|
||||
Files: "bin/makelink.exe"
|
||||
Files: "bin/7zG.exe"
|
||||
Files: "bin/*.dll"
|
||||
Files: "bin/cacert.pem"
|
||||
|
||||
[UnixBin]
|
||||
Files: "bin/nim"
|
||||
|
||||
@@ -1591,7 +1591,10 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
var t = skipTypes(typ, abstractInst)
|
||||
case t.kind
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
|
||||
result = putToSeq("0", indirect)
|
||||
if $t.sym.loc.r == "bigint":
|
||||
result = putToSeq("0n", indirect)
|
||||
else:
|
||||
result = putToSeq("0", indirect)
|
||||
of tyFloat..tyFloat128:
|
||||
result = putToSeq("0.0", indirect)
|
||||
of tyRange, tyGenericInst, tyAlias, tySink, tyOwned:
|
||||
@@ -1634,7 +1637,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
|
||||
of tyObject:
|
||||
var initList: Rope
|
||||
createObjInitList(p, t, initIntSet(), initList)
|
||||
result = ("{$1}") % [initList]
|
||||
result = ("({$1})") % [initList]
|
||||
if indirect: result = "[$1]" % [result]
|
||||
of tyVar, tyPtr, tyLent, tyRef, tyPointer:
|
||||
if mapType(p, t) == etyBaseIndex:
|
||||
@@ -2124,11 +2127,17 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) =
|
||||
if dest.kind == src.kind:
|
||||
# no-op conversion
|
||||
return
|
||||
case dest.kind:
|
||||
of tyBool:
|
||||
let toInt = (dest.kind in tyInt..tyInt32)
|
||||
let fromInt = (src.kind in tyInt..tyInt32)
|
||||
let toUint = (dest.kind in tyUInt..tyUInt32)
|
||||
let fromUint = (src.kind in tyUInt..tyUInt32)
|
||||
if toUint and (fromInt or fromUint):
|
||||
let trimmer = unsignedTrimmer(dest.size)
|
||||
r.res = "($1 $2)" % [r.res, trimmer]
|
||||
elif dest.kind == tyBool:
|
||||
r.res = "(!!($1))" % [r.res]
|
||||
r.kind = resExpr
|
||||
of tyInt:
|
||||
elif toInt:
|
||||
r.res = "(($1)|0)" % [r.res]
|
||||
else:
|
||||
# TODO: What types must we handle here?
|
||||
@@ -2470,7 +2479,8 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkRaiseStmt: genRaiseStmt(p, n)
|
||||
of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt,
|
||||
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
||||
nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard
|
||||
nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt,
|
||||
nkMixinStmt, nkBindStmt: discard
|
||||
of nkPragma: genPragma(p, n)
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
|
||||
var s = n[namePos].sym
|
||||
|
||||
@@ -493,7 +493,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
w = up
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
|
||||
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
|
||||
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr:
|
||||
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
discard
|
||||
of nkLambdaKinds, nkIteratorDef:
|
||||
if n.typ != nil:
|
||||
@@ -747,7 +748,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||
result = accessViaEnvVar(n, owner, d, c)
|
||||
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
|
||||
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef,
|
||||
nkMacroDef, nkFuncDef:
|
||||
nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt:
|
||||
discard
|
||||
of nkClosure:
|
||||
if n[1].kind == nkNilLit:
|
||||
|
||||
@@ -62,6 +62,7 @@ type
|
||||
tkComma, tkSemiColon,
|
||||
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon,
|
||||
tkOpr, tkComment, tkAccent,
|
||||
# these are fake tokens used by renderer.nim
|
||||
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
|
||||
|
||||
TTokTypes* = set[TTokType]
|
||||
|
||||
@@ -42,7 +42,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
|
||||
of nkSym:
|
||||
if interestingVar(it.sym):
|
||||
n[i] = lookupOrAdd(c, it.sym, it.info)
|
||||
of procDefs, nkTypeSection: discard
|
||||
of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard
|
||||
else:
|
||||
for i in 0..<it.safeLen:
|
||||
liftLocals(it, i, c)
|
||||
|
||||
@@ -44,6 +44,7 @@ type
|
||||
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
|
||||
warnInconsistentSpacing, warnCaseTransition, warnCycleCreated,
|
||||
warnObservableStores,
|
||||
warnResultUsed,
|
||||
warnUser,
|
||||
hintSuccess, hintSuccessX, hintCC,
|
||||
hintLineTooLong, hintXDeclaredButNotUsed,
|
||||
@@ -110,6 +111,7 @@ const
|
||||
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
||||
warnCycleCreated: "$1",
|
||||
warnObservableStores: "observable stores to '$1'",
|
||||
warnResultUsed: "used 'result' variable",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
@@ -160,7 +162,7 @@ const
|
||||
"IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit",
|
||||
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
|
||||
"Spacing", "CaseTransition", "CycleCreated",
|
||||
"ObservableStores", "User"]
|
||||
"ObservableStores", "ResultUsed", "User"]
|
||||
|
||||
HintsToStr* = [
|
||||
"Success", "SuccessX", "CC", "LineTooLong",
|
||||
@@ -191,7 +193,7 @@ type
|
||||
TNoteKinds* = set[TNoteKind]
|
||||
|
||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {}
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnResultUsed}
|
||||
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext}
|
||||
result[1] = result[2] - {warnProveField, warnProveIndex,
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
|
||||
@@ -101,7 +101,7 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
lintReport(conf, info, beau, s.name.s)
|
||||
|
||||
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
|
||||
if {optStyleHint, optStyleError} * conf.globalOptions != {} and optStyleUsages notin conf.globalOptions:
|
||||
nep1CheckDefImpl(conf, info, s, k)
|
||||
|
||||
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
|
||||
@@ -19,5 +19,6 @@ define:useStdoutAsStdmsg
|
||||
styleCheck:error
|
||||
@end
|
||||
|
||||
|
||||
#define:useNodeIds
|
||||
#gc:markAndSweep
|
||||
|
||||
@@ -59,6 +59,7 @@ type # please make sure we have under 32 options
|
||||
optRun, # run the compiled project
|
||||
optStyleHint, # check that the names adhere to NEP-1
|
||||
optStyleError, # enforce that the names adhere to NEP-1
|
||||
optStyleUsages, # only enforce consistent **usages** of the symbol
|
||||
optSkipSystemConfigFile, # skip the system's cfg/nims config file
|
||||
optSkipProjConfigFile, # skip the project's cfg/nims config file
|
||||
optSkipUserConfigFile, # skip the users's cfg/nims config file
|
||||
@@ -180,6 +181,7 @@ type
|
||||
nimname*: string
|
||||
cname*, obj*: AbsoluteFile
|
||||
flags*: set[CfileFlag]
|
||||
customArgs*: string
|
||||
CfileList* = seq[Cfile]
|
||||
|
||||
Suggest* = ref object
|
||||
@@ -309,7 +311,7 @@ proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) =
|
||||
proc hasHint*(conf: ConfigRef, note: TNoteKind): bool =
|
||||
optHints in conf.options and note in conf.notes
|
||||
|
||||
proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool =
|
||||
proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool {.inline.} =
|
||||
optWarns in conf.options and note in conf.notes
|
||||
|
||||
proc hcrOn*(conf: ConfigRef): bool = return optHotCodeReloading in conf.globalOptions
|
||||
|
||||
@@ -22,7 +22,7 @@ type
|
||||
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
|
||||
osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
|
||||
osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks
|
||||
osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osAny
|
||||
osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osAny
|
||||
|
||||
type
|
||||
TInfoOSProp* = enum
|
||||
@@ -177,6 +177,10 @@ const
|
||||
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
|
||||
scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".",
|
||||
props: {ospNeedsPIC, ospPosix}),
|
||||
(name: "FreeRTOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
|
||||
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
|
||||
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
|
||||
props: {ospPosix}),
|
||||
(name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
|
||||
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
|
||||
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
|
||||
@@ -189,7 +193,7 @@ type
|
||||
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
|
||||
cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips,
|
||||
cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430,
|
||||
cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV64, cpuWasm32
|
||||
cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32
|
||||
|
||||
type
|
||||
TEndian* = enum
|
||||
@@ -222,7 +226,9 @@ const
|
||||
(name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
|
||||
(name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
|
||||
(name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
|
||||
(name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32)]
|
||||
|
||||
type
|
||||
|
||||
@@ -20,7 +20,7 @@ const
|
||||
|
||||
const
|
||||
declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp,
|
||||
wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed, wAlign}
|
||||
wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed}
|
||||
## common pragmas for declarations, to a good approximation
|
||||
procPragmas* = declPragmas + {FirstCallConv..LastCallConv,
|
||||
wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
|
||||
@@ -54,22 +54,22 @@ const
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
|
||||
wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume}
|
||||
lambdaPragmas* = declPragmas + {FirstCallConv..LastCallConv,
|
||||
lambdaPragmas* = {FirstCallConv..LastCallConv,
|
||||
wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
|
||||
wThread, wAsmNoStackFrame,
|
||||
wRaises, wLocks, wTags, wRequires, wEnsures,
|
||||
wGcSafe, wCodegenDecl} - {wExportNims, wError, wUsed} # why exclude these?
|
||||
wGcSafe, wCodegenDecl, wNoInit}
|
||||
typePragmas* = declPragmas + {wMagic, wAcyclic,
|
||||
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
|
||||
wIncompleteStruct, wByCopy, wByRef,
|
||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||
fieldPragmas* = declPragmas + {
|
||||
wGuard, wBitsize, wCursor, wRequiresInit} - {wExportNims, wNodecl} # why exclude these?
|
||||
fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor,
|
||||
wRequiresInit, wAlign} - {wExportNims, wNodecl} # why exclude these?
|
||||
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
||||
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
||||
wNoInit, wCompileTime, wGlobal,
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor}
|
||||
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wAlign}
|
||||
constPragmas* = declPragmas + {wHeader, wMagic,
|
||||
wGensym, wInject,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
|
||||
@@ -92,11 +92,10 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
|
||||
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords;
|
||||
isStatement: bool = false)
|
||||
|
||||
proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") =
|
||||
proc recordPragma(c: PContext; n: PNode; args: varargs[string]) =
|
||||
var recorded = newNodeI(nkCommentStmt, n.info)
|
||||
recorded.add newStrNode(key, n.info)
|
||||
recorded.add newStrNode(val, n.info)
|
||||
if val2.len > 0: recorded.add newStrNode(val2, n.info)
|
||||
for i in 0..args.high:
|
||||
recorded.add newStrNode(args[i], n.info)
|
||||
c.graph.recordStmt(c.graph, c.module, recorded)
|
||||
|
||||
const
|
||||
@@ -496,11 +495,12 @@ proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile =
|
||||
if result.isEmpty: result = AbsoluteFile s
|
||||
|
||||
proc processCompile(c: PContext, n: PNode) =
|
||||
proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile) =
|
||||
proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) =
|
||||
var cf = Cfile(nimname: splitFile(src).name,
|
||||
cname: src, obj: dest, flags: {CfileFlag.External})
|
||||
cname: src, obj: dest, flags: {CfileFlag.External},
|
||||
customArgs: customArgs)
|
||||
extccomp.addExternalFileToCompile(c.config, cf)
|
||||
recordPragma(c, it, "compile", src.string, dest.string)
|
||||
recordPragma(c, it, "compile", src.string, dest.string, customArgs)
|
||||
|
||||
proc getStrLit(c: PContext, n: PNode; i: int): string =
|
||||
n[i] = c.semConstExpr(c, n[i])
|
||||
@@ -518,9 +518,19 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
var found = parentDir(toFullPath(c.config, n.info)) / s
|
||||
for f in os.walkFiles(found):
|
||||
let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
|
||||
docompile(c, it, AbsoluteFile f, obj)
|
||||
docompile(c, it, AbsoluteFile f, obj, "")
|
||||
else:
|
||||
let s = expectStrLit(c, n)
|
||||
var s = ""
|
||||
var customArgs = ""
|
||||
if n.kind in nkCallKinds:
|
||||
s = getStrLit(c, n, 1)
|
||||
if n.len <= 3:
|
||||
customArgs = getStrLit(c, n, 2)
|
||||
else:
|
||||
localError(c.config, n.info, "'.compile' pragma takes up 2 arguments")
|
||||
else:
|
||||
s = expectStrLit(c, n)
|
||||
|
||||
var found = AbsoluteFile(parentDir(toFullPath(c.config, n.info)) / s)
|
||||
if not fileExists(found):
|
||||
if isAbsolute(s): found = AbsoluteFile s
|
||||
@@ -528,7 +538,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
found = findFile(c.config, s)
|
||||
if found.isEmpty: found = AbsoluteFile s
|
||||
let obj = toObjFile(c.config, completeCfilePath(c.config, found, false))
|
||||
docompile(c, it, found, obj)
|
||||
docompile(c, it, found, obj, customArgs)
|
||||
|
||||
proc processLink(c: PContext, n: PNode) =
|
||||
let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
|
||||
|
||||
@@ -31,6 +31,7 @@ type
|
||||
TSrcGen* = object
|
||||
indent*: int
|
||||
lineLen*: int
|
||||
col: int
|
||||
pos*: int # current position for iteration over the buffer
|
||||
idx*: int # current token index for iteration over the buffer
|
||||
tokens*: TRenderTokSeq
|
||||
@@ -109,11 +110,10 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
|
||||
g.config = config
|
||||
|
||||
proc addTok(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) =
|
||||
setLen(g.tokens, g.tokens.len + 1)
|
||||
g.tokens[^1].kind = kind
|
||||
g.tokens[^1].length = int16(s.len)
|
||||
g.tokens[^1].sym = sym
|
||||
g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
|
||||
g.buf.add(s)
|
||||
if kind != tkSpaces:
|
||||
inc g.col, s.len
|
||||
|
||||
proc addPendingNL(g: var TSrcGen) =
|
||||
if g.pendingNL >= 0:
|
||||
@@ -123,6 +123,7 @@ proc addPendingNL(g: var TSrcGen) =
|
||||
const newlines = "\n"
|
||||
addTok(g, tkSpaces, newlines & spaces(g.pendingNL))
|
||||
g.lineLen = g.pendingNL
|
||||
g.col = g.pendingNL
|
||||
g.pendingNL = - 1
|
||||
g.pendingWhitespace = -1
|
||||
elif g.pendingWhitespace >= 0:
|
||||
@@ -131,7 +132,10 @@ proc addPendingNL(g: var TSrcGen) =
|
||||
|
||||
proc putNL(g: var TSrcGen, indent: int) =
|
||||
if g.pendingNL >= 0: addPendingNL(g)
|
||||
else: addTok(g, tkSpaces, "\n")
|
||||
else:
|
||||
addTok(g, tkSpaces, "\n")
|
||||
g.col = 0
|
||||
|
||||
g.pendingNL = indent
|
||||
g.lineLen = indent
|
||||
g.pendingWhitespace = -1
|
||||
@@ -146,6 +150,7 @@ proc putNL(g: var TSrcGen) =
|
||||
proc optNL(g: var TSrcGen, indent: int) =
|
||||
g.pendingNL = indent
|
||||
g.lineLen = indent
|
||||
g.col = g.indent
|
||||
when defined(nimpretty): g.pendingNewlineCount = 0
|
||||
|
||||
proc optNL(g: var TSrcGen) =
|
||||
@@ -154,6 +159,7 @@ proc optNL(g: var TSrcGen) =
|
||||
proc optNL(g: var TSrcGen; a, b: PNode) =
|
||||
g.pendingNL = g.indent
|
||||
g.lineLen = g.indent
|
||||
g.col = g.indent
|
||||
when defined(nimpretty): g.pendingNewlineCount = lineDiff(a, b)
|
||||
|
||||
proc indentNL(g: var TSrcGen) =
|
||||
@@ -173,16 +179,17 @@ proc put(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) =
|
||||
addPendingNL(g)
|
||||
if s.len > 0:
|
||||
addTok(g, kind, s, sym)
|
||||
inc(g.lineLen, s.len)
|
||||
else:
|
||||
g.pendingWhitespace = s.len
|
||||
inc g.col, s.len
|
||||
inc(g.lineLen, s.len)
|
||||
|
||||
proc putComment(g: var TSrcGen, s: string) =
|
||||
if s.len == 0: return
|
||||
var i = 0
|
||||
let hi = s.len - 1
|
||||
var isCode = (s.len >= 2) and (s[1] != ' ')
|
||||
var ind = g.lineLen
|
||||
let isCode = (s.len >= 2) and (s[1] != ' ')
|
||||
let ind = g.col
|
||||
var com = "## "
|
||||
while i <= hi:
|
||||
case s[i]
|
||||
@@ -208,7 +215,7 @@ proc putComment(g: var TSrcGen, s: string) =
|
||||
# compute length of the following word:
|
||||
var j = i
|
||||
while j <= hi and s[j] > ' ': inc(j)
|
||||
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
|
||||
if not isCode and (g.col + (j - i) > MaxLineLen):
|
||||
put(g, tkComment, com)
|
||||
optNL(g, ind)
|
||||
com = "## "
|
||||
@@ -290,15 +297,18 @@ proc shouldRenderComment(g: var TSrcGen, n: PNode): bool =
|
||||
proc gcom(g: var TSrcGen, n: PNode) =
|
||||
assert(n != nil)
|
||||
if shouldRenderComment(g, n):
|
||||
var oneSpaceAdded = 0
|
||||
if (g.pendingNL < 0) and (g.buf.len > 0) and (g.buf[^1] != ' '):
|
||||
put(g, tkSpaces, Space)
|
||||
oneSpaceAdded = 1
|
||||
# Before long comments we cannot make sure that a newline is generated,
|
||||
# because this might be wrong. But it is no problem in practice.
|
||||
if (g.pendingNL < 0) and (g.buf.len > 0) and
|
||||
(g.lineLen < LineCommentColumn):
|
||||
(g.col < LineCommentColumn):
|
||||
var ml = maxLineLength(n.comment)
|
||||
if ml + LineCommentColumn <= MaxLineLen:
|
||||
put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen))
|
||||
put(g, tkSpaces, spaces(LineCommentColumn - g.col))
|
||||
dec g.col, oneSpaceAdded
|
||||
putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n);
|
||||
|
||||
proc gcoms(g: var TSrcGen) =
|
||||
|
||||
@@ -106,6 +106,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
|
||||
decl(a[1])
|
||||
else:
|
||||
for i in 0..<n.safeLen: deps(n[i])
|
||||
of nkMixinStmt, nkBindStmt: discard
|
||||
else:
|
||||
for i in 0..<n.safeLen: deps(n[i])
|
||||
|
||||
|
||||
@@ -838,11 +838,12 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) =
|
||||
of "warning": message(g.config, n.info, warnUser, n[1].strVal)
|
||||
of "error": localError(g.config, n.info, errUser, n[1].strVal)
|
||||
of "compile":
|
||||
internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit
|
||||
internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit
|
||||
let cname = AbsoluteFile n[1].strVal
|
||||
var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
|
||||
obj: AbsoluteFile n[2].strVal,
|
||||
flags: {CfileFlag.External})
|
||||
flags: {CfileFlag.External},
|
||||
customArgs: n[3].strVal)
|
||||
extccomp.addExternalFileToCompile(g.config, cf)
|
||||
of "link":
|
||||
extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
|
||||
|
||||
@@ -219,7 +219,11 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
of kUnknownNamedParam: candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||
of kUnknownNamedParam:
|
||||
if nArg == nil:
|
||||
candidates.add("\n unknown named parameter")
|
||||
else:
|
||||
candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
|
||||
of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param")
|
||||
of kExtraArg: candidates.add("\n extra argument given")
|
||||
@@ -318,7 +322,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
|
||||
sym = nextOverloadIter(o, c, f)
|
||||
|
||||
let ident = considerQuotedIdent(c, f, n).s
|
||||
if nfDotField in n.flags and nfExplicitCall notin n.flags:
|
||||
if {nfDotField, nfExplicitCall} * n.flags == {nfDotField}:
|
||||
let sym = n[1].typ.sym
|
||||
var typeHint = ""
|
||||
if sym == nil:
|
||||
|
||||
@@ -38,6 +38,7 @@ type
|
||||
mappingExists*: bool
|
||||
mapping*: TIdTable
|
||||
caseContext*: seq[tuple[n: PNode, idx: int]]
|
||||
localBindStmts*: seq[PNode]
|
||||
|
||||
TMatchedConcept* = object
|
||||
candidateType*: PType
|
||||
|
||||
@@ -1207,6 +1207,9 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
|
||||
# not sure the symbol really ends up being used:
|
||||
# var len = 0 # but won't be called
|
||||
# genericThatUsesLen(x) # marked as taking a closure?
|
||||
if hasWarn(c.config, warnResultUsed):
|
||||
message(c.config, n.info, warnResultUsed)
|
||||
|
||||
of skGenericParam:
|
||||
onUse(n.info, s)
|
||||
if s.typ.kind == tyStatic:
|
||||
@@ -2415,7 +2418,7 @@ proc checkPar(c: PContext; n: PNode): TParKind =
|
||||
for i in 0..<n.len:
|
||||
if result == paTupleFields:
|
||||
if (n[i].kind != nkExprColonExpr) or
|
||||
n[i][0].kind notin {nkSym, nkIdent}:
|
||||
n[i][0].kind notin {nkSym, nkIdent, nkAccQuoted}:
|
||||
localError(c.config, n[i].info, errNamedExprExpected)
|
||||
return paNone
|
||||
else:
|
||||
@@ -2830,6 +2833,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
for i in 0..<n.len:
|
||||
n[i] = semExpr(c, n[i])
|
||||
of nkComesFrom: discard "ignore the comes from information for now"
|
||||
of nkMixinStmt: discard
|
||||
of nkBindStmt:
|
||||
if c.p != nil:
|
||||
c.p.localBindStmts.add n
|
||||
else:
|
||||
localError(c.config, n.info, "invalid context for 'bind' statement: " &
|
||||
renderTree(n, {renderNoComments}))
|
||||
else:
|
||||
localError(c.config, n.info, "invalid expression: " &
|
||||
renderTree(n, {renderNoComments}))
|
||||
|
||||
@@ -23,7 +23,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
result = newNode(nkEmpty)
|
||||
return
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
|
||||
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = copyNode(n)
|
||||
of nkIdent, nkSym:
|
||||
result = n
|
||||
let ident = considerQuotedIdent(c.c, n)
|
||||
@@ -52,8 +52,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
|
||||
if n.kind == nkContinueStmt:
|
||||
localError(c.c.config, n.info,
|
||||
"'continue' not supported in a 'fields' loop")
|
||||
result = copyNode(n)
|
||||
newSons(result, n.len)
|
||||
result = shallowCopy(n)
|
||||
for i in 0..<n.len:
|
||||
result[i] = instFieldLoopBody(c, n[i], forLoop)
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
of mCard: result = newIntNodeT(toInt128(nimsets.cardSet(g.config, a)), n, g)
|
||||
of mBitnotI:
|
||||
if n.typ.isUnsigned:
|
||||
result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(n.typ.size)), n, g)
|
||||
result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(getSize(g.config, n.typ))), n, g)
|
||||
else:
|
||||
result = newIntNodeT(bitnot(getInt(a)), n, g)
|
||||
of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g)
|
||||
@@ -276,23 +276,23 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, g)
|
||||
of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, g)
|
||||
of mAddU:
|
||||
let val = maskBytes(getInt(a) + getInt(b), int(n.typ.size))
|
||||
let val = maskBytes(getInt(a) + getInt(b), int(getSize(g.config, n.typ)))
|
||||
result = newIntNodeT(val, n, g)
|
||||
of mSubU:
|
||||
let val = maskBytes(getInt(a) - getInt(b), int(n.typ.size))
|
||||
let val = maskBytes(getInt(a) - getInt(b), int(getSize(g.config, n.typ)))
|
||||
result = newIntNodeT(val, n, g)
|
||||
# echo "subU: ", val, " n: ", n, " result: ", val
|
||||
of mMulU:
|
||||
let val = maskBytes(getInt(a) * getInt(b), int(n.typ.size))
|
||||
let val = maskBytes(getInt(a) * getInt(b), int(getSize(g.config, n.typ)))
|
||||
result = newIntNodeT(val, n, g)
|
||||
of mModU:
|
||||
let argA = maskBytes(getInt(a), int(a.typ.size))
|
||||
let argB = maskBytes(getInt(b), int(a.typ.size))
|
||||
let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ)))
|
||||
let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ)))
|
||||
if argB != Zero:
|
||||
result = newIntNodeT(argA mod argB, n, g)
|
||||
of mDivU:
|
||||
let argA = maskBytes(getInt(a), int(a.typ.size))
|
||||
let argB = maskBytes(getInt(b), int(a.typ.size))
|
||||
let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ)))
|
||||
let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ)))
|
||||
if argB != Zero:
|
||||
result = newIntNodeT(argA div argB, n, g)
|
||||
of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, g)
|
||||
@@ -715,6 +715,9 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
|
||||
result.typ = n.typ
|
||||
of nkBracketExpr: result = foldArrayAccess(m, n, g)
|
||||
of nkDotExpr: result = foldFieldAccess(m, n, g)
|
||||
of nkCheckedFieldExpr:
|
||||
assert n[0].kind == nkDotExpr
|
||||
result = foldFieldAccess(m, n[0], g)
|
||||
of nkStmtListExpr:
|
||||
var i = 0
|
||||
while i <= n.len - 2:
|
||||
|
||||
@@ -319,6 +319,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
prc.typ = result
|
||||
popInfoContext(c.config)
|
||||
|
||||
proc fillMixinScope(c: PContext) =
|
||||
var p = c.p
|
||||
while p != nil:
|
||||
for bnd in p.localBindStmts:
|
||||
for n in bnd:
|
||||
addSym(c.currentScope, n.sym)
|
||||
p = p.next
|
||||
|
||||
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
info: TLineInfo): PSym =
|
||||
## Generates a new instance of a generic procedure.
|
||||
@@ -345,6 +353,10 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
result.ast = n
|
||||
pushOwner(c, result)
|
||||
|
||||
# mixin scope:
|
||||
openScope(c)
|
||||
fillMixinScope(c)
|
||||
|
||||
openScope(c)
|
||||
let gp = n[genericParamsPos]
|
||||
internalAssert c.config, gp.kind != nkEmpty
|
||||
@@ -395,6 +407,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
popProcCon(c)
|
||||
popInfoContext(c.config)
|
||||
closeScope(c) # close scope for parameters
|
||||
closeScope(c) # close scope for 'mixin' declarations
|
||||
popOwner(c)
|
||||
c.currentScope = oldScope
|
||||
discard c.friendModules.pop()
|
||||
|
||||
@@ -512,12 +512,14 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
if n[^1].kind == nkSym and n[^1].sym.kind notin {skProc, skFunc}:
|
||||
localError(c.config, n.info, "finalizer must be a direct reference to a proc")
|
||||
elif optTinyRtti in c.config.globalOptions:
|
||||
let fin = if n[^1].kind == nkLambda: n[^1][namePos].sym
|
||||
else: n[^1].sym
|
||||
# check if we converted this finalizer into a destructor already:
|
||||
let t = whereToBindTypeHook(c, n[^1].sym.typ[1].skipTypes(abstractInst+{tyRef}))
|
||||
if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == n[^1].sym:
|
||||
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
|
||||
if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == fin:
|
||||
discard "already turned this one into a finalizer"
|
||||
else:
|
||||
bindTypeHook(c, turnFinalizerIntoDestructor(c, n[^1].sym, n.info), n, attachedDestructor)
|
||||
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
|
||||
result = n
|
||||
of mDestroy:
|
||||
result = n
|
||||
@@ -543,6 +545,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
let constructed = result[1].typ.base
|
||||
if constructed.requiresInit:
|
||||
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
|
||||
of mPred:
|
||||
if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}:
|
||||
n[0].sym.magic = mSubU
|
||||
result = n
|
||||
else:
|
||||
result = n
|
||||
|
||||
|
||||
@@ -359,12 +359,17 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
||||
while objType.kind != tyObject:
|
||||
objType = objType.lastSon
|
||||
assert objType != nil
|
||||
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {})
|
||||
assert constrCtx.missingFields.len > 0
|
||||
localError(c.config, info,
|
||||
"The $1 type doesn't have a default value. The following fields must be initialized: $2.",
|
||||
[typeToString(t), listSymbolNames(constrCtx.missingFields)])
|
||||
if objType.kind == tyObject:
|
||||
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {})
|
||||
if constrCtx.missingFields.len > 0:
|
||||
localError(c.config, info,
|
||||
"The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
|
||||
elif objType.kind == tyDistinct:
|
||||
localError(c.config, info,
|
||||
"The $1 distinct type doesn't have a default value." % typeToString(t))
|
||||
else:
|
||||
assert false, "Must not enter here."
|
||||
|
||||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
var t = semTypeNode(c, n[0], nil)
|
||||
|
||||
@@ -391,7 +391,8 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||
addFactNeg(c.guards, canon(n[0], c.guards.o))
|
||||
dec c.inLoop
|
||||
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
|
||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
|
||||
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef,
|
||||
nkMixinStmt, nkBindStmt, nkExportStmt:
|
||||
discard
|
||||
else:
|
||||
analyseSons(c, n)
|
||||
|
||||
@@ -784,6 +784,8 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
useVar(tracked, n)
|
||||
if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
|
||||
tracked.owner.flags.incl sfInjectDestructors
|
||||
# bug #15038: ensure consistency
|
||||
if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
|
||||
of nkRaiseStmt:
|
||||
if n[0].kind != nkEmpty:
|
||||
n[0].info = n.info
|
||||
|
||||
@@ -161,7 +161,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
if it.len == 2:
|
||||
openScope(c)
|
||||
it[0] = forceBool(c, semExprWithType(c, it[0]))
|
||||
it[1] = semExprBranch(c, it[1])
|
||||
it[1] = semExprBranch(c, it[1], flags)
|
||||
typ = commonType(typ, it[1])
|
||||
closeScope(c)
|
||||
elif it.len == 1:
|
||||
@@ -722,7 +722,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
result = n
|
||||
let iterBase = n[^2].typ
|
||||
var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
var iterAfterVarLent = iter.skipTypes({tyLent, tyVar})
|
||||
var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar})
|
||||
# n.len == 3 means that there is one for loop variable
|
||||
# and thus no tuple unpacking:
|
||||
if iterAfterVarLent.kind != tyTuple or n.len == 3:
|
||||
@@ -1748,7 +1748,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
"signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverriden)
|
||||
of "=", "=sink":
|
||||
of "=", "=copy", "=sink":
|
||||
if s.magic == mAsgn: return
|
||||
incl(s.flags, sfUsed)
|
||||
incl(s.flags, sfOverriden)
|
||||
@@ -1770,7 +1770,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
|
||||
# attach these ops to the canonical tySequence
|
||||
obj = canonType(c, obj)
|
||||
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
|
||||
let k = if name == "=": attachedAsgn else: attachedSink
|
||||
let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
|
||||
if obj.attachedOps[k] == s:
|
||||
discard "forward declared op"
|
||||
elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags:
|
||||
@@ -1828,8 +1828,8 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
|
||||
let t = tt[col]
|
||||
if t != nil and t.kind == tyGenericInvocation:
|
||||
var x = skipTypes(t[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
|
||||
tyGenericInvocation, tyGenericBody,
|
||||
tyAlias, tySink, tyOwned})
|
||||
tyGenericInvocation, tyGenericBody,
|
||||
tyAlias, tySink, tyOwned})
|
||||
if x.kind == tyObject and t.len-1 == n[genericParamsPos].len:
|
||||
foundObj = true
|
||||
x.methods.add((col,s))
|
||||
|
||||
@@ -86,6 +86,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
|
||||
a = nextOverloadIter(o, c, n)
|
||||
|
||||
proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
|
||||
result = copyNode(n)
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
# If 'a' is an overloaded symbol, we used to use the first symbol
|
||||
@@ -99,16 +100,24 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
|
||||
let sc = symChoice(c, n, s, scClosed)
|
||||
if sc.kind == nkSym:
|
||||
toBind.incl(sc.sym.id)
|
||||
result.add sc
|
||||
else:
|
||||
for x in items(sc): toBind.incl(x.sym.id)
|
||||
for x in items(sc):
|
||||
toBind.incl(x.sym.id)
|
||||
result.add x
|
||||
else:
|
||||
illFormedAst(a, c.config)
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
|
||||
proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
|
||||
result = copyNode(n)
|
||||
var count = 0
|
||||
for i in 0..<n.len:
|
||||
toMixin.incl(considerQuotedIdent(c, n[i]).id)
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
let x = symChoice(c, n[i], nil, scForceOpen)
|
||||
inc count, x.len
|
||||
result.add x
|
||||
if count == 0:
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
|
||||
proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
|
||||
case n.kind
|
||||
|
||||
@@ -660,7 +660,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
case typ.kind
|
||||
of shouldChckCovered:
|
||||
chckCovered = true
|
||||
of tyFloat..tyFloat128, tyString, tyError:
|
||||
of tyFloat..tyFloat128, tyError:
|
||||
discard
|
||||
of tyRange:
|
||||
if skipTypes(typ[0], abstractInst).kind in shouldChckCovered:
|
||||
@@ -668,7 +668,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
of tyForward:
|
||||
errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
|
||||
elif not isOrdinalType(typ):
|
||||
localError(c.config, n[0].info, "selector must be of an ordinal type, float or string")
|
||||
localError(c.config, n[0].info, "selector must be of an ordinal type, float")
|
||||
if firstOrd(c.config, typ) != 0:
|
||||
localError(c.config, n.info, "low(" & $a[0].sym.name.s &
|
||||
") must be 0 for discriminant")
|
||||
|
||||
@@ -1086,7 +1086,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let x = typeRel(c, f, branch, flags + {trDontBind})
|
||||
if x == isNone: return isNone
|
||||
if x < result: result = x
|
||||
return
|
||||
return result
|
||||
|
||||
of tyAnd:
|
||||
# XXX: deal with the current dual meaning of tyGenericParam
|
||||
@@ -1421,7 +1421,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
let ff = rootf[i]
|
||||
let aa = roota[i]
|
||||
let res = typeRel(c, ff, aa, nextFlags)
|
||||
if res != isEqual: result = isGeneric
|
||||
if res != isNone and res != isEqual: result = isGeneric
|
||||
if res notin {isEqual, isGeneric}:
|
||||
if trNoCovariance notin flags and ff.kind == aa.kind:
|
||||
let paramFlags = rootf.base[i-1].flags
|
||||
|
||||
@@ -138,12 +138,13 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, packed: bool, a
|
||||
accum.offset = szUnknownSize
|
||||
accum.maxAlign = szUnknownSize
|
||||
else:
|
||||
# the union neds to be aligned first, before the offsets can be assigned
|
||||
# the union needs to be aligned first, before the offsets can be assigned
|
||||
accum.align(maxChildAlign)
|
||||
let accumRoot = accum # copy, because each branch should start af the same offset
|
||||
for i in 1..<n.len:
|
||||
var branchAccum = accumRoot
|
||||
var branchAccum = OffsetAccum(offset: accumRoot.offset, maxAlign: 1)
|
||||
computeObjectOffsetsFoldFunction(conf, n[i].lastSon, packed, branchAccum)
|
||||
discard finish(branchAccum)
|
||||
accum.mergeBranch(branchAccum)
|
||||
of nkRecList:
|
||||
for i, child in n.sons:
|
||||
@@ -173,9 +174,10 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var
|
||||
localError(conf, n.info, "Illegal use of ``case`` in union type.")
|
||||
of nkRecList:
|
||||
let accumRoot = accum # copy, because each branch should start af the same offset
|
||||
for i, child in n.sons:
|
||||
var branchAccum = accumRoot
|
||||
for child in n.sons:
|
||||
var branchAccum = OffsetAccum(offset: accumRoot.offset, maxAlign: 1)
|
||||
computeUnionObjectOffsetsFoldFunction(conf, child, branchAccum)
|
||||
discard finish(branchAccum)
|
||||
accum.mergeBranch(branchAccum)
|
||||
of nkSym:
|
||||
var size = szUnknownSize
|
||||
|
||||
@@ -830,13 +830,6 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode =
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
|
||||
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
|
||||
# symbols that expand to a complex constant (array, etc.) should not be
|
||||
# inlined, unless it's the empty array:
|
||||
result = orig.kind == nkSym and
|
||||
cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and
|
||||
cnst.len != 0
|
||||
|
||||
proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
|
||||
result = n
|
||||
for i in 0..<n.safeLen:
|
||||
@@ -1000,7 +993,7 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
of nkConstSection:
|
||||
# do not replace ``const c = 3`` with ``const 3 = 3``
|
||||
return transformConstSection(c, n)
|
||||
of nkTypeSection, nkTypeOfExpr:
|
||||
of nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt:
|
||||
# no need to transform type sections:
|
||||
return n
|
||||
of nkVarSection, nkLetSection:
|
||||
@@ -1035,6 +1028,11 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||
return n
|
||||
of nkExceptBranch:
|
||||
result = transformExceptBranch(c, n)
|
||||
of nkCheckedFieldExpr:
|
||||
result = transformSons(c, n)
|
||||
if result[0].kind != nkDotExpr:
|
||||
# simplfied beyond a dot expression --> simplify further.
|
||||
result = result[0]
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
when false:
|
||||
@@ -1090,15 +1088,15 @@ proc liftDeferAux(n: PNode) =
|
||||
if n[i].kind == nkDefer:
|
||||
let deferPart = newNodeI(nkFinally, n[i].info)
|
||||
deferPart.add n[i][0]
|
||||
var tryStmt = newNodeI(nkTryStmt, n[i].info)
|
||||
var body = newNodeI(n.kind, n[i].info)
|
||||
var tryStmt = newNodeIT(nkTryStmt, n[i].info, n.typ)
|
||||
var body = newNodeIT(n.kind, n[i].info, n.typ)
|
||||
if i < last:
|
||||
body.sons = n.sons[(i+1)..last]
|
||||
tryStmt.add body
|
||||
tryStmt.add deferPart
|
||||
n[i] = tryStmt
|
||||
n.sons.setLen(i+1)
|
||||
n.typ = n[i].typ
|
||||
n.typ = tryStmt.typ
|
||||
goOn = true
|
||||
break
|
||||
for i in 0..n.safeLen-1:
|
||||
|
||||
@@ -169,3 +169,10 @@ proc flattenStmts*(n: PNode): PNode =
|
||||
proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
|
||||
result = newNodeI(k, n.info, b-a+1)
|
||||
for i in 0..b-a: result[i] = n[i+a]
|
||||
|
||||
proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} =
|
||||
# symbols that expand to a complex constant (array, etc.) should not be
|
||||
# inlined, unless it's the empty array:
|
||||
result = orig.kind != cnst.kind and
|
||||
cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and
|
||||
cnst.len > ord(cnst.kind == nkObjConstr)
|
||||
|
||||
@@ -71,7 +71,7 @@ const
|
||||
tyInferred, tySink, tyLent, tyOwned}
|
||||
# typedescX is used if we're sure tyTypeDesc should be included (or skipped)
|
||||
typedescPtrs* = abstractPtrs + {tyTypeDesc}
|
||||
typedescInst* = abstractInst + {tyTypeDesc, tyOwned}
|
||||
typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
|
||||
|
||||
proc invalidGenericInst*(f: PType): bool =
|
||||
result = f.kind == tyGenericInst and lastSon(f) == nil
|
||||
|
||||
@@ -2086,6 +2086,7 @@ proc execute(c: PCtx, start: int): PNode =
|
||||
result = rawExecute(c, start, tos).regToNode
|
||||
|
||||
proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
|
||||
c.loopIterations = c.config.maxLoopIterationsVM
|
||||
if sym.kind in routineKinds:
|
||||
if sym.typ.len-1 != args.len:
|
||||
localError(c.config, sym.info,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# types that use the 'node' field; the reason is that slots are
|
||||
# re-used in a register based VM. Example:
|
||||
#
|
||||
#..code-block:: nim
|
||||
#.. code-block:: nim
|
||||
# let s = a & b # no matter what, create fresh node
|
||||
# s = a & b # no matter what, keep the node
|
||||
#
|
||||
@@ -247,6 +247,8 @@ proc freeTemp(c: PCtx; r: TRegister) =
|
||||
proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister =
|
||||
# if register pressure is high, we re-use more aggressively:
|
||||
let c = cc.prc
|
||||
# we could also customize via the following (with proper caching in ConfigRef):
|
||||
# let highRegisterPressure = cc.config.getConfigVar("vm.highRegisterPressure", "40").parseInt
|
||||
if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister):
|
||||
for i in 0..c.maxSlots-n:
|
||||
if not c.slots[i].inUse:
|
||||
@@ -1521,14 +1523,14 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
let tmp = c.genx(ri)
|
||||
c.preventFalseAlias(le[0], opcWrObj, objR, idx, tmp)
|
||||
c.freeTemp(tmp)
|
||||
c.freeTemp(idx)
|
||||
# c.freeTemp(idx) # BUGFIX, see nkDotExpr
|
||||
c.freeTemp(objR)
|
||||
of nkDotExpr:
|
||||
let dest = c.genx(le[0], {gfNode})
|
||||
let idx = genField(c, le[1])
|
||||
let tmp = c.genx(ri)
|
||||
c.preventFalseAlias(le, opcWrObj, dest, idx, tmp)
|
||||
c.freeTemp(idx)
|
||||
# c.freeTemp(idx) # BUGFIX: idx is an immediate (field position), not a register
|
||||
c.freeTemp(tmp)
|
||||
c.freeTemp(dest)
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
@@ -1957,14 +1959,6 @@ proc genProc*(c: PCtx; s: PSym): int
|
||||
|
||||
proc matches(s: PSym; x: string): bool =
|
||||
let y = x.split('.')
|
||||
var s = s
|
||||
for i in 1..y.len:
|
||||
if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"):
|
||||
return false
|
||||
s = s.owner
|
||||
result = true
|
||||
|
||||
proc matches(s: PSym; y: varargs[string]): bool =
|
||||
var s = s
|
||||
for i in 1..y.len:
|
||||
if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"):
|
||||
@@ -2025,11 +2019,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
elif s.kind == skMethod:
|
||||
localError(c.config, n.info, "cannot call method " & s.name.s &
|
||||
" at compile time")
|
||||
elif matches(s, "stdlib", "marshal", "to"):
|
||||
elif matches(s, "stdlib.marshal.to"):
|
||||
# XXX marshal load&store should not be opcodes, but use the
|
||||
# general callback mechanisms.
|
||||
genMarshalLoad(c, n, dest)
|
||||
elif matches(s, "stdlib", "marshal", "$$"):
|
||||
elif matches(s, "stdlib.marshal.$$"):
|
||||
genMarshalStore(c, n, dest)
|
||||
else:
|
||||
genCall(c, n, dest)
|
||||
@@ -2112,7 +2106,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
else:
|
||||
dest = tmp0
|
||||
of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
|
||||
nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt:
|
||||
nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
|
||||
nkMixinStmt, nkBindStmt:
|
||||
unused(c, n, dest)
|
||||
of nkStringToCString, nkCStringToString:
|
||||
gen(c, n[0], dest)
|
||||
|
||||
@@ -3,3 +3,13 @@
|
||||
when defined(nimHasCppDefine):
|
||||
cppDefine "errno"
|
||||
cppDefine "unix"
|
||||
|
||||
# mangle the macro names in nimbase.h
|
||||
cppDefine "NAN_INFINITY"
|
||||
cppDefine "INF"
|
||||
cppDefine "NAN"
|
||||
|
||||
when defined(windows) and not defined(booting):
|
||||
# Avoid some rare stack corruption while using exceptions with a SEH-enabled
|
||||
# toolchain: https://github.com/nim-lang/Nim/pull/19197
|
||||
switch("define", "nimRawSetjmp")
|
||||
|
||||
@@ -136,15 +136,10 @@ doc.body_toc_group = """
|
||||
</div>
|
||||
<div id="global-links">
|
||||
<ul class="simple-boot">
|
||||
<li>
|
||||
<a href="manual.html">Manual</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="lib.html">Standard library</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="theindex.html">Index</a>
|
||||
</li>
|
||||
<li><a href="manual.html">Manual</a></li>
|
||||
<li><a href="lib.html">Standard library</a></li>
|
||||
<li><a href="theindex.html">Index</a></li>
|
||||
<li><a href="https://nim-lang.github.io/Nim/">devel</a>, <a href="https://nim-lang.org/documentation.html">stable</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchInputDiv">
|
||||
|
||||
@@ -42,6 +42,8 @@ Advanced options:
|
||||
produce hints or errors for Nim identifiers that
|
||||
do not adhere to Nim's official style guide
|
||||
https://nim-lang.org/docs/nep1.html
|
||||
--styleCheck:usages only enforce consistent spellings of identifiers,
|
||||
do not enforce the style on declarations
|
||||
--showAllMismatches:on|off
|
||||
show all mismatching candidates in overloading
|
||||
resolution
|
||||
|
||||
@@ -47,9 +47,11 @@ The commands to compile to either C, C++ or Objective-C are:
|
||||
The most significant difference between these commands is that if you look
|
||||
into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m``
|
||||
files, other than that all of them will produce a native binary for your
|
||||
project. This allows you to take the generated code and place it directly
|
||||
into a project using any of these languages. Here are some typical command
|
||||
line invocations::
|
||||
project. This allows you to take the generated code and place it directly
|
||||
into a project using any of these languages. Here are some typical command-
|
||||
line invocations:
|
||||
|
||||
.. code:: cmd
|
||||
|
||||
$ nim c hallo.nim
|
||||
$ nim cpp hallo.nim
|
||||
@@ -108,8 +110,8 @@ Nim code calling the backend
|
||||
Nim code can interface with the backend through the `Foreign function
|
||||
interface <manual.html#foreign-function-interface>`_ mainly through the
|
||||
`importc pragma <manual.html#foreign-function-interface-importc-pragma>`_.
|
||||
The ``importc`` pragma is the *generic* way of making backend symbols available
|
||||
in Nim and is available in all the target backends (JavaScript too). The C++
|
||||
The `importc` pragma is the *generic* way of making backend symbols available
|
||||
in Nim and is available in all the target backends (JavaScript too). The C++
|
||||
or Objective-C backends have their respective `ImportCpp
|
||||
<manual.html#implementation-specific-pragmas-importcpp-pragma>`_ and
|
||||
`ImportObjC <manual.html#implementation-specific-pragmas-importobjc-pragma>`_
|
||||
@@ -230,11 +232,6 @@ Also, C code requires you to specify a forward declaration for functions or
|
||||
the compiler will assume certain types for the return value and parameters
|
||||
which will likely make your program crash at runtime.
|
||||
|
||||
The Nim compiler can generate a C interface header through the ``--header``
|
||||
command line switch. The generated header will contain all the exported
|
||||
symbols and the ``NimMain`` proc which you need to call before any other
|
||||
Nim code.
|
||||
|
||||
|
||||
Nim invocation example from C
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -253,9 +250,10 @@ Create a ``maths.c`` file with the following content:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "fib.h"
|
||||
#include <stdio.h>
|
||||
|
||||
extern int fib(int a);
|
||||
|
||||
int main(void)
|
||||
{
|
||||
NimMain();
|
||||
@@ -268,13 +266,14 @@ Now you can run the following Unix like commands to first generate C sources
|
||||
form the Nim code, then link them into a static binary along your main C
|
||||
program::
|
||||
|
||||
$ nim c --noMain --noLinking --header:fib.h fib.nim
|
||||
$ gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c
|
||||
.. code:: cmd
|
||||
|
||||
nim c --noMain --noLinking fib.nim
|
||||
gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c
|
||||
|
||||
The first command runs the Nim compiler with three special options to avoid
|
||||
generating a ``main()`` function in the generated files, avoid linking the
|
||||
object files into a final binary, and explicitly generate a header file for C
|
||||
integration. All the generated files are placed into the ``nimcache``
|
||||
generating a `main()`:c: function in the generated files and to avoid linking the
|
||||
object files into a final binary. All the generated files are placed into the ``nimcache``
|
||||
directory. That's why the next command compiles the ``maths.c`` source plus
|
||||
all the ``.c`` files form ``nimcache``. In addition to this path, you also
|
||||
have to tell the C compiler where to find Nim's ``nimbase.h`` header file.
|
||||
@@ -282,12 +281,12 @@ have to tell the C compiler where to find Nim's ``nimbase.h`` header file.
|
||||
Instead of depending on the generation of the individual ``.c`` files you can
|
||||
also ask the Nim compiler to generate a statically linked library::
|
||||
|
||||
$ nim c --app:staticLib --noMain --header fib.nim
|
||||
$ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c
|
||||
nim c --app:staticLib --noMain fib.nim
|
||||
gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c
|
||||
|
||||
The Nim compiler will handle linking the source files generated in the
|
||||
``nimcache`` directory into the ``libfib.nim.a`` static library, which you can
|
||||
then link into your C program. Note that these commands are generic and will
|
||||
then link into your C program. Note that these commands are generic and will
|
||||
vary for each system. For instance, on Linux systems you will likely need to
|
||||
use ``-ldl`` too to link in required dlopen functionality.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ written as:
|
||||
dealloc(x.data)
|
||||
x.data = nil
|
||||
|
||||
proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
|
||||
proc `=copy`*[T](a: var myseq[T]; b: myseq[T]) =
|
||||
# do nothing for self-assignments:
|
||||
if a.data == b.data: return
|
||||
`=destroy`(a)
|
||||
@@ -134,7 +134,7 @@ not free the resources afterwards by setting the object to its default value
|
||||
default value is written as ``wasMoved(x)``. When not provided the compiler
|
||||
is using a combination of `=destroy` and `copyMem` instead. This is efficient
|
||||
hence users rarely need to implement their own `=sink` operator, it is enough to
|
||||
provide `=destroy` and `=`, compiler will take care about the rest.
|
||||
provide `=destroy` and `=copy`, compiler will take care about the rest.
|
||||
|
||||
The prototype of this hook for a type ``T`` needs to be:
|
||||
|
||||
@@ -156,10 +156,10 @@ The general pattern in ``=sink`` looks like:
|
||||
How self-assignments are handled is explained later in this document.
|
||||
|
||||
|
||||
`=` (copy) hook
|
||||
`=copy` hook
|
||||
---------------
|
||||
|
||||
The ordinary assignment in Nim conceptually copies the values. The ``=`` hook
|
||||
The ordinary assignment in Nim conceptually copies the values. The ``=copy`` hook
|
||||
is called for assignments that couldn't be transformed into ``=sink``
|
||||
operations.
|
||||
|
||||
@@ -167,21 +167,21 @@ The prototype of this hook for a type ``T`` needs to be:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=`(dest: var T; source: T)
|
||||
proc `=copy`(dest: var T; source: T)
|
||||
|
||||
|
||||
The general pattern in ``=`` looks like:
|
||||
The general pattern in ``=copy`` looks like:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=`(dest: var T; source: T) =
|
||||
proc `=copy`(dest: var T; source: T) =
|
||||
# protect against self-assignments:
|
||||
if dest.field != source.field:
|
||||
`=destroy`(dest)
|
||||
dest.field = duplicateResource(source.field)
|
||||
|
||||
|
||||
The ``=`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
||||
The ``=copy`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
||||
that otherwise would lead to a copy is prevented at compile-time.
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ Swap
|
||||
====
|
||||
|
||||
The need to check for self-assignments and also the need to destroy previous
|
||||
objects inside ``=`` and ``=sink`` is a strong indicator to treat
|
||||
objects inside ``=copy`` and ``=sink`` is a strong indicator to treat
|
||||
``system.swap`` as a builtin primitive of its own that simply swaps every
|
||||
field in the involved objects via ``copyMem`` or a comparable mechanism.
|
||||
In other words, ``swap(a, b)`` is **not** implemented
|
||||
@@ -320,7 +320,7 @@ not destroyed at the scope exit, but at the proc exit.
|
||||
|
||||
x = y
|
||||
------------------ (copy)
|
||||
`=`(x, y)
|
||||
`=copy`(x, y)
|
||||
|
||||
|
||||
f_sink(g())
|
||||
@@ -330,7 +330,7 @@ not destroyed at the scope exit, but at the proc exit.
|
||||
|
||||
f_sink(notLastReadOf y)
|
||||
-------------------------- (copy-to-sink)
|
||||
(let tmp; `=`(tmp, y);
|
||||
(let tmp; `=copy`(tmp, y);
|
||||
f_sink(tmp))
|
||||
|
||||
|
||||
|
||||
@@ -2483,9 +2483,10 @@ matches) is preferred:
|
||||
Overloading based on 'var T'
|
||||
----------------------------
|
||||
|
||||
If the formal parameter ``f`` is of type ``var T`` in addition to the ordinary
|
||||
type checking, the argument is checked to be an `l-value`:idx:. ``var T``
|
||||
matches better than just ``T`` then.
|
||||
If the formal parameter ``f`` is of type ``var T``
|
||||
in addition to the ordinary type checking,
|
||||
the argument is checked to be an `l-value`:idx:.
|
||||
``var T`` matches better than just ``T`` then.
|
||||
|
||||
.. code-block:: nim
|
||||
proc sayHi(x: int): string =
|
||||
@@ -4788,6 +4789,50 @@ scope is the default.
|
||||
``bind`` statements only make sense in templates and generics.
|
||||
|
||||
|
||||
Delegating bind statements
|
||||
--------------------------
|
||||
|
||||
The following example outlines a problem that can arise when generic
|
||||
instantiations cross multiple different modules:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
# module A
|
||||
proc genericA*[T](x: T) =
|
||||
mixin init
|
||||
init(x)
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
import C
|
||||
|
||||
# module B
|
||||
proc genericB*[T](x: T) =
|
||||
# Without the `bind init` statement C's init proc is
|
||||
# not available when `genericB` is instantiated:
|
||||
bind init
|
||||
genericA(x)
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
# module C
|
||||
type O = object
|
||||
proc init*(x: var O) = discard
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
# module main
|
||||
import B, C
|
||||
|
||||
genericB O()
|
||||
|
||||
In module B has an `init` proc from module C in its scope that is not
|
||||
taken into account when `genericB` is instantiated which leads to the
|
||||
instantiation of `genericA`. The solution is to `forward`:idx these
|
||||
symbols by a `bind` statement inside `genericB`.
|
||||
|
||||
|
||||
Templates
|
||||
=========
|
||||
|
||||
@@ -5714,12 +5759,12 @@ avoid ambiguity when there are multiple modules with the same path.
|
||||
There are two pseudo directories:
|
||||
|
||||
1. ``std``: The ``std`` pseudo directory is the abstract location of Nim's standard
|
||||
library. For example, the syntax ``import std / strutils`` is used to unambiguously
|
||||
refer to the standard library's ``strutils`` module.
|
||||
library. For example, the syntax ``import std / strutils`` is used to unambiguously
|
||||
refer to the standard library's ``strutils`` module.
|
||||
2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to a Nimble
|
||||
package. However, for technical details that lie outside of the scope of this document
|
||||
its semantics are: *Use the search path to look for module name but ignore the standard
|
||||
library locations*. In other words, it is the opposite of ``std``.
|
||||
package. However, for technical details that lie outside of the scope of this document
|
||||
its semantics are: *Use the search path to look for module name but ignore the standard
|
||||
library locations*. In other words, it is the opposite of ``std``.
|
||||
|
||||
|
||||
From import statement
|
||||
@@ -5981,10 +6026,10 @@ noSideEffect pragma
|
||||
The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side
|
||||
effects. This means that the proc/iterator only changes locations that are
|
||||
reachable from its parameters and the return value only depends on the
|
||||
arguments. If none of its parameters have the type ``var T``
|
||||
or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static
|
||||
error to mark a proc/iterator to have no side effect if the compiler cannot
|
||||
verify this.
|
||||
arguments. If none of its parameters have the type ``var T`` or ``ref T``
|
||||
or ``ptr T`` this means no locations are modified. It is a static error to
|
||||
mark a proc/iterator to have no side effect if the compiler cannot verify
|
||||
this.
|
||||
|
||||
As a special semantic rule, the built-in `debugEcho
|
||||
<system.html#debugEcho,varargs[typed,]>`_ pretends to be free of side effects,
|
||||
@@ -6607,6 +6652,14 @@ with the project:
|
||||
has changed. One can use the ``-f`` command line option to force recompilation
|
||||
of the file.
|
||||
|
||||
Since 1.4 the `compile` pragma is also available with this syntax:
|
||||
|
||||
.. code-block:: Nim
|
||||
{.compile("myfile.cpp", "--custom flags here").}
|
||||
|
||||
As can be seen in the example, this new variant allows for custom flags
|
||||
that are passed to the C compiler when the file is recompiled.
|
||||
|
||||
|
||||
Link pragma
|
||||
-----------
|
||||
|
||||
16
doc/nimc.rst
16
doc/nimc.rst
@@ -149,6 +149,22 @@ ignored too. ``--define:FOO`` and ``--define:foo`` are identical.
|
||||
Compile time symbols starting with the ``nim`` prefix are reserved for the
|
||||
implementation and should not be used elsewhere.
|
||||
|
||||
========================== ============================================
|
||||
Name Description
|
||||
========================== ============================================
|
||||
nimStdSetjmp Use the standard `setjmp()/longjmp()` library
|
||||
functions for setjmp-based exceptions. This is
|
||||
the default on most platforms.
|
||||
nimSigSetjmp Use `sigsetjmp()/siglongjmp()` for setjmp-based exceptions.
|
||||
nimRawSetjmp Use `_setjmp()/_longjmp()` on POSIX and `_setjmp()/longjmp()`
|
||||
on Windows, for setjmp-based exceptions. It's the default on
|
||||
BSDs and BSD-like platforms, where it's significantly faster
|
||||
than the standard functions.
|
||||
nimBuiltinSetjmp Use `__builtin_setjmp()/__builtin_longjmp()` for setjmp-based
|
||||
exceptions. This will not work if an exception is being thrown
|
||||
and caught inside the same procedure. Useful for benchmarking.
|
||||
========================== ============================================
|
||||
|
||||
|
||||
Configuration files
|
||||
-------------------
|
||||
|
||||
36
koch.nim
36
koch.nim
@@ -10,7 +10,7 @@
|
||||
#
|
||||
|
||||
const
|
||||
NimbleStableCommit = "63695f490728e3935692c29f3d71944d83bb1e83" # master
|
||||
NimbleStableCommit = "d13f3b8ce288b4dc8c34c219a4e050aaeaf43fc9" # master
|
||||
|
||||
when not defined(windows):
|
||||
const
|
||||
@@ -136,35 +136,11 @@ proc bundleC2nim(args: string) =
|
||||
|
||||
proc bundleNimbleExe(latest: bool, args: string) =
|
||||
let commit = if latest: "HEAD" else: NimbleStableCommit
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/nimble.git", commit = commit)
|
||||
cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
|
||||
commit = commit, allowBundled = true)
|
||||
# installer.ini expects it under $nim/bin
|
||||
nimCompile("dist/nimble/src/nimble.nim",
|
||||
options = "-d:release --nilseqs:on " & args)
|
||||
|
||||
proc buildNimble(latest: bool, args: string) =
|
||||
# if koch is used for a tar.xz, build the dist/nimble we shipped
|
||||
# with the tarball:
|
||||
var installDir = "dist/nimble"
|
||||
if not latest and dirExists(installDir) and not dirExists("dist/nimble/.git"):
|
||||
discard "don't do the git dance"
|
||||
else:
|
||||
if not dirExists("dist/nimble/.git"):
|
||||
if dirExists(installDir):
|
||||
var id = 0
|
||||
while dirExists("dist/nimble" & $id):
|
||||
inc id
|
||||
installDir = "dist/nimble" & $id
|
||||
# consider using/adapting cloneDependency
|
||||
exec("git clone -q https://github.com/nim-lang/nimble.git " & installDir)
|
||||
withDir(installDir):
|
||||
if latest:
|
||||
exec("git checkout -f master")
|
||||
exec("git pull")
|
||||
else:
|
||||
exec("git fetch")
|
||||
exec("git checkout " & NimbleStableCommit)
|
||||
nimCompile(installDir / "src/nimble.nim",
|
||||
options = "--noNimblePath --nilseqs:on -d:release " & args)
|
||||
options = "-d:release --noNimblePath " & args)
|
||||
|
||||
proc bundleNimsuggest(args: string) =
|
||||
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
|
||||
@@ -664,13 +640,13 @@ when isMainModule:
|
||||
of "temp": temp(op.cmdLineRest)
|
||||
of "xtemp": xtemp(op.cmdLineRest)
|
||||
of "wintools": bundleWinTools(op.cmdLineRest)
|
||||
of "nimble": buildNimble(latest, op.cmdLineRest)
|
||||
of "nimble": bundleNimbleExe(latest, op.cmdLineRest)
|
||||
of "nimsuggest": bundleNimsuggest(op.cmdLineRest)
|
||||
of "toolsnonimble":
|
||||
buildTools(op.cmdLineRest)
|
||||
of "tools":
|
||||
buildTools(op.cmdLineRest)
|
||||
buildNimble(latest, op.cmdLineRest)
|
||||
bundleNimbleExe(latest, op.cmdLineRest)
|
||||
of "pushcsource", "pushcsources": pushCsources()
|
||||
of "valgrind": valgrind(op.cmdLineRest)
|
||||
of "c2nim": bundleC2nim(op.cmdLineRest)
|
||||
|
||||
@@ -1262,7 +1262,15 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
|
||||
else:
|
||||
badNodeKind someProc, "body="
|
||||
|
||||
proc basename*(a: NimNode): NimNode {.compileTime, benign.}
|
||||
proc basename*(a: NimNode): NimNode {.raises: [].} =
|
||||
## Pull an identifier from prefix/postfix expressions.
|
||||
case a.kind
|
||||
of nnkIdent: result = a
|
||||
of nnkPostfix, nnkPrefix: result = a[1]
|
||||
of nnkPragmaExpr: result = basename(a[0])
|
||||
else:
|
||||
error("Do not know how to get basename of (" & treeRepr(a) & ")\n" &
|
||||
repr(a), a)
|
||||
|
||||
proc `$`*(node: NimNode): string {.compileTime.} =
|
||||
## Get the string of an identifier node.
|
||||
@@ -1325,16 +1333,6 @@ proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} =
|
||||
a[i + 1] = a[i]
|
||||
a[pos] = b
|
||||
|
||||
proc basename*(a: NimNode): NimNode =
|
||||
## Pull an identifier from prefix/postfix expressions.
|
||||
case a.kind
|
||||
of nnkIdent: result = a
|
||||
of nnkPostfix, nnkPrefix: result = a[1]
|
||||
of nnkPragmaExpr: result = basename(a[0])
|
||||
else:
|
||||
error("Do not know how to get basename of (" & treeRepr(a) & ")\n" &
|
||||
repr(a), a)
|
||||
|
||||
proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
|
||||
case a.kind
|
||||
of nnkIdent:
|
||||
|
||||
@@ -271,7 +271,7 @@ proc setPointer*(x: Any, y: pointer) =
|
||||
## ``akPointer``, ``akSequence``.
|
||||
assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer,
|
||||
tySequence, tyProc}
|
||||
cast[ppointer](x.value)[] = y
|
||||
genericAssign(x.value, y, x.rawType)
|
||||
|
||||
proc fieldsAux(p: pointer, n: ptr TNimNode,
|
||||
ret: var seq[tuple[name: cstring, any: Any]]) =
|
||||
|
||||
@@ -242,7 +242,7 @@ proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool
|
||||
try:
|
||||
db.prepareFetchDirect(query, args)
|
||||
var
|
||||
rCnt = -1
|
||||
rCnt:TSqlLen = -1
|
||||
res = SQLRowCount(db.stmt, rCnt)
|
||||
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||
if res != SQL_SUCCESS: dbError(db)
|
||||
@@ -461,10 +461,10 @@ proc execAffectedRows*(db: var DbConn, query: SqlQuery,
|
||||
var q = dbFormat(query, args)
|
||||
db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
|
||||
rawExec(db, query, args)
|
||||
var rCnt = -1
|
||||
var rCnt:TSqlLen = -1
|
||||
db.sqlCheck(SQLRowCount(db.hDb, rCnt))
|
||||
properFreeResult(SQL_HANDLE_STMT, db.stmt)
|
||||
result = rCnt
|
||||
result = rCnt.int64
|
||||
|
||||
proc close*(db: var DbConn) {.
|
||||
tags: [WriteDbEffect], raises: [].} =
|
||||
@@ -489,7 +489,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
## Currently the database parameter is ignored,
|
||||
## but included to match ``open()`` in the other db_xxxxx library modules.
|
||||
var
|
||||
val: TSqlInteger = SQL_OV_ODBC3
|
||||
val = SQL_OV_ODBC3
|
||||
resLen = 0
|
||||
result = (hDb: nil, env: nil, stmt: nil)
|
||||
# allocate environment handle
|
||||
@@ -497,7 +497,7 @@ proc open*(connection, user, password, database: string): DbConn {.
|
||||
if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
|
||||
res = SQLSetEnvAttr(result.env,
|
||||
SQL_ATTR_ODBC_VERSION.TSqlInteger,
|
||||
val, resLen.TSqlInteger)
|
||||
cast[SqlPointer](val.addr), resLen.TSqlInteger)
|
||||
if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
|
||||
# allocate hDb handle
|
||||
res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
|
||||
|
||||
@@ -62,7 +62,6 @@ from pcre import nil
|
||||
import nre/private/util
|
||||
import tables
|
||||
from strutils import `%`
|
||||
from math import ceil
|
||||
import options
|
||||
from unicode import runeLenAt
|
||||
|
||||
@@ -531,7 +530,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt
|
||||
raise RegexInternalError(msg : "Unknown internal error: " & $execRet)
|
||||
|
||||
proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] =
|
||||
## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the
|
||||
## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the
|
||||
## string.
|
||||
##
|
||||
runnableExamples:
|
||||
@@ -541,11 +540,11 @@ proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[R
|
||||
return str.matchImpl(pattern, start, endpos, pcre.ANCHORED)
|
||||
|
||||
iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch =
|
||||
## Works the same as ` ``find(...)`` <#proc-find>`_, but finds every
|
||||
## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every
|
||||
## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not
|
||||
## ``"22", "22", "22"``.
|
||||
##
|
||||
## Arguments are the same as ` ``find(...)`` <#proc-find>`_
|
||||
## Arguments are the same as `find(...)<#find,string,Regex,int>`_
|
||||
##
|
||||
## Variants:
|
||||
##
|
||||
@@ -624,7 +623,7 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string]
|
||||
## Splits the string with the given regex. This works according to the
|
||||
## rules that Perl and Javascript use.
|
||||
##
|
||||
## ``start`` behaves the same as in ` ``find(...)`` <#proc-find>`_.
|
||||
## ``start`` behaves the same as in `find(...)<#find,string,Regex,int>`_.
|
||||
##
|
||||
runnableExamples:
|
||||
# - If the match is zero-width, then the string is still split:
|
||||
@@ -734,8 +733,25 @@ proc replace*(str: string, pattern: Regex, sub: string): string =
|
||||
replaceImpl(str, pattern,
|
||||
formatStr(sub, match.captures[name], match.captures[id - 1]))
|
||||
|
||||
let SpecialCharMatcher = re"([\\+*?[^\]$(){}=!<>|:-])"
|
||||
proc escapeRe*(str: string): string =
|
||||
## Escapes the string so it doesn’t match any special characters.
|
||||
proc escapeRe*(str: string): string {.gcsafe.} =
|
||||
## Escapes the string so it doesn't match any special characters.
|
||||
## Incompatible with the Extra flag (``X``).
|
||||
str.replace(SpecialCharMatcher, "\\$1")
|
||||
##
|
||||
## Escaped char: `\ + * ? [ ^ ] $ ( ) { } = ! < > | : -`
|
||||
runnableExamples:
|
||||
doAssert escapeRe("fly+wind") == "fly\\+wind"
|
||||
doAssert escapeRe("!") == "\\!"
|
||||
doAssert escapeRe("nim*") == "nim\\*"
|
||||
|
||||
#([\\+*?[^\]$(){}=!<>|:-])
|
||||
const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(',
|
||||
')', '{', '}', '=', '!', '<', '>', '|', ':',
|
||||
'-'}
|
||||
|
||||
for c in items(str):
|
||||
case c
|
||||
of SpecialCharMatcher:
|
||||
result.add("\\")
|
||||
result.add(c)
|
||||
else:
|
||||
result.add(c)
|
||||
|
||||
@@ -1211,14 +1211,19 @@ else:
|
||||
let newLength = max(len(curList), InitCallbackListSize)
|
||||
var newList = newSeqOfCap[Callback](newLength)
|
||||
|
||||
var eventsExtinguished = false
|
||||
for cb in curList:
|
||||
if eventsExtinguished:
|
||||
newList.add(cb)
|
||||
continue
|
||||
if not cb(fd):
|
||||
# Callback wants to be called again.
|
||||
newList.add(cb)
|
||||
# This callback has returned with EAGAIN, so we don't need to
|
||||
# call any other callbacks as they are all waiting for the same event
|
||||
# on the same fd.
|
||||
break
|
||||
# We do need to ensure they are called again though.
|
||||
eventsExtinguished = true
|
||||
|
||||
withData(selector, fd.int, fdData) do:
|
||||
# Descriptor is still present in the queue.
|
||||
@@ -1887,3 +1892,25 @@ proc waitFor*[T](fut: Future[T]): T =
|
||||
poll()
|
||||
|
||||
fut.read
|
||||
|
||||
proc activeDescriptors*(): int {.inline.} =
|
||||
## Returns the current number of active file descriptors for the current
|
||||
## event loop. This is a cheap operation that does not involve a system call.
|
||||
when defined(windows):
|
||||
result = getGlobalDispatcher().handles.len
|
||||
elif not defined(nimdoc):
|
||||
result = getGlobalDispatcher().selector.count
|
||||
|
||||
when defined(posix):
|
||||
import posix
|
||||
|
||||
proc maxDescriptors*(): int {.raises: OSError.} =
|
||||
## Returns the maximum number of active file descriptors for the current
|
||||
## process. This involves a system call.
|
||||
when defined(windows):
|
||||
result = 16_700_000
|
||||
else:
|
||||
var fdLim: RLimit
|
||||
if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
|
||||
raiseOSError(osLastError())
|
||||
result = int(fdLim.rlim_cur) - 1
|
||||
|
||||
@@ -134,7 +134,7 @@ proc expectReply(ftp: AsyncFtpClient): Future[TaintedString] {.async.} =
|
||||
var line = await ftp.csock.recvLine()
|
||||
result = TaintedString(line)
|
||||
var count = 0
|
||||
while line[3] == '-':
|
||||
while line.len > 3 and line[3] == '-':
|
||||
## Multi-line reply.
|
||||
line = await ftp.csock.recvLine()
|
||||
string(result).add("\n" & line)
|
||||
@@ -146,7 +146,11 @@ proc send*(ftp: AsyncFtpClient, m: string): Future[TaintedString] {.async.} =
|
||||
## Send a message to the server, and wait for a primary reply.
|
||||
## ``\c\L`` is added for you.
|
||||
##
|
||||
## You need to make sure that the message ``m`` doesn't contain any newline
|
||||
## characters. Failing to do so will raise ``AssertionDefect``.
|
||||
##
|
||||
## **Note:** The server may return multiple lines of coded replies.
|
||||
doAssert(not m.contains({'\c', '\L'}), "message shouldn't contain any newline characters")
|
||||
await ftp.csock.send(m & "\c\L")
|
||||
return await ftp.expectReply()
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
import os, tables, strutils, times, heapqueue, options, deques, cstrutils
|
||||
|
||||
import "system/stacktraces"
|
||||
|
||||
# TODO: This shouldn't need to be included, but should ideally be exported.
|
||||
type
|
||||
CallbackFunc = proc () {.closure, gcsafe.}
|
||||
@@ -296,49 +298,72 @@ proc `callback=`*[T](future: Future[T],
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
future.callback = proc () = cb(future)
|
||||
|
||||
template getFilenameProcname(entry: StackTraceEntry): (string, string) =
|
||||
when compiles(entry.filenameStr) and compiles(entry.procnameStr):
|
||||
# We can't rely on "entry.filename" and "entry.procname" still being valid
|
||||
# cstring pointers, because the "string.data" buffers they pointed to might
|
||||
# be already garbage collected (this entry being a non-shallow copy,
|
||||
# "entry.filename" no longer points to "entry.filenameStr.data", but to the
|
||||
# buffer of the original object).
|
||||
(entry.filenameStr, entry.procnameStr)
|
||||
else:
|
||||
($entry.filename, $entry.procname)
|
||||
|
||||
proc getHint(entry: StackTraceEntry): string =
|
||||
## We try to provide some hints about stack trace entries that the user
|
||||
## may not be familiar with, in particular calls inside the stdlib.
|
||||
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
result = ""
|
||||
if entry.procname == cstring"processPendingCallbacks":
|
||||
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
|
||||
if procname == "processPendingCallbacks":
|
||||
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
|
||||
return "Executes pending callbacks"
|
||||
elif entry.procname == cstring"poll":
|
||||
if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0:
|
||||
elif procname == "poll":
|
||||
if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0:
|
||||
return "Processes asynchronous completion events"
|
||||
|
||||
if entry.procname.endsWith(NimAsyncContinueSuffix):
|
||||
if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0:
|
||||
if procname.endsWith(NimAsyncContinueSuffix):
|
||||
if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0:
|
||||
return "Resumes an async procedure"
|
||||
|
||||
proc `$`*(entries: seq[StackTraceEntry]): string =
|
||||
proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
|
||||
when defined(nimStackTraceOverride):
|
||||
let entries = addDebuggingInfo(stackTraceEntries)
|
||||
else:
|
||||
let entries = stackTraceEntries
|
||||
|
||||
result = ""
|
||||
# Find longest filename & line number combo for alignment purposes.
|
||||
var longestLeft = 0
|
||||
for entry in entries:
|
||||
if entry.procname.isNil: continue
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
let left = $entry.filename & $entry.line
|
||||
if left.len > longestLeft:
|
||||
longestLeft = left.len
|
||||
if procname == "": continue
|
||||
|
||||
let leftLen = filename.len + len($entry.line)
|
||||
if leftLen > longestLeft:
|
||||
longestLeft = leftLen
|
||||
|
||||
var indent = 2
|
||||
# Format the entries.
|
||||
for entry in entries:
|
||||
if entry.procname.isNil:
|
||||
if entry.line == -10:
|
||||
let (filename, procname) = getFilenameProcname(entry)
|
||||
|
||||
if procname == "":
|
||||
if entry.line == reraisedFromBegin:
|
||||
result.add(spaces(indent) & "#[\n")
|
||||
indent.inc(2)
|
||||
else:
|
||||
elif entry.line == reraisedFromEnd:
|
||||
indent.dec(2)
|
||||
result.add(spaces(indent) & "]#\n")
|
||||
continue
|
||||
|
||||
let left = "$#($#)" % [$entry.filename, $entry.line]
|
||||
let left = "$#($#)" % [filename, $entry.line]
|
||||
result.add((spaces(indent) & "$#$# $#\n") % [
|
||||
left,
|
||||
spaces(longestLeft - left.len + 2),
|
||||
$entry.procname
|
||||
procname
|
||||
])
|
||||
let hint = getHint(entry)
|
||||
if hint.len > 0:
|
||||
@@ -362,9 +387,9 @@ proc injectStacktrace[T](future: Future[T]) =
|
||||
newMsg.add($entries)
|
||||
|
||||
newMsg.add("Exception message: " & exceptionMsg & "\n")
|
||||
newMsg.add("Exception type:")
|
||||
|
||||
# # For debugging purposes
|
||||
# newMsg.add("Exception type:")
|
||||
# for entry in getStackTraceEntries(future.error):
|
||||
# newMsg.add "\n" & $entry
|
||||
future.error.msg = newMsg
|
||||
|
||||
@@ -11,24 +11,36 @@
|
||||
##
|
||||
## This HTTP server has not been designed to be used in production, but
|
||||
## for testing applications locally. Because of this, when deploying your
|
||||
## application you should use a reverse proxy (for example nginx) instead of
|
||||
## allowing users to connect directly to this server.
|
||||
## application in production you should use a reverse proxy (for example nginx)
|
||||
## instead of allowing users to connect directly to this server.
|
||||
##
|
||||
## Basic usage
|
||||
## ===========
|
||||
## Example
|
||||
## =======
|
||||
##
|
||||
## This example will create an HTTP server on port 8080. The server will
|
||||
## respond to all requests with a ``200 OK`` response code and "Hello World"
|
||||
## as the response body.
|
||||
##
|
||||
## .. code-block::nim
|
||||
## import asynchttpserver, asyncdispatch
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## var server = newAsyncHttpServer()
|
||||
## proc cb(req: Request) {.async.} =
|
||||
## await req.respond(Http200, "Hello World")
|
||||
## import asynchttpserver, asyncdispatch
|
||||
##
|
||||
## waitFor server.serve(Port(8080), cb)
|
||||
## proc main {.async.} =
|
||||
## var server = newAsyncHttpServer()
|
||||
## proc cb(req: Request) {.async.} =
|
||||
## let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT",
|
||||
## "Content-type": "text/plain; charset=utf-8"}
|
||||
## await req.respond(Http200, "Hello World", headers.newHttpHeaders())
|
||||
##
|
||||
## server.listen Port(8080)
|
||||
## while true:
|
||||
## if server.shouldAcceptRequest():
|
||||
## asyncCheck server.acceptRequest(cb)
|
||||
## else:
|
||||
## poll()
|
||||
##
|
||||
## asyncCheck main()
|
||||
## runForever()
|
||||
|
||||
import asyncnet, asyncdispatch, parseutils, uri, strutils
|
||||
import httpcore
|
||||
@@ -58,14 +70,12 @@ type
|
||||
reuseAddr: bool
|
||||
reusePort: bool
|
||||
maxBody: int ## The maximum content-length that will be read for the body.
|
||||
maxFDs: int
|
||||
|
||||
proc newAsyncHttpServer*(reuseAddr = true, reusePort = false,
|
||||
maxBody = 8388608): AsyncHttpServer =
|
||||
## Creates a new ``AsyncHttpServer`` instance.
|
||||
new result
|
||||
result.reuseAddr = reuseAddr
|
||||
result.reusePort = reusePort
|
||||
result.maxBody = maxBody
|
||||
result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody)
|
||||
|
||||
proc addHeaders(msg: var string, headers: HttpHeaders) =
|
||||
for k, v in headers:
|
||||
@@ -294,13 +304,9 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string
|
||||
)
|
||||
if not retry: break
|
||||
|
||||
proc serve*(server: AsyncHttpServer, port: Port,
|
||||
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
|
||||
address = "") {.async.} =
|
||||
## Starts the process of listening for incoming HTTP connections on the
|
||||
## specified address and port.
|
||||
##
|
||||
## When a request is made by a client the specified callback will be called.
|
||||
proc listen*(server: AsyncHttpServer; port: Port; address = "") =
|
||||
## Listen to the given port and address.
|
||||
server.maxFDs = maxDescriptors()
|
||||
server.socket = newAsyncSocket()
|
||||
if server.reuseAddr:
|
||||
server.socket.setSockOpt(OptReuseAddr, true)
|
||||
@@ -309,9 +315,44 @@ proc serve*(server: AsyncHttpServer, port: Port,
|
||||
server.socket.bindAddr(port, address)
|
||||
server.socket.listen()
|
||||
|
||||
proc shouldAcceptRequest*(server: AsyncHttpServer;
|
||||
assumedDescriptorsPerRequest = 5): bool {.inline.} =
|
||||
## Returns true if the process's current number of opened file
|
||||
## descriptors is still within the maximum limit and so it's reasonable to
|
||||
## accept yet another request.
|
||||
result = assumedDescriptorsPerRequest < 0 or
|
||||
(activeDescriptors() + assumedDescriptorsPerRequest < server.maxFDs)
|
||||
|
||||
proc acceptRequest*(server: AsyncHttpServer,
|
||||
callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} =
|
||||
## Accepts a single request. Write an explicit loop around this proc so that
|
||||
## errors can be handled properly.
|
||||
var (address, client) = await server.socket.acceptAddr()
|
||||
asyncCheck processClient(server, client, address, callback)
|
||||
|
||||
proc serve*(server: AsyncHttpServer, port: Port,
|
||||
callback: proc (request: Request): Future[void] {.closure, gcsafe.},
|
||||
address = "";
|
||||
assumedDescriptorsPerRequest = -1) {.async.} =
|
||||
## Starts the process of listening for incoming HTTP connections on the
|
||||
## specified address and port.
|
||||
##
|
||||
## When a request is made by a client the specified callback will be called.
|
||||
##
|
||||
## If `assumedDescriptorsPerRequest` is 0 or greater the server cares about
|
||||
## the process's maximum file descriptor limit. It then ensures that the
|
||||
## process still has the resources for `assumedDescriptorsPerRequest`
|
||||
## file descriptors before accepting a connection.
|
||||
##
|
||||
## You should prefer to call `acceptRequest` instead with a custom server
|
||||
## loop so that you're in control over the error handling and logging.
|
||||
listen server, port, address
|
||||
while true:
|
||||
var (address, client) = await server.socket.acceptAddr()
|
||||
asyncCheck processClient(server, client, address, callback)
|
||||
if shouldAcceptRequest(server, assumedDescriptorsPerRequest):
|
||||
var (address, client) = await server.socket.acceptAddr()
|
||||
asyncCheck processClient(server, client, address, callback)
|
||||
else:
|
||||
poll()
|
||||
#echo(f.isNil)
|
||||
#echo(f.repr)
|
||||
|
||||
@@ -320,7 +361,7 @@ proc close*(server: AsyncHttpServer) =
|
||||
server.socket.close()
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
proc main {.async.} =
|
||||
var server = newAsyncHttpServer()
|
||||
proc cb(req: Request) {.async.} =
|
||||
#echo(req.reqMethod, " ", req.url)
|
||||
@@ -329,6 +370,12 @@ when not defined(testing) and isMainModule:
|
||||
"Content-type": "text/plain; charset=utf-8"}
|
||||
await req.respond(Http200, "Hello World", headers.newHttpHeaders())
|
||||
|
||||
asyncCheck server.serve(Port(5555), cb)
|
||||
runForever()
|
||||
main()
|
||||
server.listen Port(5555)
|
||||
while true:
|
||||
if server.shouldAcceptRequest():
|
||||
asyncCheck server.acceptRequest(cb)
|
||||
else:
|
||||
poll()
|
||||
|
||||
asyncCheck main()
|
||||
runForever()
|
||||
|
||||
@@ -198,7 +198,8 @@ proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} =
|
||||
for i in 1 ..< len(params):
|
||||
expectKind(params[i], nnkIdentDefs)
|
||||
if params[i][1].kind == nnkBracketExpr and
|
||||
params[i][1][0].eqIdent("futurevar"):
|
||||
params[i][1][0].eqIdent(FutureVar.astToStr):
|
||||
## eqIdent: first char is case sensitive!!!
|
||||
result.add(params[i][0])
|
||||
|
||||
proc isInvalidReturnType(typeName: string): bool =
|
||||
|
||||
@@ -691,8 +691,12 @@ elif defined(nimdoc):
|
||||
|
||||
proc close*(socket: AsyncSocket) =
|
||||
## Closes the socket.
|
||||
if socket.closed: return
|
||||
|
||||
defer:
|
||||
socket.fd.AsyncFD.closeSocket()
|
||||
socket.closed = true # TODO: Add extra debugging checks for this.
|
||||
|
||||
when defineSsl:
|
||||
if socket.isSsl:
|
||||
let res = SSL_shutdown(socket.sslHandle)
|
||||
@@ -701,7 +705,6 @@ proc close*(socket: AsyncSocket) =
|
||||
discard
|
||||
elif res != 1:
|
||||
raiseSSLError()
|
||||
socket.closed = true # TODO: Add extra debugging checks for this.
|
||||
|
||||
when defineSsl:
|
||||
proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) =
|
||||
|
||||
@@ -110,7 +110,11 @@ proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) =
|
||||
resFut.complete(res)
|
||||
|
||||
# If the saved callback isn't nil then let's call it.
|
||||
if not savedCb.isNil: savedCb()
|
||||
if not savedCb.isNil:
|
||||
if fs.queue.len > 0:
|
||||
savedCb()
|
||||
else:
|
||||
future.cb = savedCb
|
||||
|
||||
if future.queue.len > 0 or future.finished:
|
||||
newCb(future)
|
||||
|
||||
@@ -230,11 +230,11 @@ proc decode*(s: string): string =
|
||||
|
||||
template inputChar(x: untyped) =
|
||||
let x = int decodeTable[ord(s[inputIndex])]
|
||||
inc inputIndex
|
||||
if x == invalidChar:
|
||||
raise newException(ValueError,
|
||||
"Invalid base64 format character `" & s[inputIndex] &
|
||||
"` (ord " & $s[inputIndex].ord & ") at location " & $inputIndex & ".")
|
||||
inc inputIndex
|
||||
|
||||
template outputChar(x: untyped) =
|
||||
result[outputIndex] = char(x and 255)
|
||||
|
||||
@@ -17,6 +17,7 @@ include "system/inclrtl"
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
from os import absolutePath
|
||||
else:
|
||||
import os, osproc
|
||||
|
||||
@@ -25,16 +26,21 @@ const osOpenCmd* =
|
||||
## Alias for the operating system specific *"open"* command,
|
||||
## ``"open"`` on OSX, MacOS and Windows, ``"xdg-open"`` on Linux, BSD, etc.
|
||||
|
||||
proc prepare(s: string): string =
|
||||
if s.contains("://"):
|
||||
result = s
|
||||
else:
|
||||
result = "file://" & absolutePath(s)
|
||||
|
||||
template openDefaultBrowserImpl(url: string) =
|
||||
proc openDefaultBrowserImpl(url: string) =
|
||||
when defined(windows):
|
||||
var o = newWideCString(osOpenCmd)
|
||||
var u = newWideCString(url)
|
||||
var u = newWideCString(prepare url)
|
||||
discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
|
||||
elif defined(macosx):
|
||||
discard execShellCmd(osOpenCmd & " " & quoteShell(url))
|
||||
discard execShellCmd(osOpenCmd & " " & quoteShell(prepare url))
|
||||
else:
|
||||
var u = quoteShell(url)
|
||||
var u = quoteShell(prepare url)
|
||||
if execShellCmd(osOpenCmd & " " & u) == 0: return
|
||||
for b in getEnv("BROWSER").string.split(PathSep):
|
||||
try:
|
||||
|
||||
@@ -32,12 +32,7 @@
|
||||
import strutils, os, strtabs, cookies, uri
|
||||
export uri.encodeUrl, uri.decodeUrl
|
||||
|
||||
proc handleHexChar(c: char, x: var int) {.inline.} =
|
||||
case c
|
||||
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
|
||||
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
|
||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||
else: assert(false)
|
||||
include includes/decode_helpers
|
||||
|
||||
proc addXmlChar(dest: var string, c: char) {.inline.} =
|
||||
case c
|
||||
@@ -93,40 +88,27 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string =
|
||||
iterator decodeData*(data: string): tuple[key, value: TaintedString] =
|
||||
## Reads and decodes CGI data and yields the (name, value) pairs the
|
||||
## data consists of.
|
||||
proc parseData(data: string, i: int, field: var string): int =
|
||||
result = i
|
||||
while result < data.len:
|
||||
case data[result]
|
||||
of '%': add(field, decodePercent(data, result))
|
||||
of '+': add(field, ' ')
|
||||
of '=', '&': break
|
||||
else: add(field, data[result])
|
||||
inc(result)
|
||||
|
||||
var i = 0
|
||||
var name = ""
|
||||
var value = ""
|
||||
# decode everything in one pass:
|
||||
while i < data.len:
|
||||
setLen(name, 0) # reuse memory
|
||||
while i < data.len:
|
||||
case data[i]
|
||||
of '%':
|
||||
var x = 0
|
||||
handleHexChar(data[i+1], x)
|
||||
handleHexChar(data[i+2], x)
|
||||
inc(i, 2)
|
||||
add(name, chr(x))
|
||||
of '+': add(name, ' ')
|
||||
of '=', '&': break
|
||||
else: add(name, data[i])
|
||||
inc(i)
|
||||
i = parseData(data, i, name)
|
||||
if i >= data.len or data[i] != '=': cgiError("'=' expected")
|
||||
inc(i) # skip '='
|
||||
setLen(value, 0) # reuse memory
|
||||
while i < data.len:
|
||||
case data[i]
|
||||
of '%':
|
||||
var x = 0
|
||||
if i+2 < data.len:
|
||||
handleHexChar(data[i+1], x)
|
||||
handleHexChar(data[i+2], x)
|
||||
inc(i, 2)
|
||||
add(value, chr(x))
|
||||
of '+': add(value, ' ')
|
||||
of '&', '\0': break
|
||||
else: add(value, data[i])
|
||||
inc(i)
|
||||
i = parseData(data, i, value)
|
||||
yield (name.TaintedString, value.TaintedString)
|
||||
if i < data.len:
|
||||
if data[i] == '&': inc(i)
|
||||
|
||||
@@ -140,6 +140,10 @@ proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool =
|
||||
withLock t:
|
||||
hasKeyOrPutImpl(enlarge)
|
||||
|
||||
template tabMakeEmpty(i) = t.data[i].hcode = 0
|
||||
template tabCellEmpty(i) = isEmpty(t.data[i].hcode)
|
||||
template tabCellHash(i) = t.data[i].hcode
|
||||
|
||||
proc withKey*[A, B](t: var SharedTable[A, B], key: A,
|
||||
mapper: proc(key: A, val: var B, pairExists: var bool)) =
|
||||
## Computes a new mapping for the ``key`` with the specified ``mapper``
|
||||
@@ -181,7 +185,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
|
||||
if pairExists:
|
||||
mapper(t.data[index].key, t.data[index].val, pairExists)
|
||||
if not pairExists:
|
||||
delImplIdx(t, index)
|
||||
delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash)
|
||||
else:
|
||||
var val: B
|
||||
mapper(key, val, pairExists)
|
||||
@@ -202,7 +206,7 @@ proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) =
|
||||
proc del*[A, B](t: var SharedTable[A, B], key: A) =
|
||||
## deletes `key` from hash table `t`.
|
||||
withLock t:
|
||||
delImpl()
|
||||
delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash)
|
||||
|
||||
proc len*[A, B](t: var SharedTable[A, B]): int =
|
||||
## number of elements in `t`
|
||||
|
||||
@@ -78,22 +78,52 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} =
|
||||
maybeRehashPutImpl(enlarge)
|
||||
else: result = true
|
||||
|
||||
template delImplIdx(t, i) =
|
||||
# delImplIdx is KnuthV3 Algo6.4R adapted to i=i+1 (from i=i-1) which has come to
|
||||
# be called "back shift delete". It shifts elements in the collision cluster of
|
||||
# a victim backward to make things as-if the victim were never inserted in the
|
||||
# first place. This is desirable to keep things "ageless" after many deletes.
|
||||
# It is trickier than you might guess since initial probe (aka "home") locations
|
||||
# of keys in a cluster may collide and since table addresses wrap around.
|
||||
#
|
||||
# A before-after diagram might look like ('.' means empty):
|
||||
# slot: 0 1 2 3 4 5 6 7
|
||||
# before(1)
|
||||
# hash1: 6 7 . 3 . 5 5 6 ; Really hash() and msk
|
||||
# data1: E F . A . B C D ; About to delete C @index 6
|
||||
# after(2)
|
||||
# hash2: 7 . . 3 . 5 6 6 ; Really hash() and msk
|
||||
# data2: F . . A . B D E ; After deletion of C
|
||||
#
|
||||
# This lowers total search depth over the whole table from 1+1+2+2+2+2=10 to 7.
|
||||
# Had the victim been B@5, C would need back shifting to slot 5. Total depth is
|
||||
# always lowered by at least 1, e.g. victim A@3. This is all quite fast when
|
||||
# empty slots are frequent (also needed to keep insert/miss searches fast) and
|
||||
# hash() is either fast or avoided (via `.hcode`). It need not compare keys.
|
||||
#
|
||||
# delImplIdx realizes the above transformation, but only works for dense Linear
|
||||
# Probing, nextTry(h)=h+1. This is not an important limitation since that's the
|
||||
# fastest sequence on any CPU made since the 1980s. { Performance analysis often
|
||||
# overweights "key cmp" neglecting cache behavior, giving bad ideas how big/slow
|
||||
# tables behave (when perf matters most!). Comparing hcode first means usually
|
||||
# only 1 key cmp is needed for *any* seq. Timing only predictable activity,
|
||||
# small tables, and/or integer keys often perpetuates such bad ideas. }
|
||||
|
||||
template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) =
|
||||
let msk = maxHash(t)
|
||||
if i >= 0:
|
||||
dec(t.counter)
|
||||
block outer:
|
||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||
var j = i # The correctness of this depends on (h+1) in nextTry
|
||||
var r = j # though may be adaptable to other simple sequences.
|
||||
t.data[i].hcode = 0 # mark current EMPTY
|
||||
makeEmpty(i) # mark current EMPTY
|
||||
t.data[i].key = default(type(t.data[i].key))
|
||||
t.data[i].val = default(type(t.data[i].val))
|
||||
while true:
|
||||
i = (i + 1) and msk # increment mod table size
|
||||
if isEmpty(t.data[i].hcode): # end of collision cluster; So all done
|
||||
if cellEmpty(i): # end of collision cluster; So all done
|
||||
break outer
|
||||
r = t.data[i].hcode and msk # "home" location of key@i
|
||||
r = cellHash(i) and msk # initial probe index for key@slot i
|
||||
if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||
break
|
||||
when defined(js):
|
||||
@@ -101,10 +131,19 @@ template delImplIdx(t, i) =
|
||||
else:
|
||||
t.data[j] = move(t.data[i]) # data[j] will be marked EMPTY next loop
|
||||
|
||||
template delImpl() {.dirty.} =
|
||||
template delImpl(makeEmpty, cellEmpty, cellHash) {.dirty.} =
|
||||
var hc: Hash
|
||||
var i = rawGet(t, key, hc)
|
||||
delImplIdx(t, i)
|
||||
delImplIdx(t, i, makeEmpty, cellEmpty, cellHash)
|
||||
|
||||
template delImplNoHCode(makeEmpty, cellEmpty, cellHash) {.dirty.} =
|
||||
if t.dataLen > 0:
|
||||
var i: Hash = hash(key) and maxHash(t)
|
||||
while not cellEmpty(i):
|
||||
if t.data[i].key == key:
|
||||
delImplIdx(t, i, makeEmpty, cellEmpty, cellHash)
|
||||
break
|
||||
i = nextTry(i, maxHash(t))
|
||||
|
||||
template clearImpl() {.dirty.} =
|
||||
for i in 0 ..< t.dataLen:
|
||||
|
||||
@@ -499,6 +499,10 @@ proc add*[A, B](t: var Table[A, B], key: A, val: B) =
|
||||
## (key, value) pair in the table without introducing duplicates.
|
||||
addImpl(enlarge)
|
||||
|
||||
template tabMakeEmpty(i) = t.data[i].hcode = 0
|
||||
template tabCellEmpty(i) = isEmpty(t.data[i].hcode)
|
||||
template tabCellHash(i) = t.data[i].hcode
|
||||
|
||||
proc del*[A, B](t: var Table[A, B], key: A) =
|
||||
## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist.
|
||||
##
|
||||
@@ -512,7 +516,7 @@ proc del*[A, B](t: var Table[A, B], key: A) =
|
||||
a.del('z')
|
||||
doAssert a == {'b': 9, 'c': 13}.toTable
|
||||
|
||||
delImpl()
|
||||
delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash)
|
||||
|
||||
proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool =
|
||||
## Deletes the ``key`` from the table.
|
||||
@@ -540,7 +544,7 @@ proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool =
|
||||
result = index >= 0
|
||||
if result:
|
||||
val = move(t.data[index].val)
|
||||
delImplIdx(t, index)
|
||||
delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash)
|
||||
|
||||
proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool {.inline.} =
|
||||
## Alias for:
|
||||
@@ -1804,7 +1808,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B]
|
||||
assert b == {'a': 5, 'b': 9}.newOrderedTable
|
||||
|
||||
result = newOrderedTable[A, B](rightSize(pairs.len))
|
||||
for key, val in items(pairs): result.add(key, val)
|
||||
for key, val in items(pairs): result[key] = val
|
||||
|
||||
|
||||
proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
|
||||
@@ -2218,19 +2222,6 @@ proc enlarge[A](t: var CountTable[A]) =
|
||||
if t.data[i].val != 0: ctRawInsert(t, n, move t.data[i].key, move t.data[i].val)
|
||||
swap(t.data, n)
|
||||
|
||||
proc remove[A](t: var CountTable[A], key: A) =
|
||||
var n: seq[tuple[key: A, val: int]]
|
||||
newSeq(n, len(t.data))
|
||||
var removed: bool
|
||||
for i in countup(0, high(t.data)):
|
||||
if t.data[i].val != 0:
|
||||
if t.data[i].key != key:
|
||||
ctRawInsert(t, n, move t.data[i].key, move t.data[i].val)
|
||||
else:
|
||||
removed = true
|
||||
swap(t.data, n)
|
||||
if removed: dec(t.counter)
|
||||
|
||||
proc rawGet[A](t: CountTable[A], key: A): int =
|
||||
if t.data.len == 0:
|
||||
return -1
|
||||
@@ -2244,7 +2235,7 @@ template ctget(t, key, default: untyped): untyped =
|
||||
var index = rawGet(t, key)
|
||||
result = if index >= 0: t.data[index].val else: default
|
||||
|
||||
proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1)
|
||||
proc inc*[A](t: var CountTable[A], key: A, val = 1)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@@ -2287,6 +2278,10 @@ proc `[]`*[A](t: CountTable[A], key: A): int =
|
||||
assert(not t.isSorted, "CountTable must not be used after sorting")
|
||||
ctget(t, key, 0)
|
||||
|
||||
template cntMakeEmpty(i) = t.data[i].val = 0
|
||||
template cntCellEmpty(i) = t.data[i].val == 0
|
||||
template cntCellHash(i) = hash(t.data[i].key)
|
||||
|
||||
proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
||||
## Inserts a ``(key, value)`` pair into ``t``.
|
||||
##
|
||||
@@ -2297,7 +2292,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
||||
assert(not t.isSorted, "CountTable must not be used after sorting")
|
||||
assert val >= 0
|
||||
if val == 0:
|
||||
t.remove(key)
|
||||
delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash)
|
||||
else:
|
||||
let h = rawGet(t, key)
|
||||
if h >= 0:
|
||||
@@ -2305,11 +2300,8 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) =
|
||||
else:
|
||||
insertImpl()
|
||||
|
||||
proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) =
|
||||
proc inc*[A](t: var CountTable[A], key: A, val = 1) =
|
||||
## Increments ``t[key]`` by ``val`` (default: 1).
|
||||
##
|
||||
## ``val`` must be a positive number. If you need to decrement a value,
|
||||
## use a regular ``Table`` instead.
|
||||
runnableExamples:
|
||||
var a = toCountTable("aab")
|
||||
a.inc('a')
|
||||
@@ -2320,9 +2312,11 @@ proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) =
|
||||
var index = rawGet(t, key)
|
||||
if index >= 0:
|
||||
inc(t.data[index].val, val)
|
||||
if t.data[index].val == 0: dec(t.counter)
|
||||
if t.data[index].val == 0:
|
||||
delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash)
|
||||
else:
|
||||
insertImpl()
|
||||
if val != 0:
|
||||
insertImpl()
|
||||
|
||||
proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] =
|
||||
## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n)
|
||||
@@ -2383,8 +2377,6 @@ proc len*[A](t: CountTable[A]): int =
|
||||
proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} =
|
||||
## Deletes ``key`` from table ``t``. Does nothing if the key does not exist.
|
||||
##
|
||||
## O(n) complexity.
|
||||
##
|
||||
## See also:
|
||||
## * `pop proc<#pop,CountTable[A],A,int>`_
|
||||
## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table
|
||||
@@ -2397,7 +2389,7 @@ proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} =
|
||||
a.del('c')
|
||||
assert a == toCountTable("aa")
|
||||
|
||||
remove(t, key)
|
||||
delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash)
|
||||
|
||||
proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} =
|
||||
## Deletes the ``key`` from the table.
|
||||
@@ -2405,8 +2397,6 @@ proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).}
|
||||
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
|
||||
## unchanged.
|
||||
##
|
||||
## O(n) complexity.
|
||||
##
|
||||
## See also:
|
||||
## * `del proc<#del,CountTable[A],A>`_
|
||||
## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table
|
||||
@@ -2423,7 +2413,7 @@ proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).}
|
||||
result = index >= 0
|
||||
if result:
|
||||
val = move(t.data[index].val)
|
||||
remove(t, key)
|
||||
delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash)
|
||||
|
||||
proc clear*[A](t: var CountTable[A]) =
|
||||
## Resets the table so that it is empty.
|
||||
@@ -2644,7 +2634,7 @@ proc `[]`*[A](t: CountTableRef[A], key: A): int =
|
||||
## See also:
|
||||
## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return
|
||||
## a custom value if the key doesn't exist
|
||||
## * `mget proc<#mget,CountTableRef[A],A>`_
|
||||
## * `inc proc<#inc,CountTableRef[A],A>`_ to inc even if missing
|
||||
## * `[]= proc<#[]%3D,CountTableRef[A],A,int>`_ for inserting a new
|
||||
## (key, value) pair in the table
|
||||
## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key
|
||||
@@ -2717,8 +2707,6 @@ proc len*[A](t: CountTableRef[A]): int =
|
||||
proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} =
|
||||
## Deletes ``key`` from table ``t``. Does nothing if the key does not exist.
|
||||
##
|
||||
## O(n) complexity.
|
||||
##
|
||||
## See also:
|
||||
## * `pop proc<#pop,CountTableRef[A],A,int>`_
|
||||
## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table
|
||||
@@ -2730,8 +2718,6 @@ proc pop*[A](t: CountTableRef[A], key: A, val: var int): bool {.since: (1, 1).}
|
||||
## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
|
||||
## unchanged.
|
||||
##
|
||||
## O(n) complexity.
|
||||
##
|
||||
## See also:
|
||||
## * `del proc<#del,CountTableRef[A],A>`_
|
||||
## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table
|
||||
@@ -3016,37 +3002,37 @@ when isMainModule:
|
||||
block: #5482
|
||||
var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
var b = newOrderedTable[string, string](initialSize = 2)
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
b["wrong?"] = "foo"
|
||||
b["wrong?"] = "foo2"
|
||||
assert a == b
|
||||
|
||||
block: #5482
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = newOrderedTable[string, string](initialSize = 2)
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
b["wrong?"] = "foo"
|
||||
b["wrong?"] = "foo2"
|
||||
assert a == b
|
||||
|
||||
block: #5487
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = newOrderedTable[string, string]() # notice, default size!
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
b["wrong?"] = "foo"
|
||||
b["wrong?"] = "foo2"
|
||||
assert a == b
|
||||
|
||||
block: #5487
|
||||
var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
var b = newOrderedTable[string, string]() # notice, default size!
|
||||
b.add("wrong?", "foo")
|
||||
b.add("wrong?", "foo2")
|
||||
b["wrong?"] = "foo"
|
||||
b["wrong?"] = "foo2"
|
||||
assert a == b
|
||||
|
||||
block:
|
||||
var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
|
||||
var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable()
|
||||
var c = newOrderedTable[string, string]() # notice, default size!
|
||||
c.add("wrong?", "foo")
|
||||
c.add("wrong?", "foo2")
|
||||
c["wrong?"] = "foo"
|
||||
c["wrong?"] = "foo2"
|
||||
assert a == b
|
||||
assert a == c
|
||||
|
||||
|
||||
@@ -241,7 +241,8 @@ proc contentLength*(response: Response | AsyncResponse): int =
|
||||
##
|
||||
## A ``ValueError`` exception will be raised if the value is not an integer.
|
||||
var contentLengthHeader = response.headers.getOrDefault("Content-Length")
|
||||
return contentLengthHeader.parseInt()
|
||||
result = contentLengthHeader.parseInt()
|
||||
doAssert(result >= 0 and result <= high(int32))
|
||||
|
||||
proc lastModified*(response: Response | AsyncResponse): DateTime =
|
||||
## Retrieves the specified response's last modified time.
|
||||
@@ -316,7 +317,7 @@ proc getDefaultSSL(): SslContext =
|
||||
result = defaultSslContext
|
||||
when defined(ssl):
|
||||
if result == nil:
|
||||
defaultSslContext = newContext(verifyMode = CVerifyNone)
|
||||
defaultSslContext = newContext(verifyMode = CVerifyPeer)
|
||||
result = defaultSslContext
|
||||
doAssert result != nil, "failure to initialize the SSL context"
|
||||
|
||||
@@ -1018,6 +1019,11 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string,
|
||||
##
|
||||
## This procedure will follow redirects up to a maximum number of redirects
|
||||
## specified in ``client.maxRedirects``.
|
||||
##
|
||||
## You need to make sure that the ``url`` doesn't contain any newline
|
||||
## characters. Failing to do so will raise ``AssertionDefect``.
|
||||
doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters")
|
||||
|
||||
result = await client.requestAux(url, httpMethod, body, headers, multipart)
|
||||
|
||||
var lastURL = url
|
||||
@@ -1134,15 +1140,15 @@ proc downloadFile*(client: HttpClient, url: string, filename: string) =
|
||||
client.getBody = true
|
||||
let resp = client.get(url)
|
||||
|
||||
if resp.code.is4xx or resp.code.is5xx:
|
||||
raise newException(HttpRequestError, resp.status)
|
||||
|
||||
client.bodyStream = newFileStream(filename, fmWrite)
|
||||
if client.bodyStream.isNil:
|
||||
fileError("Unable to open file")
|
||||
parseBody(client, resp.headers, resp.version)
|
||||
client.bodyStream.close()
|
||||
|
||||
if resp.code.is4xx or resp.code.is5xx:
|
||||
raise newException(HttpRequestError, resp.status)
|
||||
|
||||
proc downloadFile*(client: AsyncHttpClient, url: string,
|
||||
filename: string): Future[void] =
|
||||
proc downloadFileEx(client: AsyncHttpClient,
|
||||
|
||||
24
lib/pure/includes/decode_helpers.nim
Normal file
24
lib/pure/includes/decode_helpers.nim
Normal file
@@ -0,0 +1,24 @@
|
||||
# Include file that implements 'decodePercent' and friends. Do not import it!
|
||||
|
||||
proc handleHexChar(c: char, x: var int, f: var bool) {.inline.} =
|
||||
case c
|
||||
of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
|
||||
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
|
||||
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
|
||||
else: f = true
|
||||
|
||||
proc decodePercent(s: string, i: var int): char =
|
||||
## Converts `%xx` hexadecimal to the charracter with ordinal number `xx`.
|
||||
##
|
||||
## If `xx` is not a valid hexadecimal value, it is left intact: only the
|
||||
## leading `%` is returned as-is, and `xx` characters will be processed in the
|
||||
## next step (e.g. in `uri.decodeUrl`) as regular characters.
|
||||
result = '%'
|
||||
if i+2 < s.len:
|
||||
var x = 0
|
||||
var failed = false
|
||||
handleHexChar(s[i+1], x, failed)
|
||||
handleHexChar(s[i+2], x, failed)
|
||||
if not failed:
|
||||
result = chr(x)
|
||||
inc(i, 2)
|
||||
@@ -114,7 +114,7 @@ proc osLastError*(): OSErrorCode {.sideEffect.} =
|
||||
when defined(nimscript):
|
||||
discard
|
||||
elif defined(windows):
|
||||
result = OSErrorCode(getLastError())
|
||||
result = cast[OSErrorCode](getLastError())
|
||||
else:
|
||||
result = OSErrorCode(errno)
|
||||
{.pop.}
|
||||
|
||||
@@ -55,7 +55,7 @@ when hasThreadSupport:
|
||||
maxFD: int
|
||||
numFD: int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
count: int
|
||||
count*: int
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
type
|
||||
@@ -64,7 +64,7 @@ else:
|
||||
maxFD: int
|
||||
numFD: int
|
||||
fds: seq[SelectorKey[T]]
|
||||
count: int
|
||||
count*: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
type
|
||||
SelectEventImpl = object
|
||||
|
||||
@@ -30,7 +30,7 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly):
|
||||
proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t,
|
||||
newp: pointer, newplen: csize_t): cint
|
||||
{.importc: "sysctl",header: """#include <sys/types.h>
|
||||
#include <sys/sysctl.h>"""}
|
||||
#include <sys/sysctl.h>""".}
|
||||
elif defined(netbsd) or defined(openbsd):
|
||||
# OpenBSD and NetBSD don't have KERN_MAXFILESPERPROC, so we are using
|
||||
# KERN_MAXFILES, because KERN_MAXFILES is always bigger,
|
||||
@@ -39,7 +39,7 @@ elif defined(netbsd) or defined(openbsd):
|
||||
proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t,
|
||||
newp: pointer, newplen: csize_t): cint
|
||||
{.importc: "sysctl",header: """#include <sys/param.h>
|
||||
#include <sys/sysctl.h>"""}
|
||||
#include <sys/sysctl.h>""".}
|
||||
|
||||
when hasThreadSupport:
|
||||
type
|
||||
@@ -48,7 +48,7 @@ when hasThreadSupport:
|
||||
maxFD: int
|
||||
changes: ptr SharedArray[KEvent]
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
count: int
|
||||
count*: int
|
||||
changesLock: Lock
|
||||
changesSize: int
|
||||
changesLength: int
|
||||
@@ -61,7 +61,7 @@ else:
|
||||
maxFD: int
|
||||
changes: seq[KEvent]
|
||||
fds: seq[SelectorKey[T]]
|
||||
count: int
|
||||
count*: int
|
||||
sock: cint
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ when hasThreadSupport:
|
||||
pollcnt: int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
pollfds: ptr SharedArray[TPollFd]
|
||||
count: int
|
||||
count*: int
|
||||
lock: Lock
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
@@ -31,7 +31,7 @@ else:
|
||||
pollcnt: int
|
||||
fds: seq[SelectorKey[T]]
|
||||
pollfds: seq[TPollFd]
|
||||
count: int
|
||||
count*: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
|
||||
type
|
||||
|
||||
@@ -58,7 +58,7 @@ when hasThreadSupport:
|
||||
eSet: FdSet
|
||||
maxFD: int
|
||||
fds: ptr SharedArray[SelectorKey[T]]
|
||||
count: int
|
||||
count*: int
|
||||
lock: Lock
|
||||
Selector*[T] = ptr SelectorImpl[T]
|
||||
else:
|
||||
@@ -69,7 +69,7 @@ else:
|
||||
eSet: FdSet
|
||||
maxFD: int
|
||||
fds: seq[SelectorKey[T]]
|
||||
count: int
|
||||
count*: int
|
||||
Selector*[T] = ref SelectorImpl[T]
|
||||
|
||||
type
|
||||
|
||||
@@ -181,6 +181,8 @@ type
|
||||
of JArray:
|
||||
elems*: seq[JsonNode]
|
||||
|
||||
const DepthLimit = 1000
|
||||
|
||||
proc newJString*(s: string): JsonNode =
|
||||
## Creates a new `JString JsonNode`.
|
||||
result = JsonNode(kind: JString, str: s)
|
||||
@@ -771,7 +773,7 @@ iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] =
|
||||
for key, val in mpairs(node.fields):
|
||||
yield (key, val)
|
||||
|
||||
proc parseJson(p: var JsonParser): JsonNode =
|
||||
proc parseJson(p: var JsonParser, depth=0): JsonNode =
|
||||
## Parses JSON from a JSON Parser `p`.
|
||||
case p.tok
|
||||
of tkString:
|
||||
@@ -795,6 +797,8 @@ proc parseJson(p: var JsonParser): JsonNode =
|
||||
result = newJNull()
|
||||
discard getTok(p)
|
||||
of tkCurlyLe:
|
||||
if depth > DepthLimit:
|
||||
raiseParseErr(p, "}")
|
||||
result = newJObject()
|
||||
discard getTok(p)
|
||||
while p.tok != tkCurlyRi:
|
||||
@@ -803,16 +807,18 @@ proc parseJson(p: var JsonParser): JsonNode =
|
||||
var key = p.a
|
||||
discard getTok(p)
|
||||
eat(p, tkColon)
|
||||
var val = parseJson(p)
|
||||
var val = parseJson(p, depth+1)
|
||||
result[key] = val
|
||||
if p.tok != tkComma: break
|
||||
discard getTok(p)
|
||||
eat(p, tkCurlyRi)
|
||||
of tkBracketLe:
|
||||
if depth > DepthLimit:
|
||||
raiseParseErr(p, "]")
|
||||
result = newJArray()
|
||||
discard getTok(p)
|
||||
while p.tok != tkBracketRi:
|
||||
result.add(parseJson(p))
|
||||
result.add(parseJson(p, depth+1))
|
||||
if p.tok != tkComma: break
|
||||
discard getTok(p)
|
||||
eat(p, tkBracketRi)
|
||||
|
||||
@@ -68,11 +68,11 @@ type
|
||||
Protocol* = enum ## third argument to `socket` proc
|
||||
IPPROTO_TCP = 6, ## Transmission control protocol.
|
||||
IPPROTO_UDP = 17, ## User datagram protocol.
|
||||
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
|
||||
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
|
||||
IPPROTO_IP, ## Internet protocol.
|
||||
IPPROTO_IPV6, ## Internet Protocol Version 6.
|
||||
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
|
||||
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
|
||||
IPPROTO_ICMPV6 ## Control message protocol for IPv6. Unsupported on Windows.
|
||||
IPPROTO_ICMP ## Internet Control message protocol.
|
||||
IPPROTO_ICMPV6 ## Internet Control message protocol for IPv6.
|
||||
|
||||
Servent* = object ## information about a service
|
||||
name*: string
|
||||
@@ -113,7 +113,7 @@ proc `==`*(a, b: Port): bool {.borrow.}
|
||||
## ``==`` for ports.
|
||||
|
||||
proc `$`*(p: Port): string {.borrow.}
|
||||
## returns the port number as a string
|
||||
## Returns the port number as a string
|
||||
|
||||
proc toInt*(domain: Domain): cint
|
||||
## Converts the Domain enum to a platform-dependent ``cint``.
|
||||
@@ -174,7 +174,21 @@ else:
|
||||
result = cint(ord(typ))
|
||||
|
||||
proc toInt(p: Protocol): cint =
|
||||
result = cint(ord(p))
|
||||
case p
|
||||
of IPPROTO_IP:
|
||||
result = 0.cint
|
||||
of IPPROTO_ICMP:
|
||||
result = 1.cint
|
||||
of IPPROTO_TCP:
|
||||
result = 6.cint
|
||||
of IPPROTO_UDP:
|
||||
result = 17.cint
|
||||
of IPPROTO_IPV6:
|
||||
result = 41.cint
|
||||
of IPPROTO_ICMPV6:
|
||||
result = 58.cint
|
||||
else:
|
||||
result = cint(ord(p))
|
||||
|
||||
proc toSockType*(protocol: Protocol): SockType =
|
||||
result = case protocol
|
||||
@@ -414,7 +428,7 @@ proc getHostname*(): string {.tags: [ReadIOEffect].} =
|
||||
result.setLen(x)
|
||||
|
||||
proc getSockDomain*(socket: SocketHandle): Domain =
|
||||
## returns the socket's domain (AF_INET or AF_INET6).
|
||||
## Returns the socket's domain (AF_INET or AF_INET6).
|
||||
var name: Sockaddr_in6
|
||||
var namelen = sizeof(name).SockLen
|
||||
if getsockname(socket, cast[ptr SockAddr](addr(name)),
|
||||
@@ -426,7 +440,7 @@ proc getSockDomain*(socket: SocketHandle): Domain =
|
||||
raise newException(IOError, "Unknown socket family in getSockDomain")
|
||||
|
||||
proc getAddrString*(sockAddr: ptr SockAddr): string =
|
||||
## return the string representation of address within sockAddr
|
||||
## Returns the string representation of address within sockAddr
|
||||
if sockAddr.sa_family.cint == nativeAfInet:
|
||||
result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
|
||||
elif sockAddr.sa_family.cint == nativeAfInet6:
|
||||
@@ -459,7 +473,7 @@ when defined(posix) and not defined(nimdoc):
|
||||
copyMem(addr result.sun_path, path.cstring, path.len + 1)
|
||||
|
||||
proc getSockName*(socket: SocketHandle): Port =
|
||||
## returns the socket's associated port number.
|
||||
## Returns the socket's associated port number.
|
||||
var name: Sockaddr_in
|
||||
when useWinVersion:
|
||||
name.sin_family = uint16(ord(AF_INET))
|
||||
@@ -474,7 +488,7 @@ proc getSockName*(socket: SocketHandle): Port =
|
||||
result = Port(nativesockets.ntohs(name.sin_port))
|
||||
|
||||
proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
||||
## returns the socket's local address and port number.
|
||||
## Returns the socket's local address and port number.
|
||||
##
|
||||
## Similar to POSIX's `getsockname`:idx:.
|
||||
case domain
|
||||
@@ -511,7 +525,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
||||
raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr")
|
||||
|
||||
proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) =
|
||||
## returns the socket's peer address and port number.
|
||||
## Returns the socket's peer address and port number.
|
||||
##
|
||||
## Similar to POSIX's `getpeername`:idx:
|
||||
case domain
|
||||
|
||||
@@ -22,6 +22,17 @@
|
||||
## In order to use the SSL procedures defined in this module, you will need to
|
||||
## compile your application with the ``-d:ssl`` flag.
|
||||
##
|
||||
##
|
||||
## SSL on Windows
|
||||
## ==============
|
||||
##
|
||||
## On Windows the SSL library checks for valid certificates.
|
||||
## It uses the `cacert.pem` file for this purpose which was extracted
|
||||
## from `https://curl.se/ca/cacert.pem`. Besides
|
||||
## the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you
|
||||
## also need to ship `cacert.pem` with your `.exe` file.
|
||||
##
|
||||
##
|
||||
## Examples
|
||||
## ========
|
||||
##
|
||||
@@ -572,7 +583,7 @@ when defineSsl:
|
||||
if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1:
|
||||
raiseSSLError()
|
||||
|
||||
when defined(nimDisableCertificateValidation) or defined(windows):
|
||||
when defined(nimDisableCertificateValidation):
|
||||
newCTX.SSL_CTX_set_verify(SSL_VERIFY_NONE, nil)
|
||||
else:
|
||||
case verifyMode
|
||||
@@ -587,11 +598,13 @@ when defineSsl:
|
||||
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
|
||||
newCTX.loadCertificates(certFile, keyFile)
|
||||
|
||||
when not defined(nimDisableCertificateValidation) and not defined(windows):
|
||||
const VerifySuccess = 1 # SSL_CTX_load_verify_locations returns 1 on success.
|
||||
|
||||
when not defined(nimDisableCertificateValidation):
|
||||
if verifyMode != CVerifyNone:
|
||||
# Use the caDir and caFile parameters if set
|
||||
if caDir != "" or caFile != "":
|
||||
if newCTX.SSL_CTX_load_verify_locations(caDir, caFile) != 0:
|
||||
if newCTX.SSL_CTX_load_verify_locations(caFile, caDir) != VerifySuccess:
|
||||
raise newException(IOError, "Failed to load SSL/TLS CA certificate(s).")
|
||||
|
||||
else:
|
||||
@@ -599,7 +612,7 @@ when defineSsl:
|
||||
# the SSL_CERT_FILE and SSL_CERT_DIR env vars
|
||||
var found = false
|
||||
for fn in scanSSLCertificates():
|
||||
if newCTX.SSL_CTX_load_verify_locations(fn, "") == 0:
|
||||
if newCTX.SSL_CTX_load_verify_locations(fn, nil) == VerifySuccess:
|
||||
found = true
|
||||
break
|
||||
if not found:
|
||||
@@ -637,12 +650,11 @@ when defineSsl:
|
||||
let ctx = SslContext(context: ssl.SSL_get_SSL_CTX)
|
||||
let hintString = if hint == nil: "" else: $hint
|
||||
let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString)
|
||||
if psk.len.cuint > max_psk_len:
|
||||
if pskString.len.cuint > max_psk_len:
|
||||
return 0
|
||||
if identityString.len.cuint >= max_identity_len:
|
||||
return 0
|
||||
|
||||
copyMem(identity, identityString.cstring, pskString.len + 1) # with the last zero byte
|
||||
copyMem(identity, identityString.cstring, identityString.len + 1) # with the last zero byte
|
||||
copyMem(psk, pskString.cstring, pskString.len)
|
||||
|
||||
return pskString.len.cuint
|
||||
@@ -663,7 +675,7 @@ when defineSsl:
|
||||
max_psk_len: cint): cuint {.cdecl.} =
|
||||
let ctx = SslContext(context: ssl.SSL_get_SSL_CTX)
|
||||
let pskString = (ctx.serverGetPskFunc)($identity)
|
||||
if psk.len.cint > max_psk_len:
|
||||
if pskString.len.cint > max_psk_len:
|
||||
return 0
|
||||
copyMem(psk, pskString.cstring, pskString.len)
|
||||
|
||||
@@ -716,10 +728,11 @@ when defineSsl:
|
||||
raiseSSLError("No SSL certificate found.")
|
||||
|
||||
const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint
|
||||
const size = 1024
|
||||
var peername: string = newString(size)
|
||||
# https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html
|
||||
let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint,
|
||||
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, peername)
|
||||
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil)
|
||||
# https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html
|
||||
X509_free(certificate)
|
||||
if match != 1:
|
||||
raiseSSLError("SSL Certificate check failed.")
|
||||
|
||||
@@ -1816,4 +1829,5 @@ proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress =
|
||||
else:
|
||||
newSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)
|
||||
socket.connect($dest, 80.Port)
|
||||
socket.getLocalAddr()[0].parseIpAddress()
|
||||
result = socket.getLocalAddr()[0].parseIpAddress()
|
||||
socket.close()
|
||||
|
||||
@@ -12,24 +12,24 @@
|
||||
## produce a globally distributed unique ID. This implementation was extracted
|
||||
## from the Mongodb interface and it thus binary compatible with a Mongo OID.
|
||||
##
|
||||
## This implementation calls ``math.randomize()`` for the first call of
|
||||
## This implementation calls `initRand()` for the first call of
|
||||
## ``genOid``.
|
||||
|
||||
import hashes, times, endians
|
||||
import hashes, times, endians, random
|
||||
|
||||
type
|
||||
Oid* = object ## an OID
|
||||
Oid* = object ## An OID.
|
||||
time: int32 ##
|
||||
fuzz: int32 ##
|
||||
count: int32 ##
|
||||
|
||||
proc `==`*(oid1: Oid, oid2: Oid): bool =
|
||||
## Compare two Mongo Object IDs for equality
|
||||
## Compares two Mongo Object IDs for equality.
|
||||
return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and
|
||||
(oid1.count == oid2.count)
|
||||
|
||||
proc hash*(oid: Oid): Hash =
|
||||
## Generate hash of Oid for use in hashtables
|
||||
## Generates hash of Oid for use in hashtables.
|
||||
var h: Hash = 0
|
||||
h = h !& hash(oid.time)
|
||||
h = h !& hash(oid.fuzz)
|
||||
@@ -44,7 +44,7 @@ proc hexbyte*(hex: char): int =
|
||||
else: discard
|
||||
|
||||
proc parseOid*(str: cstring): Oid =
|
||||
## parses an OID.
|
||||
## Parses an OID.
|
||||
var bytes = cast[cstring](addr(result.time))
|
||||
var i = 0
|
||||
while i < 12:
|
||||
@@ -52,6 +52,7 @@ proc parseOid*(str: cstring): Oid =
|
||||
inc(i)
|
||||
|
||||
proc oidToString*(oid: Oid, str: cstring) =
|
||||
## Converts an oid to `str` which must have space allocated for 25 elements.
|
||||
const hex = "0123456789abcdef"
|
||||
# work around a compiler bug:
|
||||
var str = str
|
||||
@@ -66,35 +67,33 @@ proc oidToString*(oid: Oid, str: cstring) =
|
||||
str[24] = '\0'
|
||||
|
||||
proc `$`*(oid: Oid): string =
|
||||
## Converts an oid to string.
|
||||
result = newString(24)
|
||||
oidToString(oid, result)
|
||||
|
||||
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
||||
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
|
||||
|
||||
var t = getTime().toUnix.int32
|
||||
srand(t)
|
||||
|
||||
var
|
||||
incr: int = rand()
|
||||
fuzz: int32 = rand()
|
||||
t = getTime().toUnix.int32
|
||||
seed = initRand(t)
|
||||
incr: int = seed.rand(int.high)
|
||||
|
||||
let fuzz = cast[int32](seed.rand(high(int)))
|
||||
|
||||
proc genOid*(): Oid =
|
||||
## generates a new OID.
|
||||
## Generates a new OID.
|
||||
runnableExamples:
|
||||
doAssert ($genOid()).len == 24
|
||||
if false: doAssert $genOid() == "5fc7f546ddbbc84800006aaf"
|
||||
t = getTime().toUnix.int32
|
||||
var i = int32(atomicInc(incr))
|
||||
var i = cast[int32](atomicInc(incr))
|
||||
|
||||
bigEndian32(addr result.time, addr(t))
|
||||
result.fuzz = fuzz
|
||||
bigEndian32(addr result.count, addr(i))
|
||||
|
||||
proc generatedTime*(oid: Oid): Time =
|
||||
## returns the generated timestamp of the OID.
|
||||
## Returns the generated timestamp of the OID.
|
||||
var tmp: int32
|
||||
var dummy = oid.time
|
||||
bigEndian32(addr(tmp), addr(dummy))
|
||||
result = fromUnix(tmp)
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
let xo = genOid()
|
||||
echo xo.generatedTime
|
||||
|
||||
@@ -943,6 +943,8 @@ proc expandTilde*(path: string): string {.
|
||||
## Windows: this is still supported despite Windows platform not having this
|
||||
## convention; also, both ``~/`` and ``~\`` are handled.
|
||||
##
|
||||
## **Warning**: `~bob` and `~bob/` are not yet handled correctly.
|
||||
##
|
||||
## See also:
|
||||
## * `getHomeDir proc <#getHomeDir>`_
|
||||
## * `getConfigDir proc <#getConfigDir>`_
|
||||
@@ -1156,6 +1158,7 @@ when not defined(windows) and not weirdTarget:
|
||||
else: result = S_ISLNK(rawInfo.st_mode)
|
||||
|
||||
const
|
||||
maxSymlinkLen = 1024
|
||||
ExeExts* = ## Platform specific file extension for executables.
|
||||
## On Windows ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
|
||||
when defined(windows): ["exe", "cmd", "bat"] else: [""]
|
||||
@@ -1197,11 +1200,11 @@ proc findExe*(exe: string, followSymlinks: bool = true;
|
||||
when not defined(windows):
|
||||
while followSymlinks: # doubles as if here
|
||||
if x.checkSymlink:
|
||||
var r = newString(256)
|
||||
var len = readlink(x, r, 256)
|
||||
var r = newString(maxSymlinkLen)
|
||||
var len = readlink(x, r, maxSymlinkLen)
|
||||
if len < 0:
|
||||
raiseOSError(osLastError(), exe)
|
||||
if len > 256:
|
||||
if len > maxSymlinkLen:
|
||||
r = newString(len+1)
|
||||
len = readlink(x, r, len)
|
||||
setLen(r, len)
|
||||
@@ -2517,11 +2520,11 @@ proc expandSymlink*(symlinkPath: string): string {.noNimScript.} =
|
||||
when defined(windows):
|
||||
result = symlinkPath
|
||||
else:
|
||||
result = newString(256)
|
||||
var len = readlink(symlinkPath, result, 256)
|
||||
result = newString(maxSymlinkLen)
|
||||
var len = readlink(symlinkPath, result, maxSymlinkLen)
|
||||
if len < 0:
|
||||
raiseOSError(osLastError(), symlinkPath)
|
||||
if len > 256:
|
||||
if len > maxSymlinkLen:
|
||||
result = newString(len+1)
|
||||
len = readlink(symlinkPath, result, len)
|
||||
setLen(result, len)
|
||||
@@ -2811,9 +2814,9 @@ when not weirdTarget and (defined(freebsd) or defined(dragonfly)):
|
||||
|
||||
when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)):
|
||||
proc getApplAux(procPath: string): string =
|
||||
result = newString(256)
|
||||
var len = readlink(procPath, result, 256)
|
||||
if len > 256:
|
||||
result = newString(maxSymlinkLen)
|
||||
var len = readlink(procPath, result, maxSymlinkLen)
|
||||
if len > maxSymlinkLen:
|
||||
result = newString(len+1)
|
||||
len = readlink(procPath, result, len)
|
||||
setLen(result, len)
|
||||
|
||||
@@ -381,6 +381,8 @@ proc execProcesses*(cmds: openArray[string],
|
||||
raiseOSError(err)
|
||||
|
||||
if rexit >= 0:
|
||||
when defined(windows):
|
||||
let processHandle = q[rexit].fProcessHandle
|
||||
result = max(result, abs(q[rexit].peekExitCode()))
|
||||
if afterRunEvent != nil: afterRunEvent(idxs[rexit], q[rexit])
|
||||
close(q[rexit])
|
||||
@@ -395,7 +397,7 @@ proc execProcesses*(cmds: openArray[string],
|
||||
else:
|
||||
when defined(windows):
|
||||
for k in 0..wcount - 1:
|
||||
if w[k] == q[rexit].fProcessHandle:
|
||||
if w[k] == processHandle:
|
||||
w[k] = w[wcount - 1]
|
||||
w[wcount - 1] = 0
|
||||
dec(wcount)
|
||||
@@ -441,10 +443,17 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
handle: Handle
|
||||
atTheEnd: bool
|
||||
|
||||
proc closeHandleCheck(handle: Handle) {.inline.} =
|
||||
if handle.closeHandle() == 0:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc fileClose[T: Handle | FileHandle](h: var T) {.inline.} =
|
||||
if h > 4:
|
||||
closeHandleCheck(h)
|
||||
h = INVALID_HANDLE_VALUE.T
|
||||
|
||||
proc hsClose(s: Stream) =
|
||||
# xxx here + elsewhere: check instead of discard; ignoring errors leads to
|
||||
# hard to track bugs
|
||||
discard FileHandleStream(s).handle.closeHandle
|
||||
FileHandleStream(s).handle.fileClose()
|
||||
|
||||
proc hsAtEnd(s: Stream): bool = return FileHandleStream(s).atTheEnd
|
||||
|
||||
@@ -549,8 +558,8 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
|
||||
stdin = myDup(pipeIn, 0)
|
||||
stdout = myDup(pipeOut, 0)
|
||||
discard closeHandle(pipeIn)
|
||||
discard closeHandle(pipeOut)
|
||||
closeHandleCheck(pipeIn)
|
||||
closeHandleCheck(pipeOut)
|
||||
stderr = stdout
|
||||
|
||||
proc createPipeHandles(rdHandle, wrHandle: var Handle) =
|
||||
@@ -561,9 +570,6 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
if createPipe(rdHandle, wrHandle, sa, 0) == 0'i32:
|
||||
raiseOSError(osLastError())
|
||||
|
||||
proc fileClose(h: Handle) {.inline.} =
|
||||
if h > 4: discard closeHandle(h)
|
||||
|
||||
proc startProcess(command: string, workingDir: string = "",
|
||||
args: openArray[string] = [], env: StringTableRef = nil,
|
||||
options: set[ProcessOption] = {poStdErrToStdOut}):
|
||||
@@ -630,8 +636,11 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags,
|
||||
ee, wwd, si, procInfo)
|
||||
else:
|
||||
var ee =
|
||||
if e.str.isNil: cstring(nil)
|
||||
else: cstring(e.str)
|
||||
success = winlean.createProcessA(nil,
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo)
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, ee, wd, si, procInfo)
|
||||
let lastError = osLastError()
|
||||
|
||||
if poParentStreams notin options:
|
||||
@@ -655,13 +664,31 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
result.id = procInfo.dwProcessId
|
||||
result.exitFlag = false
|
||||
|
||||
proc closeThreadAndProcessHandle(p: Process) =
|
||||
if p.fThreadHandle != 0:
|
||||
closeHandleCheck(p.fThreadHandle)
|
||||
p.fThreadHandle = 0
|
||||
|
||||
if p.fProcessHandle != 0:
|
||||
closeHandleCheck(p.fProcessHandle)
|
||||
p.fProcessHandle = 0
|
||||
|
||||
proc close(p: Process) =
|
||||
if poParentStreams notin p.options:
|
||||
discard closeHandle(p.inHandle)
|
||||
discard closeHandle(p.outHandle)
|
||||
discard closeHandle(p.errHandle)
|
||||
discard closeHandle(p.fThreadHandle)
|
||||
discard closeHandle(p.fProcessHandle)
|
||||
if p.inStream == nil:
|
||||
p.inHandle.fileClose()
|
||||
else:
|
||||
# p.inHandle can be already closed via inputStream.
|
||||
p.inStream.close
|
||||
|
||||
# You may NOT close outputStream and errorStream.
|
||||
assert p.outStream == nil or FileHandleStream(p.outStream).handle != INVALID_HANDLE_VALUE
|
||||
assert p.errStream == nil or FileHandleStream(p.errStream).handle != INVALID_HANDLE_VALUE
|
||||
|
||||
if p.outHandle != p.errHandle:
|
||||
p.errHandle.fileClose()
|
||||
p.outHandle.fileClose()
|
||||
p.closeThreadAndProcessHandle()
|
||||
|
||||
proc suspend(p: Process) =
|
||||
discard suspendThread(p.fThreadHandle)
|
||||
@@ -695,8 +722,7 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
if status != STILL_ACTIVE:
|
||||
p.exitFlag = true
|
||||
p.exitStatus = status
|
||||
discard closeHandle(p.fThreadHandle)
|
||||
discard closeHandle(p.fProcessHandle)
|
||||
p.closeThreadAndProcessHandle()
|
||||
result = status
|
||||
else:
|
||||
result = -1
|
||||
@@ -712,8 +738,7 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
discard getExitCodeProcess(p.fProcessHandle, status)
|
||||
p.exitFlag = true
|
||||
p.exitStatus = status
|
||||
discard closeHandle(p.fThreadHandle)
|
||||
discard closeHandle(p.fProcessHandle)
|
||||
p.closeThreadAndProcessHandle()
|
||||
result = status
|
||||
|
||||
proc inputStream(p: Process): Stream =
|
||||
|
||||
@@ -108,10 +108,10 @@ proc open*(my: var CsvParser, input: Stream, filename: string,
|
||||
## the diverse optional parameters:
|
||||
## - `separator`: character used to separate fields
|
||||
## - `quote`: Used to quote fields containing special characters like
|
||||
## `separator`, `quote` or new-line characters. '\0' disables the parsing
|
||||
## `separator`, `quote` or new-line characters. '\\0' disables the parsing
|
||||
## of quotes.
|
||||
## - `escape`: removes any special meaning from the following character;
|
||||
## '\0' disables escaping; if escaping is disabled and `quote` is not '\0',
|
||||
## '\\0' disables escaping; if escaping is disabled and `quote` is not '\\0',
|
||||
## two `quote` characters are parsed one literal `quote` character.
|
||||
## - `skipInitialSpace`: If true, whitespace immediately following the
|
||||
## `separator` is ignored.
|
||||
|
||||
@@ -672,8 +672,8 @@ proc getPrecedence(p: SqlParser): int =
|
||||
else:
|
||||
result = - 1
|
||||
|
||||
proc parseExpr(p: var SqlParser): SqlNode
|
||||
proc parseSelect(p: var SqlParser): SqlNode
|
||||
proc parseExpr(p: var SqlParser): SqlNode {.gcsafe.}
|
||||
proc parseSelect(p: var SqlParser): SqlNode {.gcsafe.}
|
||||
|
||||
proc identOrLiteral(p: var SqlParser): SqlNode =
|
||||
case p.tok.kind
|
||||
@@ -1201,7 +1201,7 @@ proc addIden(s: var SqlWriter, thing: string) =
|
||||
iden = '"' & iden & '"'
|
||||
s.add(iden)
|
||||
|
||||
proc ra(n: SqlNode, s: var SqlWriter)
|
||||
proc ra(n: SqlNode, s: var SqlWriter) {.gcsafe.}
|
||||
|
||||
proc rs(n: SqlNode, s: var SqlWriter, prefix = "(", suffix = ")", sep = ", ") =
|
||||
if n.len > 0:
|
||||
|
||||
@@ -672,6 +672,9 @@ proc parseAttribute(my: var XmlParser) =
|
||||
parseEntity(my, my.b)
|
||||
my.kind = xmlAttribute # parseEntity overwrites my.kind!
|
||||
pos = my.bufpos
|
||||
elif c == '/':
|
||||
pos = lexbase.handleRefillChar(my, pos)
|
||||
add(my.b, '/')
|
||||
else:
|
||||
add(my.b, c)
|
||||
inc(pos)
|
||||
|
||||
@@ -204,8 +204,8 @@ proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||
if x.left.length > j:
|
||||
x = x.left
|
||||
else:
|
||||
dec(j, x.left.length)
|
||||
x = x.right
|
||||
dec(j, x.len)
|
||||
|
||||
iterator leaves*(r: Rope): string =
|
||||
## iterates over any leaf string in the rope `r`.
|
||||
@@ -262,7 +262,7 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {.
|
||||
while true:
|
||||
j = j * 10 + ord(frmt[i]) - ord('0')
|
||||
inc(i)
|
||||
if frmt[i] notin {'0'..'9'}: break
|
||||
if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
|
||||
add(result, args[j-1])
|
||||
of '{':
|
||||
inc(i)
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
## For SSL support this module relies on OpenSSL. If you want to
|
||||
## enable SSL, compile with ``-d:ssl``.
|
||||
|
||||
import net, strutils, strtabs, base64, os
|
||||
import net, strutils, strtabs, base64, os, strutils
|
||||
import asyncnet, asyncdispatch
|
||||
|
||||
export Port
|
||||
@@ -65,7 +65,22 @@ type
|
||||
Smtp* = SmtpBase[Socket]
|
||||
AsyncSmtp* = SmtpBase[AsyncSocket]
|
||||
|
||||
proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
|
||||
proc containsNewline(xs: seq[string]): bool =
|
||||
for x in xs:
|
||||
if x.contains({'\c', '\L'}):
|
||||
return true
|
||||
|
||||
proc debugSend*(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
|
||||
## Sends ``cmd`` on the socket connected to the SMTP server.
|
||||
##
|
||||
## If the ``smtp`` object was created with ``debug`` enabled,
|
||||
## debugSend will invoke ``echo("C:" & cmd)`` before sending.
|
||||
##
|
||||
## This is a lower level proc and not something that you typically
|
||||
## would need to call when using this module. One exception to
|
||||
## this is if you are implementing any
|
||||
## `SMTP extensions<https://en.wikipedia.org/wiki/Extended_SMTP>`_.
|
||||
|
||||
if smtp.debug:
|
||||
echo("C:" & cmd)
|
||||
await smtp.sock.send(cmd)
|
||||
@@ -95,6 +110,14 @@ else:
|
||||
proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
|
||||
otherHeaders: openarray[tuple[name, value: string]]): Message =
|
||||
## Creates a new MIME compliant message.
|
||||
##
|
||||
## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain
|
||||
## any newline characters. Failing to do so will raise ``AssertionDefect``.
|
||||
doAssert(not mSubject.contains({'\c', '\L'}),
|
||||
"'mSubject' shouldn't contain any newline characters")
|
||||
doAssert(not (mTo.containsNewline() or mCc.containsNewline()),
|
||||
"'mTo' and 'mCc' shouldn't contain any newline characters")
|
||||
|
||||
result.msgTo = mTo
|
||||
result.msgCc = mCc
|
||||
result.msgSubject = mSubject
|
||||
@@ -106,6 +129,13 @@ proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
|
||||
proc createMessage*(mSubject, mBody: string, mTo,
|
||||
mCc: seq[string] = @[]): Message =
|
||||
## Alternate version of the above.
|
||||
##
|
||||
## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain
|
||||
## any newline characters. Failing to do so will raise ``AssertionDefect``.
|
||||
doAssert(not mSubject.contains({'\c', '\L'}),
|
||||
"'mSubject' shouldn't contain any newline characters")
|
||||
doAssert(not (mTo.containsNewline() or mCc.containsNewline()),
|
||||
"'mTo' and 'mCc' shouldn't contain any newline characters")
|
||||
result.msgTo = mTo
|
||||
result.msgCc = mCc
|
||||
result.msgSubject = mSubject
|
||||
@@ -213,6 +243,11 @@ proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
|
||||
## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``.
|
||||
## Messages may be formed using ``createMessage`` by converting the
|
||||
## Message into a string.
|
||||
##
|
||||
## You need to make sure that ``fromAddr`` and ``toAddrs`` don't contain
|
||||
## any newline characters. Failing to do so will raise ``AssertionDefect``.
|
||||
doAssert(not (toAddrs.containsNewline() or fromAddr.contains({'\c', '\L'})),
|
||||
"'toAddrs' and 'fromAddr' shouldn't contain any newline characters")
|
||||
|
||||
await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L")
|
||||
await smtp.checkReply("250")
|
||||
|
||||
@@ -11,16 +11,48 @@
|
||||
## SSL_CERT_DIR environment variables.
|
||||
|
||||
import os, strutils
|
||||
from ospaths import existsEnv, getEnv
|
||||
import strutils
|
||||
|
||||
# SECURITY: this unnecessarily scans through dirs/files regardless of the
|
||||
# actual host OS/distribution. Hopefully all the paths are writeble only by
|
||||
# root.
|
||||
|
||||
# FWIW look for files before scanning entire dirs.
|
||||
|
||||
const certificate_paths = [
|
||||
when defined(macosx):
|
||||
const certificatePaths = [
|
||||
"/etc/ssl/cert.pem",
|
||||
"/System/Library/OpenSSL/certs/cert.pem"
|
||||
]
|
||||
elif defined(linux):
|
||||
const certificatePaths = [
|
||||
# Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo
|
||||
# NetBSD (security/mozilla-rootcerts)
|
||||
# SLES10/SLES11, https://golang.org/issue/12139
|
||||
"/etc/ssl/certs/ca-certificates.crt",
|
||||
# OpenSUSE
|
||||
"/etc/ssl/ca-bundle.pem",
|
||||
# Red Hat 5+, Fedora, Centos
|
||||
"/etc/pki/tls/certs/ca-bundle.crt",
|
||||
# Red Hat 4
|
||||
"/usr/share/ssl/certs/ca-bundle.crt",
|
||||
# Fedora/RHEL
|
||||
"/etc/pki/tls/certs",
|
||||
# Android
|
||||
"/system/etc/security/cacerts",
|
||||
]
|
||||
elif defined(bsd):
|
||||
const certificatePaths = [
|
||||
# Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo
|
||||
# NetBSD (security/mozilla-rootcerts)
|
||||
# SLES10/SLES11, https://golang.org/issue/12139
|
||||
"/etc/ssl/certs/ca-certificates.crt",
|
||||
# FreeBSD (security/ca-root-nss package)
|
||||
"/usr/local/share/certs/ca-root-nss.crt",
|
||||
# OpenBSD, FreeBSD (optional symlink)
|
||||
"/etc/ssl/cert.pem",
|
||||
# FreeBSD
|
||||
"/usr/local/share/certs",
|
||||
# NetBSD
|
||||
"/etc/openssl/certs",
|
||||
]
|
||||
else:
|
||||
const certificatePaths = [
|
||||
# Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo
|
||||
# NetBSD (security/mozilla-rootcerts)
|
||||
# SLES10/SLES11, https://golang.org/issue/12139
|
||||
@@ -37,8 +69,6 @@ const certificate_paths = [
|
||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem",
|
||||
# OpenBSD, FreeBSD (optional symlink)
|
||||
"/etc/ssl/cert.pem",
|
||||
# Mac OS X
|
||||
"/System/Library/OpenSSL/certs/cert.pem",
|
||||
# Fedora/RHEL
|
||||
"/etc/pki/tls/certs",
|
||||
# Android
|
||||
@@ -47,7 +77,7 @@ const certificate_paths = [
|
||||
"/usr/local/share/certs",
|
||||
# NetBSD
|
||||
"/etc/openssl/certs",
|
||||
]
|
||||
]
|
||||
|
||||
when defined(haiku):
|
||||
const
|
||||
@@ -67,17 +97,30 @@ iterator scanSSLCertificates*(useEnvVars = false): string =
|
||||
## if `useEnvVars` is true, the SSL_CERT_FILE and SSL_CERT_DIR
|
||||
## environment variables can be used to override the certificate
|
||||
## directories to scan or specify a CA certificate file.
|
||||
if existsEnv("SSL_CERT_FILE"):
|
||||
if useEnvVars and existsEnv("SSL_CERT_FILE"):
|
||||
yield getEnv("SSL_CERT_FILE")
|
||||
|
||||
elif existsEnv("SSL_CERT_DIR"):
|
||||
elif useEnvVars and existsEnv("SSL_CERT_DIR"):
|
||||
let p = getEnv("SSL_CERT_DIR")
|
||||
for fn in joinPath(p, "*").walkFiles():
|
||||
yield fn
|
||||
|
||||
else:
|
||||
when not defined(haiku):
|
||||
for p in certificate_paths:
|
||||
when defined(windows):
|
||||
const cacert = "cacert.pem"
|
||||
let pem = getAppDir() / cacert
|
||||
if fileExists(pem):
|
||||
yield pem
|
||||
else:
|
||||
let path = getEnv("PATH")
|
||||
for candidate in split(path, PathSep):
|
||||
if candidate.len != 0:
|
||||
let x = (if candidate[0] == '"' and candidate[^1] == '"':
|
||||
substr(candidate, 1, candidate.len-2) else: candidate) / cacert
|
||||
if fileExists(x):
|
||||
yield x
|
||||
elif not defined(haiku):
|
||||
for p in certificatePaths:
|
||||
if p.endsWith(".pem") or p.endsWith(".crt"):
|
||||
if existsFile(p):
|
||||
yield p
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user