/* 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.
*/
#include <string.h>
#include <stdio.h>
#ifndef true
#define true 1
#define false 0
#endif
#define CFC_NEED_BASE_STRUCT_DEF
#include "CFCBase.h"
#include "CFCDumpable.h"
#include "CFCClass.h"
#include "CFCFunction.h"
#include "CFCMethod.h"
#include "CFCParamList.h"
#include "CFCParcel.h"
#include "CFCSymbol.h"
#include "CFCType.h"
#include "CFCVariable.h"
#include "CFCUtil.h"
// Add an autogenerated Dump method to the CFCClass.
static void
S_add_dump_method(CFCClass *klass);
// Add an autogenerated Load method to the CFCClass.
static void
S_add_load_method(CFCClass *klass);
// Create a Clownfish::CFC::Method object for either Dump() or Load().
static CFCMethod*
S_make_method_obj(CFCClass *klass, const char *method_name);
// Generate code for dumping a single member var.
static void
S_process_dump_member(CFCClass *klass, CFCVariable *member, char *buf,
size_t buf_size);
// Generate code for loading a single member var.
static void
S_process_load_member(CFCClass *klass, CFCVariable *member, char *buf,
size_t buf_size);
struct CFCDumpable {
CFCBase base;
};
const static CFCMeta CFCDUMPABLE_META = {
"Clownfish::CFC::Dumpable",
sizeof(CFCDumpable),
(CFCBase_destroy_t)CFCDumpable_destroy
};
CFCDumpable*
CFCDumpable_new(void) {
CFCDumpable *self = (CFCDumpable*)CFCBase_allocate(&CFCDUMPABLE_META);
return CFCDumpable_init(self);
}
CFCDumpable*
CFCDumpable_init(CFCDumpable *self) {
return self;
}
void
CFCDumpable_destroy(CFCDumpable *self) {
CFCBase_destroy((CFCBase*)self);
}
void
CFCDumpable_add_dumpables(CFCDumpable *self, CFCClass *klass) {
(void)self;
if (!CFCClass_has_attribute(klass, "dumpable")) {
CFCUtil_die("Class %s isn't dumpable", CFCClass_get_class_name(klass));
}
// Inherit Dump/Load from parent if no novel member vars.
CFCClass *parent = CFCClass_get_parent(klass);
if (parent && CFCClass_has_attribute(parent, "dumpable")) {
CFCVariable **novel = CFCClass_novel_member_vars(klass);
int needs_autogenerated_dumpables = novel[0] != NULL ? true : false;
FREEMEM(novel);
if (!needs_autogenerated_dumpables) { return; }
}
if (!CFCClass_novel_method(klass, "Dump")) {
S_add_dump_method(klass);
}
if (!CFCClass_novel_method(klass, "Load")) {
S_add_load_method(klass);
}
}
static CFCMethod*
S_make_method_obj(CFCClass *klass, const char *method_name) {
const char *klass_struct_sym = CFCClass_get_struct_sym(klass);
const char *klass_name = CFCClass_get_class_name(klass);
const char *klass_cnick = CFCClass_get_cnick(klass);
CFCParcel *klass_parcel = CFCClass_get_parcel(klass);
CFCParcel *cf_parcel = CFCParcel_clownfish_parcel();
CFCType *return_type
= CFCType_new_object(CFCTYPE_INCREMENTED, cf_parcel, "Obj", 1);
CFCType *self_type = CFCType_new_object(0, klass_parcel, klass_struct_sym, 1);
CFCVariable *self_var = CFCVariable_new(klass_parcel, NULL, klass_name,
klass_cnick, "self", self_type,
false);
CFCParamList *param_list = NULL;
if (strcmp(method_name, "Dump") == 0) {
param_list = CFCParamList_new(false);
CFCParamList_add_param(param_list, self_var, NULL);
}
else if (strcmp(method_name, "Load") == 0) {
CFCType *dump_type = CFCType_new_object(0, cf_parcel, "Obj", 1);
CFCVariable *dump_var = CFCVariable_new(cf_parcel, NULL, NULL,
NULL, "dump", dump_type,
false);
param_list = CFCParamList_new(false);
CFCParamList_add_param(param_list, self_var, NULL);
CFCParamList_add_param(param_list, dump_var, NULL);
CFCBase_decref((CFCBase*)dump_var);
CFCBase_decref((CFCBase*)dump_type);
}
else {
CFCUtil_die("Unexpected method_name: '%s'", method_name);
}
CFCMethod *method = CFCMethod_new(klass_parcel, "public", klass_name,
klass_cnick, method_name, return_type,
param_list, NULL, false, false);
CFCBase_decref((CFCBase*)param_list);
CFCBase_decref((CFCBase*)self_type);
CFCBase_decref((CFCBase*)self_var);
CFCBase_decref((CFCBase*)return_type);
return method;
}
static void
S_add_dump_method(CFCClass *klass) {
CFCMethod *method = S_make_method_obj(klass, "Dump");
CFCClass_add_method(klass, method);
CFCBase_decref((CFCBase*)method);
const char *full_func_sym = CFCMethod_implementing_func_sym(method);
const char *full_typedef = CFCMethod_full_typedef(method);
const char *full_struct = CFCClass_full_struct_sym(klass);
const char *vtable_var = CFCClass_full_vtable_var(klass);
const char *cnick = CFCClass_get_cnick(klass);
CFCClass *parent = CFCClass_get_parent(klass);
const size_t BUF_SIZE = 400;
char buf[BUF_SIZE];
if (parent && CFCClass_has_attribute(parent, "dumpable")) {
const char pattern[] =
"cfish_Obj*\n"
"%s(%s *self)\n"
"{\n"
" %s super_dump = (%s)SUPER_METHOD(%s, %s, Dump);\n"
" cfish_Hash *dump = (cfish_Hash*)super_dump(self);\n";
size_t amount = sizeof(pattern)
+ strlen(full_func_sym)
+ strlen(full_struct)
+ strlen(full_typedef) * 2
+ strlen(vtable_var)
+ strlen(cnick)
+ 50;
char *autocode = (char*)MALLOCATE(amount);
sprintf(autocode, pattern, full_func_sym, full_struct, full_typedef,
full_typedef, vtable_var, cnick);
CFCClass_append_autocode(klass, autocode);
FREEMEM(autocode);
CFCVariable **novel = CFCClass_novel_member_vars(klass);
size_t i;
for (i = 0; novel[i] != NULL; i++) {
S_process_dump_member(klass, novel[i], buf, BUF_SIZE);
}
FREEMEM(novel);
}
else {
const char pattern[] =
"cfish_Obj*\n"
"%s(%s *self)\n"
"{\n"
" cfish_Hash *dump = cfish_Hash_new(0);\n"
" Cfish_Hash_Store_Str(dump, \"_class\", 6,\n"
" (cfish_Obj*)Cfish_CB_Clone(Cfish_Obj_Get_Class_Name((cfish_Obj*)self)));\n";
size_t amount = sizeof(pattern)
+ strlen(full_func_sym)
+ strlen(full_struct)
+ 50;
char *autocode = (char*)MALLOCATE(amount);
sprintf(autocode, pattern, full_func_sym, full_struct);
CFCClass_append_autocode(klass, autocode);
FREEMEM(autocode);
CFCVariable **members = CFCClass_member_vars(klass);
size_t i;
for (i = 0; members[i] != NULL; i++) {
S_process_dump_member(klass, members[i], buf, BUF_SIZE);
}
}
CFCClass_append_autocode(klass, " return (cfish_Obj*)dump;\n}\n\n");
}
static void
S_add_load_method(CFCClass *klass) {
CFCMethod *method = S_make_method_obj(klass, "Load");
CFCClass_add_method(klass, method);
CFCBase_decref((CFCBase*)method);
const char *full_func_sym = CFCMethod_implementing_func_sym(method);
const char *full_typedef = CFCMethod_full_typedef(method);
const char *full_struct = CFCClass_full_struct_sym(klass);
const char *vtable_var = CFCClass_full_vtable_var(klass);
const char *cnick = CFCClass_get_cnick(klass);
CFCClass *parent = CFCClass_get_parent(klass);
const size_t BUF_SIZE = 400;
char buf[BUF_SIZE];
if (parent && CFCClass_has_attribute(parent, "dumpable")) {
const char pattern[] =
"cfish_Obj*\n"
"%s(%s *self, cfish_Obj *dump)\n"
"{\n"
" cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n"
" %s super_load = (%s)SUPER_METHOD(%s, %s, Load);\n"
" %s *loaded = (%s*)super_load(self, dump);\n";
size_t amount = sizeof(pattern)
+ strlen(full_func_sym)
+ strlen(full_struct) * 3
+ strlen(full_typedef) * 2
+ strlen(vtable_var)
+ strlen(cnick)
+ 50;
char *autocode = (char*)MALLOCATE(amount);
sprintf(autocode, pattern, full_func_sym, full_struct, full_typedef,
full_typedef, vtable_var, cnick, full_struct, full_struct);
CFCClass_append_autocode(klass, autocode);
FREEMEM(autocode);
CFCVariable **novel = CFCClass_novel_member_vars(klass);
size_t i;
for (i = 0; novel[i] != NULL; i++) {
S_process_load_member(klass, novel[i], buf, BUF_SIZE);
}
FREEMEM(novel);
}
else {
const char pattern[] =
"cfish_Obj*\n"
"%s(%s *self, cfish_Obj *dump)\n"
"{\n"
" cfish_Hash *source = (cfish_Hash*)CFISH_CERTIFY(dump, CFISH_HASH);\n"
" cfish_CharBuf *class_name = (cfish_CharBuf*)CFISH_CERTIFY(\n"
" Cfish_Hash_Fetch_Str(source, \"_class\", 6), CFISH_CHARBUF);\n"
" cfish_VTable *vtable = cfish_VTable_singleton(class_name, NULL);\n"
" %s *loaded = (%s*)Cfish_VTable_Make_Obj(vtable);\n"
" CHY_UNUSED_VAR(self);\n";
size_t amount = sizeof(pattern)
+ strlen(full_func_sym)
+ strlen(full_struct) * 3
+ 50;
char *autocode = (char*)MALLOCATE(amount);
sprintf(autocode, pattern, full_func_sym, full_struct, full_struct,
full_struct);
CFCClass_append_autocode(klass, autocode);
FREEMEM(autocode);
CFCVariable **members = CFCClass_member_vars(klass);
size_t i;
for (i = 0; members[i] != NULL; i++) {
S_process_load_member(klass, members[i], buf, BUF_SIZE);
}
}
CFCClass_append_autocode(klass, " return (cfish_Obj*)loaded;\n}\n\n");
}
static void
S_process_dump_member(CFCClass *klass, CFCVariable *member, char *buf,
size_t buf_size) {
CFCUTIL_NULL_CHECK(member);
CFCType *type = CFCVariable_get_type(member);
const char *name = CFCVariable_micro_sym(member);
unsigned name_len = (unsigned)strlen(name);
const char *specifier = CFCType_get_specifier(type);
// Skip the VTable and the refcount/host-object.
if (strcmp(specifier, "lucy_VTable") == 0
|| strcmp(specifier, "lucy_ref_t") == 0
) {
return;
}
if (CFCType_is_integer(type) || CFCType_is_floating(type)) {
char int_pattern[] =
" Cfish_Hash_Store_Str(dump, \"%s\", %u, (cfish_Obj*)cfish_CB_newf(\"%%i64\", (int64_t)self->%s));\n";
char float_pattern[] =
" Cfish_Hash_Store_Str(dump, \"%s\", %u, (cfish_Obj*)cfish_CB_newf(\"%%f64\", (double)self->%s));\n";
char bool_pattern[] =
" Cfish_Hash_Store_Str(dump, \"%s\", %u, (cfish_Obj*)cfish_Bool_singleton(self->%s));\n";
const char *pattern;
if (strcmp(specifier, "bool_t") == 0
|| strcmp(specifier, "chy_bool_t") == 0
) {
pattern = bool_pattern;
}
else if (CFCType_is_integer(type)) {
pattern = int_pattern;
}
else {
pattern = float_pattern;
}
size_t needed = strlen(pattern) + name_len * 2 + 20;
if (buf_size < needed) {
CFCUtil_die("Buffer not big enough (%lu < %lu)",
(unsigned long)buf_size, (unsigned long)needed);
}
sprintf(buf, pattern, name, name_len, name);
}
else if (CFCType_is_object(type)) {
char pattern[] =
" if (self->%s) {\n"
" Cfish_Hash_Store_Str(dump, \"%s\", %u, Cfish_Obj_Dump((cfish_Obj*)self->%s));\n"
" }\n";
size_t needed = strlen(pattern) + name_len * 3 + 20;
if (buf_size < needed) {
CFCUtil_die("Buffer not big enough (%lu < %lu)",
(unsigned long)buf_size, (unsigned long)needed);
}
sprintf(buf, pattern, name, name, name_len, name);
}
else {
CFCUtil_die("Don't know how to dump a %s",
CFCType_get_specifier(type));
}
CFCClass_append_autocode(klass, buf);
}
static void
S_process_load_member(CFCClass *klass, CFCVariable *member, char *buf,
size_t buf_size) {
CFCUTIL_NULL_CHECK(member);
CFCType *type = CFCVariable_get_type(member);
const char *type_str = CFCType_to_c(type);
const char *name = CFCVariable_micro_sym(member);
unsigned name_len = (unsigned)strlen(name);
char extraction[200];
const char *specifier = CFCType_get_specifier(type);
// Skip the VTable and the refcount/host-object.
if (strcmp(specifier, "lucy_VTable") == 0
|| strcmp(specifier, "lucy_ref_t") == 0
) {
return;
}
if (2 * strlen(type_str) + 100 > sizeof(extraction)) { // play it safe
CFCUtil_die("type_str too long: '%s'", type_str);
}
if (CFCType_is_integer(type)) {
if (strcmp(specifier, "bool_t") == 0
|| strcmp(specifier, "chy_bool_t") == 0
) {
sprintf(extraction, "Cfish_Obj_To_Bool(var)");
}
else {
sprintf(extraction, "(%s)Cfish_Obj_To_I64(var)", type_str);
}
}
else if (CFCType_is_floating(type)) {
sprintf(extraction, "(%s)Cfish_Obj_To_F64(var)", type_str);
}
else if (CFCType_is_object(type)) {
const char *vtable_var = CFCType_get_vtable_var(type);
sprintf(extraction,
"(%s*)CFISH_CERTIFY(Cfish_Obj_Load(var, var), %s)",
specifier, vtable_var);
}
else {
CFCUtil_die("Don't know how to load %s", specifier);
}
const char *pattern =
" {\n"
" cfish_Obj *var = Cfish_Hash_Fetch_Str(source, \"%s\", %u);\n"
" if (var) { loaded->%s = %s; }\n"
" }\n";
size_t needed = sizeof(pattern)
+ (name_len * 2)
+ strlen(extraction)
+ 20;
if (buf_size < needed) {
CFCUtil_die("Buffer not big enough (%lu < %lu)",
(unsigned long)buf_size, (unsigned long)needed);
}
sprintf(buf, pattern, name, name_len, name, extraction);
CFCClass_append_autocode(klass, buf);
}