blob: 7ca59b890534762cd61eb7f3d40f0ea209c6584d [file] [log] [blame]
/* 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_CFISH_ERR
#define C_CFISH_OBJ
#define C_CFISH_CLASS
#define CFISH_USE_SHORT_NAMES
#include "charmony.h"
#include <string.h>
#include <stdio.h>
#include "Clownfish/Err.h"
#include "Clownfish/CharBuf.h"
#include "Clownfish/String.h"
#include "Clownfish/Class.h"
#include "Clownfish/Util/Memory.h"
Err*
Err_new(String *mess) {
Err *self = (Err*)Class_Make_Obj(ERR);
return Err_init(self, mess);
}
Err*
Err_init(Err *self, String *mess) {
self->mess = mess;
return self;
}
void
Err_Destroy_IMP(Err *self) {
DECREF(self->mess);
SUPER_DESTROY(self, ERR);
}
String*
Err_To_String_IMP(Err *self) {
return (String*)INCREF(self->mess);
}
void
Err_Cat_Mess_IMP(Err *self, String *mess) {
String *new_mess = Str_Cat(self->mess, mess);
DECREF(self->mess);
self->mess = new_mess;
}
// Fallbacks in case variadic macros aren't available.
#ifndef CHY_HAS_VARIADIC_MACROS
static String*
S_str_vnewf(char *pattern, va_list args) {
CharBuf *buf = CB_new(strlen(pattern) + 10);
CB_VCatF(buf, pattern, args);
String *message = CB_Yield_String(buf);
DECREF(buf);
return message;
}
void
THROW(Class *klass, char *pattern, ...) {
va_list args;
va_start(args, pattern);
String *message = S_str_vnewf(pattern, args);
va_end(args);
Err *err = (Err*)Class_Make_Obj(klass);
err = Err_init(err, message);
Err_do_throw(err);
}
void
CFISH_WARN(char *pattern, ...) {
va_list args;
va_start(args, pattern);
String *message = S_str_vnewf(pattern, args);
va_end(args);
Err_warn_mess(message);
}
String*
CFISH_MAKE_MESS(char *pattern, ...) {
va_list args;
va_start(args, pattern);
String *message = S_str_vnewf(pattern, args);
va_end(args);
return message;
}
#endif
static String*
S_vmake_mess(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;
CharBuf *buf = CB_new(guess_len);
CB_VCatF(buf, pattern, args);
if (func != NULL) {
CB_catf(buf, "\n\t%s at %s line %i32\n", func, file, (int32_t)line);
}
else {
CB_catf(buf, "\n\t%s line %i32\n", file, (int32_t)line);
}
String *message = CB_Yield_String(buf);
DECREF(buf);
return message;
}
String*
Err_make_mess(const char *file, int line, const char *func,
const char *pattern, ...) {
va_list args;
va_start(args, pattern);
String *message = S_vmake_mess(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;
va_start(args, pattern);
String *message = S_vmake_mess(file, line, func, pattern, args);
va_end(args);
Err_warn_mess(message);
}
String*
Err_Get_Mess_IMP(Err *self) {
return self->mess;
}
void
Err_Add_Frame_IMP(Err *self, const char *file, int line, const char *func) {
CharBuf *buf = CB_new(0);
CB_Cat(buf, self->mess);
if (!Str_Ends_With_Utf8(self->mess, "\n", 1)) {
CB_Cat_Char(buf, '\n');
}
if (func != NULL) {
CB_catf(buf, "\t%s at %s line %i32\n", func, file, (int32_t)line);
}
else {
CB_catf(buf, "\tat %s line %i32\n", file, (int32_t)line);
}
DECREF(self->mess);
self->mess = CB_Yield_String(buf);
DECREF(buf);
}
void
Err_rethrow(Err *self, const char *file, int line, const char *func) {
Err_Add_Frame_IMP(self, file, line, func);
Err_do_throw(self);
}
void
Err_throw_at(Class *klass, const char *file, int line,
const char *func, const char *pattern, ...) {
va_list args;
va_start(args, pattern);
String *message = S_vmake_mess(file, line, func, pattern, args);
va_end(args);
Err *err = (Err*)Class_Make_Obj(klass);
err = Err_init(err, message);
Err_do_throw(err);
}
// Inlined, slightly optimized version of Obj_is_a.
static CFISH_INLINE bool
SI_obj_is_a(Obj *obj, Class *target_class) {
Class *klass = obj->klass;
while (klass != NULL) {
if (klass == target_class) {
return true;
}
klass = klass->parent;
}
return false;
}
Obj*
Err_downcast(Obj *obj, Class *klass, const char *file, int line,
const char *func) {
if (obj && !SI_obj_is_a(obj, klass)) {
Err_throw_at(ERR, file, line, func, "Can't downcast from %o to %o",
Obj_get_class_name(obj), Class_Get_Name(klass));
}
return obj;
}
Obj*
Err_certify(Obj *obj, Class *klass, 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",
Class_Get_Name(klass));
}
else if (!SI_obj_is_a(obj, klass)) {
Err_throw_at(ERR, file, line, func, "Can't downcast from %o to %o",
Obj_get_class_name(obj), Class_Get_Name(klass));
}
return obj;
}
void
Err_abstract_method_call(Obj *obj, Class *klass, const char *method_name) {
String *class_name = obj ? Obj_get_class_name(obj) : Class_Get_Name(klass);
THROW(ERR, "Abstract method '%s' not defined by %o", method_name,
class_name);
}
void
Err_invalid_callback(const char *method_name) {
THROW(ERR, "Can't override %s via binding", method_name);
}
#ifdef CHY_HAS_WINDOWS_H
#include <windows.h>
char*
Err_win_error() {
DWORD buf_size = 256;
char *buf = (char*)MALLOCATE(buf_size);
DWORD message_len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL, // message source table
GetLastError(),
MAKELANGID(LANG_ENGLISH,
SUBLANG_ENGLISH_US),
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