]> ocean-lang.org Git - ocean/commitdiff
oceani: pass a destination buffer into interp_exec to receive large result.
authorNeilBrown <neil@brown.name>
Mon, 15 Nov 2021 21:23:06 +0000 (08:23 +1100)
committerNeilBrown <neil@brown.name>
Sat, 20 Nov 2021 00:19:56 +0000 (11:19 +1100)
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 <neil@brown.name>
csrc/oceani.mdc

index 12beeb2c71952315a6373ec17407c15a1dcf6738..f375db62f818b5f07faf7cefb6d2cebef05ec562 100644 (file)
@@ -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, &ltype);
-               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;