View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0000613 | LDMud 3.3 | Efuns | public | 2009-03-13 03:42 | 2018-01-29 21:57 |
Reporter | Gnomi | Assigned To | Gnomi | ||
Priority | normal | Severity | minor | Reproducibility | always |
Status | resolved | Resolution | fixed | ||
Product Version | 3.3.718 | ||||
Target Version | 3.3.719 | Fixed in Version | 3.3.719 | ||
Summary | 0000613: Context closures in a lambda expression loose their context variables | ||||
Description | int x = 0; funcall(lambda(0,({ (: x :) }))); gives "(eval_instruction) context_identifier: inter_context is NULL". | ||||
Tags | No tags attached. | ||||
Attached Files | bug613.diff (12,105 bytes)
Index: trunk/HISTORY =================================================================== --- trunk/HISTORY (Revision 2543) +++ trunk/HISTORY (Arbeitskopie) @@ -26,6 +26,8 @@ hash() only). hash() costs 10 ticks per iteration. + sha1() calculated wrong hash values with multiple interations. + md5() and sha1() are obsoleted by hash(). + + lambda() now supports bound and unbound lambdas as the first element + in an array of a lambda expression. - Runtime + The --max-malloced memory limit was renamed to --hard-malloc-limit. Index: trunk/src/closure.c =================================================================== --- trunk/src/closure.c (Revision 2547) +++ trunk/src/closure.c (Arbeitskopie) @@ -4439,12 +4439,12 @@ break; } /* CLOSURE_SIMUL_EFUN */ + case CLOSURE_PRELIMINARY: + lambda_error("Unimplemented closure type for lambda()\n"); + case CLOSURE_UNBOUND_LAMBDA: case CLOSURE_BOUND_LAMBDA: case CLOSURE_LAMBDA: - case CLOSURE_PRELIMINARY: - lambda_error("Unimplemented closure type for lambda()\n"); - case CLOSURE_LFUN: { /* This is compiled as @@ -4458,9 +4458,9 @@ * * alien-lfun: lambda->ob != lambda->function.lfun.ob * - * For now inherited lfun closures are compiled as alien - * lfuns. - * TODO: Implement them using internal calls. + * Inherited lfun closures, context lfun closures and lambda + * closures are compiled similar to alien lfuns using + * F_CALL_CLOSURE. */ mp_int i; @@ -4469,9 +4469,8 @@ block_size = (mp_int)VEC_SIZE(block); l = argp->u.lambda; - if (l->ob != current.lambda_origin - || l->ob != l->function.lfun.ob - || l->function.lfun.inhProg + if ((type != CLOSURE_UNBOUND_LAMBDA && l->ob != current.lambda_origin) + || (type == CLOSURE_LFUN && l->ob != l->function.lfun.ob) ) { /* Compile it like an alien lfun */ @@ -4493,10 +4492,36 @@ STORE_CODE(current.codep, instrs[F_FUNCALL].opcode); STORE_CODE(current.codep, instrs[F_RESTORE_ARG_FRAME].opcode); } + else if (type != CLOSURE_LFUN + || l->function.lfun.inhProg +#ifdef USE_NEW_INLINES + || l->function.lfun.context_size +#endif + ) + { + /* Compile it using F_CALL_CLOSURE. */ + + if (current.code_left < 1) + realloc_code(); + current.code_left -= 1; + STORE_CODE(current.codep, instrs[F_SAVE_ARG_FRAME].opcode); + + insert_value_push(argp); /* Push the closure */ + for (i = block_size; --i; ) + { + compile_value(++argp, 0); + } + if (current.code_left < 3) + realloc_code(); + current.code_left -= 3; + STORE_CODE(current.codep, instrs[F_CALL_CLOSURE].opcode); + STORE_CODE(current.codep, instrs[F_POP_SECOND].opcode); + STORE_CODE(current.codep, instrs[F_RESTORE_ARG_FRAME].opcode); + } else { - /* Intra-object call: we can call by address */ - + /* Intra-object call: we can call by address */ + if (current.code_left < 1) realloc_code(); current.code_left -= 1; Index: trunk/src/func_spec =================================================================== --- trunk/src/func_spec (Revision 2543) +++ trunk/src/func_spec (Arbeitskopie) @@ -143,6 +143,7 @@ */ pop_value + pop_second dup ldup swap_values @@ -163,6 +164,7 @@ call_function call_inherited call_inherited_noargs + call_closure #ifdef USE_NEW_INLINES context_closure context_identifier Index: trunk/src/interpret.c =================================================================== --- trunk/src/interpret.c (Revision 2547) +++ trunk/src/interpret.c (Arbeitskopie) @@ -13168,6 +13168,14 @@ pop_stack(); break; + CASE(F_POP_SECOND); /* --- pop_second --- */ + /* Pop the value under the topmost value and put the + * topmost value there. + */ + free_svalue(--sp); + *sp = sp[1]; + break; + CASE(F_DUP); /* --- dup --- */ /* Push a duplicate of sp[0] onto the stack. */ @@ -13691,6 +13699,58 @@ break; } + CASE(F_CALL_CLOSURE); /* --- call_closure --- */ + /* Call the closure on the stack with the arguments on the stack. + * Just like funcall(), but as an internal call. + * We leave the closure an the stack for a following F_POP_SECOND + * to clear it up. This instruction is only used by lambda closures. + */ + { + num_arg = sp - ap; + + if (ap->type == T_CLOSURE) + { + inter_sp = sp; + + /* No external calls may be done when this object is + * destructed. + */ + if (current_object->flags & O_DESTRUCTED) + { + sp = _pop_n_elems(num_arg, sp); + push_number(sp, 0); + inter_sp = sp; + warnf("Call from destructed object '%s' ignored.\n" + , get_txt(current_object->name)); + return sp; + } + + /* Call the closure and push the result. + * Note that the closure might destruct itself. + */ + inter_pc = pc; + + int_call_lambda(ap, num_arg, MY_FALSE, MY_FALSE); + + pc = inter_pc; + sp = inter_sp; + fp = inter_fp; + } + else + { + /* Not a closure: pop the excess args and return <cl> + * as result. + */ + + sp = _pop_n_elems(num_arg, sp); + push_number(sp, 0); + } + + break; + } + + + #ifdef USE_NEW_INLINES CASE(F_CONTEXT_IDENTIFIER); /* --- context_identifier <var_ix> --- */ /* Push value of context variable <var_ix>. @@ -17075,7 +17135,7 @@ } else /* hook->type == T_CLOSURE */ { - int_call_lambda(hook, num_arg+num_extra, MY_TRUE); + int_call_lambda(hook, num_arg+num_extra, MY_TRUE, MY_TRUE); rc = 1; /* This call obviously succeeds */ } @@ -17794,7 +17854,7 @@ /*-------------------------------------------------------------------------*/ void -int_call_lambda (svalue_t *lsvp, int num_arg, Bool allowRefs) +int_call_lambda (svalue_t *lsvp, int num_arg, Bool allowRefs, Bool external) /* Call the closure <lsvp> with <num_arg> arguments on the stack. On * success, the arguments are replaced with the result, else an errorf() @@ -17802,6 +17862,10 @@ * If <allowRefs> is TRUE, references may be passed as extended varargs * ('(varargs mixed *)'). Currently this is used only for simul efuns. * is generated. + * + * If <external> is TRUE, the eval_instruction is called to execute the + * closure. Otherwise inter_pc is just set and int_call_lambda returns + * (this is only valid for non-alien lfun or lambda closures). */ { @@ -17908,6 +17972,7 @@ csp->prev_ob = previous_ob; csp->num_local_variables = num_arg; previous_ob = current_object; + external = MY_TRUE; } else extra_frame = MY_FALSE; @@ -17915,7 +17980,7 @@ /* Finish the setup of the control frame. * This is a real inter-object call. */ - csp->extern_call = MY_TRUE; + csp->extern_call = external; current_object = l->function.lfun.ob; current_prog = current_object->prog; /* inter_sp == sp */ @@ -17924,7 +17989,10 @@ if (l->function.lfun.context_size > 0) inter_context = l->context; #endif /* USE_NEW_INLINES */ - eval_instruction(FUNCTION_CODE(csp->funstart), inter_sp); + if (external) + eval_instruction(FUNCTION_CODE(csp->funstart), inter_sp); + else + inter_pc = FUNCTION_CODE(csp->funstart); /* If l->ob selfdestructs during the call, l might have been * deallocated at this point! @@ -17987,6 +18055,13 @@ return; } + case CLOSURE_PRELIMINARY: + /* no valid current_object: fall out of the switch + * and let the error handling clean up the control + * stack. + */ + break; + case CLOSURE_BOUND_LAMBDA: /* --- bound lambda closure --- */ { lambda_t *l2; @@ -18000,6 +18075,19 @@ } /* FALLTHROUGH */ + case CLOSURE_UNBOUND_LAMBDA: + if (lsvp->x.closure_type == CLOSURE_UNBOUND_LAMBDA) + { + if (external) + break; + + /* Internal call of an unbound closure. + * Bind it on the fly. + */ + l->ob = current_object; + } + /* FALLTHROUGH */ + case CLOSURE_LAMBDA: { fun_hdr_p funstart; @@ -18038,22 +18126,21 @@ function_index_offset = 0; funstart = l->function.code + 1; csp->funstart = funstart; + csp->extern_call = external; sp = setup_new_frame2(funstart, sp, allowRefs, MY_TRUE); current_variables = current_object->variables; current_strings = current_prog->strings; - eval_instruction(FUNCTION_CODE(funstart), sp); + if (external) + eval_instruction(FUNCTION_CODE(funstart), sp); + else + { + inter_pc = FUNCTION_CODE(funstart); + inter_sp = sp; + } /* The result is on the stack (inter_sp). */ return; } - case CLOSURE_UNBOUND_LAMBDA: - case CLOSURE_PRELIMINARY: - /* no valid current_object: fall out of the switch - * and let the error handling clean up the control - * stack. - */ - break; - default: /* --- efun-, simul efun-, operator closure */ { int i; /* the closure type */ Index: trunk/src/interpret.h =================================================================== --- trunk/src/interpret.h (Revision 2543) +++ trunk/src/interpret.h (Arbeitskopie) @@ -207,8 +207,8 @@ #define secure_callback_lambda(fun, num_arg) secure_call_lambda(fun, num_arg, MY_TRUE) extern void remove_object_from_stack(object_t *ob); -extern void int_call_lambda(svalue_t *lsvp, int num_arg, Bool allowRefs); -#define call_lambda(lsvp, num_arg) int_call_lambda(lsvp, num_arg, MY_FALSE) +extern void int_call_lambda(svalue_t *lsvp, int num_arg, Bool allowRefs, Bool external); +#define call_lambda(lsvp, num_arg) int_call_lambda(lsvp, num_arg, MY_FALSE, MY_TRUE) extern inherit_t *adjust_variable_offsets(const inherit_t *inheritp, const program_t *prog, const object_t *obj); extern void free_interpreter_temporaries(void); extern void invalidate_apply_low_cache(void); Index: trunk/CHANGELOG =================================================================== --- trunk/CHANGELOG (Revision 2548) +++ trunk/CHANGELOG (Arbeitskopie) @@ -1,6 +1,11 @@ This file lists all changes made to the game driver in all glory detail. See the file HISTORY for a user-oriented summary of all the changes. +16-Apr-2009 (Gnomi) + - (closure.c, interpret.c) New opcodes F_CALL_CLOSURE and F_POP_SECOND + for calling more complicated closures like lambda, inherited lfun + and context closures from within a lambda closure. (Bug #613) + 15-Apr-2009 (Gnomi) - (pkg-gnutls.c) Support for older versions of libgnutls. | ||||
|
I added a patch that implements two opcodes: F_CALL_CLOSURE and F_POP_SECOND. F_CALL_CLOSURE is just like funcall() but as an internal call (so no recursive calls to eval_instruction()). F_POP_SECOND removes the closure from the stack after it was evaluated (and left its return value on top). |
|
Committed as r2553. |
Date Modified | Username | Field | Change |
---|---|---|---|
2009-03-13 03:42 | Gnomi | New Issue | |
2009-03-13 03:42 | Gnomi | Status | new => assigned |
2009-03-13 03:42 | Gnomi | Assigned To | => Gnomi |
2009-04-16 01:16 | Gnomi | File Added: bug613.diff | |
2009-04-16 01:25 | Gnomi | Note Added: 0001042 | |
2009-04-19 10:47 | Gnomi | Note Added: 0001057 | |
2009-04-19 10:47 | Gnomi | Status | assigned => resolved |
2009-04-19 10:47 | Gnomi | Fixed in Version | => 3.3.719 |
2009-04-19 10:47 | Gnomi | Resolution | open => fixed |
2010-11-16 09:42 | Gnomi | Source_changeset_attached | => ldmud.git master d5e8cb91 |
2018-01-29 18:59 | Gnomi | Source_changeset_attached | => ldmud.git master d5e8cb91 |
2018-01-29 21:57 | Gnomi | Source_changeset_attached | => ldmud.git master d5e8cb91 |