PMunch / nimlsp

Language Server Protocol implementation for Nim
MIT License
418 stars 51 forks source link

Failure through duplicated C:\C:\ path prefix on Windows #23

Closed akavel closed 5 years ago

akavel commented 5 years ago

I am trying to run nimlsp with vim-lsp on Windows. I added some extra debugging statements in vim-lsp, and as of now I'm seeing the following error (nimlsp compiled with Nim 0.20.0):

23.07.2019 20:56:44:["lsp#register_server","server registered","nimlsp"]
23.07.2019 20:56:44:["s:on_text_document_did_open()",1,"","C:\\Users\\Mateusz",null]
23.07.2019 20:56:54:["s:on_text_document_did_open()",1,"nim","C:\\prog\\dali","file:///C:/prog/dali/src/dali.nim"]
23.07.2019 20:56:54:[{"response":{"data":{"__data__":"vim-lsp","lsp_id":1,"server_name":"nimlsp"},"message":"started lsp server successfully"}}]
23.07.2019 20:56:54:["--->",1,"nimlsp",{"method":"initialize","params":{"rootUri":"file:///C:/prog/dali","capabilities":{"workspace":{"configuration":true,"applyEdit":true}},"rootPath":"C:\\prog\\dali","processId":3180,"trace":"off"}}]
23.07.2019 20:56:54:["s:on_text_document_did_change()",1]
23.07.2019 20:56:54:[{"response":{"data":{"__data__":"vim-lsp","server_name":"nimlsp"},"message":"server already started"}}]
23.07.2019 20:56:54:[{"response":{"data":{"__data__":"vim-lsp","server_name":"nimlsp"},"message":"waiting for lsp server to initialize"}}]
23.07.2019 20:56:55:["<---",1,"nimlsp",{"response":{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"referencesProvider":true,"hoverProvider":true,"renameProvider":true,"definitionProvider":true,"textDocumentSync":{"save":{"includeText":true},"willSaveWaitUntil":false,"willSave":false,"change":1,"openClose":true},"completionProvider":{"resolveProvider":true,"triggerCharacters":["."," "]}}}},"request":{"id":1,"jsonrpc":"2.0","method":"initialize","params":{"rootUri":"file:///C:/prog/dali","capabilities":{"workspace":{"configuration":true,"applyEdit":true}},"rootPath":"C:\\prog\\dali","processId":3180,"trace":"off"}}}]
23.07.2019 20:56:55:["--->",1,"nimlsp",{"method":"initialized","params":{}}]
23.07.2019 20:56:55:[{"response":{"data":{"__data__":"vim-lsp","server_name":"nimlsp"},"message":"configuration sent"}}]
23.07.2019 20:56:55:["s:update_file_content()",1]
23.07.2019 20:56:55:["--->",1,"nimlsp",{"method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///C:/prog/dali/src/dali.nim","version":1,"languageId":"nim","text":"{.experimental: \"codeReordering\".}\nimport strutils\nimport critbits\nimport bitops\nimport std/sha1\nimport sets\nimport tables\nimport hashes\nimport patty\nimport dali/sortedset\nimport dali/blob\n\n# NOTE(akavel): this must be early, to make sure it's used, as codeReordering fails to move it\nproc `<`(p1, p2: Prototype): bool =\n  # echo \"called <\"\n  if p1.ret != p2.ret:\n    return p1.ret < p2.ret\n  for i in 0 ..< min(p1.params.len, p2.params.len):\n    if p1.params[i] != p2.params[i]:\n      return p1.params[i] < p2.params[i]\n  return p1.params.len < p2.params.len\n\nconverter toUint32[T: enum](s: set[T]): uint32 =\n  for v in s:\n    result = result or v.ord.uint32\n\n# Potentially useful bibliography\n#\n# DEX:\n# - https://github.com/corkami/pics/blob/master/binary/DalvikEXecutable.pdf\n# - [dex-format]: https://source.android.com/devices/tech/dalvik/dex-format\n# - https://blog.bugsnag.com/dex-and-d8/\n# - http://benlynn.blogspot.com/2009/02/minimal-dalvik-executables_06.html\n#\n# APK:\n# - https://fractalwrench.co.uk/posts/playing-apk-golf-how-low-can-an-android-app-go/\n# - https://github.com/fractalwrench/ApkGolf\n#\n# Opcodes:\n# - https://github.com/corkami/pics/blob/master/binary/opcodes_tables_compact.pdf\n# - https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html\n#\n# MORE:\n# - https://github.com/JesusFreke/smali\n# - https://github.com/linkedin/dexmaker\n# - https://github.com/iBotPeaches/Apktool\n\nvariant Arg:  # Argument of an instruction of Dalvik bytecode\n  RawX(raw4: uint4)\n  RawXX(raw8: uint8)\n  RawXXXX(raw16: uint16)\n  RegX(reg4: uint4)\n  RegXX(reg8: uint8)\n  FieldXXXX(field16: Field)\n  StringXXXX(string16: String)\n  TypeXXXX(type16: Type)\n  MethodXXXX(method16: Method)\n\nvariantp MaybeType:\n  SomeType(typ: Type)\n  NoType\n\nvariantp MaybeCode:\n  SomeCode(code: Code)\n  NoCode\n\n\ntype\n  Dex* = ref object\n    # Note: below fields are generally ordered from simplest to more complex\n    # (in order of dependency)\n    strings: CritBitTree[int]  # value: order of addition\n    types: SortedSet[string]\n    typeLists: seq[seq[Type]]\n    # NOTE: prototypes must have no duplicates, TODO: and be sorted by:\n    # (ret's type ID; args' type ID)\n    prototypes: SortedSet[Prototype]\n    # NOTE: fields must have no duplicates, TODO: and be sorted by:\n    # (class type ID, field name's string ID, field's type ID)\n    fields: SortedSet[tuple[class: Type, name: string, typ: Type]]\n    # NOTE: methods must have no duplicates, TODO: and be sorted by:\n    # (class type ID, name's string ID, prototype's proto ID)\n    methods: SortedSet[tuple[class: Type, name: string, proto: Prototype]]\n    classes*: seq[ClassDef]\n  NotImplementedYetError* = object of CatchableError\n  ConsistencyError* = object of CatchableError\n\n  Field* = ref object\n    class*: Type\n    typ*: Type\n    name*: String\n  Type* = String\n  String* = string\n  Method* = ref object\n    class*: Type\n    prototype*: Prototype  # a.k.a. method signature\n    name*: String\n  Prototype* = ref object\n    ret*: Type\n    params*: TypeList\n  TypeList* = seq[Type]\n\n  uint4* = range[0..15]   # e.g. register v0..v15\n\ntype\n  Instr* = ref object\n    opcode: uint8\n    args: seq[Arg]\n  Code* = ref object\n    registers*: uint16\n    ins*: uint16\n    outs*: uint16 # \"the number of words of outgoing argument space required by this code for method invocation\"\n    # tries: ?\n    # debug_info: ?\n    instrs*: seq[Instr]\n\ntype\n  ClassDef* = ref object\n    class*: Type\n    access*: set[Access]\n    superclass*: MaybeType\n    # interfaces: TypeList\n    # sourcefile: String\n    # annotations: ?\n    class_data*: ClassData\n    # static_values: ?\n  ClassData* = ref object\n    # static_fields*: ?\n    # instance_fields*: ?\n    direct_methods*: seq[EncodedMethod]\n    virtual_methods*: seq[EncodedMethod]\n  EncodedMethod* = ref object\n    m*: Method\n    access*: set[Access]\n    code*: MaybeCode\n  Access* = enum\n    Public = 0x1\n    Private = 0x2\n    Protected = 0x4\n    Static = 0x8\n    Final = 0x10\n    Synchronized = 0x20\n    Varargs = 0x80\n    Native = 0x100\n    Interface = 0x200\n    Abstract = 0x400\n    Annotation = 0x2000\n    Enum = 0x4000\n    Constructor = 0x1_0000\n\nproc hash*(proto: Prototype): Hash =\n  var h: Hash = 0\n  h = h !& hash(proto.ret)\n  h = h !& hash(proto.params)\n  result = !$h\n\nproc newDex*(): Dex =\n  new(result)\n  init(result.prototypes)\n  init(result.fields)\n  init(result.methods)\n\nproc render*(dex: Dex): string =\n  dex.collect()\n\n  # Storage for offsets where various sections of the file\n  # start. Will be needed to render map_list.\n  var sectionOffsets = newSeq[tuple[typ: uint16, size: uint32, offset: uint32]]()\n\n  # FIXME: ensure correct padding everywhere\n  var blob = \"\".Blob\n  #-- Partially render header\n  # Most of it can only be calculated after the rest of the segments.\n  sectionOffsets.add((0x0000'u16, 1'u32, blob.pos))\n  # TODO: handle various versions of targetSdkVersion file, not only 035\n  blob.puts(\"dex\\n035\\x00\")  # Magic prefix\n  let adlerSumSlot = blob.slot32()\n  blob.reserve(20)        # TODO: Fill sha1 sum\n  let fileSizeSlot = blob.slot32()\n  blob.put32(0x70)        # Header size\n  blob.put32(0x12345678)  # Endian constant\n  blob.put32(0)           # link_size\n  blob.put32(0)           # link_off\n  let mapOffsetSlot = blob.slot32()\n  blob.put32(dex.strings.len.uint32)\n  let stringIdsOffSlot = blob.slot32()\n  blob.put32(dex.types.len.uint32)\n  let typeIdsOffSlot = blob.slot32()\n  blob.put32(dex.prototypes.len.uint32)\n  let protoIdsOffSlot = blob.slot32()\n  blob.put32(dex.fields.len.uint32)\n  let fieldIdsOffSlot = blob.slot32()\n  blob.put32(dex.methods.len.uint32)\n  let methodIdsOffSlot = blob.slot32()\n  blob.put32(dex.classes.len.uint32)\n  let classDefsOffSlot = blob.slot32()\n  let dataSizeSlot = blob.slot32()\n  let dataOffSlot = blob.slot32()\n  # blob.reserve(0x70 - blob.pos.int)\n  #-- Partially render string_ids\n  # We preallocate space for the list of string offsets. We cannot fill it yet, as its contents\n  # will depend on the size of the other segments.\n  sectionOffsets.add((0x0001'u16, dex.strings.len.uint32, blob.pos))\n  blob.set(stringIdsOffSlot, blob.pos)\n  var stringOffsets = newSeq[Slot32](dex.strings.len)\n  for i in 0 ..< dex.strings.len:\n    stringOffsets[i] = blob.slot32()\n  #-- Render typeIDs.\n  sectionOffsets.add((0x0002'u16, dex.types.len.uint32, blob.pos))\n  blob.set(typeIdsOffSlot, blob.pos)\n  let stringIds = dex.stringsOrdering\n  # dex.types are already stored sorted, same as dex.strings, so we don't need\n  # to sort again by type IDs\n  for t in dex.types:\n    blob.put32(stringIds[dex.strings[t]].uint32)\n  #-- Partially render proto IDs.\n  # We cannot fill offsets for parameters (type lists), as they'll depend on the size of the\n  # segments inbetween.\n  sectionOffsets.add((0x0003'u16, dex.prototypes.len.uint32, blob.pos))\n  blob.set(protoIdsOffSlot, blob.pos)\n  var typeListOffsets = newSlots32[seq[Type]]()\n  for p in dex.prototypes:\n    blob.put32(stringIds[dex.strings[p.descriptor]].uint32)\n    blob.put32(dex.types.search(p.ret).uint32)\n    typeListOffsets.add(p.params, blob.slot32())\n    # echo p.ret, \" \", p.params\n  #-- Render field IDs\n  if dex.fields.len > 0:\n    sectionOffsets.add((0x0004'u16, dex.fields.len.uint32, blob.pos))\n    blob.set(fieldIdsOffSlot, blob.pos)\n  for f in dex.fields:\n    blob.put16(dex.types.search(f.class).uint16)\n    blob.put16(dex.types.search(f.typ).uint16)\n    blob.put32(stringIds[dex.strings[f.name]].uint32)\n  #-- Render method IDs\n  sectionOffsets.add((0x0005'u16, dex.methods.len.uint32, blob.pos))\n  if dex.methods.len > 0:\n    blob.set(methodIdsOffSlot, blob.pos)\n  for m in dex.methods:\n    # echo $m\n    blob.put16(dex.types.search(m.class).uint16)\n    blob.put16(dex.prototypes.search(m.proto).uint16)\n    blob.put32(stringIds[dex.strings[m.name]].uint32)\n  #-- Partially render class defs.\n  sectionOffsets.add((0x0006'u16, dex.classes.len.uint32, blob.pos))\n  blob.set(classDefsOffSlot, blob.pos)\n  var classDataOffsets = newSlots32[Type]()\n  const NO_INDEX = 0xffff_ffff'u32\n  for c in dex.classes:\n    blob.put32(dex.types.search(c.class).uint32)\n    blob.put32(c.access.uint32)\n    match c.superclass:\n      SomeType(t):\n        blob.put32(dex.types.search(t).uint32)\n      NoType:\n        blob.put32(NO_INDEX)\n    blob.put32(0'u32)  # TODO: interfaces_off\n    blob.put32(NO_INDEX)  # TODO: source_file_idx\n    blob.put32(0'u32)  # TODO: annotations_off\n    classDataOffsets.add(c.class, blob.slot32())\n    blob.put32(0'u32)  # TODO: static_values\n  #-- Render code items\n  let codeOffset = blob.pos\n  var codeItems = 0'u32\n  blob.set(dataOffSlot, blob.pos)\n  let dataStart = blob.pos\n  var codeOffsets = initTable[tuple[class: Type, name: string, proto: Prototype], uint32]()\n  for c in dex.classes:\n    let cd = c.class_data\n    for dm in cd.direct_methods & cd.virtual_methods:\n      if dm.code.kind == MaybeCodeKind.SomeCode:\n        inc(codeItems)\n        let code = dm.code.code\n        codeOffsets[dm.m.asTuple] = blob.pos\n        blob.put16(code.registers)\n        blob.put16(code.ins)\n        blob.put16(code.outs)\n        blob.put16(0'u16)  # TODO: tries_size\n        blob.put32(0'u32)  # TODO: debug_info_off\n        let slot = blob.slot32() # This shall be filled with size of instrs, in 16-bit code units\n        dex.renderInstrs(blob, code.instrs, stringIds)\n        blob.set(slot, (blob.pos - slot.uint32 - 4) div 2)\n  if codeItems > 0'u32:\n    sectionOffsets.add((0x2001'u16, codeItems, codeOffset))\n  #-- Render type lists\n  blob.pad32()\n  if dex.typeLists.len > 0:\n    sectionOffsets.add((0x1001'u16, dex.typeLists.len.uint32, blob.pos))\n  for l in dex.typeLists:\n    blob.pad32()\n    typeListOffsets.setAll(l, blob.pos, blob)\n    blob.put32(l.len.uint32)\n    for t in l:\n      blob.put16(dex.types.search(t).uint16)\n  #-- Render strings data\n  sectionOffsets.add((0x2002'u16, dex.strings.len.uint32, blob.pos))\n  for s in dex.stringsAsAdded:\n    let slot = stringOffsets[stringIds[dex.strings[s]]]\n    blob.set(slot, blob.pos)\n    # FIXME: MUTF-8: encode U+0000 as hex: C0 80\n    # FIXME: MUTF-8: use CESU-8 to encode code-points from beneath Basic Multilingual Plane (> U+FFFF)\n    # FIXME: length *in UTF-16 code units*, as ULEB128\n    blob.put_uleb128(s.len.uint32)\n    blob.puts(s & \"\\x00\")\n  #-- Render class data\n  sectionOffsets.add((0x2000'u16, dex.classes.len.uint32, blob.pos))\n  for c in dex.classes:\n    classDataOffsets.setAll(c.class, blob.pos, blob)\n    let d = c.class_data\n    blob.put_uleb128(0)  # TODO: static_fields_size\n    blob.put_uleb128(0)  # TODO: instance_fields_size\n    blob.put_uleb128(d.direct_methods.len.uint32)\n    blob.put_uleb128(d.virtual_methods.len.uint32)\n    # TODO: static_fields\n    # TODO: instance_fields\n    dex.renderEncodedMethods(blob, d.direct_methods, codeOffsets)\n    dex.renderEncodedMethods(blob, d.virtual_methods, codeOffsets)\n  #-- Render map_list\n  blob.pad32()\n  sectionOffsets.add((0x1000'u16, 1'u32, blob.pos))\n  blob.set(mapOffsetSlot, blob.pos)\n  blob.put32(sectionOffsets.len.uint32)\n  for s in sectionOffsets:\n    blob.put16(s.typ)\n    blob.reserve(2)  # unused\n    blob.put32(s.size)\n    blob.put32(s.offset)\n\n  #-- Fill remaining slots related to file size\n  blob.set(dataSizeSlot, blob.pos - dataStart)  # FIXME: round to 64?\n  blob.set(fileSizeSlot, blob.pos)\n  #-- Fill checksums\n  let sha1 = secureHash(blob.string.substr(0x20))\n  let sha1sum = parseHexStr($sha1)  # FIXME(akavel): should not have to go through string!\n  for i in 0 ..< 20:\n    blob.string[0x0c + i] = sha1sum[i]\n  blob.set(adlerSumSlot, adler32(blob.string.substr(0x0c)))\n  return blob.string\n\n\nproc collect(dex: Dex) =\n  # Collect strings and all the things from classes.\n  # (types, prototypes/signatures, fields, methods)\n  for c in dex.classes:\n    dex.addType(c.class)\n    if c.superclass.kind == MaybeTypeKind.SomeType:\n      dex.addType(c.superclass.typ)\n    let cd = c.class_data\n    for dm in cd.direct_methods & cd.virtual_methods:\n      dex.addMethod(dm.m)\n      if dm.code.kind == MaybeCodeKind.SomeCode:\n        for instr in dm.code.code.instrs:\n          for arg in instr.args:\n            match arg:\n              RawX(r): discard\n              RawXX(r): discard\n              RawXXXX(r): discard\n              RegX(r): discard\n              RegXX(r): discard\n              FieldXXXX(f):\n                dex.addField(f)\n              StringXXXX(s):\n                dex.addStr(s)\n              TypeXXXX(t):\n                dex.addType(t)\n              MethodXXXX(m):\n                dex.addMethod(m)\n  echo $dex.strings\n\nproc renderEncodedMethods(dex: Dex, blob: var Blob, methods: openArray[EncodedMethod], codeOffsets: Table[tuple[class: Type, name: string, proto: Prototype], uint32]) =\n  var prev = 0\n  for m in methods:\n    let tupl = m.m.asTuple\n    let idx = dex.methods.search(tupl)\n    blob.put_uleb128(uint32(idx - prev))\n    prev = idx\n    blob.put_uleb128(m.access.toUint32)\n    if Native notin m.access and Abstract notin m.access:\n      blob.put_uleb128(codeOffsets[tupl])\n    else:\n      blob.put_uleb128(0)\n\nproc renderInstrs(dex: Dex, blob: var Blob, instrs: openArray[Instr], stringIds: openArray[int]) =\n  var\n    high = true\n  for instr in instrs:\n    blob.putc(instr.opcode.chr)\n    for arg in instr.args:\n      # FIXME(akavel): padding\n      match arg:\n        RawX(v):\n          blob.put4(v, high)\n          high = not high\n        RawXX(v):\n          blob.putc(v.chr)\n        RawXXXX(v):\n          blob.put16(v)\n        RegX(v):\n          blob.put4(v, high)\n          high = not high\n        RegXX(v):\n          blob.putc(v.chr)\n        FieldXXXX(v):\n          blob.put16(dex.fields.search((v.class, v.name, v.typ)).uint16)\n        StringXXXX(v):\n          blob.put16(stringIds[dex.strings[v]].uint16)\n        TypeXXXX(v):\n          blob.put16(dex.types.search(v).uint16)\n        MethodXXXX(v):\n          blob.put16(dex.methods.search((v.class, v.name, v.prototype)).uint16)\n\nproc move_result_object*(reg: uint8): Instr =\n  return newInstr(0x0c, RegXX(reg))\nproc return_void*(): Instr =\n  return newInstr(0x0e, RawXX(0))\nproc const_high16*(reg: uint8, highBits: uint16): Instr =\n  return newInstr(0x15, RegXX(reg), RawXXXX(highBits))\nproc const_string*(reg: uint8, s: String): Instr =\n  return newInstr(0x1a, RegXX(reg), StringXXXX(s))\nproc new_instance*(reg: uint8, t: Type): Instr =\n  return newInstr(0x22, RegXX(reg), TypeXXXX(t))\nproc sget_object*(reg: uint8, field: Field): Instr =\n  return newInstr(0x62, RegXX(reg), FieldXXXX(field))\n\nproc invoke_virtual*(regC: uint4, m: Method): Instr =\n  return newInvoke1(0x6e, regC, m)\nproc invoke_virtual*(regC: uint4, regD: uint4, m: Method): Instr =\n  return newInvoke2(0x6e, regC, regD, m)\n\nproc invoke_super*(regC: uint4, regD: uint4, m: Method): Instr =\n  return newInvoke2(0x6f, regC, regD, m)\n\nproc invoke_direct*(regC: uint4, m: Method): Instr =\n  return newInvoke1(0x70, regC, m)\nproc invoke_direct*(regC: uint4, regD: uint4, m: Method): Instr =\n  return newInvoke2(0x70, regC, regD, m)\n\nproc invoke_static*(regC: uint4, m: Method): Instr =\n  return newInvoke1(0x71, regC, m)\n\n\nproc newInvoke1(opcode: uint8, regC: uint4, m: Method): Instr =\n  return newInstr(opcode, RawX(1), RawX(0), MethodXXXX(m), RawX(0), RegX(regC), RawXX(0))\nproc newInvoke2(opcode: uint8, regC: uint4, regD: uint4, m: Method): Instr =\n  return newInstr(opcode, RawX(2), RawX(0), MethodXXXX(m), RawX(regD), RegX(regC), RawXX(0))\n\nproc newInstr(opcode: uint8, args: varargs[Arg]): Instr =\n  ## NOTE: We're assuming little endian encoding of the\n  ## file here; 8-bit args should be ordered in\n  ## \"swapped order\" vs. the one listed in official\n  ## Android bytecode spec (i.e., add lower byte first,\n  ## higher byte later). On the other hand, 16-bit\n  ## words should not have contents rotated (just fill\n  ## them as in the spec).\n  return Instr(opcode: opcode, args: @args)\n\nproc addField(dex: Dex, f: Field) =\n  dex.addType(f.class)\n  dex.addType(f.typ)\n  dex.addStr(f.name)\n  dex.fields.incl((f.class, f.name, f.typ))\n\nproc addMethod(dex: Dex, m: Method) =\n  dex.addType(m.class)\n  dex.addPrototype(m.prototype)\n  dex.addStr(m.name)\n  dex.methods.incl((m.class, m.name, m.prototype))\n\nproc addPrototype(dex: Dex, proto: Prototype) =\n  dex.addType(proto.ret)\n  dex.addTypeList(proto.params)\n  dex.prototypes.incl(proto)\n  dex.addStr(proto.descriptor)\n\nproc descriptor(proto: Prototype): string =\n  proc typeChar(t: Type): string =\n    case t\n    of \"V\", \"Z\", \"B\", \"S\", \"C\", \"I\", \"J\", \"F\", \"D\": return t\n    else:\n      if t.startsWith\"[\" or t.startsWith\"L\": return \"L\"\n      else: raise newException(ConsistencyError, \"unexpected type in prototype: \" & t)\n  result = typeChar(proto.ret)\n  for p in proto.params:\n    result &= typeChar(p)\n\nproc addTypeList(dex: Dex, ts: seq[Type]) =\n  if ts.len == 0:\n    return\n  for p in ts:\n    dex.addType(p)\n  if not dex.typeLists.contains(ts):\n    dex.typeLists.add(ts)\n\nproc addType(dex: Dex, t: Type) =\n  dex.addStr(t)\n  dex.types.incl(t)\n\nproc addStr(dex: Dex, s: string) =\n  echo \"addStr \", repr(s)\n  if s.contains({'\\x00', '\\x80'..'\\xFF'}):\n    raise newException(NotImplementedYetError, \"strings with 0x00 or 0x80..0xFF bytes are not yet supported\")\n  let n = dex.strings.len\n  discard dex.strings.containsOrIncl(s, n)\n  echo dex.strings\n  # \"This list must be sorted by string contents, using UTF-16 code point\n  # values (not in a locale-sensitive manner), and it must not contain any\n  # duplicate entries.\" [dex-format] <- I think this is guaranteed by UTF-8 + CritBitTree type\n\nproc stringsOrdering(dex: Dex): seq[int] =\n  var i = 0\n  newSeq(result, dex.strings.len)\n  for s, added in dex.strings:\n    result[added] = i\n    inc i\n\nproc stringsAsAdded(dex: Dex): seq[string] =\n  newSeq(result, dex.strings.len)\n  for s, added in dex.strings:\n    result[added] = s\n\nproc renderStringsAndOffsets(dex: Dex, baseOffset: int): (string, string) =\n  var\n    ordering = dex.stringsOrdering\n    offsets = newString(4 * dex.strings.len)\n    buf = \"\"\n    pos = 0\n  for s in dex.stringsAsAdded:\n    offsets.write(4 * ordering[dex.strings[s]], uint32(baseOffset + pos))\n    # FIXME: MUTF-8: encode U+0000 as hex: C0 80\n    # FIXME: MUTF-8: use CESU-8 to encode code-points from beneath Basic Multilingual Plane (> U+FFFF)\n    # FIXME: length *in UTF-16 code units*, as ULEB128\n    pos += buf.write_uleb128(pos, s.len.uint32)\n    pos += buf.write(pos, s & \"\\x00\")\n  return (buf, offsets)\n\nproc sample_dex(tail: string): string =\n  var header = newString(0x2C)\n  # Magic prefix\n  # TODO: handle various versions of targetSdkVersion file, not only 035\n  header.write(0, \"dex\\n035\\x00\")\n  # File size\n  header.write(0x20, header.len.uint32 + tail.len.uint32)\n  # Header size\n  header.write(0x24, 0x70'u32)\n  # Endian constant\n  header.write(0x28, 0x12345678)\n\n  # SHA1 hash\n  # TODO: should allow hashing a \"stream\", to not allocate new string...\n  let sha1 = secureHash(header.substr(0x20) & tail)\n  header.write(0x0c, parseHexStr($sha1))  # FIXME(akavel): should not have to go through string!\n  # Adler checksum\n  header.write(0x08, adler32(header.substr(0x0c) & tail))\n  return header & tail\n\nproc write(s: var string, pos: int, what: string): int {.discardable.} =\n  if pos + what.len > s.len:\n    setLen(s, pos + what.len)\n  copyMem(addr(s[pos]), cstring(what), what.len)\n  return what.len\n\nproc write(s: var string, pos: int, what: uint32): int {.discardable.} =\n  # Little-endian\n  var buf = newString(4)\n  buf[0] = chr(what and 0xff)\n  buf[1] = chr(what shr 8 and 0xff)\n  buf[2] = chr(what shr 16 and 0xff)\n  buf[3] = chr(what shr 24 and 0xff)\n  s.write(pos, buf)\n  return 4\n\nproc write_uleb128(s: var string, pos: int, what: uint32): int =\n  ## Writes an uint32 in ULEB128 (https://source.android.com/devices/tech/dalvik/dex-format#leb128)\n  ## format, returning the number of bytes taken by the encoding.\n  if what == 0:\n    s.write(pos, \"\\x00\")\n    return 1\n  let\n    topBit = fastLog2(what)  # position of the highest bit set\n    n = topBit div 7 + 1         # number of bytes required for ULEB128 encoding of 'what'\n  var\n    buf = newString(n.Natural)\n    work = what\n    i = 0\n  while work >= 0x80'u32:\n    buf[i] = chr(0x80.byte or (work and 0x7F).byte)\n    work = work shr 7\n    inc i\n  buf[i] = chr(work.byte)\n  s.write(pos, buf)\n  return n\n\nfunc asTuple(m: Method): tuple[class: Type, name: string, proto: Prototype] =\n  return (class: m.class, name: m.name, proto: m.prototype)\n\nproc adler32(s: string): uint32 =\n  # https://en.wikipedia.org/wiki/Adler-32\n  var a: uint32 = 1\n  var b: uint32 = 0\n  const MOD_ADLER = 65521\n  for c in s:\n    a = (a + c.uint32) mod MOD_ADLER\n    b = (b + a) mod MOD_ADLER\n  result = (b shl 16) or a\n"}}}]
23.07.2019 20:56:55:[{"response":{"data":{"path":"file:///C:/prog/dali/src/dali.nim","__data__":"vim-lsp","filetype":"nim","server_name":"nimlsp"},"message":"textDocument/open sent"}}]
23.07.2019 20:56:55:[{"response":{"data":{"path":"file:///C:/prog/dali/src/dali.nim","__data__":"vim-lsp","server_name":"nimlsp"},"message":"not dirty"}}]
23.07.2019 20:56:55:[{"response":{"data":{"__data__":"vim-lsp","server_name":"nimlsp"},"message":"configuration sent"}}]
23.07.2019 20:56:55:[{"response":{"data":{"path":"file:///C:/prog/dali/src/dali.nim","__data__":"vim-lsp","server_name":"nimlsp"},"message":"already opened"}}]
23.07.2019 20:56:55:[{"response":{"data":{"path":"file:///C:/prog/dali/src/dali.nim","__data__":"vim-lsp","server_name":"nimlsp"},"message":"not dirty"}}]
23.07.2019 20:56:55:["<---(stderr)",1,"nimlsp",["C:\\Users\\Mateusz\\.choosenim\\toolchains\\nim-0.20.0\\lib\\pure\\os.nim(1851) expandFilename","Error: unhandled exception: file 'C:\\C:\\prog\\dali\\src' does not exist [OSError]",""]]

Any idea why the file 'C:\\C:\\prog\\dali\\src' does not exist [OSError] error could be happening (please note the duplicated C:\\C:\\ prefix), or how I could get a longer stack trace to try and debug it myself?

moigagoo commented 5 years ago

Got similar error with Sublime Text on Windows:

'chcp' is not recognized as an internal or external command,:
operable program or batch file.:
'chcp' is not recognized as an internal or external command,:
operable program or batch file.:
os.nim(1855)             expandFilename:
Error: unhandled exception: file 'C:\C:\Users\moigagoo\Projects\norm\src\norm' does not exist [OSError]:
os.nim(1855)             expandFilename:
Error: unhandled exception: file 'C:\C:\Users\moigagoo\Projects\norm\src\norm' does not exist [OSError]: