*/ REBSER *Merge_Frames(REBSER *parent1, REBSER *parent2) /* ** Create a child frame from two parent frames. Merge common fields. ** Values from the second parent take precedence. ** ** Deep copy and rebind the child. ** ***********************************************************************/ { REBSER *wrds; REBSER *child; REBVAL *words; REBVAL *value; REBCNT n; REBINT *binds = WORDS_HEAD(Bind_Table); // Merge parent1 and parent2 words. // Keep the binding table. Collect_Start(BIND_ALL); // Setup binding table and BUF_WORDS with parent1 words: if (parent1) Collect_Object(parent1); // Add parent2 words to binding table and BUF_WORDS: Collect_Words(BLK_SKIP(FRM_WORD_SERIES(parent2), 1), BIND_ALL); // Allocate child (now that we know the correct size): wrds = Copy_Series(BUF_WORDS); child = Make_Block(SERIES_TAIL(wrds)); value = Append_Value(child); VAL_SET(value, REB_FRAME); VAL_FRM_WORDS(value) = wrds; VAL_FRM_SPEC(value) = 0; // Copy parent1 values: COPY_VALUES(FRM_VALUES(parent1)+1, FRM_VALUES(child)+1, SERIES_TAIL(parent1)-1); // Copy parent2 values: words = FRM_WORDS(parent2)+1; value = FRM_VALUES(parent2)+1; for (; NOT_END(words); words++, value++) { // no need to search when the binding table is available n = binds[VAL_WORD_CANON(words)]; BLK_HEAD(child)[n] = *value; } // Terminate the child frame: SERIES_TAIL(child) = SERIES_TAIL(wrds); BLK_TERM(child); // Deep copy the child Copy_Deep_Values(child, 1, SERIES_TAIL(child), TS_CLONE); // Rebind the child Rebind_Block(parent1, child, BLK_SKIP(child, 1), REBIND_FUNC); Rebind_Block(parent2, child, BLK_SKIP(child, 1), REBIND_FUNC | REBIND_TABLE); // release the bind table Collect_End(wrds); return child; }
*/ void Rebind_Frame(REBSER *src_frame, REBSER *dst_frame) /* ** Clone old src_frame to new dst_frame knowing ** which types of values need to be copied, deep copied, and rebound. ** ***********************************************************************/ { // Rebind all values: Rebind_Block(src_frame, dst_frame, BLK_SKIP(dst_frame, 1), REBIND_FUNC); }
*/ void Clone_Function(REBVAL *value, REBVAL *func) /* ***********************************************************************/ { REBSER *src_frame = VAL_FUNC_ARGS(func); VAL_FUNC_SPEC(value) = VAL_FUNC_SPEC(func); VAL_FUNC_BODY(value) = Clone_Block(VAL_FUNC_BODY(func)); VAL_FUNC_ARGS(value) = Copy_Block(src_frame, 0); // VAL_FUNC_BODY(value) = Clone_Block(VAL_FUNC_BODY(func)); VAL_FUNC_BODY(value) = Copy_Block_Values(VAL_FUNC_BODY(func), 0, SERIES_TAIL(VAL_FUNC_BODY(func)), TS_CLONE); Rebind_Block(src_frame, VAL_FUNC_ARGS(value), BLK_HEAD(VAL_FUNC_BODY(value)), 0); }
*/ void Rebind_Block(REBSER *src_frame, REBSER *dst_frame, REBVAL *data, REBFLG modes) /* ** Rebind all words that reference src frame to dst frame. ** Rebind is always deep. ** ** There are two types of frames: relative frames and normal frames. ** When frame_src type and frame_dst type differ, ** modes must have REBIND_TYPE. ** ***********************************************************************/ { REBINT *binds = WORDS_HEAD(Bind_Table); for (; NOT_END(data); data++) { if (ANY_BLOCK(data)) Rebind_Block(src_frame, dst_frame, VAL_BLK_DATA(data), modes); else if (ANY_WORD(data) && VAL_WORD_FRAME(data) == src_frame) { VAL_WORD_FRAME(data) = dst_frame; if (modes & REBIND_TABLE) VAL_WORD_INDEX(data) = binds[VAL_WORD_CANON(data)]; if (modes & REBIND_TYPE) VAL_WORD_INDEX(data) = - VAL_WORD_INDEX(data); } else if ((modes & REBIND_FUNC) && (IS_FUNCTION(data) || IS_CLOSURE(data))) Rebind_Block(src_frame, dst_frame, BLK_HEAD(VAL_FUNC_BODY(data)), modes); } }
*/ void Rebind_Block(REBSER *frame_src, REBSER *frame_dst, REBSER *block) /* ** Rebind all words that reference src frame to dst frame. ** Rebind is always deep. ** ***********************************************************************/ { REBVAL *value; for (value = BLK_HEAD(block); NOT_END(value); value++) { if (ANY_BLOCK(value)) Rebind_Block(frame_src, frame_dst, VAL_SERIES(value)); else if (ANY_WORD(value) && VAL_WORD_FRAME(value) == frame_src) { VAL_WORD_FRAME(value) = frame_dst; } } }
*/ void Do_Closure(REBVAL *func) /* ** Do a closure by cloning its body and rebinding it to ** a new frame of words/values. ** ***********************************************************************/ { REBSER *body; REBSER *frame; REBVAL *result; REBVAL *ds; Eval_Functions++; //DISABLE_GC; // Clone the body of the function to allow rebinding to it: body = Clone_Block(VAL_FUNC_BODY(func)); // Copy stack frame args as the closure object (one extra at head) frame = Copy_Values(BLK_SKIP(DS_Series, DS_ARG_BASE), SERIES_TAIL(VAL_FUNC_ARGS(func))); SET_FRAME(BLK_HEAD(frame), 0, VAL_FUNC_ARGS(func)); // Rebind the body to the new context (deeply): Rebind_Block(VAL_FUNC_ARGS(func), frame, BLK_HEAD(body), REBIND_TYPE); ds = DS_OUT; SET_OBJECT(ds, body); // keep it GC safe result = Do_Blk(body, 0); // GC-OK - also, result returned on DS stack ds = DS_OUT; if (IS_ERROR(result) && IS_RETURN(result)) { // Value below is kept safe from GC because no-allocation is // done between point of SET_THROW and here. if (VAL_ERR_VALUE(result)) *ds = *VAL_ERR_VALUE(result); else SET_UNSET(ds); } else *ds = *result; // Set return value (atomic) }