# # # Nim's Runtime Library # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # Nim high-level memory manager: It supports Boehm's GC, Go's GC, no GC and the # native Nim GC. The native Nim GC is the default. #{.push checks:on, assertions:on.} {.push checks:off.} const debugGC = false # we wish to debug the GC... logGC = false traceGC = false # extensive debugging alwaysCycleGC = defined(smokeCycles) alwaysGC = defined(fulldebug) # collect after every memory # allocation (for debugging) leakDetector = defined(leakDetector) overwriteFree = defined(nimBurnFree) # overwrite memory with 0xFF before free trackAllocationSource = leakDetector cycleGC = true # (de)activate the cycle GC reallyDealloc = true # for debugging purposes this can be set to false reallyOsDealloc = true coalescRight = true coalescLeft = true logAlloc = false useCellIds = defined(corruption) type PPointer = ptr pointer ByteArray = UncheckedArray[byte] PByte = ptr ByteArray PString = ptr string {.deprecated: [TByteArray: ByteArray].} # Page size of the system; in most cases 4096 bytes. For exotic OS or # CPU this needs to be changed: const PageShift = when defined(cpu16): 8 else: 12 PageSize = 1 shl PageShift PageMask = PageSize-1 MemAlign = 8 # also minimal allocatable memory block BitsPerPage = PageSize div MemAlign UnitsPerPage = BitsPerPage div (sizeof(int)*8) # how many ints do we need to describe a page: # on 32 bit systems this is only 16 (!) TrunkShift = 9 BitsPerTrunk = 1 shl TrunkShift # needs to be power of 2 and divisible by 64 TrunkMask = BitsPerTrunk - 1 IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8) IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width IntMask = 1 shl IntShift - 1 proc raiseOutOfMem() {.noinline.} = if outOfMemHook != nil: outOfMemHook() echo("out of memory") quit(1) when defined(boehmgc): proc boehmGCinit {.importc: "GC_init", boehmGC.} proc boehmGC_disable {.importc: "GC_disable", boehmGC.} proc boehmGC_enable {.importc: "GC_enable", boehmGC.} proc boehmGCincremental {. importc: "GC_enable_incremental", boehmGC.} proc boehmGCfullCollect {.importc: "GC_gcollect", boehmGC.} proc boehmAlloc(size: int): pointer {.importc: "GC_malloc", boehmGC.} proc boehmAllocAtomic(size: int): pointer {. importc: "GC_malloc_atomic", boehmGC.} proc boehmRealloc(p: pointer, size: int): pointer {. importc: "GC_realloc", boehmGC.} proc boehmDealloc(p: pointer) {.importc: "GC_free", boehmGC.} when hasThreadSupport: proc boehmGC_allow_register_threads {. importc: "GC_allow_register_threads", boehmGC.} proc boehmGetHeapSize: int {.importc: "GC_get_heap_size", boehmGC.} ## Return the number of bytes in the heap. Excludes collector private ## data structures. Includes empty blocks and fragmentation loss. ## Includes some pages that were allocated but never written. proc boehmGetFreeBytes: int {.importc: "GC_get_free_bytes", boehmGC.} ## Return a lower bound on the number of free bytes in the heap. proc boehmGetBytesSinceGC: int {.importc: "GC_get_bytes_since_gc", boehmGC.} ## Return the number of bytes allocated since the last collection. proc boehmGetTotalBytes: int {.importc: "GC_get_total_bytes", boehmGC.} ## Return the total number of bytes allocated in this process. ## Never decreases. proc allocAtomic(size: int): pointer = result = boehmAllocAtomic(size) zeroMem(result, size) when not defined(useNimRtl): proc alloc(size: Natural): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() proc alloc0(size: Natural): pointer = result = alloc(size) zeroMem(result, size) proc realloc(p: pointer, newsize: Natural): pointer = result = boehmRealloc(p, newsize) if result == nil: raiseOutOfMem() proc dealloc(p: pointer) = boehmDealloc(p) proc allocShared(size: Natural): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() proc allocShared0(size: Natural): pointer = result = alloc(size) zeroMem(result, size) proc reallocShared(p: pointer, newsize: Natural): pointer = result = boehmRealloc(p, newsize) if result == nil: raiseOutOfMem() proc deallocShared(p: pointer) = boehmDealloc(p) when hasThreadSupport: proc getFreeSharedMem(): int = boehmGetFreeBytes() proc getTotalSharedMem(): int = boehmGetHeapSize() proc getOccupiedSharedMem(): int = getTotalSharedMem() - getFreeSharedMem() #boehmGCincremental() proc GC_disable() = boehmGC_disable() proc GC_enable() = boehmGC_enable() proc GC_fullCollect() = boehmGCfullCollect() proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" proc getOccupiedMem(): int = return boehmGetHeapSize()-boehmGetFreeBytes() proc getFreeMem(): int = return boehmGetFreeBytes() proc getTotalMem(): int = return boehmGetHeapSize() proc setStackBottom(theStackBottom: pointer) = discard proc initGC() = boehmGCinit() when hasThreadSupport: boehmGC_allow_register_threads() proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = if ntfNoRefs in typ.flags: result = allocAtomic(size) else: result = alloc(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len proc growObj(old: pointer, newsize: int): pointer = result = realloc(old, newsize) proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src type MemRegion = object {.final, pure.} {.deprecated: [TMemRegion: MemRegion].} proc alloc(r: var MemRegion, size: int): pointer = result = boehmAlloc(size) if result == nil: raiseOutOfMem() proc alloc0(r: var MemRegion, size: int): pointer = result = alloc(size) zeroMem(result, size) proc dealloc(r: var MemRegion, p: pointer) = boehmDealloc(p) proc deallocOsPages(r: var MemRegion) {.inline.} = discard proc deallocOsPages() {.inline.} = discard include "system/cellsets" elif defined(gogc): when defined(windows): const goLib = "libgo.dll" elif defined(macosx): const goLib = "libgo.dylib" else: const goLib = "libgo.so" proc roundup(x, v: int): int {.inline.} = result = (x + (v-1)) and not (v-1) proc initGC() = discard # runtime_setgcpercent is only available in GCC 5 proc GC_disable() = discard proc GC_enable() = discard proc goRuntimeGC(force: int32) {.importc: "runtime_gc", dynlib: goLib.} proc GC_fullCollect() = goRuntimeGC(2) proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard const
# This nimscript is used to test if the following modules can be imported
# http://nim-lang.org/docs/nims.html

