From 211c350aee530ff43771ddc1f49c013e7183f507 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 16 Nov 2021 08:23:06 +1100 Subject: [PATCH] oceani: pass a destination buffer into interp_exec to receive large result. To handle assignment from large objects - particularly a structure returned by a function - we cannot just pass back a 'struct value'. Instead we need to pass in a sufficiently large buffer, and have the value producer copy into it. This patch passes in a 'dest' and 'dtype' for this purpose. It is not yet used to full potential. Signed-off-by: NeilBrown --- csrc/oceani.mdc | 63 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index 12beeb2..f375db6 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -1642,12 +1642,16 @@ in `rval`. struct value rval, *lval; }; - static struct lrval _interp_exec(struct parse_context *c, struct exec *e); + /* If dest is passed, dtype must give the expected type, and + * result can go there, in which case type is returned as NULL. + */ + static struct lrval _interp_exec(struct parse_context *c, struct exec *e, + struct value *dest, struct type *dtype); static struct value interp_exec(struct parse_context *c, struct exec *e, struct type **typeret) { - struct lrval ret = _interp_exec(c, e); + struct lrval ret = _interp_exec(c, e, NULL, NULL); if (!ret.type) abort(); if (typeret) @@ -1660,8 +1664,9 @@ in `rval`. static struct value *linterp_exec(struct parse_context *c, struct exec *e, struct type **typeret) { - struct lrval ret = _interp_exec(c, e); + struct lrval ret = _interp_exec(c, e, NULL, NULL); + if (!ret.type) abort(); if (ret.lval) *typeret = ret.type; else @@ -1669,8 +1674,28 @@ in `rval`. return ret.lval; } - static struct lrval _interp_exec(struct parse_context *c, struct exec *e) + /* dinterp_exec is used when the destination type is certain and + * the value has a place to go. + */ + static void dinterp_exec(struct parse_context *c, struct exec *e, + struct value *dest, struct type *dtype, + int need_free) + { + struct lrval ret = _interp_exec(c, e, dest, dtype); + if (!ret.type) + return; // NOTEST + if (need_free) + free_value(dtype, dest); + if (ret.lval) + dup_value(dtype, ret.lval, dest); + else + memcpy(dest, &ret.rval, dtype->size); + } + + static struct lrval _interp_exec(struct parse_context *c, struct exec *e, + struct value *dest, struct type *dtype) { + /* If the result is copied to dest, ret.type is set to NULL */ struct lrval ret; struct value rv = {}, *lrv = NULL; struct type *rvtype; @@ -1698,9 +1723,11 @@ in `rval`. } ## interp exec cases } - ret.lval = lrv; - ret.rval = rv; - ret.type = rvtype; + if (rvtype) { + ret.lval = lrv; + ret.rval = rv; + ret.type = rvtype; + } ## interp exec cleanup return ret; } @@ -1992,7 +2019,7 @@ with a const size by whether they are prepared at parse time or not. if (i >= 0 && i < ltype->array.size) lrv = ptr + i * rvtype->size; else - val_init(ltype->array.member, &rv); + val_init(ltype->array.member, &rv); // UNSAFE ltype = NULL; break; } @@ -3999,12 +4026,9 @@ it is declared, and error will be raised as the name is created as case Assign: lleft = linterp_exec(c, b->left, <ype); - right = interp_exec(c, b->right, &rtype); - if (lleft) { - free_value(ltype, lleft); - dup_value(ltype, &right, lleft); - ltype = NULL; - } + if (lleft) + dinterp_exec(c, b->right, lleft, ltype, 1); + ltype = Tnone; break; case Declare: @@ -4015,13 +4039,10 @@ it is declared, and error will be raised as the name is created as val = var_value(c, v); if (v->type->prepare_type) v->type->prepare_type(c, v->type, 0); - if (b->right) { - right = interp_exec(c, b->right, &rtype); - memcpy(val, &right, rtype->size); - rtype = Tnone; - } else { + if (b->right) + dinterp_exec(c, b->right, val, v->type, 0); + else val_init(v->type, val); - } break; } @@ -4533,7 +4554,7 @@ casepart` to track a list of case parts. rv = interp_exec(c, b->left, &rvtype); if (rvtype == Tnone || (rvtype == Tbool && rv.bool != 0)) - // cnd is Tnone or Tbool, doesn't need to be freed + // rvtype is Tnone or Tbool, doesn't need to be freed interp_exec(c, b->right, NULL); break; -- 2.43.0