summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgtypes.nim129
-rw-r--r--compiler/debuginfo.nim89
-rw-r--r--compiler/extccomp.nim4
3 files changed, 164 insertions, 58 deletions
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index d7a5debdc..da671940a 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2016 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -11,6 +11,8 @@
 
 # ------------------------- Name Mangling --------------------------------
 
+import debuginfo
+
 proc isKeyword(w: PIdent): bool =
   # Nim and C++ share some keywords
   # it's more efficient to test the whole Nim keywords range
@@ -26,67 +28,66 @@ proc mangleField(name: PIdent): string =
     result[0] = result[0].toUpper # Mangling makes everything lowercase,
                                   # but some identifiers are C keywords
 
+proc hashOwner(s: PSym): FilenameHash =
+  var m = s
+  while m.kind != skModule: m = m.owner
+  let p = m.owner
+  assert p.kind == skPackage
+  result = gDebugInfo.register(p.name.s, m.name.s)
+
 proc mangleName(s: PSym): Rope =
   result = s.loc.r
   if result == nil:
-    when oKeepVariableNames:
-      let keepOrigName = s.kind in skLocalVars - {skForVar} and
-        {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
-        not isKeyword(s.name)
-      # XXX: This is still very experimental
-      #
-      # Even with all these inefficient checks, the bootstrap
-      # time is actually improved. This is probably because so many
-      # rope concatenations are now eliminated.
-      #
-      # Future notes:
-      # sfFromGeneric seems to be needed in order to avoid multiple
-      # definitions of certain variables generated in transf with
-      # names such as:
-      # `r`, `res`
-      # I need to study where these come from.
-      #
-      # about sfShadowed:
-      # consider the following Nim code:
-      #   var x = 10
-      #   block:
-      #     var x = something(x)
-      # The generated C code will be:
-      #   NI x;
-      #   x = 10;
-      #   {
-      #     NI x;
-      #     x = something(x); // Oops, x is already shadowed here
-      #   }
-      # Right now, we work-around by not keeping the original name
-      # of the shadowed variable, but we can do better - we can
-      # create an alternative reference to it in the outer scope and
-      # use that in the inner scope.
-      #
-      # about isCKeyword:
-      # Nim variable names can be C keywords.
-      # We need to avoid such names in the generated code.
-      # XXX: Study whether mangleName is called just once per variable.
-      # Otherwise, there might be better place to do this.
-      #
-      # about sfGlobal:
-      # This seems to be harder - a top level extern variable from
-      # another modules can have the same name as a local one.
-      # Maybe we should just implement sfShadowed for them too.
-      #
-      # about skForVar:
-      # These are not properly scoped now - we need to add blocks
-      # around for loops in transf
-      if keepOrigName:
-        result = s.name.s.mangle.rope
-      else:
-        add(result, rope(mangle(s.name.s)))
-        add(result, ~"_")
-        add(result, rope(s.id))
+    let keepOrigName = s.kind in skLocalVars - {skForVar} and
+      {sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
+      not isKeyword(s.name)
+    # Even with all these inefficient checks, the bootstrap
+    # time is actually improved. This is probably because so many
+    # rope concatenations are now eliminated.
+    #
+    # sfFromGeneric is needed in order to avoid multiple
+    # definitions of certain variables generated in transf with
+    # names such as:
+    # `r`, `res`
+    # I need to study where these come from.
+    #
+    # about sfShadowed:
+    # consider the following Nim code:
+    #   var x = 10
+    #   block:
+    #     var x = something(x)
+    # The generated C code will be:
+    #   NI x;
+    #   x = 10;
+    #   {
+    #     NI x;
+    #     x = something(x); // Oops, x is already shadowed here
+    #   }
+    # Right now, we work-around by not keeping the original name
+    # of the shadowed variable, but we can do better - we can
+    # create an alternative reference to it in the outer scope and
+    # use that in the inner scope.
+    #
+    # about isCKeyword:
+    # Nim variable names can be C keywords.
+    # We need to avoid such names in the generated code.
+    #
+    # about sfGlobal:
+    # This seems to be harder - a top level extern variable from
+    # another modules can have the same name as a local one.
+    # Maybe we should just implement sfShadowed for them too.
+    #
+    # about skForVar:
+    # These are not properly scoped now - we need to add blocks
+    # around for loops in transf
+    result = s.name.s.mangle.rope
+    if keepOrigName:
+      result.add "0"
     else:
-      add(result, rope(mangle(s.name.s)))
       add(result, ~"_")
       add(result, rope(s.id))
+      add(result, ~"_")
+      add(result, rope(hashOwner(s).BiggestInt))
     s.loc.r = result
 
 proc typeName(typ: PType): Rope =
@@ -570,15 +571,27 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope =
     let t = if t.kind == tyRange: t.lastSon else: t
     result = getTypeName(t)
     idTablePut(m.typeCache, t, result)
+    var size: int
     if firstOrd(t) < 0:
       addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
+      size = 4
     else:
-      case int(getSize(typ))
+      size = int(getSize(t))
+      case size
       of 1: addf(m.s[cfsTypes], "typedef NU8 $1;$n", [result])
       of 2: addf(m.s[cfsTypes], "typedef NU16 $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])
-      else: internalError(t.sym.info, "getTypeDescAux: " & $getSize(t))
+      else: internalError(t.sym.info, "getTypeDescAux: enum")
+    let owner = hashOwner(t.sym)
+    if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
+      var vals: seq[(string, int)] = @[]
+      for i in countup(0, t.n.len - 1):
+        assert(t.n.sons[i].kind == nkSym)
+        let field = t.n.sons[i].sym
+        vals.add((field.name.s, field.position.int))
+      gDebugInfo.registerEnum(EnumDesc(size: size, owner: owner, id: t.sym.id,
+        name: t.sym.name.s, values: vals))
   of tyProc:
     result = getTypeName(t)
     idTablePut(m.typeCache, t, result)
diff --git a/compiler/debuginfo.nim b/compiler/debuginfo.nim
new file mode 100644
index 000000000..f30de2893
--- /dev/null
+++ b/compiler/debuginfo.nim
@@ -0,0 +1,89 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2016 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## The compiler can generate debuginfo to help debuggers in translating back from C/C++/JS code
+## to Nim. The data structure has been designed to produce something useful with Nim's marshal
+## module.
+
+type
+  FilenameHash* = uint32
+  FilenameMapping* = object
+    package*, file*: string
+    mangled*: FilenameHash
+  EnumDesc* = object
+    size*: int
+    owner*: FilenameHash
+    id*: int
+    name*: string
+    values*: seq[(string, int)]
+  DebugInfo* = object
+    version*: int
+    files*: seq[FilenameMapping]
+    enums*: seq[EnumDesc]
+    conflicts*: bool
+
+{.experimental.}
+
+using
+  self: var DebugInfo
+  package, file: string
+
+{.this: self.}
+
+proc sdbmHash(hash: FilenameHash, c: char): FilenameHash {.inline.} =
+  return FilenameHash(c) + (hash shl 6) + (hash shl 16) - hash
+
+proc sdbmHash(package, file): FilenameHash =
+  template `&=`(x, c) = x = sdbmHash(x, c)
+  result = 0
+  for i in 0..<package.len:
+    result &= package[i]
+  result &= '.'
+  for i in 0..<file.len:
+    result &= file[i]
+
+proc register*(self; package, file): FilenameHash =
+  result = sdbmHash(package, file)
+  for f in files:
+    if f.mangled == result:
+      if f.package == package and f.file == file: return
+      conflicts = true
+      break
+  files.add(FilenameMapping(package: package, file: file, mangled: result))
+
+proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: FilenameHash): bool =
+  for en in enums:
+    if en.owner == owner and en.name == ename and en.id == id: return true
+
+proc registerEnum*(self; ed: EnumDesc) =
+  enums.add ed
+
+proc init*(self) =
+  version = 1
+  files = @[]
+  enums = @[]
+
+var gDebugInfo*: DebugInfo
+debuginfo.init gDebugInfo
+
+import marshal, streams
+
+proc writeDebugInfo*(self; file) =
+  let s = newFileStream(file, fmWrite)
+  store(s, self)
+  s.close
+
+proc writeDebugInfo*(file) = writeDebugInfo(gDebugInfo, file)
+
+proc loadDebugInfo*(self; file) =
+  let s = newFileStream(file, fmRead)
+  load(s, self)
+  s.close
+
+proc loadDebugInfo*(file) = loadDebugInfo(gDebugInfo, file)
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 16a8b8bd4..b2ee9c7f1 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -16,6 +16,8 @@ import
   lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
   securehash, streams
 
+from debuginfo import writeDebugInfo
+
 type
   TSystemCC* = enum
     ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
@@ -736,6 +738,8 @@ proc callCCompiler*(projectfile: string) =
       if not noAbsolutePaths():
         if not exefile.isAbsolute():
           exefile = joinPath(splitFile(projectfile).dir, exefile)
+      if optCDebug in gGlobalOptions:
+        writeDebugInfo(exefile.changeFileExt("ndb"))
       exefile = quoteShell(exefile)
       let linkOptions = getLinkOptions() & " " &
                         getConfigVar(cCompiler, ".options.linker")