{.warning[UnusedImport]: off.}

from stdtest/specialpaths import buildDir

when defined(nimPreviewSlimSystem):
  import std/[
    syncio, assertions, formatfloat, objectdollar, widestrs
  ]

import std/[
  # Core:
  bitops, typetraits, lenientops, macros, volatile,
  # fails due to FFI: typeinfo
  # fails due to cstring cast/copyMem: endians
  # works but uses FFI: cpuinfo, rlocks, locks

  # Algorithms:
  algorithm, enumutils, sequtils, setutils,

  # Collections:
  critbits, deques, heapqueue, intsets, lists, options, sets,
  tables, packedsets,

  # Strings:
  editdistance, wordwrap, parseutils, ropes,
  pegs, strformat, strmisc, strscans, strtabs,
  strutils, unicode, unidecode, cstrutils,
  # works but uses FFI: encodings

  # Time handling:
  # fails due to FFI: monotimes, times
  # but times.getTime() implemented for VM

  # Generic operator system services:
  os, streams, distros,
  # fails due to FFI: memfiles, osproc, terminal
  # works but uses FFI: dynlib
  # intentionally fails: marshal

  # Math libraries:
  complex, math, random, rationals, stats, sums,
  # works but uses FFI: fenv, sysrand

  # Internet protocols:
  httpcore, mimetypes, uri,
  # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver,
  # asyncnet, cgi, cookies, httpclient, nativesockets, net, selectors, smtp
  # works but no need to test: asyncstreams, asyncfutures

  # Threading:
  # fails due to FFI: threadpool

  # Parsers:
  htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml,
  parseopt, jsonutils,

  # XML processing:
  xmltree, xmlparser,

  # Generators:
  htmlgen,

  # Hashing:
  base64, hashes, md5,
  # fails due to cstring cast/times import/endians import: oids
  # fails due to copyMem/endians import: sha1

  # Miscellaneous:
  colors, sugar, varints, enumerate, with,
  # fails due to FFI: browsers, coro, segfaults
  # fails due to times import/methods: logging
  # fails due to methods: unittest

  # Modules for JS backend:
  # fails intentionally: asyncjs, dom, jsconsole, jscore, jsffi, jsbigints,
  # jsfetch, jsformdata, jsheaders

  # Unlisted in lib.html:
  decls, compilesettings, wrapnils, effecttraits, genasts,
  importutils, isolation
]

