/* * 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 * * exec.c: The interpreter runloop. All the opcodes are defined here. */ #include #include #include #include #include #include #include #include "spl.h" #include "compat.h" #ifdef ENABLE_REGEX_SUPPORT # include #endif /* wrapper to catch empty-stack conditions */ #define spl_pop(t) \ ({ \ struct spl_node *inner_n = spl_pop(t); \ if ( !inner_n ) retval = -1; \ inner_n; \ }) int spl_exec(struct spl_task *task) { unsigned char op, orig_op; int orig_ip; int32_t arg = 0; char *txtarg = 0; int retval = 0; if ( !task ) return -1; if ( task->flags & SPL_TASK_FLAG_BUSY ) { spl_report(SPL_REPORT_RUNTIME, task, "This task is already busy! (spl_exec() recursion?)\n"); return -1; } if ( !task->code ) { spl_report(SPL_REPORT_RUNTIME, task, "No code to execute! (Got already HALT instruction?)\n"); return -1; } task->flags |= SPL_TASK_FLAG_BUSY; task->vm->ic++; orig_ip = task->code_ip; orig_op = op = task->code->code[task->code_ip++]; if ( orig_ip == 0 && op != SPL_OP_SIG ) { spl_report(SPL_REPORT_RUNTIME, task, "Bytecode has no signature!\n"); spl_task_setcode(task, 0); retval = -1; goto finish; } if ( op < 0x60 ) { int arg_size = 4 - (op & 3); op = op & ~3; arg = spl_bytes_to_int(arg_size, task->code->code + task->code_ip); task->code_ip += arg_size; if ( op >= 0x20 ) txtarg = (char*)(task->code->code + task->code_ip + arg); } if ( task->debug ) { if ( op < 0x20 ) spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, " :%d\t%s\t:%d (%d, %d bytes)\n", orig_ip, spl_asm_op2txt(op), (int)(task->code_ip + arg), (int)arg, 4 - (orig_op & 3)); else if ( op < 0x60 ) spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, " :%d\t%s\t\"%s\" (%d, %d bytes)\n", orig_ip, spl_asm_op2txt(op), txtarg, (int)arg, 4 - (orig_op & 3)); else spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, " :%d\t%s\n", orig_ip, spl_asm_op2txt(op)); } switch ( op ) { /* opcodes with addr argument */ case SPL_OP_JUMP: { task->code_ip += arg; task->debug_str = 0; goto finish; } case SPL_OP_REGF: case SPL_OP_REGM: { struct spl_node *f = spl_get(0); f->ctx = spl_get(task->ctx); f->code = spl_code_get(task->code); f->code_ip = task->code_ip; if ( op == SPL_OP_REGF ) f->flags |= SPL_NODE_FLAG_FUNCTION; if ( op == SPL_OP_REGM ) f->flags |= SPL_NODE_FLAG_METHOD; char *s; if (task->debug_str) my_asprintf(&s, "[SPL Codepointer: %s]", task->debug_str); else my_asprintf(&s, "[SPL Codepointer: byte %d in '%s']", f->code_ip, f->code && f->code->id ? f->code->id : ""); spl_set_string(f, s); spl_push(task, f); task->code_ip += arg; task->debug_str = 0; goto finish; } case SPL_OP_ENTER: { struct spl_node *r = spl_get(0); r->ctx = task->ctx; task->ctx = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->code = spl_code_get(task->code); r->code_ip = task->code_ip + arg; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = spl_get(r->ctx); goto finish; } case SPL_OP_GOTO: { int newip = task->code_ip + arg; int c_create = 0, c_destroy = 0; int c_min = 0, c_cur = 0; if ( newip > task->code_ip ) { /* Jumping down */ for (int i = task->code_ip; i < newip; i++) { int inner_op = task->code->code[i]; if ( inner_op < 0x60 ) i += 4 - (inner_op & 3); if ( inner_op == SPL_OP_BEGIN ) c_cur++; if ( inner_op == SPL_OP_END ) c_cur--; if ( c_cur < c_min ) c_min = c_cur; } c_create = c_cur - c_min; c_destroy = -c_min; } if ( newip < task->code_ip ) { /* Jumping up */ for (int i = newip; i < task->code_ip; i++) { int inner_op = task->code->code[i]; if ( inner_op < 0x60 ) i += 4 - (inner_op & 3); if ( inner_op == SPL_OP_BEGIN ) c_cur++; if ( inner_op == SPL_OP_END ) c_cur--; if ( c_cur < c_min ) c_min = c_cur; } c_create = -c_min; c_destroy = c_cur - c_min; } for (int i = 0; i < c_destroy; i++) { spl_cleanup(task, task->ctx); task->ctx = spl_get(task->ctx->ctx); } for (int i = 0; i < c_create; i++) { struct spl_node *c = spl_get(0); c->ctx_type = SPL_CTX_LOCAL; c->ctx = task->ctx; task->ctx=c; } task->code_ip = newip; goto finish; } case SPL_OP_CATCH: { char *n = spl_get_string(spl_cleanup(task, spl_pop(task))); char cn[strlen(n) + 100]; struct spl_node *e = spl_lookup(task, task->ctx, n, 0); if (!e) { spl_report(SPL_REPORT_RUNTIME, task, "Trying to catch '%s', but no such object is declared!\n", n); retval = -1; goto finish; } snprintf(cn, strlen(n) + 100, "#catch_%s", n); struct spl_node *c = spl_get(0); c->flags |= SPL_NODE_FLAG_CATCH; c->code = spl_code_get(task->code); c->code_ip = task->code_ip + arg; c->ctx = spl_get(e); spl_create(task, task->ctx, cn, c, SPL_CREATE_LOCAL); goto finish; } /* opcodes with string argument */ case SPL_OP_PUSH: { struct spl_node *src = spl_lookup(task, task->ctx, txtarg, 0); spl_push(task, spl_get(src)); goto finish; } case SPL_OP_PUSHC: { spl_push(task, SPL_NEW_SPL_STRING(spl_string_new(SPL_STRING_STATIC, 0, 0, txtarg, task->code))); goto finish; } case SPL_OP_POP: { spl_create(task, task->ctx, txtarg, spl_pop(task), 0); goto finish; } case SPL_OP_PUSHA: { struct spl_node *c = spl_pop(task); struct spl_node *src = spl_lookup(task, task->ctx, txtarg, 0); spl_push(task, spl_get(src)); spl_set_int(c, spl_get_int(c) + 1); c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); goto finish; } case SPL_OP_PUSHAC: { struct spl_node *c = spl_pop(task); spl_push(task, SPL_NEW_SPL_STRING(spl_string_new(SPL_STRING_STATIC, 0, 0, txtarg, task->code))); spl_set_int(c, spl_get_int(c) + 1); c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); goto finish; } case SPL_OP_POPA: { struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); if ( spl_get_int(c) > 0 ) { struct spl_node *v = spl_pop(task); spl_create(task, task->ctx, txtarg, v, SPL_CREATE_LOCAL); spl_set_int(c, spl_get_int(c) - 1); } else { spl_create(task, task->ctx, txtarg, spl_get(0), SPL_CREATE_LOCAL); } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_push(task, retinfo); goto finish; } case SPL_OP_CLIB: { if ( spl_clib_call(task, txtarg) < 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Can't find CLIB handler for '%s'!\n", txtarg); retval = -1; } else { if (task->stack && task->stack->node && (task->stack->node->flags & SPL_NODE_FLAG_CLEXCEPT)) { task->stack->node->flags &= ~SPL_NODE_FLAG_CLEXCEPT; goto throw_entry_point; } } goto finish; } case SPL_OP_DCALL: { char txtarg_dyn[strlen(txtarg)+2]; sprintf(txtarg_dyn, "?%s", txtarg); struct spl_node *f = spl_lookup(task, task->ctx, txtarg_dyn, 0); if ( f && f->code && (f->flags & (SPL_NODE_FLAG_FUNCTION|SPL_NODE_FLAG_METHOD)) ) { struct spl_node *r = spl_get(0); r->ctx = task->ctx; task->ctx = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->code = task->code; task->code = spl_code_get(f->code); r->code_ip = task->code_ip; task->code_ip = f->code_ip; task->debug_str = 0; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = f->ctx ? spl_get(f->ctx) : 0; task->ctx->cls = f->cls ? spl_get(f->cls) : 0; goto finish; } if ( spl_clib_call(task, txtarg) < 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Can't find function or CLIB handler for '%s'!\n", txtarg); retval = -1; } else { if (task->stack && task->stack->node && (task->stack->node->flags & SPL_NODE_FLAG_CLEXCEPT)) { task->stack->node->flags &= ~SPL_NODE_FLAG_CLEXCEPT; goto throw_entry_point; } } goto finish; } case SPL_OP_DBGSYM: { task->debug_str = txtarg; goto finish; } op_objop: case SPL_OP_OBJOP: { struct spl_node *n2 = SPL_POP_CLEANUP(task); struct spl_node *n1 = SPL_POP_CLEANUP(task); if ( !n1 || n1->ctx_type != SPL_CTX_OBJECT) { spl_report(SPL_REPORT_RUNTIME, task, "Opcode OBJOP executed with a non-object argument!\n"); retval = -1; goto finish; } char method_name[strlen(txtarg) + 20]; snprintf(method_name, strlen(txtarg) + 20, "operator_%s", txtarg); struct spl_node *method = spl_lookup(task, n1, method_name, SPL_LOOKUP_NOCTX); if ( !method ) { spl_report(SPL_REPORT_RUNTIME, task, "Opcode OBJOP executed for non-defined method '%s'!\n", method_name); retval = -1; goto finish; } spl_push(task, spl_get(n2)); spl_push(task, spl_get(n1)); spl_push(task, SPL_NEW_INT(2)); spl_push(task, spl_get(method)); goto op_call; } /* opcodes without arguments */ case SPL_OP_ZERO: { spl_push(task, SPL_NEW_INT(0)); goto finish; } case SPL_OP_ONE: { spl_push(task, SPL_NEW_INT(1)); goto finish; } case SPL_OP_UNDEF: { spl_push(task, spl_get(0)); goto finish; } #ifdef ENABLE_REGEX_SUPPORT case SPL_OP_REMATCH: case SPL_OP_RESUBST: { unsigned char *text = 0; unsigned int text_len = 0; char *id = 0, *match, *subst = 0, *mod; struct spl_node *result_node = spl_get(0); struct spl_node *last_result_for_context = 0; struct spl_node *last_result2_for_context = 0; struct spl_string *origtext = 0; struct spl_node *textnode = 0; mod = spl_get_string(SPL_POP_CLEANUP(task)); if (op == SPL_OP_RESUBST) subst = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_string *match_string = spl_get_spl_string(SPL_POP_CLEANUP(task)); match = spl_string(match_string); int options = 0; int do_global = 0; const char *error; int erroffset, rc; int ovector[300]; int rematched = 0; pcre *re = 0; pcre_extra *pe = 0; int with_context = 0; int use_last_match = 0; int pass_array = 0; int pass_by_number = 0; int pass_by_name = 0; int pass_subst = 0; int do_import = 0; int do_split = 0; int startoffset = 0; int newtext_pos = 0; int newtext_len = 0; int newtext_avail = 0; char *newtext = 0; for (int i=0; mod[i]; i++) switch (mod[i]) { case 'i': options |= PCRE_CASELESS; break; case 's': options |= PCRE_DOTALL; break; case 'x': options |= PCRE_EXTENDED; break; case 'm': options |= PCRE_MULTILINE; break; case 'g': do_global = 1; break; case 'E': with_context = 1; break; case 'L': use_last_match = 1; break; case 'A': pass_array = 1; break; case 'N': pass_by_number = 1; break; case 'P': pass_by_name = 1; break; case 'R': pass_subst = 1; break; case 'I': do_import = 1; break; case 'S': do_split = 1; break; default: spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Unrecognised regex modifier: '%c'!\n", mod[i]); retval = -1; goto re_finish; } if (do_split && (pass_by_number || pass_by_name || pass_array)) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Unrecognised regex modifier combination: '%s'!\n", mod); retval = -1; goto re_finish; } if (op == SPL_OP_RESUBST) { if (pass_subst) { textnode = spl_pop(task); } else { id = spl_get_string(SPL_POP_CLEANUP(task)); textnode = spl_get(spl_lookup(task, task->ctx, id, 0)); } origtext = spl_get_spl_string(textnode); } else { origtext = spl_get_spl_string(SPL_POP_CLEANUP(task)); } text = (unsigned char *)spl_string(origtext); text_len = origtext ? origtext->total_len : 0; if (match_string && (match_string->flags & SPL_STRING_UTF8)) options |= PCRE_UTF8; if (origtext && (origtext->flags & SPL_STRING_UTF8)) options |= PCRE_UTF8; if (pass_subst && textnode) spl_set_spl_string(result_node, spl_string_get(spl_get_spl_string(textnode))); re = pcre_compile(match, options, &error, &erroffset, 0); if ( re == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: %s at char %d!\n", error, erroffset); retval = -1; goto re_finish; } pe = pcre_study(re, 0, &error); if ( error ) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: %s!\n", error); retval = -1; goto re_finish; } regex_redo: if (do_global == 2) { rc = pcre_exec(re, pe, (char*)text, text_len, startoffset, PCRE_NOTEMPTY|PCRE_ANCHORED, ovector, 300); if ( rc == PCRE_ERROR_NOMATCH && text[startoffset]) { // skip one utf-8 encoded unicode character int extra_startoffset = 1; while ( text[startoffset+extra_startoffset] > 0x7f && text[startoffset+extra_startoffset] < 0xc0 ) extra_startoffset++; rc = pcre_exec(re, pe, (char*)text, text_len, startoffset+extra_startoffset, 0, ovector, 300); } } else { rc = pcre_exec(re, pe, (char*)text, text_len, startoffset, 0, ovector, 300); } if ( rc == 0 ) rc = 100; if ( rc == PCRE_ERROR_NOMATCH ) goto re_finish; if ( rc < 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: PCRE Error %d (see 'man pcreapi')!\n", rc); retval = -1; goto re_finish; } struct spl_node *result = spl_get(0); result->flags |= SPL_NODE_FLAG_RERES; struct spl_node *result_node2 = 0; if (pass_by_number || pass_by_name) { if (pass_array) { result_node2 = spl_get(0); spl_create(task, result_node, 0, result_node2, SPL_CREATE_LOCAL); } else result_node2 = result_node; } for (int i = 0; i < rc; i++) { int offset = ovector[2*i]; int length = ovector[2*i+1] - ovector[2*i]; char key[32]; snprintf(key, 32, "%d", i); struct spl_node *valnode = offset < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, offset, length)); if (pass_by_number) spl_create(task, result_node2, key, spl_get(valnode), SPL_CREATE_LOCAL); if (i == 0 && pass_array && !pass_by_number && !pass_by_name) spl_create(task, result_node, 0, spl_get(valnode), SPL_CREATE_LOCAL); spl_create(task, result, key, valnode, SPL_CREATE_LOCAL); } if (do_split || with_context) { if (do_split) spl_create(task, result_node, 0, ovector[0] < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, ovector[0] - startoffset)), SPL_CREATE_LOCAL); if (with_context) { struct spl_node *valnode = ovector[0] < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, ovector[0] - startoffset)); // HENC "+" => "=LC" if (last_result_for_context) spl_create(task, last_result_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); if (last_result2_for_context) spl_create(task, last_result2_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); // HENC "-" => "=NC" spl_create(task, result, "=NC", valnode, SPL_CREATE_LOCAL); if (result_node2) spl_create(task, result_node2, "=NC", spl_get(valnode), SPL_CREATE_LOCAL); last_result_for_context = result; last_result2_for_context = result_node2; } } int nametab_count; int nametab_entsize; const unsigned char *nametab; if ( !pcre_fullinfo(re, 0, PCRE_INFO_NAMECOUNT, &nametab_count) && !pcre_fullinfo(re, 0, PCRE_INFO_NAMEENTRYSIZE, &nametab_entsize) && !pcre_fullinfo(re, 0, PCRE_INFO_NAMETABLE, &nametab) ) for (int i=0; i < nametab_count; i++) { unsigned int captnum = nametab[i*nametab_entsize] << 8 | nametab[i*nametab_entsize+1]; const char *name = (const char*)nametab + (i*nametab_entsize+2); int offset = ovector[2*captnum]; int length = ovector[2*captnum+1] - ovector[2*captnum]; struct spl_node *valnode = offset < 0 ? spl_get(0) : SPL_NEW_SPL_STRING(spl_string_split(origtext, offset, length)); if (pass_by_name) spl_create(task, result_node2, name, spl_get(valnode), SPL_CREATE_LOCAL); if (do_import) spl_create(task, task->ctx, name, spl_get(valnode), SPL_CREATE_LOCAL); spl_create(task, result, name, valnode, SPL_CREATE_LOCAL); } if (rematched == 0 || use_last_match == 1) spl_create(task, task->ctx, "#reres", result, SPL_CREATE_FUNCLOCAL); else { spl_put(task->vm, result); last_result_for_context = 0; } rematched++; if (op == SPL_OP_RESUBST) { newtext_len = newtext_pos + (ovector[0] - startoffset) + (text_len - ovector[1]); for (int i=0; subst[i]; i++) { if (subst[i] == '\\' && subst[i+1]) { newtext_len++; i++; } else if (subst[i] == '$' && isdigit((unsigned char)subst[i+1])) { int captnum = subst[i+1] - '0'; if (captnum >= rc) { rematched = 0; spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Substitution refers to not existing capture $<%d>!\n", captnum); retval = -1; goto re_finish; } newtext_len += ovector[2*captnum+1] - ovector[2*captnum]; i++; } else if (subst[i] == '$' && subst[i+1] == '<') { int namelen = strcspn(subst+2, ">"); char *endptr, *name = my_strndupa(subst+2, namelen); if (subst[i+namelen+2] != '>') { rematched = 0; spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Missing '>' in '$< .. >' in regex substitution!\n"); retval = -1; goto re_finish; } int captnum = strtol(name, &endptr, 10); if (*endptr) captnum = pcre_get_stringnumber(re, name); if (captnum >= rc || captnum < 0) { rematched = 0; spl_report(SPL_REPORT_RUNTIME, task, "Regex Engine: Substitution refers to not existing capture $<%s>!\n", name); retval = -1; goto re_finish; } newtext_len += ovector[2*captnum+1] - ovector[2*captnum]; i += namelen+2; } else newtext_len++; } if (newtext_len+1 > newtext_avail) { newtext_avail = newtext_len + 1000; newtext = realloc(newtext, newtext_avail); } if (startoffset < ovector[0]) { int length = ovector[0] - startoffset; memcpy(newtext + newtext_pos, text + startoffset, length); newtext_pos += length; } for (int i=0; subst[i]; i++) { if (subst[i] == '\\' && subst[i+1]) { switch (subst[i+1]) { case 'a': newtext[newtext_pos++] = '\a'; break; case 'b': newtext[newtext_pos++] = '\b'; break; case 't': newtext[newtext_pos++] = '\t'; break; case 'n': newtext[newtext_pos++] = '\n'; break; case 'v': newtext[newtext_pos++] = '\v'; break; case 'f': newtext[newtext_pos++] = '\f'; break; case 'r': newtext[newtext_pos++] = '\r'; break; default: newtext[newtext_pos++] = subst[i+1]; } i++; } else if (subst[i] == '$' && isdigit((unsigned char)subst[i+1])) { int captnum = subst[i+1] - '0'; int length = ovector[2*captnum+1] - ovector[2*captnum]; memcpy(newtext+newtext_pos, text + ovector[2*captnum], length); newtext_pos += length; i++; } else if (subst[i] == '$' && subst[i+1] == '<') { int namelen = strcspn(subst+2, ">"); char *endptr, *name = my_strndupa(subst+2, namelen); int captnum = strtol(name, &endptr, 10); if (*endptr) captnum = pcre_get_stringnumber(re, name); int length = ovector[2*captnum+1] - ovector[2*captnum]; memcpy(newtext+newtext_pos, text + ovector[2*captnum], length); newtext_pos += length; i += namelen+2; } else newtext[newtext_pos++] = subst[i]; } strcpy(newtext + newtext_pos, (char*)text + ovector[1]); } startoffset = ovector[1]; if (do_global && text[startoffset]) { do_global = 2; goto regex_redo; } re_finish: if (last_result_for_context || last_result2_for_context) { struct spl_node *valnode = SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, (origtext ? origtext->total_len : 0) - startoffset)); // HENC "+" => "=LC" if (last_result_for_context) spl_create(task, last_result_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); if (last_result2_for_context) spl_create(task, last_result2_for_context, "=LC", spl_get(valnode), SPL_CREATE_LOCAL); spl_put(task->vm, valnode); } if (do_split) spl_create(task, result_node, 0, SPL_NEW_SPL_STRING(spl_string_split(origtext, startoffset, (origtext ? origtext->total_len : 0) - startoffset)), SPL_CREATE_LOCAL); if (textnode) spl_put(task->vm, textnode); if (newtext) { newtext = realloc(newtext, newtext_len+1); if (pass_subst && textnode) spl_set_spl_string(result_node, spl_string_new(0, 0, 0, newtext, 0)); else spl_create(task, task->ctx, id, SPL_NEW_STRING(newtext), 0); } if ( !(pass_subst && textnode) ) spl_set_int(result_node, rematched); spl_push(task, result_node); pcre_free(re); pcre_free(pe); goto finish; } #else case SPL_OP_REMATCH: case SPL_OP_RESUBST: { spl_report(SPL_REPORT_RUNTIME, task, "Regex support not enabled.\n"); retval = -1; goto finish; } #endif case SPL_OP_DEFINED: { struct spl_node *n = SPL_POP_CLEANUP(task); spl_push(task, SPL_NEW_INT( n->value != 0 ? 1 : 0 )); goto finish; } case SPL_OP_DECLARED: { char *name = spl_get_string(SPL_POP_CLEANUP(task)); char *name_dot = strrchr(name, '.'); char name_dyn[strlen(name)+2]; if (name_dot) sprintf(name_dyn, "%.*s.?%s", (int)(name_dot-name), name, name_dot+1); else sprintf(name_dyn, "?%s", name); struct spl_node *n = spl_lookup(task, task->ctx, name_dyn, 0); spl_push(task, SPL_NEW_INT( n != 0 ? 1 : 0 )); goto finish; } case SPL_OP_NEXT: case SPL_OP_PREV: { struct spl_node *key_node = SPL_POP_CLEANUP(task); struct spl_node *node = SPL_POP_CLEANUP(task); struct spl_node_sub *s; if (node->hnode_name) { char *key = key_node->value ? spl_hash_encode(spl_get_string(key_node)) : 0; struct spl_node *result = (op == SPL_OP_NEXT ? spl_hnode_next : spl_hnode_prev) (task, node, key); free(key); if (result) { spl_push(task, result); goto finish; } } if (!key_node->value) { next_prev_first: s = op == SPL_OP_NEXT ? node->subs_begin : node->subs_end; spl_push(task, s ? SPL_NEW_STRING(spl_hash_decode(s->key)) : spl_get(0)); goto finish; } char *key = spl_hash_encode(spl_get_string(key_node)); s = spl_sub_lookup(node, key); if (!s) { free(key); goto next_prev_first; } s = op == SPL_OP_NEXT ? s->next : s->last; if (s) { spl_push(task, SPL_NEW_STRING(spl_hash_decode(s->key))); free(key); goto finish; } spl_push(task, spl_get(0)); free(key); goto finish; } case SPL_OP_HGETA: { struct spl_node *h = spl_pop(task); struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); struct spl_node_sub *s = c->subs_end; while (s) { spl_create(task, h, s->key, spl_get(s->node), SPL_CREATE_LOCAL); s = s->last; } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_push(task, retinfo); spl_put(task->vm, h); goto finish; } case SPL_OP_HSETA: { struct spl_node *h = spl_pop(task); struct spl_node *c = spl_pop(task); struct spl_node_sub *s = h->subs_end; while (s) { spl_create(task, c, s->key, spl_get(s->node), SPL_CREATE_LOCAL); s = s->last; } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_put(task->vm, h); goto finish; } op_call: case SPL_OP_CALL: { struct spl_node *f = SPL_POP_CLEANUP(task); if ( !f->ctx || !f->code || !(f->flags & (SPL_NODE_FLAG_FUNCTION|SPL_NODE_FLAG_METHOD)) ) { spl_report(SPL_REPORT_RUNTIME, task, "Tried to execute CALL on a variable with no function/method reference!\n"); retval = -1; goto finish; } struct spl_node *r = spl_get(0); r->ctx = task->ctx; task->ctx = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->code = task->code; task->code = spl_code_get(f->code); r->code_ip = task->code_ip; task->code_ip = f->code_ip; task->debug_str = 0; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); task->ctx->ctx_type = SPL_CTX_FUNCTION; task->ctx->ctx = f->ctx ? spl_get(f->ctx) : 0; task->ctx->cls = f->cls ? spl_get(f->cls) : 0; goto finish; } case SPL_OP_BEGIN: { struct spl_node *c = spl_get(0); c->ctx_type = SPL_CTX_LOCAL; c->ctx = task->ctx; task->ctx=c; goto finish; } case SPL_OP_END: { spl_cleanup(task, task->ctx); task->ctx = spl_get(task->ctx->ctx); goto finish; } case SPL_OP_RETURN: { struct spl_node *results = spl_pop(task); struct spl_node *retinfo = spl_pop(task); while ( retinfo && (retinfo->flags & SPL_NODE_FLAG_RETINFO) == 0 ) { spl_put(task->vm, retinfo); retinfo = spl_pop(task); } struct spl_code *old_code = task->code; struct spl_node *old_ctx = task->ctx; if ( !retinfo || !retinfo->ctx || !retinfo->code ) { spl_report(SPL_REPORT_RUNTIME, task, "Tried to execute RETURN with no return information!\n"); retval = -1; goto finish; } task->ctx = spl_get(retinfo->ctx); spl_put(task->vm, old_ctx); task->code = spl_code_get(retinfo->code); spl_code_put(old_code); task->code_ip = retinfo->code_ip; task->debug_str = 0; spl_put(task->vm, retinfo); spl_push(task, results); goto finish; } case SPL_OP_OBJECT: { struct spl_node *b = spl_pop(task); struct spl_node *n = spl_pop(task); struct spl_node *src = 0; if (b->value) { src = spl_lookup(task, task->ctx, spl_get_string(b), 0); if ( !src ) { spl_report(SPL_REPORT_RUNTIME, task, "Parent object '%s' not found!\n", spl_get_string(b)); retval = -1; goto finish; } } if ( src && (src->flags & SPL_NODE_FLAG_CLASS) == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Parent object '%s' isn't really an object!\n", spl_get_string(b)); retval = -1; goto finish; } struct spl_node *c = spl_get(0); if ( src ) { c->cls = spl_get(src); spl_set_spl_string(c, spl_string_new(SPL_STRING_STATIC, spl_get_spl_string(src), spl_get_spl_string(n), " | ", 0)); } else spl_set_spl_string(c, spl_string_get(spl_get_spl_string(n))); // struct spl_node *r = spl_get(0); // r->flags |= SPL_NODE_FLAG_REF; // r->ctx = c; spl_create(task, task->ctx, spl_get_string(n), c, SPL_CREATE_LOCAL); spl_put(task->vm, c->ctx); c->flags |= SPL_NODE_FLAG_CLASS; c->ctx_type = SPL_CTX_OBJECT; c->ctx = task->ctx; task->ctx = spl_get(c); spl_put(task->vm, b); spl_put(task->vm, n); goto finish; } case SPL_OP_ENDOBJ: { struct spl_node *old_ctx = task->ctx; task->ctx = spl_get(task->ctx->ctx); spl_put(task->vm, old_ctx); goto finish; } case SPL_OP_IMPORT: { struct spl_node *src = SPL_POP_CLEANUP(task); struct spl_node_sub *s = src->subs_begin; while (s) { int name_is_ok = s->key[0] != 0; for (int i=0; name_is_ok && s->key[i]; i++) { if (s->key[i] == '_') continue; if (s->key[i] >= 'a' && s->key[i] <= 'z') continue; if (s->key[i] >= 'A' && s->key[i] <= 'Z') continue; if (s->key[i] >= '0' && s->key[i] <= '9' && i > 0) continue; name_is_ok = 0; } if (name_is_ok) { struct spl_node *n; if (s->node->flags & SPL_NODE_FLAG_METHOD) n = spl_copy(task->vm, s->node, task->ctx); else n = spl_get(s->node); spl_create(task, task->ctx, s->key, n, SPL_CREATE_LOCAL|SPL_CREATE_NOSTATIC); } s = s->next; } goto finish; } case SPL_OP_LIFTCALL: { struct spl_node_stack *sp = task->stack, *lp = 0; int c = sp ? spl_get_int(sp->node)+1 : 0; while (c && sp) c--, sp = (lp=sp)->next; if (sp && lp && !c) { lp->next = sp->next; sp->next = task->stack; task->stack = sp; } goto finish; } case SPL_OP_IF: { if ( spl_get_int(SPL_POP_CLEANUP(task)) == 0 ) { if ( task->code->code[task->code_ip] < 0x60 ) task->code_ip += 5 - (task->code->code[task->code_ip] & 3); else task->code_ip++; } goto finish; } case SPL_OP_UNLESS: { if ( spl_get_int(SPL_POP_CLEANUP(task)) != 0 ) { if ( task->code->code[task->code_ip] < 0x60 ) task->code_ip += 5 - (task->code->code[task->code_ip] & 3); else task->code_ip++; } goto finish; } case SPL_OP_TRY: { struct spl_node *n = spl_pop(task); if (task->ctx) { task->ctx->flags |= SPL_NODE_FLAG_TRY; spl_create(task, task->ctx, "#try", n, SPL_CREATE_LOCAL); } else spl_put(task->vm, n); goto finish; } case SPL_OP_THROW: { throw_entry_point:; struct spl_node *e = spl_pop(task); e->flags |= SPL_NODE_FLAG_EXCEPTION; char *backtrace = spl_backtrace_string(task); retry_catch_lookup: while ( task->ctx && (task->ctx->ctx_type == SPL_CTX_FUNCTION || task->ctx->ctx_type == SPL_CTX_LOCAL) ) { struct spl_node *this_ctx = task->ctx; if ( this_ctx->flags & SPL_NODE_FLAG_TRY ) { struct spl_node_sub *s = this_ctx->subs_begin; while (s) { if ((s->node->flags & SPL_NODE_FLAG_CATCH) == 0) goto next_throw_sub; struct spl_node *el = e; while (el && el != s->node->ctx) el = el->cls ? el->cls : el->ctx; if (el != s->node->ctx) goto next_throw_sub; task->code_ip = s->node->code_ip; spl_code_put(task->code); task->code = spl_code_get(s->node->code); task->debug_str = 0; struct spl_node *bt = SPL_NEW_STRING(backtrace); spl_create(task, e, "backtrace", bt, SPL_CREATE_LOCAL); struct spl_node *t = spl_lookup(task, this_ctx, "#try", SPL_LOOKUP_NOCTX); if (t) spl_create(task, this_ctx, spl_get_string(t), e, SPL_CREATE_LOCAL); else spl_put(task->vm, e); goto finish; next_throw_sub: s = s->next; } } task->ctx = spl_get(this_ctx->ctx); spl_put(task->vm, this_ctx); } struct spl_node *retinfo = task->stack ? spl_pop(task) : 0; while ( retinfo && (retinfo->flags & SPL_NODE_FLAG_RETINFO) == 0 ) { spl_put(task->vm, retinfo); retinfo = task->stack ? spl_pop(task) : 0; } if ( !retinfo || !retinfo->ctx || !retinfo->code ) { spl_code_put(task->code); task->code_ip = 0; task->code = 0; struct spl_node *dn = spl_lookup(task, e, "description", SPL_LOOKUP_NOCTX|SPL_LOOKUP_TEST); char *ds = dn ? spl_get_string(dn) : ""; int ds_len = 10; for (int i=0; ds[i]; i++) ds_len += ds[i] == '\n' ? 10 : 1; char dsc[ds_len]; strcpy(dsc, *ds ? ">> " : ""); for (int i=0, j=3; ds[i]; i++) if (ds[i] == '\n') { if (ds[i+1] == 0) break; strcpy(dsc+j, "\n>> "); j += 4; } else { dsc[j++] = ds[i]; dsc[j] = 0; } spl_report(SPL_REPORT_RUNTIME, task, "Thrown uncaught exception: %s\n%s%s%s", spl_get_string(e), dsc, *dsc ? "\n" : "", backtrace); spl_put(task->vm, e); free(backtrace); retval = -1; goto finish; } struct spl_code *old_code = task->code; struct spl_node *old_ctx = task->ctx; task->debug_str = 0; task->ctx = spl_get(retinfo->ctx); spl_put(task->vm, old_ctx); task->code = spl_code_get(retinfo->code); spl_code_put(old_code); task->code_ip = retinfo->code_ip; spl_put(task->vm, retinfo); goto retry_catch_lookup; } case SPL_OP_NEW: { struct spl_node *src = SPL_POP_CLEANUP(task); struct spl_node *n = spl_get(0); struct spl_string *ts = spl_string_new(SPL_STRING_STATIC, 0, spl_get_spl_string(src), "[ ", 0); spl_set_spl_string(n, spl_string_new(SPL_STRING_STATIC, ts, 0, " ]", 0)); spl_string_put(ts); if ( !src || (src->flags & SPL_NODE_FLAG_CLASS) == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "Opcode NEW executed with a non-object argument!\n"); spl_put(task->vm, n); retval = -1; goto finish; } n->cls = spl_get(src); if (src->ctx) n->ctx = spl_get(src->ctx); n->ctx_type = SPL_CTX_OBJECT; struct spl_node *in = spl_lookup(task, n, "?init", SPL_LOOKUP_NOCTX); if ( in ) { spl_cleanup(task, n); spl_push(task, spl_get(in)); goto op_call; } spl_report(SPL_REPORT_RUNTIME, task, "Opcode NEW executed with an object argument without constructor!\n"); spl_put(task->vm, n); retval = -1; goto finish; } case SPL_OP_SETCTX: { struct spl_node *n = spl_pop(task); spl_put(task->vm, n->ctx); spl_put(task->vm, n->cls); n->ctx = spl_get(task->ctx); n->cls = 0; spl_push(task, n); goto finish; } case SPL_OP_GETVAL: { struct spl_node *id = spl_cleanup(task, spl_pop(task)); struct spl_node *src = spl_lookup(task, task->ctx, spl_get_string(id), 0); spl_push(task, spl_get(src)); goto finish; } case SPL_OP_COPY: { struct spl_node *n = spl_pop(task); spl_push(task, spl_get(n)); spl_push(task, n); goto finish; } case SPL_OP_DROP: { spl_put(task->vm, spl_pop(task)); goto finish; } case SPL_OP_POPI: case SPL_OP_POPIC: case SPL_OP_POPL: case SPL_OP_POPLC: case SPL_OP_POPS: case SPL_OP_POPSC: case SPL_OP_POPU: case SPL_OP_POPUC: { struct spl_node *val = spl_pop(task); struct spl_node *id = spl_pop(task); if ( op == SPL_OP_POPU || op == SPL_OP_POPUC ) { struct spl_node *val_old = val; val = spl_copy(task->vm, val_old, 0); spl_put(task->vm, val_old); } if ( op == SPL_OP_POPIC || op == SPL_OP_POPLC || op == SPL_OP_POPSC || op == SPL_OP_POPUC ) spl_push(task, spl_get(val)); if ( op == SPL_OP_POPS || op == SPL_OP_POPSC ) { struct spl_node *static_pointer = spl_get(0); static_pointer->flags |= SPL_NODE_FLAG_STATIC; static_pointer->ctx = val; val = static_pointer; } spl_create(task, task->ctx, spl_get_string(id), val, op == SPL_OP_POPL || op == SPL_OP_POPLC || op == SPL_OP_POPS || op == SPL_OP_POPSC ? SPL_CREATE_LOCAL : 0); spl_put(task->vm, id); goto finish; } case SPL_OP_PUSHAV: { struct spl_node *v = spl_pop(task); struct spl_node *c = spl_pop(task); spl_set_int(c, spl_get_int(c) + 1); c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, v); spl_push(task, c); goto finish; } case SPL_OP_CLEARA: { struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); while ( spl_get_int(c) > 0 ) { spl_set_int(c, spl_get_int(c) - 1); spl_put(task->vm, spl_pop(task)); } spl_put(task->vm, c); spl_push(task, retinfo); goto finish; } case SPL_OP_APUSHA: { struct spl_node *a = spl_cleanup(task, spl_pop(task)); struct spl_node *c = spl_pop(task); struct spl_node_sub *s = a ? a->subs_end : 0; while (s) { spl_set_int(c, spl_get_int(c) + 1); spl_push(task, spl_get(s->node)); s = s->last; } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); goto finish; } case SPL_OP_APOPA: { const char *n = spl_get_string(spl_cleanup(task, spl_pop(task))); struct spl_node *retinfo = spl_pop(task); struct spl_node *c = spl_pop(task); struct spl_node *a = spl_get(0); while ( spl_get_int(c) > 0 ) { spl_set_int(c, spl_get_int(c) - 1); struct spl_node *v = spl_pop(task); spl_create(task, a, NULL, v, SPL_CREATE_LOCAL); } c->flags |= SPL_NODE_FLAG_ARGC; spl_push(task, c); spl_push(task, retinfo); spl_create(task, task->ctx, n, a, SPL_CREATE_LOCAL); goto finish; } case SPL_OP_HENC: { char *source = spl_get_string(spl_cleanup(task, spl_pop(task))); spl_push(task, SPL_NEW_STRING(spl_hash_encode(source))); goto finish; } case SPL_OP_HDEC: { char *source = spl_get_string(spl_cleanup(task, spl_pop(task))); spl_push(task, SPL_NEW_STRING(spl_hash_decode(source))); goto finish; } case SPL_OP_CAT: case SPL_OP_DOTCAT: { struct spl_string *n2 = spl_get_spl_string(SPL_POP_CLEANUP(task)); struct spl_string *n1 = spl_get_spl_string(SPL_POP_CLEANUP(task)); struct spl_string *result = spl_string_new(op == SPL_OP_DOTCAT ? SPL_STRING_DOTCAT : 0, n1, n2, 0, 0); spl_push(task, SPL_NEW_SPL_STRING(result)); goto finish; } case SPL_OP_APOP: case SPL_OP_ASHIFT: { struct spl_node *a = SPL_POP_CLEANUP(task); if ( !a ) { spl_push(task, spl_get(0)); goto finish; } struct spl_node_sub *s = (op == SPL_OP_APOP) ? a->subs_end : a->subs_begin; if ( !s ) { spl_push(task, spl_get(0)); goto finish; } spl_push(task, s->node); if ( s->last ) s->last->next = s->next; else a->subs_begin = s->next; if ( s->next ) s->next->last = s->last; else a->subs_end = s->last; a->subs_counter--; if ( a->subs_hash ) { int hash = spl_subs_hash(s->key, a->subs_hash_size); struct spl_node_sub **l = &a->subs_hash[hash]; while (*l) { if (*l == s) { *l = s->hash_next; break; } l = &((*l)->hash_next); } } if (s->module) free(s->module); free(s->key); free(s); goto finish; } case SPL_OP_APUSH: case SPL_OP_AUNSHIFT: { struct spl_node *v = spl_pop(task); char *aid = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *a = spl_lookup(task, task->ctx, aid, 0); if (!a) a = spl_create(task, task->ctx, aid, spl_get(0), 0); spl_push(task, SPL_NEW_INT(a->subs_next_idx)); spl_create(task, a, NULL, v, SPL_CREATE_LOCAL | (op == SPL_OP_AUNSHIFT ? SPL_CREATE_BEGIN : 0)); goto finish; } case SPL_OP_APUSHREF: case SPL_OP_APUSHREFID: { struct spl_node *v = spl_pop(task); char *key = 0; if (op == SPL_OP_APUSHREFID) key = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *a = spl_pop(task); spl_create(task, a, key, v, SPL_CREATE_LOCAL); spl_push(task, a); goto finish; } case SPL_OP_LENGTH: { struct spl_string *string = spl_get_spl_string(SPL_POP_CLEANUP(task)); if (!string) spl_push(task, SPL_NEW_INT(0)); else if (string->flags & SPL_STRING_UTF8) { unsigned char *str = (unsigned char *)spl_string(string); int len = 0; for (int i=0; str[i]; i++) if (str[i] <= 0x7F || str[i] >= 0xC0) len++; spl_push(task, SPL_NEW_INT(len)); } else spl_push(task, SPL_NEW_INT(string->total_len)); goto finish; } case SPL_OP_ELEMENTS: { struct spl_node *n = SPL_POP_CLEANUP(task); spl_push(task, SPL_NEW_INT(n->subs_counter)); goto finish; } case SPL_OP_LAND: { int n2 = spl_get_int(SPL_POP_CLEANUP(task)); int n1 = spl_get_int(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_INT(n1 && n2 ? 1 : 0)); goto finish; } case SPL_OP_LOR: { int n2 = spl_get_int(SPL_POP_CLEANUP(task)); int n1 = spl_get_int(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_INT(n1 || n2 ? 1 : 0)); goto finish; } case SPL_OP_LNOT: { spl_push(task, SPL_NEW_INT( ! spl_get_int(SPL_POP_CLEANUP(task)) )); goto finish; } case SPL_OP_NEG: { struct spl_node *n1 = SPL_POP_CLEANUP(task); char *new, *old = spl_get_string(n1); switch ( old[0] ) { case '-': new = strdup(old+1); break; case '+': my_asprintf(&new, "-%s", old+1); break; default: my_asprintf(&new, "-%s", old); break; } struct spl_node *n2 = SPL_NEW_STRING(new); n2->flags |= n1->flags & (SPL_NODE_FLAG_IS_INT | SPL_NODE_FLAG_IS_FLOAT); spl_push(task, n2); goto finish; } case SPL_OP_EQ ... SPL_OP_POW: { struct spl_node *n1 = 0; struct spl_node *n2 = 0; if (task->stack) { n2 = task->stack->node; if (task->stack->next) n1 = task->stack->next->node; } if (!n1) { SPL_POP_CLEANUP(task); SPL_POP_CLEANUP(task); goto finish; } int n1_type = spl_get_type(n1); int n2_type = spl_get_type(n2); if (n1_type == SPL_TYPE_OBJ || n2_type == SPL_TYPE_OBJ) { switch ( op ) { case SPL_OP_EQ: txtarg="eq"; break; case SPL_OP_NE: txtarg="ne"; break; case SPL_OP_LT: txtarg="lt"; break; case SPL_OP_GT: txtarg="gt"; break; case SPL_OP_LE: txtarg="le"; break; case SPL_OP_GE: txtarg="ge"; break; case SPL_OP_ADD: txtarg="add"; break; case SPL_OP_SUB: txtarg="sub"; break; case SPL_OP_MUL: txtarg="mul"; break; case SPL_OP_DIV: txtarg="div"; break; case SPL_OP_MOD: txtarg="mod"; break; case SPL_OP_POW: txtarg="pow"; break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented opcode 0x%02x\n", op); retval = -1; goto finish; } goto op_objop; } if (n1_type == SPL_TYPE_FLOAT || n2_type == SPL_TYPE_FLOAT || op == SPL_OP_DIV) { op += SPL_OP_FEQ - SPL_OP_EQ; goto op_floatop; } op += SPL_OP_IEQ - SPL_OP_EQ; goto op_intop; } op_intop: case SPL_OP_IEQ ... SPL_OP_IPOW: { int n2 = spl_get_int(SPL_POP_CLEANUP(task)); int n1 = spl_get_int(SPL_POP_CLEANUP(task)); if ( (op == SPL_OP_IDIV || op == SPL_OP_IMOD) && !n2 ) { spl_report(SPL_REPORT_RUNTIME, task, "Integer division by zero!\n"); retval = -1; goto finish; } switch ( op ) { case SPL_OP_IEQ: spl_push(task, SPL_NEW_INT( n1 == n2 ? 1 : 0 )); break; case SPL_OP_INE: spl_push(task, SPL_NEW_INT( n1 != n2 ? 1 : 0 )); break; case SPL_OP_ILT: spl_push(task, SPL_NEW_INT( n1 < n2 ? 1 : 0 )); break; case SPL_OP_IGT: spl_push(task, SPL_NEW_INT( n1 > n2 ? 1 : 0 )); break; case SPL_OP_ILE: spl_push(task, SPL_NEW_INT( n1 <= n2 ? 1 : 0 )); break; case SPL_OP_IGE: spl_push(task, SPL_NEW_INT( n1 >= n2 ? 1 : 0 )); break; case SPL_OP_IADD: spl_push(task, SPL_NEW_INT( n1 + n2 )); break; case SPL_OP_ISUB: spl_push(task, SPL_NEW_INT( n1 - n2 )); break; case SPL_OP_IMUL: spl_push(task, SPL_NEW_INT( n1 * n2 )); break; case SPL_OP_IDIV: spl_push(task, SPL_NEW_INT( n1 / n2 )); break; case SPL_OP_IMOD: spl_push(task, SPL_NEW_INT( n1 % n2 )); break; case SPL_OP_IPOW: spl_push(task, SPL_NEW_INT( (int)pow(n1, n2) )); break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented integer opcode 0x%02x\n", op); retval = -1; } goto finish; } op_floatop: case SPL_OP_FEQ ... SPL_OP_FPOW: { double n2 = spl_get_float(SPL_POP_CLEANUP(task)); double n1 = spl_get_float(SPL_POP_CLEANUP(task)); if ( (op == SPL_OP_FDIV || op == SPL_OP_FMOD) && !n2 ) { spl_report(SPL_REPORT_RUNTIME, task, "Floating-point division by zero!\n"); retval = -1; goto finish; } switch ( op ) { case SPL_OP_FEQ: spl_push(task, SPL_NEW_FLOAT( n1 == n2 ? 1 : 0 )); break; case SPL_OP_FNE: spl_push(task, SPL_NEW_FLOAT( n1 != n2 ? 1 : 0 )); break; case SPL_OP_FLT: spl_push(task, SPL_NEW_FLOAT( n1 < n2 ? 1 : 0 )); break; case SPL_OP_FGT: spl_push(task, SPL_NEW_FLOAT( n1 > n2 ? 1 : 0 )); break; case SPL_OP_FLE: spl_push(task, SPL_NEW_FLOAT( n1 <= n2 ? 1 : 0 )); break; case SPL_OP_FGE: spl_push(task, SPL_NEW_FLOAT( n1 >= n2 ? 1 : 0 )); break; case SPL_OP_FADD: spl_push(task, SPL_NEW_FLOAT( n1 + n2 )); break; case SPL_OP_FSUB: spl_push(task, SPL_NEW_FLOAT( n1 - n2 )); break; case SPL_OP_FMUL: spl_push(task, SPL_NEW_FLOAT( n1 * n2 )); break; case SPL_OP_FDIV: spl_push(task, SPL_NEW_FLOAT( n1 / n2 )); break; case SPL_OP_FMOD: spl_push(task, SPL_NEW_FLOAT( fmod(n1, n2) )); break; case SPL_OP_FPOW: spl_push(task, SPL_NEW_FLOAT( pow(n1, n2) )); break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented floating-point opcode 0x%02x\n", op); retval = -1; } goto finish; } case SPL_OP_SEQ ... SPL_OP_SGE: { char *n2 = spl_get_string(SPL_POP_CLEANUP(task)); char *n1 = spl_get_string(SPL_POP_CLEANUP(task)); int r = strcmp(n1, n2); switch ( op ) { case SPL_OP_SEQ: spl_push(task, SPL_NEW_INT( r == 0 )); break; case SPL_OP_SNE: spl_push(task, SPL_NEW_INT( r != 0 )); break; case SPL_OP_SLT: spl_push(task, SPL_NEW_INT( r < 0 )); break; case SPL_OP_SGT: spl_push(task, SPL_NEW_INT( r > 0 )); break; case SPL_OP_SLE: spl_push(task, SPL_NEW_INT( r <= 0 )); break; case SPL_OP_SGE: spl_push(task, SPL_NEW_INT( r >= 0 )); break; default: spl_report(SPL_REPORT_RUNTIME, task, "Unimplemented string-compare opcode 0x%02x\n", op); retval = -1; } goto finish; } case SPL_OP_PEQ: { struct spl_node *n2 = SPL_POP_CLEANUP(task); struct spl_node *n1 = SPL_POP_CLEANUP(task); spl_push(task, SPL_NEW_INT( n1 == n2 )); goto finish; } case SPL_OP_POSTINC: case SPL_OP_POSTDEC: case SPL_OP_PREINC: case SPL_OP_PREDEC: { char *id = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *n = spl_lookup(task, task->ctx, id, 0); struct spl_node *r = spl_get(0); int v = spl_get_int(n); switch ( op ) { case SPL_OP_POSTINC: spl_set_int(r, v+1); break; case SPL_OP_POSTDEC: spl_set_int(r, v-1); break; case SPL_OP_PREINC: spl_set_int(r, ++v); break; case SPL_OP_PREDEC: spl_set_int(r, --v); break; } spl_create(task, task->ctx, id, r, 0); spl_push(task, SPL_NEW_INT(v)); goto finish; } case SPL_OP_TOINT: { int val = spl_get_int(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_INT(val)); goto finish; } case SPL_OP_TOFLOAT: { double val = spl_get_float(SPL_POP_CLEANUP(task)); spl_push(task, SPL_NEW_FLOAT(val)); goto finish; } case SPL_OP_NOP: { goto finish; } case SPL_OP_DELETE: { struct spl_node *n = SPL_POP_CLEANUP(task); spl_delete(task, task->ctx, spl_get_string(n)); goto finish; } case SPL_OP_RLRET: { retval = spl_get_int(SPL_POP_CLEANUP(task)); goto finish; } case SPL_OP_CHECKP: { if ( task->stack && (task->stack->node->flags & SPL_NODE_FLAG_RETINFO) == 0 ) { spl_report(SPL_REPORT_RUNTIME, task, "VM Stack not empty on checkpoint!\n"); retval = -1; } goto finish; } case SPL_OP_LOAD: { char *name = spl_get_string(SPL_POP_CLEANUP(task)); if ( spl_module_load(task->vm, name, 0) == -1 ) retval = -1; goto finish; } case SPL_OP_XCHG: { struct spl_node *a = spl_pop(task); struct spl_node *b = spl_pop(task); spl_push(task, a); spl_push(task, b); goto finish; } case SPL_OP_EVAL: { char *program = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_asm *as = spl_asm_create(); as->vm = task->vm; if ( spl_compiler(as, program, "eval", 0, 0) ) { spl_asm_destroy(as); spl_push(task, SPL_NEW_INT(-1)); goto finish; } spl_asm_add(as, SPL_OP_ZERO, 0); spl_asm_add(as, SPL_OP_RETURN, 0); struct spl_node *r = spl_get(0); if (task->debug_str) spl_set_string(r, strdup(task->debug_str)); r->ctx = spl_get(task->ctx); r->code = spl_code_get(task->code); r->code_ip = task->code_ip + arg; r->flags |= SPL_NODE_FLAG_RETINFO; spl_push(task, r); spl_task_setcode(task, spl_asm_dump(as)); spl_asm_destroy(as); goto finish; } case SPL_OP_LXCHG: { char *an = spl_get_string(SPL_POP_CLEANUP(task)); char *bn = spl_get_string(SPL_POP_CLEANUP(task)); struct spl_node *a = spl_get(spl_lookup(task, task->ctx, an, 0)); struct spl_node *b = spl_get(spl_lookup(task, task->ctx, bn, 0)); spl_create(task, task->ctx, an, b, 0); spl_create(task, task->ctx, bn, a, 0); goto finish; } case SPL_OP_DEBUG: { struct spl_node *n = SPL_POP_CLEANUP(task); char *msg = spl_get_string(n); int msg_len = strlen(msg); spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_DEBUG, task, "%s%s", msg, msg_len == 0 || msg[msg_len-1] != '\n' ? "\n" : ""); goto finish; } case SPL_OP_WARNING: { struct spl_node *n = SPL_POP_CLEANUP(task); char *msg = spl_get_string(n); int msg_len = strlen(msg); spl_report(SPL_REPORT_RUNTIME|SPL_REPORT_WARNING, task, "%s%s", msg, msg_len == 0 || msg[msg_len-1] != '\n' ? "\n" : ""); goto finish; } case SPL_OP_ERROR: { struct spl_node *n = SPL_POP_CLEANUP(task); char *msg = spl_get_string(n); int msg_len = strlen(msg); spl_report(SPL_REPORT_RUNTIME, task, "%s%s", msg, msg_len == 0 || msg[msg_len-1] != '\n' ? "\n" : ""); retval = -1; goto finish; } case SPL_OP_SIG: { if (memcmp(task->code->code+orig_ip, SPL_SIGNATURE, 16)) { spl_report(SPL_REPORT_RUNTIME, task, "Bytecode has invalid signature!\n"); spl_task_setcode(task, 0); retval = -1; goto finish; } task->code_ip += 15; goto finish; } case SPL_OP_HALT: { spl_task_setcode(task, 0); goto finish; } default: break; } spl_report(SPL_REPORT_RUNTIME, task, "Opcode 0x%02x not implemented!\n", op); retval = -1; finish:; struct spl_node *ex = 0; while (task->cleanup) { struct spl_node_stack *n = task->cleanup->next; if (task->cleanup->node && (task->cleanup->node->flags & SPL_NODE_FLAG_CLEXCEPT)) { if (ex) spl_put(task->vm, ex); ex = task->cleanup->node; } else spl_put(task->vm, task->cleanup->node); spl_vm_free_node_stack_element(task->vm, task->cleanup); task->cleanup = n; } if (ex) { spl_push(task, ex); goto throw_entry_point; } if (task->goterr) { retval = -1; task->goterr = 0; } task->flags &= ~SPL_TASK_FLAG_BUSY; return retval; }