use a full MD5 hash with no collision detection for proc names

This commit is contained in:
Araq 2016-11-08 08:36:33 +01:00
commit 72af7e6821
5 changed files with 127 additions and 64 deletions

View file

@ -11,7 +11,7 @@
# ------------------------- Name Mangling -------------------------------- # ------------------------- Name Mangling --------------------------------
import debuginfo, sighashes import sighashes
proc isKeyword(w: PIdent): bool = proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords # Nim and C++ share some keywords
@ -29,26 +29,29 @@ proc mangleField(name: PIdent): string =
# Mangling makes everything lowercase, # Mangling makes everything lowercase,
# but some identifiers are C keywords # but some identifiers are C keywords
proc hashOwner(s: PSym): SigHash = when false:
proc hashOwner(s: PSym): SigHash =
var m = s var m = s
while m.kind != skModule: m = m.owner while m.kind != skModule: m = m.owner
let p = m.owner let p = m.owner
assert p.kind == skPackage assert p.kind == skPackage
result = gDebugInfo.register(p.name.s, m.name.s) result = gDebugInfo.register(p.name.s, m.name.s)
proc idOrSig(m: BModule; s: PSym): BiggestInt = proc idOrSig(m: BModule; s: PSym): Rope =
if s.kind in routineKinds and s.typ != nil and sfExported in s.flags and if s.kind in routineKinds and s.typ != nil and sfExported in s.flags and
s.typ.callConv != ccInline: s.typ.callConv != ccInline:
# signatures for exported routines are reliable enough to # signatures for exported routines are reliable enough to
# produce a unique name and this means produced C++ is more stable wrt # produce a unique name and this means produced C++ is more stable wrt
# Nim changes: # Nim changes:
when false:
let h = hashType(s.typ, {considerParamNames}) let h = hashType(s.typ, {considerParamNames})
if m.hashConflicts.containsOrIncl(cast[int](h)): if m.hashConflicts.containsOrIncl(cast[int](h)):
result = s.id result = s.id
else: else:
result = BiggestInt(h) result = BiggestInt(h)
result = rope($hashProc(s))
else: else:
result = s.id result = rope s.id
proc mangleName(m: BModule; s: PSym): Rope = proc mangleName(m: BModule; s: PSym): Rope =
result = s.loc.r result = s.loc.r
@ -100,9 +103,9 @@ proc mangleName(m: BModule; s: PSym): Rope =
result.add "0" result.add "0"
else: else:
add(result, ~"_") add(result, ~"_")
add(result, rope(m.idOrSig(s))) add(result, m.idOrSig(s))
add(result, ~"_") #add(result, ~"_")
add(result, rope(hashOwner(s).BiggestInt)) #add(result, rope(hashOwner(s).BiggestInt))
s.loc.r = result s.loc.r = result
proc typeName(typ: PType): Rope = proc typeName(typ: PType): Rope =
@ -114,7 +117,7 @@ proc getTypeName(m: BModule; typ: PType): Rope =
result = typ.sym.loc.r result = typ.sym.loc.r
else: else:
if typ.loc.r == nil: if typ.loc.r == nil:
when true: when false:
# doesn't work yet and would require bigger rewritings # doesn't work yet and would require bigger rewritings
let h = hashType(typ, {considerParamNames})# and 0x0fff_ffffu32 let h = hashType(typ, {considerParamNames})# and 0x0fff_ffffu32
let sig = let sig =
@ -125,8 +128,8 @@ proc getTypeName(m: BModule; typ: PType): Rope =
else: else:
let sig = BiggestInt typ.id let sig = BiggestInt typ.id
typ.loc.r = typ.typeName & sig.rope #& ("_" & m.module.name.s) typ.loc.r = typ.typeName & sig.rope #& ("_" & m.module.name.s)
if typ.kind != tySet: #if typ.kind != tySet:
typ.loc.r.add "_" & m.module.name.s # typ.loc.r.add "_" & m.module.name.s
result = typ.loc.r result = typ.loc.r
if result == nil: internalError("getTypeName: " & $typ.kind) if result == nil: internalError("getTypeName: " & $typ.kind)
@ -625,6 +628,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
else: internalError(t.sym.info, "getTypeDescAux: enum") else: internalError(t.sym.info, "getTypeDescAux: enum")
when false:
let owner = hashOwner(t.sym) let owner = hashOwner(t.sym)
if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner): if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
var vals: seq[(string, int)] = @[] var vals: seq[(string, int)] = @[]

View file

@ -7,9 +7,9 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## The compiler can generate debuginfo to help debuggers in translating back from C/C++/JS code ## The compiler can generate debuginfo to help debuggers in translating back
## to Nim. The data structure has been designed to produce something useful with Nim's marshal ## from C/C++/JS code to Nim. The data structure has been designed to produce
## module. ## something useful with Nim's marshal module.
import sighashes import sighashes

View file

@ -16,7 +16,7 @@ import
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
securehash, streams securehash, streams
from debuginfo import writeDebugInfo #from debuginfo import writeDebugInfo
type type
TSystemCC* = enum TSystemCC* = enum
@ -734,8 +734,8 @@ proc callCCompiler*(projectfile: string) =
if not noAbsolutePaths(): if not noAbsolutePaths():
if not exefile.isAbsolute(): if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile) exefile = joinPath(splitFile(projectfile).dir, exefile)
if optCDebug in gGlobalOptions: #if optCDebug in gGlobalOptions:
writeDebugInfo(exefile.changeFileExt("ndb")) # writeDebugInfo(exefile.changeFileExt("ndb"))
exefile = quoteShell(exefile) exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() & " " & let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker") getConfigVar(cCompiler, ".options.linker")

View file

@ -9,19 +9,63 @@
## Computes hash values for routine (proc, method etc) signatures. ## Computes hash values for routine (proc, method etc) signatures.
import ast import ast, md5
export md5.`==`
type when false:
type
SigHash* = uint32 ## a hash good enough for a filename or a proc signature SigHash* = uint32 ## a hash good enough for a filename or a proc signature
proc sdbmHash(hash: SigHash, c: char): SigHash {.inline.} = proc sdbmHash(hash: SigHash, c: char): SigHash {.inline.} =
return SigHash(c) + (hash shl 6) + (hash shl 16) - hash return SigHash(c) + (hash shl 6) + (hash shl 16) - hash
template `&=`*(x: var SigHash, c: char) = x = sdbmHash(x, c) template `&=`*(x: var SigHash, c: char) = x = sdbmHash(x, c)
template `&=`*(x: var SigHash, s: string) = template `&=`*(x: var SigHash, s: string) =
for c in s: x = sdbmHash(x, c) for c in s: x = sdbmHash(x, c)
proc hashSym(c: var SigHash, s: PSym) = else:
type
SigHash* = Md5Digest
const
cb64 = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9a",
"9b", "9c"]
proc toBase64a(s: cstring, len: int): string =
## encodes `s` into base64 representation.
result = newStringOfCap(((len + 2) div 3) * 4)
var i = 0
while i < len - 2:
let a = ord(s[i])
let b = ord(s[i+1])
let c = ord(s[i+2])
result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
result.add cb64[c and 0x3F]
inc(i, 3)
if i < len-1:
let a = ord(s[i])
let b = ord(s[i+1])
result.add cb64[a shr 2]
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
result.add cb64[((b and 0x0F) shl 2)]
elif i < len:
let a = ord(s[i])
result.add cb64[a shr 2]
result.add cb64[(a and 3) shl 4]
proc `$`*(u: SigHash): string =
toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
proc `&=`(c: var MD5Context, ch: char) = md5Update(c, unsafeAddr ch, 1)
proc hashSym(c: var MD5Context, s: PSym) =
if sfAnon in s.flags or s.kind == skGenericParam: if sfAnon in s.flags or s.kind == skGenericParam:
c &= ":anon" c &= ":anon"
else: else:
@ -31,9 +75,9 @@ proc hashSym(c: var SigHash, s: PSym) =
c &= "." c &= "."
it = it.owner it = it.owner
proc hashTree(c: var SigHash, n: PNode) = proc hashTree(c: var MD5Context, n: PNode) =
template lowlevel(v) = template lowlevel(v) =
for i in 0..<sizeof(v): c = sdbmHash(c, cast[cstring](unsafeAddr(v))[i]) md5Update(c, cast[cstring](unsafeAddr(v)), sizeof(v))
if n == nil: if n == nil:
c &= "\255" c &= "\255"
@ -63,7 +107,7 @@ type
ConsiderFlag* = enum ConsiderFlag* = enum
considerParamNames considerParamNames
proc hashType(c: var SigHash, t: PType; flags: set[ConsiderFlag]) = proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
# modelled after 'typeToString' # modelled after 'typeToString'
if t == nil: if t == nil:
c &= "\254" c &= "\254"
@ -133,5 +177,35 @@ proc hashType(c: var SigHash, t: PType; flags: set[ConsiderFlag]) =
if tfNotNil in t.flags: c &= "not nil" if tfNotNil in t.flags: c &= "not nil"
proc hashType*(t: PType; flags: set[ConsiderFlag]): SigHash = proc hashType*(t: PType; flags: set[ConsiderFlag]): SigHash =
result = 0 var c: MD5Context
hashType result, t, flags md5Init c
hashType c, t, flags
md5Final c, result
proc hashProc*(s: PSym): SigHash =
var c: MD5Context
md5Init c
hashType c, s.typ, {considerParamNames}
var m = s
while m.kind != skModule: m = m.owner
let p = m.owner
assert p.kind == skPackage
c &= p.name.s
c &= "."
c &= m.name.s
md5Final c, result
proc hashOwner*(s: PSym): SigHash =
var c: MD5Context
md5Init c
var m = s
while m.kind != skModule: m = m.owner
let p = m.owner
assert p.kind == skPackage
c &= p.name.s
c &= "."
c &= m.name.s
md5Final c, result

View file

@ -173,21 +173,6 @@ proc sha1(src: string): Sha1Digest =
## Calculate SHA1 from input string ## Calculate SHA1 from input string
sha1(src, src.len) sha1(src, src.len)
proc `!&`*(h: Hash, val: int): Hash {.inline.} =
## mixes a hash value `h` with `val` to produce a new hash value. This is
## only needed if you need to implement a hash proc for a new datatype.
result = h +% val
result = result +% result shl 10
result = result xor (result shr 6)
proc `!$`*(h: Hash): Hash {.inline.} =
## finishes the computation of the hash value. This is
## only needed if you need to implement a hash proc for a new datatype.
proc
proc hashData*(data: pointer, size: int): Hash =
proc secureHash*(str: string): SecureHash = SecureHash(sha1(str)) proc secureHash*(str: string): SecureHash = SecureHash(sha1(str))
proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename)) proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename))
proc `$`*(self: SecureHash): string = proc `$`*(self: SecureHash): string =