| /* 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 "charmony.h" |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #define CFC_NEED_BASE_STRUCT_DEF |
| #include "CFCBase.h" |
| #include "CFCClass.h" |
| #include "CFCHierarchy.h" |
| #include "CFCParcel.h" |
| #include "CFCRuby.h" |
| #include "CFCUtil.h" |
| |
| struct CFCRuby { |
| CFCBase base; |
| CFCParcel *parcel; |
| CFCHierarchy *hierarchy; |
| char *lib_dir; |
| char *boot_class; |
| char *c_header; |
| char *c_footer; |
| char *boot_h_file; |
| char *boot_c_file; |
| char *boot_h_path; |
| char *boot_c_path; |
| char *boot_func; |
| }; |
| |
| // Modify a string in place, swapping out "::" for the supplied character. |
| static void |
| S_replace_double_colons(char *text, char replacement); |
| |
| static const CFCMeta CFCRUBY_META = { |
| "Clownfish::CFC::Binding::Ruby", |
| sizeof(CFCRuby), |
| (CFCBase_destroy_t)CFCRuby_destroy |
| }; |
| |
| CFCRuby* |
| CFCRuby_new(CFCParcel *parcel, CFCHierarchy *hierarchy, const char *lib_dir, |
| const char *boot_class, const char *header, const char *footer) { |
| CFCRuby *self = (CFCRuby*)CFCBase_allocate(&CFCRUBY_META); |
| return CFCRuby_init(self, parcel, hierarchy, lib_dir, boot_class, header, |
| footer); |
| } |
| |
| CFCRuby* |
| CFCRuby_init(CFCRuby *self, CFCParcel *parcel, CFCHierarchy *hierarchy, |
| const char *lib_dir, const char *boot_class, const char *header, |
| const char *footer) { |
| CFCUTIL_NULL_CHECK(parcel); |
| CFCUTIL_NULL_CHECK(hierarchy); |
| CFCUTIL_NULL_CHECK(lib_dir); |
| CFCUTIL_NULL_CHECK(boot_class); |
| CFCUTIL_NULL_CHECK(header); |
| CFCUTIL_NULL_CHECK(footer); |
| self->parcel = (CFCParcel*)CFCBase_incref((CFCBase*)parcel); |
| self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); |
| self->lib_dir = CFCUtil_strdup(lib_dir); |
| self->boot_class = CFCUtil_strdup(boot_class); |
| self->c_header = CFCUtil_make_c_comment(header); |
| self->c_footer = CFCUtil_make_c_comment(footer); |
| |
| const char *prefix = CFCParcel_get_prefix(parcel); |
| const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy); |
| const char *src_dest = CFCHierarchy_get_source_dest(hierarchy); |
| self->boot_h_file = CFCUtil_sprintf("%sboot.h", prefix); |
| self->boot_c_file = CFCUtil_sprintf("%sboot.c", prefix); |
| self->boot_h_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", inc_dest, |
| self->boot_h_file); |
| self->boot_c_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", src_dest, |
| self->boot_c_file); |
| self->boot_func = CFCUtil_sprintf("%s%s_bootstrap", prefix, boot_class); |
| |
| for (int i = 0; self->boot_func[i] != 0; i++) { |
| if (!isalnum(self->boot_func[i])) { |
| self->boot_func[i] = '_'; |
| } |
| } |
| |
| return self; |
| } |
| |
| void |
| CFCRuby_destroy(CFCRuby *self) { |
| CFCBase_decref((CFCBase*)self->parcel); |
| CFCBase_decref((CFCBase*)self->hierarchy); |
| FREEMEM(self->lib_dir); |
| FREEMEM(self->boot_class); |
| FREEMEM(self->c_header); |
| FREEMEM(self->c_footer); |
| FREEMEM(self->boot_h_file); |
| FREEMEM(self->boot_c_file); |
| FREEMEM(self->boot_h_path); |
| FREEMEM(self->boot_c_path); |
| FREEMEM(self->boot_func); |
| CFCBase_destroy((CFCBase*)self); |
| } |
| |
| static void |
| S_replace_double_colons(char *text, char replacement) { |
| size_t pos = 0; |
| for (char *ptr = text; *ptr != '\0'; ptr++) { |
| if (strncmp(ptr, "::", 2) == 0) { |
| text[pos++] = replacement; |
| ptr++; |
| } |
| else { |
| text[pos++] = *ptr; |
| } |
| } |
| text[pos] = '\0'; |
| } |
| |
| static void |
| S_write_boot_h(CFCRuby *self) { |
| char *guard = CFCUtil_cat(CFCUtil_strdup(""), self->boot_class, |
| "_BOOT", NULL); |
| S_replace_double_colons(guard, '_'); |
| for (char *ptr = guard; *ptr != '\0'; ptr++) { |
| if (isalpha(*ptr)) { |
| *ptr = toupper(*ptr); |
| } |
| } |
| |
| const char pattern[] = |
| "%s\n" |
| "\n" |
| "#ifndef %s\n" |
| "#define %s 1\n" |
| "\n" |
| "void\n" |
| "%s();\n" |
| "\n" |
| "#endif /* %s */\n" |
| "\n" |
| "%s\n"; |
| |
| size_t size = sizeof(pattern) |
| + strlen(self->c_header) |
| + strlen(guard) |
| + strlen(guard) |
| + strlen(self->boot_func) |
| + strlen(guard) |
| + strlen(self->c_footer) |
| + 20; |
| char *content = (char*)MALLOCATE(size); |
| sprintf(content, pattern, self->c_header, guard, guard, self->boot_func, |
| guard, self->c_footer); |
| CFCUtil_write_file(self->boot_h_path, content, strlen(content)); |
| |
| FREEMEM(content); |
| FREEMEM(guard); |
| } |
| |
| static void |
| S_write_boot_c(CFCRuby *self) { |
| CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); |
| char *pound_includes = CFCUtil_strdup(""); |
| const char *prefix = CFCParcel_get_prefix(self->parcel); |
| |
| for (size_t i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| if (CFCClass_included(klass)) { continue; } |
| |
| const char *include_h = CFCClass_include_h(klass); |
| pound_includes = CFCUtil_cat(pound_includes, "#include \"", |
| include_h, "\"\n", NULL); |
| |
| if (CFCClass_inert(klass)) { continue; } |
| |
| CFCClass *parent = CFCClass_get_parent(klass); |
| if (parent) { |
| /* Need to implement */ |
| } |
| } |
| |
| const char pattern[] = |
| "%s\n" |
| "\n" |
| "#include \"charmony.h\"\n" |
| "#include \"%s\"\n" |
| "#include \"%sparcel.h\"\n" |
| "#include \"Clownfish/String.h\"\n" |
| "#include \"Clownfish/Class.h\"\n" |
| "%s\n" |
| "\n" |
| "void\n" |
| "%s() {\n" |
| " %sbootstrap_parcel();\n" |
| "\n" |
| " cfish_StackString *alias = CFISH_SSTR_WRAP_UTF8(\"\", 0);\n" |
| "}\n" |
| "\n" |
| "%s\n" |
| "\n"; |
| |
| char *content |
| = CFCUtil_sprintf(pattern, self->c_header, self->boot_h_file, prefix, |
| pound_includes, self->boot_func, prefix, |
| self->c_footer); |
| CFCUtil_write_file(self->boot_c_path, content, strlen(content)); |
| |
| FREEMEM(content); |
| FREEMEM(pound_includes); |
| FREEMEM(ordered); |
| } |
| |
| void |
| CFCRuby_write_boot(CFCRuby *self) { |
| S_write_boot_h(self); |
| S_write_boot_c(self); |
| } |
| |
| void |
| CFCRuby_write_hostdefs(CFCRuby *self) { |
| const char pattern[] = |
| "%s\n" |
| "\n" |
| "#ifndef H_CFISH_HOSTDEFS\n" |
| "#define H_CFISH_HOSTDEFS 1\n" |
| "\n" |
| "/* Refcount / host object */\n" |
| "typedef union {\n" |
| " size_t count;\n" |
| " void *host_obj;\n" |
| "} cfish_ref_t;\n" |
| "\n" |
| "#define CFISH_OBJ_HEAD\\\n" |
| " cfish_ref_t ref;\n" |
| "\n" |
| "#endif /* H_CFISH_HOSTDEFS */\n" |
| "\n" |
| "%s\n"; |
| char *content |
| = CFCUtil_sprintf(pattern, self->c_header, self->c_footer); |
| |
| // Unlink then write file. |
| const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy); |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h", |
| inc_dest); |
| remove(filepath); |
| CFCUtil_write_file(filepath, content, strlen(content)); |
| FREEMEM(filepath); |
| |
| FREEMEM(content); |
| } |
| |