# non-std imports
import stdtest/testutils
# tests (increase coverage via code reuse)
import stdlib/trandom
import stdlib/tosenv

echo "Nimscript imports are successful."

block:
  doAssert "./foo//./bar/".normalizedPath == "foo/bar".unixToNativePath
block:
  doAssert $3'u == "3"
  doAssert $3'u64 == "3"

block: # #14142
  discard dirExists("/usr")
  discard fileExists("/usr/foo")
  discard findExe("nim")

block:
  doAssertRaises(AssertionDefect): doAssert false
  try: doAssert false
  except Exception as e:
    discard

block:  # cpDir, cpFile, dirExists, fileExists, mkDir, mvDir, mvFile, rmDir, rmFile
  const dname = buildDir/"D20210121T175016"
  const subDir = dname/"sub"
  const subDir2 = dname/"sub2"
  const fpath = subDir/"f"
  const fpath2 = subDir/"f2"
  const fpath3 = subDir2/"f"
  mkDir(subDir)
  writeFile(fpath, "some text")
  cpFile(fpath, fpath2)
  doAssert fileExists(fpath2)
  rmFile(fpath2)
  cpDir(subDir, subDir2)
  doAssert fileExists(fpath3)
  rmDir(subDir2)
  mvFile(fpath, fpath2)
  doAssert not fileExists(fpath)
  doAssert fileExists(fpath2)
  mvFile(fpath2, fpath)
  mvDir(subDir, subDir2)
  doAssert not dirExists(subDir)
  doAssert dirExists(subDir2)
  mvDir(subDir2, subDir)
  rmDir(dname)

block:
  # check parseopt can get command line:
  discard initOptParser()
nitGC() = discard proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = result = alloc0(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len proc newObjNoInit(typ: PNimType, size: int): pointer = result = alloc(size) proc growObj(old: pointer, newsize: int): pointer = result = realloc(old, newsize) proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src type MemRegion = object {.final, pure.} {.deprecated: [TMemRegion: MemRegion].} proc alloc(r: var MemRegion, size: int): pointer = result = alloc(size) proc alloc0(r: var MemRegion, size: int): pointer = result = alloc0(size) proc dealloc(r: var MemRegion, p: pointer) = dealloc(p) proc deallocOsPages(r: var MemRegion) {.inline.} = discard proc deallocOsPages() {.inline.} = discard elif defined(nogc): # Even though we don't want the GC, we cannot simply use C's memory manager # because Nim's runtime wants ``realloc`` to zero out the additional # space which C's ``realloc`` does not. And we cannot get the old size of an # object, because C does not support this operation... Even though every # possible implementation has to have a way to determine the object's size. # C just sucks. when appType == "lib": {.warning: "nogc in a library context may not work".} include "system/alloc" proc initGC() = discard proc GC_disable() = discard proc GC_enable() = discard proc GC_fullCollect() = discard proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_enableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard proc GC_getStatistics(): string = return "" proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = result = alloc0(size) proc newObjNoInit(typ: PNimType, size: int): pointer = result = alloc(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len cast[PGenericSeq](result).reserved = len proc growObj(old: pointer, newsize: int): pointer = result = realloc(old, newsize) proc setStackBottom(theStackBottom: pointer) = discard proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src var allocator {.rtlThreadVar.}: MemRegion instantiateForRegion(allocator) include "system/cellsets" else: when not defined(gcRegions): include "system/alloc" include "system/cellsets" when not leakDetector and not useCellIds: sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") when compileOption("gc", "v2"): include "system/gc2" elif defined(gcRegions): # XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here include "system/gc_regions" elif defined(gcMarkAndSweep): # XXX use 'compileOption' here include "system/gc_ms" elif defined(gcGenerational): include "system/gc" else: include "system/gc" when not declared(nimNewSeqOfCap): proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} = let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize) when declared(newObjNoInit): result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s) else: result = newObj(typ, s) cast[PGenericSeq](result).len = 0 cast[PGenericSeq](result).reserved = cap {.pop.}