i have exc_bad_access(code=2 ...)
error keeps popping need with. believe i've managed pin down source of malformed pointer, i'm @ loss how fix it.
apparently swift compiler choosing wrong initializer 1 of classes. according instruments, when class description
initialized, calls initializer lyricblock
. not time, sometimes. regardless of whether compiler set -onone or -o whole-module-optimization.
here's 2 classes like:
class lyricblock: node, leftdelimited, rightdelimited { var leftdelimiter: delimiter var rigthdelimiter: delimiter init(start: int, body: range<int>, end: int) { self.leftdelimiter = delimiter(range: start..<body.lowerbound) self.rightdelimiter = delimiter(range: body.upperbound..<end) super.init(range: body) } } class description: node, leftdelimited, rightdelimited { var leftdelimiter: delimiter var leftdelimiter: delimiter init(start: int, body: range<int>, end: int) { self.leftdelimiter = delimiter(range: start..<body.lowerbound) self.rightdelimiter = delimiter(range: body.upperbound..<end) super.init(range: body) } }
as can see, lyricblock
, description
inherit node
base class , share couple protocols in common, otherwise have nothing each other.
some possibly relevant code:
class node { weak var parent: node? var next: node? var firstchild: node? weak var lastchild: node? let offset: int internal(set) var length: int init(range: range<int>) { self.offset = range.lowerbound self.length = range.upperbound - range.lowerbound } func addchild(_ child: node) { if firstchild == nil { firstchild = child } else { lastchild?.next = child } lastchild = child child.parent = self } } class parser { // ... func processline(in buffer: buffer) { // parse current line block node. var block = blockforline(in: buffer) // try find appropriate container node. if none can found, block replaced description. let container = appropriatecontainer(for: &block, in: buffer) container.addchild(block) // edge case parse first-line lyrics if let cueblock = block as? cueblock { if let lyricblock = scanforlyric(in: buffer, at: cueblock.direction.range.lowerbound) { let lyriccontainer = lyriccontainer(range: lyricblock.range.lowerbound..<endoflinecharnumber) lyriccontainer.addchild(lyricblock) cueblock.replacedirection(with: lyriccontainer) parseinlines(for: lyricblock, in: buffer) } } // parse inlines appropriate switch block { case facsimileblock, description, lyricblock: parseinlines(for: block, in: buffer) // ... } } func blockforline(in buffer: buffer) -> node { let whitespace = buffer.scanforfirstnonspace(at: charnumber, limit: endoflinecharnumber) // ... let endwhitespace = buffer.scanbackwardforfirstnonspace(at: endoflinecharnumber, limit: wc) let description = description(start: charnumber, body: whitespace..< endwhitespace, end: endoflinecharnumber) return description } func appropriatecontainer(for block: inout node, in buffer: buffer) -> node { switch block { // these block types can ever level-1 case header, description, endblock, horizontalbreak: return root // ... case lyricblock: guard let cuecontainer = root.lastchild as? cuecontainer else { break } guard let cueblock = cuecontainer.lastchild as? cueblock else { break } guard let direction = cueblock.direction as? lyriccontainer else { break } direction.extendlengthtoinclude(node: block) cueblock.extendlengthtoinclude(node: direction) cuecontainer.extendlengthtoinclude(node: cueblock) return direction default: break } let whitespace = buffer.scanforfirstnonspace(at: charnumber, limit: endoflinecharnumber) let endwhitespace = buffer.scanbackwardforfirstnonspace(at: endoflinecharnumber, limit: wc) // invalid syntax, time fail gracefully block = description(start: charnumber, body: whitespace..< endwhitespace, end: endoflinecharnumber) return root } func parseinlines(for stream: node, in buffer: buffer) { // ... scans buffer inlines , enques them in queue while let next = queue.dequeue() { let nextrange = next.rangeincludingmarkers if nextrange.lowerbound > j { let lit = literal(range: j..<nextrange.lowerbound) stream.addchild(lit) } stream.addchild(next) j = nextrange.upperbound } if j < endoflinecharnumber { let lit = literal(range: j..<noderange.upperbound) stream.addchild(lit) } } // ... }
as side note, wondered if might running mangling issue class signatures , tried making rightdelimiter
, leftdelimiter
properties of node
instead of using protocols. resulted in compiler calling identifier
initializer instead. don't know proves. frankly i'm @ loss. help?
after more tinkering able @ least partially solve problem.
it turns out that, whatever reason, arc craps out on linked lists above size. able confirm creating generic linked list in new project , stress testing progressively longer lists.
class llnode<t> { var value: t? var next: llnode<t>? weak var previous: llnode<t>? } class llist<t> { var head: llnode<t> var tail: llnode<t> // boring old linked list implementation } let list = list<int>() in 0..<10000 { list.append(i) }
sure enough, after 10,000 nodes started getting exc_bad_access again on generic list, when list
deinitialized. matched behavior getting parser above. since code uses linked lists model children in tree (two dimensions of reference counting!), arc having resolve references on own -- , failing. why crashes still beyond ability explain, @ least explains source of crash.
to confirm, created linked list in c , made swift wrapper around it, of garbage collection being implemented in c.
import liblist class list<t> { var list: unsafemutablepointer<llist> // swift interface list deinit { list_free(list) } }
the wrapper able handle every size threw @ -- as 500,000 nodes, @ point felt satisfied , stopped testing.
if interested in full code used around problem, i've created swift package @ https://github.com/dmcarth/list
Comments
Post a Comment