# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements the canonalization for the various caching mechanisms. import strutils, db_sqlite, md5 var db: DbConn # We *hash* the relevant information into 128 bit hashes. This should be good # enough to prevent any collisions. type TUid = distinct MD5Digest # For name mangling we encode these hashes via a variant of base64 (called # 'base64a') and prepend the *primary* identifier to ease the debugging pain. # So a signature like: # # proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) # # is mangled into: # gABI_MTdmOWY5MTQ1MDcyNGQ3ZA # # This is a good compromise between correctness and brevity. ;-) 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", "9", "_A", "_B"] proc toBase64a(s: cstring, len: int): string = ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. result = newStringOfCap(((len + 2) div 3) * 4) var i = 0 while i < s.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 < s.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 < s.len: let a = ord(s[i]) result.add cb64[a shr 2] result.add cb64[(a and 3) shl 4] proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u)) proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len) proc hashSym(c: var MD5Context, s: PSym) = if sfAnon in s.flags or s.kind == skGenericParam: c &= ":anon" else: var it = s.owner while it != nil: hashSym(c, it) c &= "." it = s.owner c &= s.name.s proc hashTree(c: var MD5Context, n: PNode) = if n == nil: c &= "\255" return var k = n.kind md5Update(c, cast[cstring](addr(k)), 1) # we really must not hash line information. 'n.typ' is debatable but # shouldn't be necessary for now and avoids potential infinite recursions. case n.kind of nkEmpty, nkNilLit, nkType: discard of nkIdent: c &= n.ident.s of nkSym: hashSym(c, n.sym) of nkCharLit..nkUInt64Lit: var v = n.intVal md5Update(c, cast[cstring](addr(v)), sizeof(v)) of nkFloatLit..nkFloat64Lit: var v = n.floatVal md5Update(c, cast[cstring](addr(v)), sizeof(v)) of nkStrLit..nkTripleStrLit: c &= n.strVal else: for i in 0..') proc encodeType(w: PRodWriter, t: PType, result: var string) = if t == nil: # nil nodes have to be stored too: result.add("[]") return # we need no surrounding [] here because the type is in a line of its own if t.kind == tyForward: internalError("encodeType: tyForward") # for the new rodfile viewer we use a preceding [ so that the data section # can easily be disambiguated: result.add('[') encodeVInt(ord(t.kind), result) result.add('+') encodeVInt(t.id, result) if t.n != nil: encodeNode(w, unknownLineInfo, t.n, result) if t.flags != {}: result.add('$') encodeVInt(cast[int32](t.flags), result) if t.callConv != low(t.callConv): result.add('?') encodeVInt(ord(t.callConv), result) if t.owner != nil: result.add('*') encodeVInt(t.owner.id, result) pushSym(w, t.owner) if t.sym != nil: result.add('&') encodeVInt(t.sym.id, result) pushSym(w, t.sym) if t.size != - 1: result.add('/') encodeVBiggestInt(t.size, result) if t.align != - 1: result.add('=') encodeVInt(t.align, result) encodeLoc(w, t.loc, result) for i in 0..