Add push raises (#101)

* Add push raises

* Fix gcsafe violation

* remove debug code
This commit is contained in:
andri lim 2024-02-12 12:39:19 +07:00 committed by GitHub
parent 7340359702
commit 57ff0b8555
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 111 additions and 42 deletions

View File

@ -26,7 +26,7 @@ const
when not defined(nimscript):
import
os, terminal,
terminal,
confutils/shell_completion
type
@ -70,6 +70,8 @@ const
confutils_description_width {.intdefine.} = 80
confutils_narrow_terminal_width {.intdefine.} = 36
{.push gcsafe, raises: [].}
func getFieldName(caseField: NimNode): NimNode =
result = caseField
if result.kind == nnkIdentDefs: result = result[0]
@ -565,7 +567,10 @@ proc parseCmdArgAux(T: type, s: string): T {.raises: [ValueError].} =
# If you have provided your own specializations, please handle
# all other exception types.
mixin parseCmdArg
parseCmdArg(T, s)
try:
parseCmdArg(T, s)
except CatchableError as exc:
raise newException(ValueError, exc.msg)
func completeCmdArg*(T: type enum, val: string): seq[string] =
for e in low(T)..high(T):
@ -1291,3 +1296,5 @@ func load*(f: TypedInputFile): f.ContentType =
else:
mixin loadFile
loadFile(f.Format, f.string, f.ContentType)
{.pop.}

View File

@ -29,6 +29,8 @@ type
## or the argument, and the value is not "" if
## the option was given a value
{.push gcsafe, raises: [].}
func parseWord(s: string, i: int, w: var string,
delim: set[char] = {'\t', ' '}): int =
result = i
@ -161,3 +163,4 @@ iterator getopt*(cmds: seq[string],
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)
{.pop.}

View File

@ -12,6 +12,8 @@ import
stew/byteutils, testutils/fuzzing,
../confutils
{.push gcsafe, raises: [].}
template fuzzCliParsing*(Conf: type) =
test:
block:
@ -22,3 +24,4 @@ template fuzzCliParsing*(Conf: type) =
except ConfigurationError as err:
discard
{.pop.}

View File

@ -45,6 +45,8 @@ type
OriginalToGeneratedFields = OrderedTable[string, GeneratedFieldInfo]
{.push gcsafe, raises: [].}
func isOption(n: NimNode): bool =
if n.kind != nnkBracketExpr: return false
eqIdent(n[0], "Option")
@ -248,19 +250,20 @@ proc generateTypes(root: ConfFileSection): seq[NimNode] =
recList.add generateOptionalField(child.getRenamedName.ident, child.typ)
result[index].putRecList(recList)
proc generateSettersPaths(node: ConfFileSection, result: var OriginalToGeneratedFields) =
var path {.global.}: seq[string]
path.add node.getRenamedName
proc generateSettersPaths(node: ConfFileSection,
result: var OriginalToGeneratedFields,
pathsCache: var seq[string]) =
pathsCache.add node.getRenamedName
if node.children.len == 0:
result[node.fieldName] = (node.isCommandOrArgument, path)
result[node.fieldName] = (node.isCommandOrArgument, pathsCache)
else:
for child in node.children:
generateSettersPaths(child, result)
path.del path.len - 1
generateSettersPaths(child, result, pathsCache)
pathsCache.del pathsCache.len - 1
proc generateSettersPaths(root: ConfFileSection): OriginalToGeneratedFields =
proc generateSettersPaths(root: ConfFileSection, pathsCache: var seq[string]): OriginalToGeneratedFields =
for child in root.children:
generateSettersPaths(child, result)
generateSettersPaths(child, result, pathsCache)
template cfSetter(a, b: untyped): untyped =
when a is Option:
@ -346,9 +349,13 @@ macro generateSecondarySources*(ConfType: type): untyped =
let
model = generateConfigFileModel(ConfType)
modelType = generateTypes(model)
var
pathsCache: seq[string]
result = newTree(nnkStmtList)
result.add newTree(nnkTypeSection, modelType)
let settersPaths = model.generateSettersPaths
let settersPaths = model.generateSettersPaths(pathsCache)
result.add generateConfigFileSetters(ConfType, result[^1], settersPaths)
{.pop.}

View File

@ -45,6 +45,8 @@ type
SomeDistinctString = InputFile|InputDir|OutPath|OutDir|OutFile
{.push gcsafe, raises: [].}
template `/`*(dir: InputDir|OutDir, path: string): auto =
string(dir) / path
@ -70,3 +72,5 @@ template implicitlySelectable* {.pragma.}
## to allow the value of the discriminator to be determined
## implicitly when the user specifies any of the sub-options
## that depend on the disciminator value.
{.pop.}

View File

@ -24,13 +24,21 @@ const
WORDBREAKS = "\"'@><=;|&(:"
SAFE_CHARS = {'a'..'z', 'A'..'Z', '0'..'9', '@', '%', '+', '=', ':', ',', '.', '/', '-'}
proc open(l: var ShellLexer, input: Stream, wordBreakChars: string = WORDBREAKS, preserveTrailingWs = true) =
{.push gcsafe, raises: [].}
proc open(l: var ShellLexer,
input: Stream,
wordBreakChars: string = WORDBREAKS,
preserveTrailingWs = true) {.gcsafe, raises: [IOError, OSError].} =
lexbase.open(l, input)
l.preserveTrailingWs = preserveTrailingWs
l.mergeWordBreaks = false
l.wordBreakChars = wordBreakChars
proc parseQuoted(l: var ShellLexer, pos: int, isSingle: bool, output: var string): int =
proc parseQuoted(l: var ShellLexer,
pos: int,
isSingle: bool,
output: var string): int {.gcsafe, raises: [IOError, OSError].} =
var pos = pos
while true:
case l.buf[pos]:
@ -62,7 +70,7 @@ proc parseQuoted(l: var ShellLexer, pos: int, isSingle: bool, output: var string
inc(pos)
return pos
proc getTok(l: var ShellLexer): Option[string] =
proc getTok(l: var ShellLexer): Option[string] {.gcsafe, raises: [IOError, OSError].} =
var pos = l.bufpos
# Skip the initial whitespace
@ -179,6 +187,8 @@ proc shellPathEscape*(path: string): string =
result.add('\\')
result.add(ch)
{.pop.}
when isMainModule:
# Test data lifted from python's shlex unit-tests
const data = """
@ -265,9 +275,9 @@ foo\ bar|foo bar|
echo "expected ", expected
doAssert(false)
doAssert(quoteWord("") == "''")
doAssert(quoteWord("\\\"") == "'\\\"'")
doAssert(quoteWord("foobar") == "foobar")
doAssert(quoteWord("foo$bar") == "'foo$bar'")
doAssert(quoteWord("foo bar") == "'foo bar'")
doAssert(quoteWord("foo'bar") == "'foo\\'bar'")
doAssert(shellQuote("") == "''")
doAssert(shellQuote("\\\"") == "'\\\"'")
doAssert(shellQuote("foobar") == "foobar")
doAssert(shellQuote("foo$bar") == "'foo$bar'")
doAssert(shellQuote("foo bar") == "'foo bar'")
doAssert(shellQuote("foo'bar") == "'foo\\'bar'")

View File

@ -11,14 +11,16 @@ import std/net
from std/parseutils import parseInt
export net
func parseCmdArg*(T: type IpAddress, s: string): T =
{.push gcsafe, raises: [].}
func parseCmdArg*(T: type IpAddress, s: string): T {.gcsafe, raises: [ValueError].} =
parseIpAddress(s)
func completeCmdArg*(T: type IpAddress, val: string): seq[string] =
# TODO: Maybe complete the local IP address?
@[]
func parseCmdArg*(T: type Port, s: string): T =
func parseCmdArg*(T: type Port, s: string): T {.gcsafe, raises: [ValueError].} =
template fail =
raise newException(ValueError,
"The supplied port must be an integer value in the range 1-65535")
@ -34,3 +36,5 @@ func parseCmdArg*(T: type Port, s: string): T =
func completeCmdArg*(T: type Port, val: string): seq[string] =
@[]
{.pop.}

View File

@ -13,6 +13,8 @@ import
export
toml_serialization, confutilsDefs
{.push gcsafe, raises: [].}
template readConfutilsType(T: type) =
template readValue*(r: var TomlReader, val: var T) =
val = T r.readValue(string)
@ -22,3 +24,5 @@ readConfutilsType InputDir
readConfutilsType OutPath
readConfutilsType OutDir
readConfutilsType OutFile
{.pop.}

View File

@ -14,16 +14,20 @@ import
export
net, toml_serialization
{.push gcsafe, raises: [].}
proc readValue*(r: var TomlReader, val: var IpAddress)
{.raises: [SerializationError, IOError, Defect].} =
{.raises: [SerializationError, IOError].} =
val = try: parseIpAddress(r.readValue(string))
except ValueError as err:
r.lex.raiseUnexpectedValue("IP address")
r.lex.raiseUnexpectedValue("IP address " & err.msg)
proc readValue*(r: var TomlReader, val: var Port)
{.raises: [SerializationError, IOError, Defect].} =
{.raises: [SerializationError, IOError].} =
let port = try: r.readValue(uint16)
except ValueError:
r.lex.raiseUnexpectedValue("Port")
except ValueError as exc:
r.lex.raiseUnexpectedValue("Port " & exc.msg)
val = Port port
{.pop.}

View File

@ -14,9 +14,12 @@ import
export
uri, toml_serialization
{.push gcsafe, raises: [].}
proc readValue*(r: var TomlReader, val: var Uri)
{.raises: [SerializationError, IOError, Defect].} =
{.raises: [SerializationError, IOError].} =
val = try: parseUri(r.readValue(string))
except ValueError as err:
r.lex.raiseUnexpectedValue("URI")
r.lex.raiseUnexpectedValue("URI " & err.msg)
{.pop.}

View File

@ -9,7 +9,7 @@
import
tables, typetraits, options,
serialization/[object_serialization],
serialization/[object_serialization, errors],
./utils, ./types
type
@ -24,11 +24,13 @@ type
deserializedField*: string
innerException*: ref CatchableError
{.push gcsafe, raises: [].}
proc handleReadException*(r: WinregReader,
Record: type,
fieldName: string,
field: auto,
err: ref CatchableError) =
err: ref CatchableError) {.gcsafe, raises: [WinregError].} =
var ex = new GenericWinregReaderError
ex.deserializedField = fieldName
ex.innerException = err
@ -39,7 +41,8 @@ proc init*(T: type WinregReader,
result.hKey = hKey
result.path = path
proc readValue*[T](r: var WinregReader, value: var T) =
proc readValue*[T](r: var WinregReader, value: var T)
{.gcsafe, raises: [SerializationError, IOError].} =
mixin readValue
# TODO: reduce allocation
@ -88,3 +91,5 @@ proc readValue*[T](r: var WinregReader, value: var T) =
else:
const typeName = typetraits.name(T)
{.fatal: "Failed to convert from Winreg an unsupported type: " & typeName.}
{.pop.}

View File

@ -26,5 +26,9 @@ const
HKCR* = HKEY_CLASSES_ROOT
HKU* = HKEY_USERS
{.push gcsafe, raises: [].}
proc `==`*(a, b: HKEY): bool {.borrow.}
proc `==`*(a, b: RegType): bool {.borrow.}
{.pop.}

View File

@ -26,6 +26,8 @@ const
RT_QWORD* = 0x00000040
RT_ANY* = 0x0000ffff
{.push gcsafe, raises: [].}
proc regGetValue(hKey: HKEY, lpSubKey, lpValue: cstring,
dwFlags: int32, pdwType: ptr RegType,
pvData: pointer, pcbData: ptr int32): int32 {.
@ -39,12 +41,18 @@ template call(f) =
if f != 0:
return false
template safeCast(destType: type, src: typed): auto =
when sizeof(src) < sizeof(destType):
destType(src)
else:
cast[destType](src)
proc setValue*(hKey: HKEY, path, key: string, val: SomePrimitives): bool =
when sizeof(val) < 8:
var dw = cast[int32](val)
var dw = int32.safeCast(val)
call regSetValue(hKey, path, key, REG_DWORD, dw.addr, sizeof(dw).int32)
else:
var dw = cast[int64](val)
var dw = int64.safeCast(val)
call regSetValue(hKey, path, key, REG_QWORD, dw.addr, sizeof(dw).int32)
result = true
@ -160,3 +168,5 @@ func constructPath*(root: string, keys: openArray[string]): string =
result.add keys[i]
if i < keys.len-2:
result. add '\\'
{.pop.}

View File

@ -14,6 +14,8 @@ import
export
serialization, reader, writer, types
{.push gcsafe, raises: [].}
serializationFormat Winreg
Winreg.setReader WinregReader
@ -65,3 +67,5 @@ template saveFile*(_: type Winreg, filename: string, value: auto, params: vararg
let (hKey, path) = parseWinregPath(filename)
var writer = unpackArgs(init, [WinregWriter, hKey, path, params])
writer.writeValue(value)
{.pop.}

View File

@ -18,6 +18,8 @@ type
path: string
key: seq[string]
{.push gcsafe, raises: [].}
proc init*(T: type WinregWriter,
hKey: HKEY, path: string): T =
result.hKey = hKey
@ -55,3 +57,5 @@ proc writeValue*(w: var WinregWriter, value: auto) {.raises: [IOError].} =
else:
const typeName = typetraits.name(value.type)
{.fatal: "Failed to convert to Winreg an unsupported type: " & typeName.}
{.pop.}

View File

@ -20,10 +20,7 @@ type
field_a: int
field_b: string
CheckPoint = int
RuntimePreset = int
GraffitiBytes = array[16, byte]
WalletName = string
VCStartUpCmd = enum
VCNoCommand

View File

@ -34,10 +34,6 @@ type
abbr: "d"
name: "data-dir" }: OutDir
func defaultObject(conf: TestConf): SomeObject =
discard
func completeCmdArg(T: type SomeObject, val: string): seq[string] =
@[]

View File

@ -2,7 +2,7 @@ import
std/[strutils],
unittest2,
../confutils,
./specialint
./private/specialint
type
TestConf* = object