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 |