blob: 8d2ad43ac18b4bc8468aa020b28db82f824668ab [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.
*/
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#define CFC_NEED_FUNCTION_STRUCT_DEF
#include "CFCFunction.h"
#include "CFCMethod.h"
#include "CFCType.h"
#include "CFCUtil.h"
#include "CFCParamList.h"
#include "CFCParcel.h"
#include "CFCDocuComment.h"
#include "CFCVariable.h"
#ifndef true
#define true 1
#define false 0
#endif
struct CFCMethod {
CFCFunction function;
char *macro_sym;
char *short_typedef;
char *full_typedef;
char *full_callback_sym;
char *full_override_sym;
int is_final;
int is_abstract;
int is_novel;
};
static void
S_update_typedefs(CFCMethod *self, const char *short_sym);
const static CFCMeta CFCMETHOD_META = {
"Clownfish::CFC::Method",
sizeof(CFCMethod),
(CFCBase_destroy_t)CFCMethod_destroy
};
CFCMethod*
CFCMethod_new(CFCParcel *parcel, const char *exposure, const char *class_name,
const char *class_cnick, const char *macro_sym,
CFCType *return_type, CFCParamList *param_list,
CFCDocuComment *docucomment, int is_final, int is_abstract) {
CFCMethod *self = (CFCMethod*)CFCBase_allocate(&CFCMETHOD_META);
return CFCMethod_init(self, parcel, exposure, class_name, class_cnick,
macro_sym, return_type, param_list, docucomment,
is_final, is_abstract);
}
static int
S_validate_macro_sym(const char *macro_sym) {
if (!macro_sym || !strlen(macro_sym)) { return false; }
int need_upper = true;
int need_letter = true;
for (;; macro_sym++) {
if (need_upper && !isupper(*macro_sym)) { return false; }
if (need_letter && !isalpha(*macro_sym)) { return false; }
need_upper = false;
need_letter = false;
// We've reached NULL-termination without problems, so succeed.
if (!*macro_sym) { return true; }
if (!isalnum(*macro_sym)) {
if (*macro_sym != '_') { return false; }
need_upper = true;
}
}
}
CFCMethod*
CFCMethod_init(CFCMethod *self, CFCParcel *parcel, const char *exposure,
const char *class_name, const char *class_cnick,
const char *macro_sym, CFCType *return_type,
CFCParamList *param_list, CFCDocuComment *docucomment,
int is_final, int is_abstract) {
// Validate macro_sym, derive micro_sym.
if (!S_validate_macro_sym(macro_sym)) {
CFCBase_decref((CFCBase*)self);
CFCUtil_die("Invalid macro_sym: '%s'",
macro_sym ? macro_sym : "[NULL]");
}
char *micro_sym = CFCUtil_strdup(macro_sym);
size_t i;
for (i = 0; micro_sym[i] != '\0'; i++) {
micro_sym[i] = tolower(micro_sym[i]);
}
// Super-init and clean up derived micro_sym.
CFCFunction_init((CFCFunction*)self, parcel, exposure, class_name,
class_cnick, micro_sym, return_type, param_list,
docucomment, false);
FREEMEM(micro_sym);
// Verify that the first element in the arg list is a self.
CFCVariable **args = CFCParamList_get_variables(param_list);
if (!args[0]) { CFCUtil_die("Missing 'self' argument"); }
CFCType *type = CFCVariable_get_type(args[0]);
const char *specifier = CFCType_get_specifier(type);
const char *prefix = CFCMethod_get_prefix(self);
const char *last_colon = strrchr(class_name, ':');
const char *struct_sym = last_colon ? last_colon + 1 : class_name;
char *wanted = (char*)MALLOCATE(strlen(prefix) + strlen(struct_sym) + 1);
sprintf(wanted, "%s%s", prefix, struct_sym);
int mismatch = strcmp(wanted, specifier);
FREEMEM(wanted);
if (mismatch) {
CFCUtil_die("First arg type doesn't match class: '%s' '%s",
class_name, specifier);
}
self->macro_sym = CFCUtil_strdup(macro_sym);
self->short_typedef = NULL;
self->full_typedef = NULL;
self->is_final = is_final;
self->is_abstract = is_abstract;
// Derive more symbols.
const char *full_func_sym = CFCMethod_implementing_func_sym(self);
size_t amount = strlen(full_func_sym) + sizeof("_OVERRIDE") + 1;
self->full_callback_sym = (char*)MALLOCATE(amount);
self->full_override_sym = (char*)MALLOCATE(amount);
sprintf(self->full_callback_sym, "%s_CALLBACK", full_func_sym);
sprintf(self->full_override_sym, "%s_OVERRIDE", full_func_sym);
// Assume that this method is novel until we discover when applying
// inheritance that it was overridden.
self->is_novel = 1;
// Cache typedefs.
S_update_typedefs(self, CFCSymbol_short_sym((CFCSymbol*)self));
return self;
}
void
CFCMethod_destroy(CFCMethod *self) {
FREEMEM(self->macro_sym);
FREEMEM(self->short_typedef);
FREEMEM(self->full_typedef);
FREEMEM(self->full_callback_sym);
FREEMEM(self->full_override_sym);
CFCFunction_destroy((CFCFunction*)self);
}
int
CFCMethod_compatible(CFCMethod *self, CFCMethod *other) {
if (!other) { return false; }
if (strcmp(self->macro_sym, other->macro_sym)) { return false; }
int my_public = CFCMethod_public(self);
int other_public = CFCMethod_public(other);
if (!!my_public != !!other_public) { return false; }
// Check arguments and initial values.
CFCParamList *my_param_list = self->function.param_list;
CFCParamList *other_param_list = other->function.param_list;
CFCVariable **my_args = CFCParamList_get_variables(my_param_list);
CFCVariable **other_args = CFCParamList_get_variables(other_param_list);
const char **my_vals = CFCParamList_get_initial_values(my_param_list);
const char **other_vals = CFCParamList_get_initial_values(other_param_list);
size_t i;
for (i = 1; ; i++) { // start at 1, skipping self
if (!!my_args[i] != !!other_args[i]) { return false; }
if (!!my_vals[i] != !!other_vals[i]) { return false; }
if (my_vals[i]) {
if (strcmp(my_vals[i], other_vals[i])) { return false; }
}
if (my_args[i]) {
if (!CFCVariable_equals(my_args[i], other_args[i])) {
return false;
}
}
else {
break;
}
}
// Check return types.
CFCType *type = CFCMethod_get_return_type(self);
CFCType *other_type = CFCMethod_get_return_type(other);
if (CFCType_is_object(type)) {
// Weak validation to allow covariant object return types.
if (!CFCType_is_object(other_type)) { return false; }
if (!CFCType_similar(type, other_type)) { return false; }
}
else {
if (!CFCType_equals(type, other_type)) { return false; }
}
return true;
}
void
CFCMethod_override(CFCMethod *self, CFCMethod *orig) {
// Check that the override attempt is legal.
if (CFCMethod_final(orig)) {
const char *orig_class = CFCMethod_get_class_name(orig);
const char *my_class = CFCMethod_get_class_name(self);
CFCUtil_die("Attempt to override final method '%s' from '%s' by '%s'",
orig->macro_sym, orig_class, my_class);
}
if (!CFCMethod_compatible(self, orig)) {
const char *func = CFCMethod_implementing_func_sym(self);
const char *orig_func = CFCMethod_implementing_func_sym(orig);
CFCUtil_die("Non-matching signatures for %s and %s", func, orig_func);
}
// Mark the Method as no longer novel.
self->is_novel = false;
}
CFCMethod*
CFCMethod_finalize(CFCMethod *self) {
CFCParcel *parcel = CFCMethod_get_parcel(self);
const char *exposure = CFCMethod_get_exposure(self);
const char *class_name = CFCMethod_get_class_name(self);
const char *class_cnick = CFCMethod_get_class_cnick(self);
CFCMethod *finalized
= CFCMethod_new(parcel, exposure, class_name, class_cnick,
self->macro_sym, self->function.return_type,
self->function.param_list,
self->function.docucomment, true,
self->is_abstract);
finalized->is_novel = self->is_final; // Is this right?
S_update_typedefs(finalized, CFCSymbol_short_sym((CFCSymbol*)self));
return finalized;
}
size_t
CFCMethod_short_method_sym(CFCMethod *self, const char *invoker, char *buf,
size_t buf_size) {
CFCUTIL_NULL_CHECK(invoker);
size_t needed = strlen(invoker) + 1 + strlen(self->macro_sym) + 1;
if (buf_size >= needed) {
sprintf(buf, "%s_%s", invoker, self->macro_sym);
}
return needed;
}
size_t
CFCMethod_full_method_sym(CFCMethod *self, const char *invoker, char *buf,
size_t buf_size) {
CFCUTIL_NULL_CHECK(invoker);
const char *Prefix = CFCMethod_get_Prefix(self);
size_t needed = strlen(Prefix)
+ strlen(invoker)
+ 1
+ strlen(self->macro_sym)
+ 1;
if (buf_size >= needed) {
sprintf(buf, "%s%s_%s", Prefix, invoker, self->macro_sym);
}
return needed;
}
size_t
CFCMethod_full_offset_sym(CFCMethod *self, const char *invoker, char *buf,
size_t buf_size) {
CFCUTIL_NULL_CHECK(invoker);
size_t needed = CFCMethod_full_method_sym(self, invoker, NULL, 0)
+ strlen("_OFFSET");
if (buf_size >= needed) {
CFCMethod_full_method_sym(self, invoker, buf, buf_size);
strcat(buf, "_OFFSET");
}
return needed;
}
const char*
CFCMethod_get_macro_sym(CFCMethod *self) {
return self->macro_sym;
}
const char*
CFCMethod_micro_sym(CFCMethod *self) {
return CFCSymbol_micro_sym((CFCSymbol*)self);
}
static void
S_update_typedefs(CFCMethod *self, const char *short_sym) {
FREEMEM(self->short_typedef);
FREEMEM(self->full_typedef);
if (short_sym) {
const char *prefix = CFCMethod_get_prefix(self);
size_t amount = strlen(short_sym) + 3;
self->short_typedef = (char*)MALLOCATE(amount);
sprintf(self->short_typedef, "%s_t", short_sym);
amount += strlen(prefix);
self->full_typedef = (char*)MALLOCATE(amount);
sprintf(self->full_typedef, "%s%s_t", prefix, short_sym);
}
else {
self->short_typedef = NULL;
self->full_typedef = NULL;
}
}
const char*
CFCMethod_short_typedef(CFCMethod *self) {
return self->short_typedef;
}
const char*
CFCMethod_full_typedef(CFCMethod *self) {
return self->full_typedef;
}
const char*
CFCMethod_full_callback_sym(CFCMethod *self) {
return self->full_callback_sym;
}
const char*
CFCMethod_full_override_sym(CFCMethod *self) {
return self->full_override_sym;
}
int
CFCMethod_final(CFCMethod *self) {
return self->is_final;
}
int
CFCMethod_abstract(CFCMethod *self) {
return self->is_abstract;
}
int
CFCMethod_novel(CFCMethod *self) {
return self->is_novel;
}
CFCType*
CFCMethod_self_type(CFCMethod *self) {
CFCVariable **vars = CFCParamList_get_variables(self->function.param_list);
return CFCVariable_get_type(vars[0]);
}
CFCParcel*
CFCMethod_get_parcel(CFCMethod *self) {
return CFCSymbol_get_parcel((CFCSymbol*)self);
}
const char*
CFCMethod_get_prefix(CFCMethod *self) {
return CFCSymbol_get_prefix((CFCSymbol*)self);
}
const char*
CFCMethod_get_Prefix(CFCMethod *self) {
return CFCSymbol_get_Prefix((CFCSymbol*)self);
}
const char*
CFCMethod_get_exposure(CFCMethod *self) {
return CFCSymbol_get_exposure((CFCSymbol*)self);
}
const char*
CFCMethod_get_class_name(CFCMethod *self) {
return CFCSymbol_get_class_name((CFCSymbol*)self);
}
const char*
CFCMethod_get_class_cnick(CFCMethod *self) {
return CFCSymbol_get_class_cnick((CFCSymbol*)self);
}
int
CFCMethod_public(CFCMethod *self) {
return CFCSymbol_public((CFCSymbol*)self);
}
CFCType*
CFCMethod_get_return_type(CFCMethod *self) {
return CFCFunction_get_return_type((CFCFunction*)self);
}
CFCParamList*
CFCMethod_get_param_list(CFCMethod *self) {
return CFCFunction_get_param_list((CFCFunction*)self);
}
const char*
CFCMethod_implementing_func_sym(CFCMethod *self) {
return CFCFunction_full_func_sym((CFCFunction*)self);
}
const char*
CFCMethod_short_implementing_func_sym(CFCMethod *self) {
return CFCFunction_short_func_sym((CFCFunction*)self);
}