diff --git a/common/uipriv.h b/common/uipriv.h index 8bffda4e..a36be4eb 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -12,10 +12,12 @@ extern void *uiAlloc(size_t, const char *); extern void *uiRealloc(void *, size_t, const char *); extern void uiFree(void *); +#define _ns2(s) #s +#define _ns(s) _ns2(s) extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); -#define implbug(...) _implbug(__FILE__, #__LINE__, __func__, __VA_ARGS__) +#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _implbug(__FILE__, #__LINE__, __func__, __VA_ARGS__) +#define userbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index 49acd1d4..85d89615 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -13,6 +13,7 @@ MFILES += \ darwin/datetimepicker.m \ darwin/draw.m \ darwin/drawtext.m \ + darwin/debug.m \ darwin/entry.m \ darwin/fontbutton.m \ darwin/group.m \ diff --git a/darwin/alloc.m b/darwin/alloc.m index 5fd42f3b..9f0531ba 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -22,7 +22,9 @@ void initAlloc(void) void uninitAlloc(void) { + NSMutableString *str; NSUInteger i; + NSValue *v; // delegates might have mapTables allocated // TODO verify they are empty @@ -33,16 +35,14 @@ void uninitAlloc(void) [allocations release]; return; } - fprintf(stderr, "[libui] leaked allocations:\n"); - [allocations enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { - NSValue *v; + str = [NSMutableString new]; + for (v in allocations) { void *ptr; - v = (NSValue *) obj; ptr = [v pointerValue]; - fprintf(stderr, "[libui] %p %s\n", ptr, *TYPE(ptr)); - }]; - complain("either you left something around or there's a bug in libui"); + [str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]]; + } + userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); } void *uiAlloc(size_t size, const char *type) @@ -86,7 +86,7 @@ void *uiRealloc(void *p, size_t new, const char *type) void uiFree(void *p) { if (p == NULL) - complain("attempt to uiFree(NULL); there's a bug somewhere"); + implbug("attempt to uiFree(NULL)"); p = BASE(p); free(p); [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/darwin/area.m b/darwin/area.m index 72aee13c..ada4ae6d 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -335,7 +335,7 @@ int sendAreaEvents(NSEvent *e) void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) { if (!a->scrolling) - complain("attempt to call uiAreaSetSize() on a non-scrolling uiArea"); + userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (uiArea: %p)", a); [a->area setFrameSize:NSMakeSize(width, height)]; } @@ -347,7 +347,7 @@ void uiAreaQueueRedrawAll(uiArea *a) void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) { if (!a->scrolling) - complain("attempt to call uiAreaScrollTo() on a non-scrolling uiArea"); + userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (uiArea: %p)", a); [a->area scrollRectToVisible:NSMakeRect(x, y, width, height)]; // don't worry about the return value; it just says whether scrolling was needed } diff --git a/darwin/debug.m b/darwin/debug.m new file mode 100644 index 00000000..ec55a84b --- /dev/null +++ b/darwin/debug.m @@ -0,0 +1,37 @@ +// 13 may 2016 +#import "uipriv_darwin.h" + +// TODO don't halt on release builds + +static void bug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +{ + NSMutableString *str; + NSString *formatted; + + str = [NSMutableString new]; + [str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s %s", file, line, func, prefix]]; + formatted = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:ap]; + [str appendString:formatted]; + [formatted release]; + NSLog(@"%@", str); + [str release]; + __builtin_trap(); +} + +void _implbug(const char *file, const char *line, const char *func, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + bug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); + va_end(ap); +} + +void _userbug(const char *file, const char *line, const char *func, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + bug(file, line, func, "You have a bug: ", format, ap); + va_end(ap); +} diff --git a/darwin/draw.m b/darwin/draw.m index a147283c..6661d6a1 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -26,7 +26,7 @@ void uiDrawFreePath(uiDrawPath *p) void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) { if (p->ended) - complain("attempt to add figure to ended path in uiDrawPathNewFigure()"); + userbug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathMoveToPoint(p->path, NULL, x, y); } @@ -36,7 +36,7 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d double startx, starty; if (p->ended) - complain("attempt to add figure to ended path in uiDrawPathNewFigureWithArc()"); + userbug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p); sinStart = sin(startAngle); cosStart = cos(startAngle); startx = xCenter + radius * cosStart; @@ -47,8 +47,9 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d void uiDrawPathLineTo(uiDrawPath *p, double x, double y) { + // TODO refine this to require being in a path if (p->ended) - complain("attempt to add line to ended path in uiDrawPathLineTo()"); + implbug("attempt to add line to ended path in uiDrawPathLineTo()"); CGPathAddLineToPoint(p->path, NULL, x, y); } @@ -56,8 +57,9 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu { bool cw; + // TODO likewise if (p->ended) - complain("attempt to add arc to ended path in uiDrawPathArcTo()"); + implbug("attempt to add arc to ended path in uiDrawPathArcTo()"); if (sweep > 2 * M_PI) sweep = 2 * M_PI; cw = false; @@ -72,8 +74,9 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY) { + // TODO likewise if (p->ended) - complain("attempt to add bezier to ended path in uiDrawPathBezierTo()"); + implbug("attempt to add bezier to ended path in uiDrawPathBezierTo()"); CGPathAddCurveToPoint(p->path, NULL, c1x, c1y, c2x, c2y, @@ -82,15 +85,16 @@ void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, doubl void uiDrawPathCloseFigure(uiDrawPath *p) { + // TODO likewise if (p->ended) - complain("attempt to close figure of ended path in uiDrawPathCloseFigure()"); + implbug("attempt to close figure of ended path in uiDrawPathCloseFigure()"); CGPathCloseSubpath(p->path); } void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height) { if (p->ended) - complain("attempt to add rectangle to ended path in uiDrawPathAddRectangle()"); + userbug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height)); } @@ -132,7 +136,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro uiDrawPath p2; if (!path->ended) - complain("path not ended in uiDrawStroke()"); + userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); switch (p->Cap) { case uiDrawLineCapFlat: @@ -275,7 +279,7 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) { if (!path->ended) - complain("path not ended in uiDrawFill()"); + userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (b->Type) { case uiDrawBrushTypeSolid: @@ -289,7 +293,7 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) // TODO return; } - complain("unknown brush type %d in uiDrawFill()", b->Type); + userbug("Unknown brush type %d passed to uiDrawFill().", b->Type); } static void m2c(uiDrawMatrix *m, CGAffineTransform *c) @@ -421,7 +425,7 @@ void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m) void uiDrawClip(uiDrawContext *c, uiDrawPath *path) { if (!path->ended) - complain("path not ended in uiDrawClip()"); + userbug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (path->fillMode) { case uiDrawFillModeWinding: diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 86fcb753..dbbe4db6 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -1,6 +1,9 @@ // 6 september 2015 #import "uipriv_darwin.h" +// TODO +#define complain(...) implbug(__VA_ARGS__) + // TODO for all relevant routines, make sure we are freeing memory correctly // TODO make sure allocation failures throw exceptions? struct uiDrawFontFamilies { @@ -15,7 +18,7 @@ uiDrawFontFamilies *uiDrawListFontFamilies(void) // TODO is there a way to get an error reason? ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); if (ff->fonts == NULL) - complain("error getting available font names (no reason specified)"); + implbug("error getting available font names (no reason specified) (TODO)"); return ff; } diff --git a/darwin/main.m b/darwin/main.m index 07db4459..e40c0616 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -48,7 +48,7 @@ static BOOL canQuit = NO; NSEvent *e; if (!canQuit) - complain("call to [NSApp terminate:] when not ready to terminate"); + implbug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); [realNSApp() stop:realNSApp()]; // stop: won't register until another event has passed; let's synthesize one diff --git a/darwin/map.m b/darwin/map.m index 9fa2de96..67b5edb6 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -22,7 +22,7 @@ struct mapTable *newMap(void) void mapDestroy(struct mapTable *m) { if ([m->m count] != 0) - complain("attempt to destroy map with items inside; did you forget to deallocate something?"); + implbug("attempt to destroy map with items inside"); [m->m release]; uiFree(m); } diff --git a/darwin/menu.m b/darwin/menu.m index 49bf2cdc..0f33df1d 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -69,17 +69,17 @@ enum { switch (smi->type) { case typeQuit: if (self->hasQuit) - complain("attempt to add multiple Quit menu items"); + userbug("You can't have multiple Quit menu items in one program."); self->hasQuit = YES; break; case typePreferences: if (self->hasPreferences) - complain("attempt to add multiple Preferences menu items"); + userbug("You can't have multiple Preferences menu items in one program."); self->hasPreferences = YES; break; case typeAbout: if (self->hasAbout) - complain("attempt to add multiple About menu items"); + userbug("You can't have multiple About menu items in one program."); self->hasAbout = YES; break; } @@ -200,7 +200,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - complain("attempt to call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead"); + userbug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -225,7 +225,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - complain("attempt to create a new menu item after menus have been finalized"); + userbug("You can't create a new menu item after menus have been finalized."); item = uiNew(uiMenuItem); @@ -297,7 +297,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - complain("attempt to create a new menu after menus have been finalized"); + userbug("You can't create a new menu after menus have been finalized."); if (menus == nil) menus = [NSMutableArray new]; diff --git a/darwin/progressbar.m b/darwin/progressbar.m index bb64ab2e..d8c0b060 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -11,7 +11,7 @@ uiDarwinControlAllDefaults(uiProgressBar, pi) void uiProgressBarSetValue(uiProgressBar *p, int value) { if (value < 0 || value > 100) - complain("value %d out of range in progressbarSetValue()", value); + userbug("Value %d out of range for a uiProgressBar.", value); // on 10.8 there's an animation when the progress bar increases, just like with Aero if (value == 100) { [p->pi setMaxValue:101]; diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 409bd5e0..5817efae 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -183,8 +183,9 @@ uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) { uiSpinbox *s; + // TODO implicitly swap instead? if (min >= max) - complain("error: min >= max in uiNewSpinbox()"); + userbug("min >= max is invalid for a uiSpinbox."); uiDarwinNewControl(uiSpinbox, s); diff --git a/darwin/util.m b/darwin/util.m index fa6f5994..7031e9f8 100644 --- a/darwin/util.m +++ b/darwin/util.m @@ -13,15 +13,3 @@ void disableAutocorrect(NSTextView *tv) [tv setAutomaticLinkDetectionEnabled:NO]; [tv setSmartInsertDeleteEnabled:NO]; } - -void complain(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "[libui] "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - abort(); -}