/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define C_LUCY_ERR
#define C_LUCY_OBJ
#define C_LUCY_VTABLE
#define LUCY_USE_SHORT_NAMES
#define CHY_USE_SHORT_NAMES
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "Lucy/Object/Err.h"
#include "Lucy/Object/CharBuf.h"
#include "Lucy/Object/VTable.h"
#include "Lucy/Util/Memory.h"
Err*
Err_new(CharBuf *mess) {
Err *self = (Err*)VTable_Make_Obj(ERR);
return Err_init(self, mess);
}
Err*
Err_init(Err *self, CharBuf *mess) {
self->mess = mess;
return self;
}
void
Err_destroy(Err *self) {
DECREF(self->mess);
SUPER_DESTROY(self, ERR);
}
Err*
Err_make(Err *self) {
UNUSED_VAR(self);
return Err_new(CB_new(0));
}
CharBuf*
Err_to_string(Err *self) {
return (CharBuf*)INCREF(self->mess);
}
void
Err_cat_mess(Err *self, const CharBuf *mess) {
CB_Cat(self->mess, mess);
}
// Fallbacks in case variadic macros aren't available.
#ifndef CHY_HAS_VARIADIC_MACROS
void
THROW(VTable *vtable, char *pattern, ...) {
va_list args;
Err_make_t make
= (Err_make_t)METHOD(CERTIFY(vtable, VTABLE), Err, Make);
Err *err = (Err*)CERTIFY(make(NULL), ERR);
CharBuf *mess = Err_Get_Mess(err);
va_start(args, pattern);
CB_VCatF(mess, pattern, args);
va_end(args);
Err_do_throw(err);
}
void
CFISH_WARN(char *pattern, ...) {
va_list args;
CharBuf *const message = CB_new(strlen(pattern) + 10);
va_start(args, pattern);
CB_VCatF(message, pattern, args);
va_end(args);
Err_warn_mess(message);
}
CharBuf*
CFISH_MAKE_MESS(char *pattern, ...) {
va_list args;
CharBuf *const message = CB_new(strlen(pattern) + 10);
va_start(args, pattern);
CB_VCatF(message, pattern, args);
va_end(args);
return message;
}
#endif
static void
S_vcat_mess(CharBuf *message, const char *file, int line, const char *func,
const char *pattern, va_list args) {
size_t guess_len = strlen(file)
+ func ? strlen(func) : 0
+ strlen(pattern)
+ 30;
CB_Grow(message, guess_len);
CB_VCatF(message, pattern, args);
if (func != NULL) {
CB_catf(message, "\n\t%s at %s line %i32\n", func, file, (int32_t)line);
}
else {
CB_catf(message, "\n\t%s line %i32\n", file, (int32_t)line);
}
}
CharBuf*
Err_make_mess(const char *file, int line, const char *func,
const char *pattern, ...) {
va_list args;
size_t guess_len = strlen(pattern) + strlen(file) + 20;
CharBuf *message = CB_new(guess_len);
va_start(args, pattern);
S_vcat_mess(message, file, line, func, pattern, args);
va_end(args);
return message;
}
void
Err_warn_at(const char *file, int line, const char *func,
const char *pattern, ...) {
va_list args;
CharBuf *message = CB_new(0);
va_start(args, pattern);
S_vcat_mess(message, file, line, func, pattern, args);
va_end(args);
Err_warn_mess(message);
}
CharBuf*
Err_get_mess(Err *self) {
return self->mess;
}
void
Err_add_frame(Err *self, const char *file, int line, const char *func) {
if (CB_Ends_With_Str(self->mess, "\n", 1)) { CB_Chop(self->mess, 1); }
if (func != NULL) {
CB_catf(self->mess, "\n\t%s at %s line %i32\n", func, file,
(int32_t)line);
}
else {
CB_catf(self->mess, "\n\tat %s line %i32\n", file, (int32_t)line);
}
}
void
Err_rethrow(Err *self, const char *file, int line, const char *func) {
Err_add_frame(self, file, line, func);
Err_do_throw(self);
}
void
Err_throw_at(VTable *vtable, const char *file, int line,
const char *func, const char *pattern, ...) {
va_list args;
Err_make_t make
= (Err_make_t)METHOD(CERTIFY(vtable, VTABLE), Err, Make);
Err *err = (Err*)CERTIFY(make(NULL), ERR);
CharBuf *mess = Err_Get_Mess(err);
va_start(args, pattern);
S_vcat_mess(mess, file, line, func, pattern, args);
va_end(args);
Err_do_throw(err);
}
// Inlined, slightly optimized version of Obj_is_a.
static INLINE bool_t
SI_obj_is_a(Obj *obj, VTable *target_vtable) {
VTable *vtable = obj->vtable;
while (vtable != NULL) {
if (vtable == target_vtable) {
return true;
}
vtable = vtable->parent;
}
return false;
}
Obj*
Err_downcast(Obj *obj, VTable *vtable, const char *file, int line,
const char *func) {
if (obj && !SI_obj_is_a(obj, vtable)) {
Err_throw_at(ERR, file, line, func, "Can't downcast from %o to %o",
Obj_Get_Class_Name(obj), VTable_Get_Name(vtable));
}
return obj;
}
Obj*
Err_certify(Obj *obj, VTable *vtable, const char *file, int line,
const char *func) {
if (!obj) {
Err_throw_at(ERR, file, line, func, "Object isn't a %o, it's NULL",
VTable_Get_Name(vtable));
}
else if (!SI_obj_is_a(obj, vtable)) {
Err_throw_at(ERR, file, line, func, "Can't downcast from %o to %o",
Obj_Get_Class_Name(obj), VTable_Get_Name(vtable));
}
return obj;
}
#ifdef CHY_HAS_WINDOWS_H
#include <windows.h>
char*
Err_win_error() {
size_t buf_size = 256;
char *buf = (char*)MALLOCATE(buf_size);
size_t message_len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL, // message source table
GetLastError(),
0, // language id
buf,
buf_size,
NULL // empty va_list
);
if (message_len == 0) {
char unknown[] = "Unknown error";
size_t len = sizeof(unknown);
strncpy(buf, unknown, len);
}
else if (message_len > 1) {
// Kill stupid newline.
buf[message_len - 2] = '\0';
}
return buf;
}
#else
char*
Err_win_error() {
return NULL; // Never called.
}
#endif // CHY_HAS_WINDOWS_H