/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * state.c: Interface to the in-memory databases holding the VM state. */ #include #include #include #include #include #ifndef USEWIN32API # include # include #else # include # include # include #endif #ifdef ENABLE_PTHREAD_SUPPORT #include #endif #include "spl.h" #include "compat.h" static int spl_state_counter_malloc = 0; static int spl_state_counter_free = 0; #ifdef ENABLE_PTHREAD_SUPPORT static pthread_mutex_t spl_state_counter_mutex = PTHREAD_MUTEX_INITIALIZER; #endif void spl_state_counter_malloc_inc() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif spl_state_counter_malloc++; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif } int spl_state_counter_malloc_get() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif int retval = spl_state_counter_malloc; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif return retval; } void spl_state_counter_free_inc() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif spl_state_counter_free++; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif } int spl_state_counter_free_get() { #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_lock(&spl_state_counter_mutex); #endif int retval = spl_state_counter_free; #ifdef ENABLE_PTHREAD_SUPPORT pthread_mutex_unlock(&spl_state_counter_mutex); #endif return retval; } struct spl_builtin_module *spl_builtin_module_list = 0; struct spl_vm *spl_vm_create(void) { struct spl_vm *vm = calloc(1, sizeof(struct spl_vm)); vm->root = spl_get(0); vm->root->ctx_type = SPL_CTX_ROOT; /* Allocate the initial array. We assume that there is almost no SPL script which loads no module. */ vm->clib_hash_size = 2; vm->clib_hash = calloc(vm->clib_hash_size, sizeof(*vm->clib_hash)); vm->clib_hash_count = 0; spl_state_counter_malloc_inc(); return vm; } void spl_vm_destroy(struct spl_vm *vm) { vm->destroy_in_progress = 1; while (vm->task_list) spl_task_destroy(vm, vm->task_list); spl_put(vm, vm->root); vm->root = 0; spl_gc(vm); for(int i = 0; i < vm->clib_hash_size; i++) { struct spl_clib *clib_list = vm->clib_hash[i]; while (clib_list) { struct spl_clib *n = clib_list->next; free(clib_list->name); free(clib_list); clib_list = n; } } while (vm->hnode_list) { struct spl_hnode *n = vm->hnode_list->next; free(vm->hnode_list->name); free(vm->hnode_list); vm->hnode_list = n; } spl_module_unload_all(vm); spl_state_counter_free_inc(); if (vm->clib_hash) free(vm->clib_hash); if (vm->current_dir_name) free(vm->current_dir_name); if (vm->codecache_dir) free(vm->codecache_dir); if (vm->path) free(vm->path); struct spl_node_stack *p = vm->free_node_stack_elements; while (p) { struct spl_node_stack *n = p->next; free(p); p = n; } free(vm); } struct spl_node_stack *spl_vm_malloc_node_stack_element(struct spl_vm *vm) { struct spl_node_stack *n; if (vm->free_node_stack_elements) { n = vm->free_node_stack_elements; vm->free_node_stack_elements = n->next; } else { n = malloc(sizeof(struct spl_node_stack)); } return n; } void spl_vm_free_node_stack_element(struct spl_vm *vm, struct spl_node_stack * const n) { n->next = vm->free_node_stack_elements; vm->free_node_stack_elements = n; } void spl_undumpable_inc(struct spl_vm *vm, const char *info) { if (info && !vm->undumpable_info) { vm->undumpable_info = info; vm->undumpable_info_counter = 1; } vm->undumpable++; } void spl_undumpable_dec(struct spl_vm *vm, const char *info) { if (vm->undumpable_info == info) { if (vm->undumpable_info_counter-- == 1) vm->undumpable_info = 0; } vm->undumpable--; } struct spl_code *spl_code_get(struct spl_code *code) { if (!code) { code = calloc(1, sizeof(struct spl_code)); spl_state_counter_malloc_inc(); } code->ref_counter++; return code; } void spl_code_put(struct spl_code *code) { if (!code) return; code->ref_counter--; if ( !code->ref_counter ) { if ( code->code ) switch ( code->code_type ) { case SPL_CODE_MALLOCED: free(code->code); break; case SPL_CODE_MAPPED: #ifndef USEWIN32API munmap(code->code, code->size); #else free(code->code); #endif break; } if ( code->sha1 ) { free(code->sha1); code->sha1 = 0; } if ( code->code_type != SPL_CODE_STATIC ) { spl_state_counter_free_inc(); if (code->id) free(code->id); free(code); } } } const char *spl_code_sha1(struct spl_code *code) { if (!code || !code->code) return 0; if (!code->sha1) { unsigned char md[20]; spl_sha1(code->code, code->size, md); code->sha1 = malloc(41); for (int i=0; i<20; i++) { code->sha1[i*2+0] = "0123456789ABCDEF"[(md[i] >> 4) & 0x0f]; code->sha1[i*2+1] = "0123456789ABCDEF"[(md[i] >> 0) & 0x0f]; } code->sha1[40] = 0; } return code->sha1; } struct spl_task *spl_task_lookup(struct spl_vm *vm, const char *id) { struct spl_task *task = vm->task_list; if (id) while ( task ) { if ( task->id && !strcmp(task->id, id) ) return task; task = task->next; } return 0; } struct spl_task *spl_task_create(struct spl_vm *vm, const char *id) { struct spl_task *task = spl_task_lookup(vm, id); if (task) return task; task = calloc(1, sizeof(struct spl_task)); spl_state_counter_malloc_inc(); if (id) task->id = strdup(id); task->ctx = spl_get(vm->root); task->next = vm->task_list; if (vm) { vm->task_list = task; task->vm = vm; } return task; } void spl_task_destroy(struct spl_vm *vm, struct spl_task *task) { struct spl_task *t = vm->task_list; struct spl_task *l = 0; while (t) { if ( t == task ) { struct spl_task *n = t->next; spl_put(vm, t->ctx); spl_code_put(t->code); spl_state_counter_free_inc(); if (t->module) free(t->module); while (t->stack) spl_put(vm, spl_pop(t)); if (t->id) free(t->id); free(t); if (l) l->next = n; else vm->task_list = n; t = n; } else t = (l=t)->next; } } void spl_task_setcode(struct spl_task *task, struct spl_code *code) { spl_code_put(task->code); task->code = code; task->code_ip = 0; task->debug_str = 0; } unsigned int spl_subs_hash(const char *key, int max_hash) { unsigned int c, hash = 0; const unsigned char *ukey = (const unsigned char *)key; /* hashing algorithms are fun. this one is from the sdbm package. */ while ( (c = *ukey++) ) hash = c + (hash << 6) + (hash << 16) - hash; return hash % max_hash; } /* this is a debug function for checking hash subs. if something * isn't correct anymore, this should segfault.. */ #if 0 static void spl_subs_knock_off(struct spl_node *node) { struct spl_node_sub *s; int max_count = 1024; int count = 0; for (s = node->subs_begin; s; s = s->next) { if ( s->key[0] ) __asm__ __volatile__ (""); assert( count++ < max_count ); } for (s = node->subs_end; s; s = s->last) { if ( s->key[0] ) __asm__ __volatile__ (""); assert( count++ < max_count ); } for (int i=0; isubs_hash_size; i++) for (s = node->subs_hash[i]; s; s = s->hash_next) { if ( s->key[0] ) __asm__ __volatile__ (""); assert( count++ < max_count ); } } #endif void spl_subs_hash_check(struct spl_node *node) { if ( node->subs_counter < 8 ) return; if ( node->subs_counter < node->subs_hash_size*2 ) return; if ( node->subs_hash ) free(node->subs_hash); node->subs_hash_size = node->subs_counter*2; if ( node->subs_hash_size < 64 ) node->subs_hash_size = 64; node->subs_hash = calloc(node->subs_hash_size, sizeof(struct spl_node_sub*)); struct spl_node_sub *s = node->subs_begin; while (s) { unsigned int hash = spl_subs_hash(s->key, node->subs_hash_size); s->hash_next = node->subs_hash[hash]; node->subs_hash[hash] = s; s = s->next; } } struct spl_node_sub *spl_sub_lookup(struct spl_node *node, const char *key) { const char *real_key = key; while ( *real_key == '?' ) real_key++; spl_subs_hash_check(node); struct spl_node_sub *s = node->subs_hash ? node->subs_hash [spl_subs_hash(real_key, node->subs_hash_size)] : node->subs_begin; while (s) { if (!strcmp(real_key, s->key)) { return s; } s = node->subs_hash ? s->hash_next : s->next; } return 0; } struct spl_node *spl_lookup(struct spl_task *task, struct spl_node *node, const char *key, int flags) { if ( !node || !key ) return 0; if ( *key == 0 ) { while (node && node->ctx && node->ctx_type == SPL_CTX_LOCAL) node = node->ctx; return node->ctx; } if ( !strcmp(key, "!CLS") ) { while (node && node->ctx && node->ctx_type == SPL_CTX_LOCAL) node = node->ctx; return node->cls; } if ( !strcmp(key, "!ROOT") ) { if (!task || !task->vm) return 0; return task->vm->root; } if ( !strcmp(key, "!THIS") ) { struct spl_node *n = task->ctx; while ( n ) { if ( n->cls ) { n = n->cls; break; } if ( n->ctx_type == SPL_CTX_OBJECT ) break; n = n->ctx; } if ( !n || n->ctx_type != SPL_CTX_OBJECT ) { spl_report(SPL_REPORT_RUNTIME, task, "Lookup of THIS outside of object context!\n"); return 0; } return n; } if ( node->hnode_name ) { if ( !task ) return 0; struct spl_node *result = spl_hnode_lookup(task, node, key, flags); if (result) spl_cleanup(task, result); if ( !result || (result->flags & SPL_NODE_FLAG_CLNULL) ) { if ( !strchr(key, '?') ) spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of undeclared variable '%s'!\n", key); return 0; } return result; } /* A dot in the middle is an object reference, lookup head and tail */ char *obj_tail = strchr(key, '.'); if ( obj_tail ) { char obj_head[obj_tail - key + 1]; memcpy(obj_head, key, obj_tail - key); obj_head[obj_tail - key] = 0; obj_tail++; struct spl_node *o = spl_lookup(task, node, obj_head, flags); if (o) return spl_lookup(task, o, obj_tail, flags | SPL_LOOKUP_NOCTX); while ( obj_tail ) { if ( *obj_tail != '?' ) { spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of variable '%s' in undeclared context!\n", obj_tail); break; } obj_tail = strchr(obj_tail, '.'); } return 0; } spl_subs_hash_check(node); struct spl_node_sub *s = spl_sub_lookup(node, key); if (s) { if (s->node && (s->node->flags & SPL_NODE_FLAG_STATIC) && !(flags & SPL_LOOKUP_NOSTATIC)) return s->node->ctx; return s->node; } if ( node->cls && node->ctx_type != SPL_CTX_OBJECT ) { struct spl_node *result = spl_lookup(task, node->cls, key, (flags | SPL_LOOKUP_NOCTX | SPL_LOOKUP_TEST)); if (result) return result; } else if ( node->cls ) { assert(node != node->cls); struct spl_node *result = spl_lookup(task, node->cls, key, (flags | SPL_LOOKUP_CLS | SPL_LOOKUP_NOSTATIC)); if (result) { if ((flags & SPL_LOOKUP_CLS) == 0) { if (result->flags & SPL_NODE_FLAG_METHOD) { struct spl_node *tmp = result; result = spl_copy(task->vm, result, node); if (result->ctx) spl_put(task->vm, result->ctx); if (result->cls) spl_put(task->vm, result->cls); if (tmp->cls) result->ctx = tmp->ctx ? spl_get(tmp->ctx) : 0; else result->ctx = tmp->ctx && tmp->ctx->ctx ? spl_get(tmp->ctx->ctx) : 0; result->cls = spl_get(node); spl_cleanup(task, result); } else if ((result->flags & SPL_NODE_FLAG_STATIC) == 0) { result = spl_copy(task->vm, result, node); spl_create(task, node, key, result, flags | SPL_CREATE_LOCAL); } } if (!(flags & SPL_LOOKUP_NOSTATIC) && (result->flags & SPL_NODE_FLAG_STATIC)) result = result->ctx; return result; } } if ( flags & SPL_LOOKUP_CLS ) return 0; if ( (flags & SPL_LOOKUP_NOCTX) == 0 && node->ctx ) { assert(node != node->ctx); return spl_lookup(task, node->ctx, key, flags); } if ( *key != '?' && !(flags & SPL_LOOKUP_TEST) ) { if (node->flags & SPL_NODE_FLAG_RERES) spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of not existing regex capture $<%s>!\n", key); else spl_report(SPL_REPORT_RUNTIME, task, "Use (lookup) of undeclared variable '%s'!\n", key); } return 0; } struct spl_node *spl_create(struct spl_task *task, struct spl_node *node, const char *key, struct spl_node *newnode, int flags) { if ( !node ) return 0; if ( !key ) { key = my_alloca(32); snprintf((char*)key, 32, "%d", node->subs_next_idx); } if ( node->hnode_name ) { if ( !task ) return 0; struct spl_node *result = spl_hnode_create(task, node, key, newnode, flags); if (result) spl_cleanup(task, result); if ( !result || (result->flags & SPL_NODE_FLAG_CLNULL) ) { if ( !strchr(key, '?') ) spl_report(SPL_REPORT_RUNTIME, task, "Use (assignment) of undeclared variable '%s'!\n", key); return 0; } return result; } /* Writing context or class pointer - this is stuff for real men. ;-) */ if ( !key[0] || !strcmp(key, "!CLS") ) { while (node && node->ctx && node->ctx_type == SPL_CTX_LOCAL) node = node->ctx; if (node) { if (!key[0]) { if (node->ctx) spl_put(task->vm, node->ctx); node->ctx = newnode; } else { if (node->cls) spl_put(task->vm, node->cls); node->cls = newnode; } return newnode; } spl_put(task->vm, newnode); return 0; } /* Writing root pointer - this is insane, so we don't do it. */ if ( !strcmp(key, "!ROOT") ) { spl_report(SPL_REPORT_RUNTIME, task, "Writing to the root " "pointer is a bad idea and not supported!\n"); spl_put(task->vm, newnode); return 0; } /* Writing THIS pointer - this is insane, so we don't do it. */ if ( !strcmp(key, "!THIS") ) { spl_report(SPL_REPORT_RUNTIME, task, "Writing to the THIS " "pointer is a bad idea and not supported!\n"); spl_put(task->vm, newnode); return 0; } /* A dot in the middle is an object reference, lookup head and create tail */ /* Also create head if it is new */ char *obj_tail = strchr(key, '.'); if ( obj_tail ) { char obj_head[obj_tail - key + 1]; memcpy(obj_head, key, obj_tail - key); obj_head[obj_tail - key] = 0; obj_tail++; struct spl_node *o = spl_lookup(task, node, obj_head, flags); if (!o) o = spl_create(task, node, obj_head, spl_get(0), flags); return spl_create(task, o, obj_tail, newnode, flags); } struct spl_node_sub *s = spl_sub_lookup(node, key); if (s) { struct spl_node *target = s->node; if ( (target->flags & SPL_NODE_FLAG_STATIC) && !(flags & SPL_CREATE_NOSTATIC) ) { if (target->ctx) spl_cleanup(task, target->ctx); target->ctx = newnode; } else { spl_cleanup(task, s->node); s->node = newnode; } return target; } /* create local if it is forced by the flags */ if ( flags & SPL_CREATE_LOCAL ) goto create_local; /* create in first object if it is defined in the class path */ if ( node->cls ) { struct spl_node *cn = node; while (cn->ctx_type != SPL_CTX_OBJECT && cn->cls) cn = cn->cls; struct spl_node *tmp = spl_lookup(task, cn, key, flags | SPL_LOOKUP_CLS|SPL_LOOKUP_NOSTATIC); if (tmp) { if (tmp->flags & SPL_NODE_FLAG_STATIC) { if (tmp->ctx) spl_cleanup(task, tmp->ctx); return tmp->ctx = newnode; } return spl_create(task, cn, key, newnode, flags | SPL_CREATE_LOCAL); } } /* create local if no next CTX is available */ if ( !node->ctx ) goto create_local; /* create local if this is a function and SPL_CREATE_FUNCLOCAL is set */ if ( node->ctx_type == SPL_CTX_FUNCTION && (flags & SPL_CREATE_FUNCLOCAL) ) goto create_local; /* create local if this is a function and is not defined in next context */ if ( (node->ctx_type == SPL_CTX_FUNCTION || node->ctx_type == SPL_CTX_OBJECT) && !spl_lookup(task, node->ctx, key, flags) ) goto create_local; /* in all other cases: go to next context */ assert(node->ctx != node); return spl_create(task, node->ctx, key, newnode, flags); create_local: if ( ((flags & (SPL_CREATE_LOCAL|SPL_CREATE_FUNCLOCAL)) == 0) && *key != '?' ) spl_report(SPL_REPORT_RUNTIME, task, "Use (assignment) of undeclared variable '%s'!\n", key); const char *real_key = key; while (*real_key == '?') real_key++; s = calloc(1, sizeof(struct spl_node_sub)); if (node->ctx_type == SPL_CTX_ROOT && task && task->module) s->module = strdup(task->module); s->key = strdup(real_key); if ( real_key[strspn(real_key, "0123456789")] == 0 ) { int this_idx = atoi(real_key); if ( this_idx < 1024*1024*1024 && this_idx >= node->subs_next_idx ) node->subs_next_idx = this_idx+1; } if ( !node->subs_end ) { node->subs_begin = s; node->subs_end = s; } else { if ( (flags & SPL_CREATE_BEGIN) == 0 ) { node->subs_end->next = s; s->last = node->subs_end; node->subs_end = s; } else { node->subs_begin->last = s; s->next = node->subs_begin; node->subs_begin = s; } } if ( node->subs_hash_size ) { unsigned int hash = spl_subs_hash(s->key, node->subs_hash_size); s->hash_next = node->subs_hash[hash]; node->subs_hash[hash] = s; } node->subs_counter++; s->node = newnode; return newnode; } void spl_delete(struct spl_task *task, struct spl_node *node, const char *key) { if ( !node || !key ) return; if ( node->hnode_name ) { if ( task ) spl_hnode_delete(task, node, key); return; } /* A dot in the middle is an object reference, lookup head and delete tail */ char *obj_tail = strchr(key, '.'); if ( obj_tail ) { char obj_head[obj_tail - key + 1]; memcpy(obj_head, key, obj_tail - key); obj_head[obj_tail - key] = 0; obj_tail++; struct spl_node *o = spl_lookup(task, node, obj_head, 0); if (o) spl_delete(task, o, obj_tail); return; } const char *real_key = key; while ( *real_key == '?' ) real_key++; spl_subs_hash_check(node); int hash = node->subs_hash ? spl_subs_hash(real_key, node->subs_hash_size) : 0; struct spl_node_sub *s = node->subs_hash ? node->subs_hash[hash] : node->subs_begin; struct spl_node_sub **l = node->subs_hash ? &node->subs_hash[hash] : 0; while (s) { if (!strcmp(real_key, s->key)) { if ( s->last ) s->last->next = s->next; else node->subs_begin = s->next; if ( s->next ) s->next->last = s->last; else node->subs_end = s->last; if ( l ) *l = s->hash_next; struct spl_node_sub *n = node->subs_hash ? s->hash_next : s->next; spl_put(task ? task->vm : 0, s->node); if (s->module) free(s->module); free(s->key); free(s); node->subs_counter--; s = n; } else { if ( l ) l = &s->hash_next; s = node->subs_hash ? s->hash_next : s->next; } } } int spl_get_int(struct spl_node *node) { if ( !node ) return 0; if ( node->value & SPL_VALUE_INT ) return node->value_int; if ( (node->value & SPL_VALUE_FLOAT) && (node->flags & SPL_NODE_FLAG_IS_FLOAT) ) { node->value_int = node->value_float; node->value |= SPL_VALUE_INT; return node->value_int; } if ( node->value & SPL_VALUE_STRING ) { const char *str = spl_string(node->value_string); int negative = 0; node->value_int = 0; if (str && *str == '-') { negative = 1; str++; } if (str && *str == '+') str++; while (str && *str >= '0' && *str <= '9') { node->value_int = node->value_int*10 + (*str - '0'); str++; } if (negative) node->value_int *= -1; node->value |= SPL_VALUE_INT; return node->value_int; } return 0; } double spl_get_float(struct spl_node *node) { if ( !node ) return 0; if ( node->value & SPL_VALUE_FLOAT ) return node->value_float; if ( (node->value & SPL_VALUE_INT) && (node->flags & SPL_NODE_FLAG_IS_INT) ) { node->value_float = node->value_int; node->value |= SPL_VALUE_FLOAT; return node->value_float; } if ( node->value & SPL_VALUE_STRING ) { const char *str = spl_string(node->value_string); int negative = 0; node->value_float = 0; if (str && *str == '-') { negative = 1; str++; } if (str && *str == '+') str++; while (str && *str >= '0' && *str <= '9') { node->value_float = node->value_float*10 + (*str - '0'); str++; } if (str && *str == '.') { str++; double dez_pos = 10; while (str && *str >= '0' && *str <= '9') { node->value_float += (*str - '0') / dez_pos; dez_pos *= 10; str++; } } if (negative) node->value_float *= -1; node->value |= SPL_VALUE_FLOAT; return node->value_float; } return 0; } char *spl_get_string(struct spl_node *node) { if ( !node ) return ""; if ( node->value & SPL_VALUE_STRING ) return spl_string(node->value_string); if ( (node->value & SPL_VALUE_FLOAT) && (node->flags & SPL_NODE_FLAG_IS_FLOAT) ) { node->value_string = spl_string_printf(0, 0, 0, "%lg", node->value_float); node->value |= SPL_VALUE_STRING; return spl_string(node->value_string); } if ( (node->value & SPL_VALUE_INT) && (node->flags & SPL_NODE_FLAG_IS_INT) ) { node->value_string = spl_string_printf(0, 0, 0, "%d", node->value_int); node->value |= SPL_VALUE_STRING; return spl_string(node->value_string); } return ""; } struct spl_string *spl_get_spl_string(struct spl_node *node) { if ( !node ) return 0; if ( node->value & SPL_VALUE_STRING ) return node->value_string; if ( node->value & SPL_VALUE_FLOAT ) { node->value_string = spl_string_printf(0, 0, 0, "%lg", node->value_float); node->value |= SPL_VALUE_STRING; return node->value_string; } if ( node->value & SPL_VALUE_INT ) { node->value_string = spl_string_printf(0, 0, 0, "%d", node->value_int); node->value |= SPL_VALUE_STRING; return node->value_string; } return 0; } char *spl_get_value(struct spl_node *node) { if ( !node ) return 0; if ( !node->value ) return 0; return spl_get_string(node); } int spl_get_type(struct spl_node *node) { if ( !node ) return SPL_TYPE_NONE; if ( !node->value ) return SPL_TYPE_NONE; if ( node->flags & SPL_NODE_FLAG_IS_FLOAT ) return SPL_TYPE_FLOAT; if ( node->flags & SPL_NODE_FLAG_IS_INT ) return SPL_TYPE_INT; if ( node->ctx_type == SPL_CTX_OBJECT ) return SPL_TYPE_OBJ; if ( node->value & SPL_VALUE_STRING ) { char *string = spl_string(node->value_string); for (int i=0; string[i]; i++) { if (string[i] == '.') { node->flags |= SPL_NODE_FLAG_IS_FLOAT; return SPL_TYPE_FLOAT; } if (string[i] < '0' || string[i] > '9') return SPL_TYPE_NONE; } if (string[0]) { node->flags |= SPL_NODE_FLAG_IS_INT; return SPL_TYPE_INT; } } return SPL_TYPE_NONE; } struct spl_node *spl_set_int(struct spl_node *node, int value) { if ( node ) { spl_string_put(node->value_string); node->value_string = 0; node->value_float = 0; node->value_int = value; node->value = SPL_VALUE_INT; node->flags |= SPL_NODE_FLAG_IS_INT; } return node; } struct spl_node *spl_set_float(struct spl_node *node, double value) { if ( node ) { spl_string_put(node->value_string); node->value_string = 0; node->value_int = 0; node->value_float = value; node->value = SPL_VALUE_FLOAT; node->flags |= SPL_NODE_FLAG_IS_FLOAT; } return node; } struct spl_node *spl_set_string(struct spl_node *node, char *value) { if ( node ) { spl_string_put(node->value_string); node->value_float = 0; node->value_int = 0; node->value_string = spl_string_new(0, 0, 0, value, 0); node->value = value ? SPL_VALUE_STRING : 0; } return node; } struct spl_node *spl_set_spl_string(struct spl_node *node, struct spl_string *value) { if ( node ) { spl_string_put(node->value_string); node->value_float = 0; node->value_int = 0; node->value_string = value; node->value = value ? SPL_VALUE_STRING : 0; } return node; } struct spl_node *spl_tos(struct spl_task *task) { return task->stack ? task->stack->node : 0; } struct spl_node *spl_push(struct spl_task *task, struct spl_node *node) { struct spl_node_stack *n = spl_vm_malloc_node_stack_element(task->vm); n->node = node; n->next = task->stack; task->stack = n; return node; } struct spl_node *spl_pop(struct spl_task *task) { if (!task->stack) { spl_report(SPL_REPORT_RUNTIME, task, "Tried to pop from empty VM Stack!\n"); return 0; } struct spl_node_stack *s = task->stack; struct spl_node *n = s->node; task->stack = s->next; spl_vm_free_node_stack_element(task->vm, s); return n; } struct spl_node *spl_get(struct spl_node *node) { if (!node) { node = calloc(1, sizeof(struct spl_node)); spl_state_counter_malloc_inc(); } node->ref_counter++; return node; } void spl_put(struct spl_vm *vm, struct spl_node *node) { if (!node) return; node->ref_counter--; if ( node->ref_counter < 0 ) { spl_report(SPL_REPORT_RUNTIME, 0, "Got a negative reference counter!\n"); node->ref_counter = 0; } if (!vm) { if ( !node->ref_counter ) spl_report(SPL_REPORT_RUNTIME, 0, "Got final (refcount=0) spl_put() call without vm pointer!\n"); return; } if ( !node->ref_counter && node->hnode_name ) { spl_hnode_put(vm, node); } if ( !node->ref_counter ) { struct spl_node_sub *s = node->subs_begin; while (s) { struct spl_node_sub *n = s->next; spl_put(vm, s->node); if (s->module) free(s->module); free(s->key); free(s); s=n; } if (node->ctx) spl_put(vm, node->ctx); if (node->cls) spl_put(vm, node->cls); if (node->code) spl_code_put(node->code); if (node->value_string) spl_string_put(node->value_string); if ( (node->flags & SPL_NODE_FLAG_GC) != 0 ) { if ( node->gc_left ) node->gc_left->gc_right = node->gc_right; else vm->gc_list = node->gc_right; if ( node->gc_right ) node->gc_right->gc_left = node->gc_left; } spl_state_counter_free_inc(); if (node->subs_hash) free(node->subs_hash); if (node->hnode_name) free(node->hnode_name); if (node->hnode_dump) free(node->hnode_dump); if (node->path) free(node->path); free(node); } else { if ( (node->subs_counter > 0 || node->ctx || node->cls) && (node->flags & SPL_NODE_FLAG_GC) == 0 ) { node->flags |= SPL_NODE_FLAG_GC; if (!vm->gc_list) node->gc_right = 0; else (node->gc_right = vm->gc_list)->gc_left = node; (vm->gc_list = node)->gc_left = 0; } } } struct spl_node *spl_cleanup(struct spl_task *task, struct spl_node *node) { struct spl_node_stack *n = spl_vm_malloc_node_stack_element(task->vm); n->node = node; n->next = task->cleanup; task->cleanup = n; return node; } static struct spl_node *spl_copy_worker(struct spl_vm *vm, struct spl_node *node, struct spl_node *cls, int recurs) { if (node->flags & SPL_NODE_FLAG_STATIC) return spl_get(node); if (node && node->hnode_name) { struct spl_node *copy_data = spl_hnode_copy(vm, node); if (copy_data) return copy_data; } struct spl_node *newnode = spl_get(0); if (!node) return newnode; if ( recurs > 1024 ) { spl_report(SPL_REPORT_RUNTIME, 0, "Trying to copy cyclic object tree!\n"); return newnode; } newnode->flags = node->flags & ~SPL_NODE_FLAG_GC; newnode->ctx_type = node->ctx_type; if ((node->flags & SPL_NODE_FLAG_METHOD) && cls) { if (node->cls) { if (node->ctx) newnode->ctx = spl_get(node->ctx); } else { if (node->ctx && node->ctx->ctx) newnode->ctx = spl_get(node->ctx->ctx); } newnode->cls = spl_get(cls); } else { if (node->ctx) newnode->ctx = spl_get(node->ctx); if (node->cls) newnode->cls = spl_get(node->cls); } if (newnode->code) spl_code_put(newnode->code); newnode->code = node->code ? spl_code_get(node->code) : 0; newnode->code_ip = node->code_ip; newnode->value = node->value; newnode->value_int = node->value_int; newnode->value_float = node->value_float; spl_string_put(newnode->value_string); newnode->value_string = spl_string_get(node->value_string); struct spl_node_sub *s = newnode->subs_begin; while (s) { struct spl_node_sub *n = s->next; spl_put(vm, s->node); if (s->module) free(s->module); free(s->key); free(s); s=n; } if ( newnode->subs_hash ) free(newnode->subs_hash); newnode->subs_next_idx = node->subs_next_idx; newnode->subs_hash = 0; newnode->subs_hash_size = 0; newnode->subs_counter = 0; newnode->subs_begin = 0; newnode->subs_end = 0; s = node->subs_begin; while (s) { struct spl_node_sub *n = calloc(1, sizeof(struct spl_node_sub)); n->key = strdup(s->key); n->node = spl_copy_worker(vm, s->node, newnode, recurs+1); if ( !newnode->subs_end ) { newnode->subs_begin = n; newnode->subs_end = n; } else { newnode->subs_end->next = n; n->last = newnode->subs_end; newnode->subs_end = n; } newnode->subs_counter++; s = s->next; } return newnode; } struct spl_node *spl_copy(struct spl_vm *vm, struct spl_node *node, struct spl_node *cls) { return spl_copy_worker(vm, node, cls, 0); } char *spl_hash_encode(const char *source) { int source_i, target_i; if (*source == 0) return strdup("?="); for (source_i = 0, target_i = 1; source[source_i]; source_i++, target_i++) switch (source[source_i]) { case '0' ... '9': case 'A' ... 'Z': case 'a' ... 'z': case '_': break; default: target_i+=2; } char *target = malloc(target_i+1); target[0] = '?'; for (source_i = 0, target_i = 1; source[source_i]; source_i++, target_i++) switch (source[source_i]) { case '0' ... '9': case 'A' ... 'Z': case 'a' ... 'z': case '_': target[target_i] = source[source_i]; break; default: target[target_i++] = '='; target[target_i++] = (source[source_i] & 0x0f) + 'A'; target[target_i] = ((source[source_i] & 0xf0) >> 4) + 'A'; } target[target_i] = 0; return target; } char *spl_hash_decode(const char *source) { int source_i, target_i; if (*source == '?') source++; if (!strcmp(source, "=")) return strdup(""); for (source_i = target_i = 0; source[source_i]; source_i++, target_i++) if (source[source_i] == '=' && source[source_i+1] && source[source_i+2]) source_i+=2; else if (source[source_i] == '?') target_i--; char *target = malloc(target_i+1); for (source_i = target_i = 0; source[source_i]; source_i++, target_i++) if (source[source_i] == '=' && source[source_i+1] && source[source_i+2]) { target[target_i] = source[++source_i] - 'A'; target[target_i] |= (source[++source_i] - 'A') << 4; } else { if (source[source_i] != '?') target[target_i] = source[source_i]; else target_i--; } target[target_i] = 0; return target; }