Beispiel #1
0
*/	REBINT Awake_System(REBSER *ports, REBINT only)
/*
**	Returns:
**		-1 for errors
**		 0 for nothing to do
**		 1 for wait is satisifed
**
***********************************************************************/
{
	REBVAL *port;
	REBVAL *state;
	REBVAL *waked;
	REBVAL *awake;
	REBVAL tmp;
	REBVAL ref_only;
	REBINT result;
	REBVAL out;

	// Get the system port object:
	port = Get_System(SYS_PORTS, PORTS_SYSTEM);
	if (!IS_PORT(port)) return -10; // verify it is a port object

	// Get wait queue block (the state field):
	state = VAL_OBJ_VALUE(port, STD_PORT_STATE);
	if (!IS_BLOCK(state)) return -10;
	//Debug_Num("S", VAL_TAIL(state));

	// Get waked queue block:
	waked = VAL_OBJ_VALUE(port, STD_PORT_DATA);
	if (!IS_BLOCK(waked)) return -10;

	// If there is nothing new to do, return now:
	if (VAL_TAIL(state) == 0 && VAL_TAIL(waked) == 0) return -1;

	//Debug_Num("A", VAL_TAIL(waked));
	// Get the system port AWAKE function:
	awake = VAL_OBJ_VALUE(port, STD_PORT_AWAKE);
	if (!ANY_FUNC(awake)) return -1;
	if (ports) Val_Init_Block(&tmp, ports);
	else SET_NONE(&tmp);

	if (only) SET_TRUE(&ref_only);
	else SET_NONE(&ref_only);
	// Call the system awake function:
	if (Apply_Func_Throws(&out, awake, port, &tmp, &ref_only, 0))
		raise Error_No_Catch_For_Throw(&out);

	// Awake function returns 1 for end of WAIT:
	result = (IS_LOGIC(&out) && VAL_LOGIC(&out)) ? 1 : 0;

	return result;
}
Beispiel #2
0
//
//  Get_Simple_Value_Into: C
//
// Does easy lookup, else just returns the value as is.
//
void Get_Simple_Value_Into(REBVAL *out, const RELVAL *val, REBCTX *specifier)
{
    if (IS_WORD(val) || IS_GET_WORD(val)) {
        *out = *GET_OPT_VAR_MAY_FAIL(val, specifier);
    }
    else if (IS_PATH(val) || IS_GET_PATH(val)) {
        if (Do_Path_Throws_Core(out, NULL, val, specifier, NULL))
            fail (Error_No_Catch_For_Throw(out));
    }
    else {
        COPY_VALUE(out, val, specifier);
    }
}
Beispiel #3
0
//
//  Get_Simple_Value_Into: C
//
// Does easy lookup, else just returns the value as is.
//
void Get_Simple_Value_Into(REBVAL *out, const REBVAL *val)
{
    if (IS_WORD(val) || IS_GET_WORD(val)) {
        *out = *GET_OPT_VAR_MAY_FAIL(val);
    }
    else if (IS_PATH(val) || IS_GET_PATH(val)) {
        if (Do_Path_Throws(out, NULL, val, NULL))
            fail (Error_No_Catch_For_Throw(out));
    }
    else {
        *out = *val;
    }
}
Beispiel #4
0
*/	void Make_Port(REBVAL *out, const REBVAL *spec)
/*
**		Create a new port. This is done by calling the MAKE_PORT
**		function stored in the system/intrinsic object.
**
***********************************************************************/
{
	if (Do_Sys_Func_Throws(out, SYS_CTX_MAKE_PORT_P, spec, 0)) {
		// Gave back an unhandled RETURN, BREAK, CONTINUE, etc...
		raise Error_No_Catch_For_Throw(out);
	}

	// !!! Shouldn't this be testing for !IS_PORT( ) ?
	if (IS_NONE(out)) raise Error_1(RE_INVALID_SPEC, spec);
}
Beispiel #5
0
*/	void Make_Module(REBVAL *out, const REBVAL *spec)
/*
**      Create a module from a spec and an init block.
**		Call the Make_Module function in the system/intrinsic object.
**
***********************************************************************/
{
	if (Do_Sys_Func_Throws(out, SYS_CTX_MAKE_MODULE_P, spec, 0)) {
		// Gave back an unhandled RETURN, BREAK, CONTINUE, etc...
		raise Error_No_Catch_For_Throw(out);
	}

	// !!! Shouldn't this be testing for !IS_MODULE(out)?
	if (IS_NONE(out)) raise Error_1(RE_INVALID_SPEC, spec);
}
Beispiel #6
0
//
//  RL_Do_Commands: C
// 
// Evaluate a block with a command context passed in.
// 
// Returns:
//     Nothing
// Arguments:
//     array - a pointer to the REBVAL array series
//     flags - set to zero for now
//     cec - command evaluation context struct or NULL if not used.
// Notes:
//     The context allows passing to each command a struct that is
//     used for back-referencing your environment data or for tracking
//     the evaluation block and its index.
//
RL_API void RL_Do_Commands(REBARR *array, REBCNT flags, REBCEC *cec)
{
    // !!! Only 2 calls to RL_Do_Commands known to exist (R3-View), like:
    //
    //     REBCEC innerCtx;
    //     innerCtx.envr = ctx->envr;
    //     innerCtx.block = RXA_SERIES(frm, 1);
    //     innerCtx.index = 0;
    //
    //     rebdrw_push_matrix(ctx->envr);
    //     RL_Do_Commands(RXA_SERIES(frm, 1), 0, &innerCtx);
    //     rebdrw_pop_matrix(ctx->envr);
    //
    // innerCtx.block is just a copy of the commands list, and not used by
    // any C-based COMMAND! implementation code.  But ->envr is needed.
    // Ren-C modifies ordinary COMMAND! dispatch to pass in whatever the
    // global TG_Command_Rebcec is (instead of NULL)

    void *cec_before;

    REBIXO indexor; // "index -or- a flag"

    REBVAL result;
    VAL_INIT_WRITABLE_DEBUG(&result);

    cec_before = TG_Command_Execution_Context;
    TG_Command_Execution_Context = cec; // push

    indexor = Do_Array_At_Core(
        &result,
        NULL, // `first`: NULL means start at array head (no injected head)
        array,
        0, // start evaluating at index 0
        DO_FLAG_TO_END | DO_FLAG_ARGS_EVALUATE | DO_FLAG_LOOKAHEAD
    );

    TG_Command_Execution_Context = cec_before; // pop

    if (indexor == THROWN_FLAG)
        fail (Error_No_Catch_For_Throw(&result));

    assert(indexor == END_FLAG); // if it didn't throw, should reach end

    // "Returns: nothing" :-/
}
Beispiel #7
0
//
//  Do_Breakpoint_Throws: C
//
// A call to Do_Breakpoint_Throws does delegation to a hook in the host, which
// (if registered) will generally start an interactive session for probing the
// environment at the break.  The `resume` native cooperates by being able to
// give back a value (or give back code to run to produce a value) that the
// call to breakpoint returns.
//
// RESUME has another feature, which is to be able to actually unwind and
// simulate a return /AT a function *further up the stack*.  (This may be
// switched to a feature of a "STEP OUT" command at some point.)
//
REBOOL Do_Breakpoint_Throws(
    REBVAL *out,
    REBOOL interrupted, // Ctrl-C (as opposed to a BREAKPOINT)
    const REBVAL *default_value,
    REBOOL do_default
) {
    REBVAL *target = NONE_VALUE;

    REBVAL temp;
    VAL_INIT_WRITABLE_DEBUG(&temp);

    if (!PG_Breakpoint_Quitting_Hook) {
        //
        // Host did not register any breakpoint handler, so raise an error
        // about this as early as possible.
        //
        fail (Error(RE_HOST_NO_BREAKPOINT));
    }

    // We call the breakpoint hook in a loop, in order to keep running if any
    // inadvertent FAILs or THROWs occur during the interactive session.
    // Only a conscious call of RESUME speaks the protocol to break the loop.
    //
    while (TRUE) {
        struct Reb_State state;
        REBCTX *error;

    push_trap:
        PUSH_TRAP(&error, &state);

        // The host may return a block of code to execute, but cannot
        // while evaluating do a THROW or a FAIL that causes an effective
        // "resumption".  Halt is the exception, hence we PUSH_TRAP and
        // not PUSH_UNHALTABLE_TRAP.  QUIT is also an exception, but a
        // desire to quit is indicated by the return value of the breakpoint
        // hook (which may or may not decide to request a quit based on the
        // QUIT command being run).
        //
        // The core doesn't want to get involved in presenting UI, so if
        // an error makes it here and wasn't trapped by the host first that
        // is a bug in the host.  It should have done its own PUSH_TRAP.
        //
        if (error) {
        #if !defined(NDEBUG)
            REBVAL error_value;
            VAL_INIT_WRITABLE_DEBUG(&error_value);

            Val_Init_Error(&error_value, error);
            PROBE_MSG(&error_value, "Error not trapped during breakpoint:");
            Panic_Array(CTX_VARLIST(error));
        #endif

            // In release builds, if an error managed to leak out of the
            // host's breakpoint hook somehow...just re-push the trap state
            // and try it again.
            //
            goto push_trap;
        }

        // Call the host's breakpoint hook.
        //
        if (PG_Breakpoint_Quitting_Hook(&temp, interrupted)) {
            //
            // If a breakpoint hook returns TRUE that means it wants to quit.
            // The value should be the /WITH value (as in QUIT/WITH)
            //
            assert(!THROWN(&temp));
            *out = *ROOT_QUIT_NATIVE;
            CONVERT_NAME_TO_THROWN(out, &temp, FALSE);
            return TRUE; // TRUE = threw
        }

        // If a breakpoint handler returns FALSE, then it should have passed
        // back a "resume instruction" triggered by a call like:
        //
        //     resume/do [fail "This is how to fail from a breakpoint"]
        //
        // So now that the handler is done, we will allow any code handed back
        // to do whatever FAIL it likes vs. trapping that here in a loop.
        //
        DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

        // Decode and process the "resume instruction"
        {
            struct Reb_Frame *frame;
            REBVAL *mode;
            REBVAL *payload;

            assert(IS_GROUP(&temp));
            assert(VAL_LEN_HEAD(&temp) == RESUME_INST_MAX);

            mode = VAL_ARRAY_AT_HEAD(&temp, RESUME_INST_MODE);
            payload = VAL_ARRAY_AT_HEAD(&temp, RESUME_INST_PAYLOAD);
            target = VAL_ARRAY_AT_HEAD(&temp, RESUME_INST_TARGET);

            // The first thing we need to do is determine if the target we
            // want to return to has another breakpoint sandbox blocking
            // us.  If so, what we need to do is actually retransmit the
            // resume instruction so it can break that wall, vs. transform
            // it into an EXIT/FROM that would just get intercepted.
            //
            if (!IS_NONE(target)) {
            #if !defined(NDEBUG)
                REBOOL found = FALSE;
            #endif

                for (frame = FS_TOP; frame != NULL; frame = frame->prior) {
                    if (frame->mode != CALL_MODE_FUNCTION)
                        continue;

                    if (
                        frame != FS_TOP
                        && FUNC_CLASS(frame->func) == FUNC_CLASS_NATIVE
                        && (
                            FUNC_CODE(frame->func) == &N_pause
                            || FUNC_CODE(frame->func) == &N_breakpoint
                        )
                    ) {
                        // We hit a breakpoint (that wasn't this call to
                        // breakpoint, at the current FS_TOP) before finding
                        // the sought after target.  Retransmit the resume
                        // instruction so that level will get it instead.
                        //
                        *out = *ROOT_RESUME_NATIVE;
                        CONVERT_NAME_TO_THROWN(out, &temp, FALSE);
                        return TRUE; // TRUE = thrown
                    }

                    if (IS_FRAME(target)) {
                        if (NOT(frame->flags & DO_FLAG_FRAME_CONTEXT))
                            continue;
                        if (
                            VAL_CONTEXT(target)
                            == AS_CONTEXT(frame->data.context)
                        ) {
                            // Found a closure matching the target before we
                            // reached a breakpoint, no need to retransmit.
                            //
                        #if !defined(NDEBUG)
                            found = TRUE;
                        #endif
                            break;
                        }
                    }
                    else {
                        assert(IS_FUNCTION(target));
                        if (frame->flags & DO_FLAG_FRAME_CONTEXT)
                            continue;
                        if (VAL_FUNC(target) == frame->func) {
                            //
                            // Found a function matching the target before we
                            // reached a breakpoint, no need to retransmit.
                            //
                        #if !defined(NDEBUG)
                            found = TRUE;
                        #endif
                            break;
                        }
                    }
                }

                // RESUME should not have been willing to use a target that
                // is not on the stack.
                //
            #if !defined(NDEBUG)
                assert(found);
            #endif
            }

            if (IS_NONE(mode)) {
                //
                // If the resume instruction had no /DO or /WITH of its own,
                // then it doesn't override whatever the breakpoint provided
                // as a default.  (If neither the breakpoint nor the resume
                // provided a /DO or a /WITH, result will be UNSET.)
                //
                goto return_default; // heeds `target`
            }

            assert(IS_LOGIC(mode));

            if (VAL_LOGIC(mode)) {
                if (DO_VAL_ARRAY_AT_THROWS(&temp, payload)) {
                    //
                    // Throwing is not compatible with /AT currently.
                    //
                    if (!IS_NONE(target))
                        fail (Error_No_Catch_For_Throw(&temp));

                    // Just act as if the BREAKPOINT call itself threw
                    //
                    *out = temp;
                    return TRUE; // TRUE = thrown
                }

                // Ordinary evaluation result...
            }
            else
                temp = *payload;
        }

        // The resume instruction will be GC'd.
        //
        goto return_temp;
    }

    DEAD_END;

return_default:

    if (do_default) {
        if (DO_VAL_ARRAY_AT_THROWS(&temp, default_value)) {
            //
            // If the code throws, we're no longer in the sandbox...so we
            // bubble it up.  Note that breakpoint runs this code at its
            // level... so even if you request a higher target, any throws
            // will be processed as if they originated at the BREAKPOINT
            // frame.  To do otherwise would require the EXIT/FROM protocol
            // to add support for DO-ing at the receiving point.
            //
            *out = temp;
            return TRUE; // TRUE = thrown
        }
    }
    else
        temp = *default_value; // generally UNSET! if no /WITH

return_temp:

    // The easy case is that we just want to return from breakpoint
    // directly, signaled by the target being NONE!.
    //
    if (IS_NONE(target)) {
        *out = temp;
        return FALSE; // FALSE = not thrown
    }

    // If the target is a function, then we're looking to simulate a return
    // from something up the stack.  This uses the same mechanic as
    // definitional returns--a throw named by the function or closure frame.
    //
    // !!! There is a weak spot in definitional returns for FUNCTION! that
    // they can only return to the most recent invocation; which is a weak
    // spot of FUNCTION! in general with stack relative variables.  Also,
    // natives do not currently respond to definitional returns...though
    // they can do so just as well as FUNCTION! can.
    //
    *out = *target;
    CONVERT_NAME_TO_THROWN(out, &temp, TRUE);

    return TRUE; // TRUE = thrown
}
Beispiel #8
0
x*/ REBRXT Do_Callback(REBSER *obj, u32 name, RXIARG *rxis, RXIARG *result)
/*
**      Given an object and a word id, call a REBOL function.
**      The arguments are converted from extension format directly
**      to the data stack. The result is passed back in ext format,
**      with the datatype returned or zero if there was a problem.
**
***********************************************************************/
{
    REBVAL *val;
    struct Reb_Call *call;
    REBCNT len;
    REBCNT n;
    REBVAL label;
    REBVAL out;

    // Find word in object, verify it is a function.
    if (!(val = Find_Word_Value(obj, name))) {
        SET_EXT_ERROR(result, RXE_NO_WORD);
        return 0;
    }
    if (!ANY_FUNC(val)) {
        SET_EXT_ERROR(result, RXE_NOT_FUNC);
        return 0;
    }

    // Create stack frame (use prior stack frame for location info):
    SET_TRASH_SAFE(&out); // OUT slot for function eval result
    Val_Init_Word_Unbound(&label, REB_WORD, name);
    call = Make_Call(
        &out,
        VAL_SERIES(DSF_WHERE(PRIOR_DSF(DSF))),
        VAL_INDEX(DSF_WHERE(PRIOR_DSF(DSF))),
        &label,
        val
    );
    obj = VAL_FUNC_PARAMLIST(val);  // func words
    len = SERIES_TAIL(obj)-1;   // number of args (may include locals)

    // Push args. Too short or too long arg frames are handled W/O error.
    // Note that refinements args can be set to anything.
    for (n = 1; n <= len; n++) {
        REBVAL *arg = DSF_ARG(call, n);

        if (n <= RXI_COUNT(rxis))
            RXI_To_Value(arg, rxis[n], RXI_TYPE(rxis, n));
        else
            SET_NONE(arg);

        // Check type for word at the given offset:
        if (!TYPE_CHECK(BLK_SKIP(obj, n), VAL_TYPE(arg))) {
            result->i2.int32b = n;
            SET_EXT_ERROR(result, RXE_BAD_ARGS);
            Free_Call(call);
            return 0;
        }
    }

    // Evaluate the function:
    if (Dispatch_Call_Throws(call)) {
        // !!! Does this need handling such that there is a way for the thrown
        // value to "bubble up" out of the callback, or is an error sufficient?
        fail (Error_No_Catch_For_Throw(DSF_OUT(call)));
    }

    // Return resulting value from output
    *result = Value_To_RXI(&out);
    return Reb_To_RXT[VAL_TYPE(&out)];
}
Beispiel #9
0
*/	REBFLG MT_Struct(REBVAL *out, REBVAL *data, enum Reb_Kind type)
/*
 * Format:
 * make struct! [
 *     field1 [type1]
 *     field2: [type2] field2-init-value
 * 	   field3: [struct [field1 [type1]]]
 * 	   field4: [type1[3]]
 * 	   ...
 * ]
***********************************************************************/
{
	//RL_Print("%s\n", __func__);
	REBINT max_fields = 16;

	VAL_STRUCT_FIELDS(out) = Make_Series(
		max_fields, sizeof(struct Struct_Field), MKS_NONE
	);
	MANAGE_SERIES(VAL_STRUCT_FIELDS(out));

	if (IS_BLOCK(data)) {
		//if (Reduce_Block_No_Set_Throws(VAL_SERIES(data), 0, NULL))...
		//data = DS_POP;
		REBVAL *blk = VAL_BLK_DATA(data);
		REBINT field_idx = 0; /* for field index */
		u64 offset = 0; /* offset in data */
		REBCNT eval_idx = 0; /* for spec block evaluation */
		REBVAL *init = NULL; /* for result to save in data */
		REBOOL expect_init = FALSE;
		REBINT raw_size = -1;
		REBUPT raw_addr = 0;
		REBCNT alignment = 0;

		VAL_STRUCT_SPEC(out) = Copy_Array_Shallow(VAL_SERIES(data));
		VAL_STRUCT_DATA(out) = Make_Series(
			1, sizeof(struct Struct_Data), MKS_NONE
		);
		EXPAND_SERIES_TAIL(VAL_STRUCT_DATA(out), 1);

		VAL_STRUCT_DATA_BIN(out) = Make_Series(max_fields << 2, 1, MKS_NONE);
		VAL_STRUCT_OFFSET(out) = 0;

		// We tell the GC to manage this series, but it will not cause a
		// synchronous garbage collect.  Still, when's the right time?
		ENSURE_SERIES_MANAGED(VAL_STRUCT_SPEC(out));
		MANAGE_SERIES(VAL_STRUCT_DATA(out));
		MANAGE_SERIES(VAL_STRUCT_DATA_BIN(out));

		/* set type early such that GC will handle it correctly, i.e, not collect series in the struct */
		SET_TYPE(out, REB_STRUCT);

		if (IS_BLOCK(blk)) {
			parse_attr(blk, &raw_size, &raw_addr);
			++ blk;
		}

		while (NOT_END(blk)) {
			REBVAL *inner;
			struct Struct_Field *field = NULL;
			u64 step = 0;

			EXPAND_SERIES_TAIL(VAL_STRUCT_FIELDS(out), 1);

			DS_PUSH_NONE;
			inner = DS_TOP; /* save in stack so that it won't be GC'ed when MT_Struct is recursively called */

			field = (struct Struct_Field *)SERIES_SKIP(VAL_STRUCT_FIELDS(out), field_idx);
			field->offset = (REBCNT)offset;
			if (IS_SET_WORD(blk)) {
				field->sym = VAL_WORD_SYM(blk);
				expect_init = TRUE;
				if (raw_addr) {
					/* initialization is not allowed for raw memory struct */
					raise Error_Invalid_Arg(blk);
				}
			} else if (IS_WORD(blk)) {
				field->sym = VAL_WORD_SYM(blk);
				expect_init = FALSE;
			}
			else
				raise Error_Has_Bad_Type(blk);

			++ blk;

			if (!IS_BLOCK(blk))
				raise Error_Invalid_Arg(blk);

			if (!parse_field_type(field, blk, inner, &init)) { return FALSE; }
			++ blk;

			STATIC_assert(sizeof(field->size) <= 4);
			STATIC_assert(sizeof(field->dimension) <= 4);

			step = (u64)field->size * (u64)field->dimension;
			if (step > VAL_STRUCT_LIMIT)
				raise Error_1(RE_SIZE_LIMIT, out);

			EXPAND_SERIES_TAIL(VAL_STRUCT_DATA_BIN(out), step);

			if (expect_init) {
				REBVAL safe; // result of reduce or do (GC saved during eval)
				init = &safe;

				if (IS_BLOCK(blk)) {
					if (Reduce_Block_Throws(init, VAL_SERIES(blk), 0, FALSE))
						raise Error_No_Catch_For_Throw(init);

					++ blk;
				} else {
					DO_NEXT_MAY_THROW(
						eval_idx,
						init,
						VAL_SERIES(data),
						blk - VAL_BLK_DATA(data)
					);
					if (eval_idx == THROWN_FLAG)
						raise Error_No_Catch_For_Throw(init);

					blk = VAL_BLK_SKIP(data, eval_idx);
				}

				if (field->array) {
					if (IS_INTEGER(init)) { /* interpreted as a C pointer */
						void *ptr = cast(void *, cast(REBUPT, VAL_INT64(init)));

						/* assuming it's an valid pointer and holding enough space */
						memcpy(SERIES_SKIP(VAL_STRUCT_DATA_BIN(out), (REBCNT)offset), ptr, field->size * field->dimension);
					} else if (IS_BLOCK(init)) {
						REBCNT n = 0;

						if (VAL_LEN(init) != field->dimension)
							raise Error_Invalid_Arg(init);

						/* assign */
						for (n = 0; n < field->dimension; n ++) {
							if (!assign_scalar(&VAL_STRUCT(out), field, n, VAL_BLK_SKIP(init, n))) {
								//RL_Print("Failed to assign element value\n");
								goto failed;
							}
						}
					}
					else
						raise Error_Unexpected_Type(REB_BLOCK, VAL_TYPE(blk));
				} else {
					/* scalar */
					if (!assign_scalar(&VAL_STRUCT(out), field, 0, init)) {
						//RL_Print("Failed to assign scalar value\n");
						goto failed;
					}
				}
			} else if (raw_addr == 0) {
Beispiel #10
0
static REBOOL parse_field_type(struct Struct_Field *field, REBVAL *spec, REBVAL *inner, REBVAL **init)
{
	REBVAL *val = VAL_BLK_DATA(spec);

	if (IS_WORD(val)){
		switch (VAL_WORD_CANON(val)) {
			case SYM_UINT8:
				field->type = STRUCT_TYPE_UINT8;
				field->size = 1;
				break;
			case SYM_INT8:
				field->type = STRUCT_TYPE_INT8;
				field->size = 1;
				break;
			case SYM_UINT16:
				field->type = STRUCT_TYPE_UINT16;
				field->size = 2;
				break;
			case SYM_INT16:
				field->type = STRUCT_TYPE_INT16;
				field->size = 2;
				break;
			case SYM_UINT32:
				field->type = STRUCT_TYPE_UINT32;
				field->size = 4;
				break;
			case SYM_INT32:
				field->type = STRUCT_TYPE_INT32;
				field->size = 4;
				break;
			case SYM_UINT64:
				field->type = STRUCT_TYPE_UINT64;
				field->size = 8;
				break;
			case SYM_INT64:
				field->type = STRUCT_TYPE_INT64;
				field->size = 8;
				break;
			case SYM_FLOAT:
				field->type = STRUCT_TYPE_FLOAT;
				field->size = 4;
				break;
			case SYM_DOUBLE:
				field->type = STRUCT_TYPE_DOUBLE;
				field->size = 8;
				break;
			case SYM_POINTER:
				field->type = STRUCT_TYPE_POINTER;
				field->size = sizeof(void*);
				break;
			case SYM_STRUCT_TYPE:
				++ val;
				if (IS_BLOCK(val)) {
					REBFLG res;

					res = MT_Struct(inner, val, REB_STRUCT);

					if (!res) {
						//RL_Print("Failed to make nested struct!\n");
						return FALSE;
					}

					field->size = SERIES_TAIL(VAL_STRUCT_DATA_BIN(inner));
					field->type = STRUCT_TYPE_STRUCT;
					field->fields = VAL_STRUCT_FIELDS(inner);
					field->spec = VAL_STRUCT_SPEC(inner);
					*init = inner; /* a shortcut for struct intialization */
				}
				else
					raise Error_Unexpected_Type(REB_BLOCK, VAL_TYPE(val));
				break;
			case SYM_REBVAL:
				field->type = STRUCT_TYPE_REBVAL;
				field->size = sizeof(REBVAL);
				break;
			default:
				raise Error_Has_Bad_Type(val);
		}
	} else if (IS_STRUCT(val)) { //[b: [struct-a] val-a]
		field->size = SERIES_TAIL(VAL_STRUCT_DATA_BIN(val));
		field->type = STRUCT_TYPE_STRUCT;
		field->fields = VAL_STRUCT_FIELDS(val);
		field->spec = VAL_STRUCT_SPEC(val);
		*init = val;
	}
	else
		raise Error_Has_Bad_Type(val);

	++ val;

	if (IS_BLOCK(val)) {// make struct! [a: [int32 [2]] [0 0]]
		REBVAL ret;

		if (DO_ARRAY_THROWS(&ret, val)) {
			// !!! Does not check for thrown cases...what should this
			// do in case of THROW, BREAK, QUIT?
			raise Error_No_Catch_For_Throw(&ret);
		}

		if (!IS_INTEGER(&ret))
			raise Error_Unexpected_Type(REB_INTEGER, VAL_TYPE(val));

		field->dimension = cast(REBCNT, VAL_INT64(&ret));
		field->array = TRUE;
		++ val;
	} else {
		field->dimension = 1; /* scalar */
		field->array = FALSE;
	}

	if (NOT_END(val))
		raise Error_Has_Bad_Type(val);

	return TRUE;
}
Beispiel #11
0
*/	RL_API int RL_Do_String(int *exit_status, const REBYTE *text, REBCNT flags, RXIARG *result)
/*
**	Load a string and evaluate the resulting block.
**
**	Returns:
**		The datatype of the result if a positive number (or 0 if the
**		type has no representation in the "RXT" API).  An error code
**		if it's a negative number.  Two negative numbers are reserved
**		for non-error conditions: -1 for halting (e.g. Escape), and
**		-2 is reserved for exiting with exit_status set.
**
**	Arguments:
**		text - A null terminated UTF-8 (or ASCII) string to transcode
**			into a block and evaluate.
**		flags - set to zero for now
**		result - value returned from evaluation, if NULL then result
**			will be returned on the top of the stack
**
**	Notes:
**		This API was from before Rebol's open sourcing and had little
**		vetting and few clients.  The one client it did have was the
**		"sample" console code (which wound up being the "only"
**		console code for quite some time).
**
***********************************************************************/
{
	REBSER *code;
	REBVAL out;

	REBOL_STATE state;
	const REBVAL *error;

	// assumes it can only be run at the topmost level where
	// the data stack is completely empty.
	assert(DSP == -1);

	PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `raise Error` can longjmp here, so 'error' won't be NULL *if* that happens!

	if (error) {
		if (VAL_ERR_NUM(error) == RE_HALT)
			return -1; // !!! Revisit hardcoded #

		// Save error for WHY?
		*Get_System(SYS_STATE, STATE_LAST_ERROR) = *error;

		if (result)
			*result = Value_To_RXI(error);
		else
			DS_PUSH(error);

		return -VAL_ERR_NUM(error);
	}

	code = Scan_Source(text, LEN_BYTES(text));
	PUSH_GUARD_SERIES(code);

	// Bind into lib or user spaces?
	if (flags) {
		// Top words will be added to lib:
		Bind_Values_Set_Forward_Shallow(BLK_HEAD(code), Lib_Context);
		Bind_Values_Deep(BLK_HEAD(code), Lib_Context);
	} else {
		REBCNT len;
		REBVAL vali;
		REBSER *user = VAL_OBJ_FRAME(Get_System(SYS_CONTEXTS, CTX_USER));
		len = user->tail;
		Bind_Values_All_Deep(BLK_HEAD(code), user);
		SET_INTEGER(&vali, len);
		Resolve_Context(user, Lib_Context, &vali, FALSE, 0);
	}

	if (Do_At_Throws(&out, code, 0)) {
		DROP_GUARD_SERIES(code);

		if (
			IS_NATIVE(&out) && (
				VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_QUIT_NATIVE)
				|| VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_EXIT_NATIVE)
			)
		) {
			CATCH_THROWN(&out, &out);
			DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

			*exit_status = Exit_Status_From_Value(&out);
			return -2; // Revisit hardcoded #
		}

		raise Error_No_Catch_For_Throw(&out);
	}

	DROP_GUARD_SERIES(code);

	DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

	if (result)
		*result = Value_To_RXI(&out);
	else
		DS_PUSH(&out);

	return Reb_To_RXT[VAL_TYPE(&out)];
}
Beispiel #12
0
*/	RL_API int RL_Start(REBYTE *bin, REBINT len, REBYTE *script, REBINT script_len, REBCNT flags)
/*
**	Evaluate the default boot function.
**
**	Returns:
**		Zero on success, otherwise indicates an error occurred.
**	Arguments:
**		bin - optional startup code (compressed), can be null
**		len - length of above bin
**		flags - special flags
**	Notes:
**		This function completes the startup sequence by calling
**		the sys/start function.
**
***********************************************************************/
{
	REBVAL *val;
	REBSER *ser;

	REBOL_STATE state;
	const REBVAL *error;

	REBVAL start_result;

	int result;
	REBVAL out;

	if (bin) {
		ser = Decompress(bin, len, -1, FALSE, FALSE);
		if (!ser) return 1;

		val = BLK_SKIP(Sys_Context, SYS_CTX_BOOT_HOST);
		Val_Init_Binary(val, ser);
	}

	if (script && script_len > 4) {
		/* a 4-byte long payload type at the beginning */
		i32 ptype = 0;
		REBYTE *data = script + sizeof(ptype);
		script_len -= sizeof(ptype);

		memcpy(&ptype, script, sizeof(ptype));

		if (ptype == 1) {/* COMPRESSed data */
			ser = Decompress(data, script_len, -1, FALSE, FALSE);
		} else {
			ser = Make_Binary(script_len);
			if (ser == NULL) {
				OS_FREE(script);
				return 1;
			}
			memcpy(BIN_HEAD(ser), data, script_len);
		}
		OS_FREE(script);

		val = BLK_SKIP(Sys_Context, SYS_CTX_BOOT_EMBEDDED);
		Val_Init_Binary(val, ser);
	}

	PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `raise Error` can longjmp here, so 'error' won't be NULL *if* that happens!

	if (error) {
		// Save error for EXPLAIN and return it
		*Get_System(SYS_STATE, STATE_LAST_ERROR) = *error;

		Print_Value(error, 1024, FALSE);

		// !!! Whether or not the Rebol interpreter just throws and quits
		// in an error case with a bad error code or breaks you into the
		// console to debug the environment should be controlled by
		// a command line option.  Defaulting to returning an error code
		// seems better, because kicking into an interactive session can
		// cause logging systems to hang.  For now we throw instead of
		// just quietly returning a code if the script fails, but add
		// that option!

		// For RE_HALT and all other errors we return the error
		// number.  Error numbers are not set in stone (currently), but
		// are never zero...which is why we can use 0 for success.
		return VAL_ERR_NUM(error);
	}

	if (Do_Sys_Func_Throws(&out, SYS_CTX_FINISH_RL_START, 0)) {
		#if !defined(NDEBUG)
			if (LEGACY(OPTIONS_EXIT_FUNCTIONS_ONLY))
				raise Error_No_Catch_For_Throw(&out);
		#endif

		if (
			IS_NATIVE(&out) && (
				VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_QUIT_NATIVE)
				|| VAL_FUNC_CODE(&out) == VAL_FUNC_CODE(ROOT_EXIT_NATIVE)
			)
		) {
			int status;

			CATCH_THROWN(&out, &out);
			status = Exit_Status_From_Value(&out);

			DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

			Shutdown_Core();
			OS_EXIT(status);
			DEAD_END;
		}

		raise Error_No_Catch_For_Throw(&out);
	}

	DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

	// The convention in the API was to return 0 for success.  We use the
	// convention (as for FINISH_INIT_CORE) that any non-UNSET! result from
	// FINISH_RL_START indicates something went wrong.

	if (IS_UNSET(&out))
		result = 0;
	else {
		assert(FALSE); // should not happen (raise an error instead)
		Debug_Fmt("** finish-rl-start returned non-NONE!:");
		Debug_Fmt("%r", &out);
		result = RE_MISC;
	}

	return result;
}
Beispiel #13
0
//
//  RL_Do_String: C
// 
// Load a string and evaluate the resulting block.
// 
// Returns:
//     The datatype of the result if a positive number (or 0 if the
//     type has no representation in the "RXT" API).  An error code
//     if it's a negative number.  Two negative numbers are reserved
//     for non-error conditions: -1 for halting (e.g. Escape), and
//     -2 is reserved for exiting with exit_status set.
// 
// Arguments:
//     text - A null terminated UTF-8 (or ASCII) string to transcode
//         into a block and evaluate.
//     flags - set to zero for now
//     result - value returned from evaluation, if NULL then result
//         will be returned on the top of the stack
// 
// Notes:
//     This API was from before Rebol's open sourcing and had little
//     vetting and few clients.  The one client it did have was the
//     "sample" console code (which wound up being the "only"
//     console code for quite some time).
//
RL_API int RL_Do_String(
    int *exit_status,
    const REBYTE *text,
    REBCNT flags,
    RXIARG *out
) {
    REBARR *code;

    struct Reb_State state;
    REBCTX *error;

    REBVAL result;
    VAL_INIT_WRITABLE_DEBUG(&result);

    // assumes it can only be run at the topmost level where
    // the data stack is completely empty.
    //
    assert(DSP == 0);

    PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `fail` can longjmp here, so 'error' won't be NULL *if* that happens!

    if (error) {
        // Save error for WHY?
        REBVAL *last = Get_System(SYS_STATE, STATE_LAST_ERROR);
        Val_Init_Error(last, error);

        if (ERR_NUM(error) == RE_HALT)
            return -1; // !!! Revisit hardcoded #

        if (out)
            Value_To_RXI(out, last);
        else
            DS_PUSH(last);

        return -ERR_NUM(error);
    }

    code = Scan_Source(text, LEN_BYTES(text));
    PUSH_GUARD_ARRAY(code);

    // Bind into lib or user spaces?
    if (flags) {
        // Top words will be added to lib:
        Bind_Values_Set_Midstream_Shallow(ARR_HEAD(code), Lib_Context);
        Bind_Values_Deep(ARR_HEAD(code), Lib_Context);
    }
    else {
        REBCTX *user = VAL_CONTEXT(Get_System(SYS_CONTEXTS, CTX_USER));

        REBVAL vali;
        VAL_INIT_WRITABLE_DEBUG(&vali);
        SET_INTEGER(&vali, CTX_LEN(user) + 1);

        Bind_Values_All_Deep(ARR_HEAD(code), user);
        Resolve_Context(user, Lib_Context, &vali, FALSE, FALSE);
    }

    if (Do_At_Throws(&result, code, 0)) {
        DROP_GUARD_ARRAY(code);

        if (
            IS_FUNCTION_AND(&result, FUNC_CLASS_NATIVE) && (
                VAL_FUNC_CODE(&result) == &N_quit
                || VAL_FUNC_CODE(&result) == &N_exit
            )
        ) {
            CATCH_THROWN(&result, &result);
            DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

            *exit_status = Exit_Status_From_Value(&result);
            return -2; // Revisit hardcoded #
        }

        fail (Error_No_Catch_For_Throw(&result));
    }

    DROP_GUARD_ARRAY(code);

    DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

    if (out)
        Value_To_RXI(out, &result);
    else
        DS_PUSH(&result);

    return Reb_To_RXT[VAL_TYPE_0(&result)];
}
Beispiel #14
0
//
//  RL_Start: C
// 
// Evaluate the default boot function.
// 
// Returns:
//     Zero on success, otherwise indicates an error occurred.
// Arguments:
//     bin - optional startup code (compressed), can be null
//     len - length of above bin
//     flags - special flags
// Notes:
//     This function completes the startup sequence by calling
//     the sys/start function.
//
RL_API int RL_Start(REBYTE *bin, REBINT len, REBYTE *script, REBINT script_len, REBCNT flags)
{
    REBVAL *val;
    REBSER *ser;

    struct Reb_State state;
    REBCTX *error;
    int error_num;

    REBVAL result;
    VAL_INIT_WRITABLE_DEBUG(&result);

    if (bin) {
        ser = Decompress(bin, len, -1, FALSE, FALSE);
        if (!ser) return 1;

        val = CTX_VAR(Sys_Context, SYS_CTX_BOOT_HOST);
        Val_Init_Binary(val, ser);
    }

    if (script && script_len > 4) {
        /* a 4-byte long payload type at the beginning */
        i32 ptype = 0;
        REBYTE *data = script + sizeof(ptype);
        script_len -= sizeof(ptype);

        memcpy(&ptype, script, sizeof(ptype));

        if (ptype == 1) {/* COMPRESSed data */
            ser = Decompress(data, script_len, -1, FALSE, FALSE);
        } else {
            ser = Make_Binary(script_len);
            if (ser == NULL) {
                OS_FREE(script);
                return 1;
            }
            memcpy(BIN_HEAD(ser), data, script_len);
        }
        OS_FREE(script);

        val = CTX_VAR(Sys_Context, SYS_CTX_BOOT_EMBEDDED);
        Val_Init_Binary(val, ser);
    }

    PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `fail` can longjmp here, so 'error' won't be NULL *if* that happens!

    if (error) {
        //
        // !!! We are not allowed to ask for a print operation that can take
        // arbitrarily long without allowing for cancellation via Ctrl-C,
        // but here we are wanting to print an error.  If you're printing
        // out an error and get a halt, it won't print the halt.
        //
        REBCTX *halt_error;

        // Save error for WHY?
        //
        REBVAL *last = Get_System(SYS_STATE, STATE_LAST_ERROR);
        Val_Init_Error(last, error);

        PUSH_UNHALTABLE_TRAP(&halt_error, &state);

// The first time through the following code 'error' will be NULL, but...
// `fail` can longjmp here, so 'error' won't be NULL *if* that happens!

        if (halt_error) {
            assert(ERR_NUM(halt_error) == RE_HALT);
            return ERR_NUM(halt_error);
        }

        Print_Value(last, 1024, FALSE);

        DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

        // !!! When running in a script, whether or not the Rebol interpreter
        // just exits in an error case with a bad error code or breaks you
        // into the console to debug the environment should be controlled by
        // a command line option.  Defaulting to returning an error code
        // seems better, because kicking into an interactive session can
        // cause logging systems to hang.

        // For RE_HALT and all other errors we return the error
        // number.  Error numbers are not set in stone (currently), but
        // are never zero...which is why we can use 0 for success.
        //
        return ERR_NUM(error);
    }

    if (Apply_Only_Throws(
        &result, Sys_Func(SYS_CTX_FINISH_RL_START), END_VALUE
    )) {
        #if !defined(NDEBUG)
            if (LEGACY(OPTIONS_EXIT_FUNCTIONS_ONLY))
                fail (Error_No_Catch_For_Throw(&result));
        #endif

        if (
            IS_FUNCTION_AND(&result, FUNC_CLASS_NATIVE) && (
                VAL_FUNC_CODE(&result) == &N_quit
                || VAL_FUNC_CODE(&result) == &N_exit
            )
        ) {
            int status;

            CATCH_THROWN(&result, &result);
            status = Exit_Status_From_Value(&result);

            DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

            Shutdown_Core();
            OS_EXIT(status);
            DEAD_END;
        }

        fail (Error_No_Catch_For_Throw(&result));
    }

    DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

    // The convention in the API was to return 0 for success.  We use the
    // convention (as for FINISH_INIT_CORE) that any non-UNSET! result from
    // FINISH_RL_START indicates something went wrong.

    if (IS_UNSET(&result))
        error_num = 0; // no error
    else {
        assert(FALSE); // should not happen (raise an error instead)
        Debug_Fmt("** finish-rl-start returned non-NONE!:");
        Debug_Fmt("%r", &result);
        error_num = RE_MISC;
    }

    return error_num;
}
Beispiel #15
0
//
//  Do_String()
//
// This is a version of a routine that was offered by the RL_Api, which has
// been expanded here in order to permit the necessary customizations for
// interesting REPL behavior w.r.t. binding, error handling, and response
// to throws.
//
// !!! Now that this code has been moved into the host, the convoluted
// integer-return-scheme can be eliminated and the code integrated more
// clearly into the surrounding calls.
//
int Do_String(
    int *exit_status,
    REBVAL *out,
    const REBYTE *text,
    REBOOL at_breakpoint
) {
    struct Reb_State state;
    REBCTX *error;

    // Breakpoint REPLs are nested, and we may wish to jump out of them to
    // the topmost level via a HALT.  However, all other errors need to be
    // confined, so that if one is doing evaluations during the pause of
    // a breakpoint an error doesn't "accidentally resume" by virtue of
    // jumping the stack out of the REPL.
    //
    // The topmost layer REPL, however, needs to catch halts in order to
    // keep control and not crash out.
    //
    if (at_breakpoint)
        PUSH_TRAP(&error, &state);
    else
        PUSH_UNHALTABLE_TRAP(&error, &state);

// The first time through the following code 'error' will be NULL, but...
// `fail` can longjmp here, so 'error' won't be NULL *if* that happens!

    if (error) {
        // Save error for WHY?
        REBVAL *last = Get_System(SYS_STATE, STATE_LAST_ERROR);

        if (ERR_NUM(error) == RE_HALT) {
            assert(!at_breakpoint);
            return -1; // !!! Revisit hardcoded #
        }

        Val_Init_Error(out, error);
        *last = *out;
        return -cast(REBINT, ERR_NUM(error));
    }

    REBARR *code = Scan_UTF8_Managed(text, LEN_BYTES(text));

    // Where code ends up being bound when loaded at the REPL prompt should
    // be more generally configurable.  (It may be, for instance, that one
    // wants to run something with it not bound at all.)  Such choices
    // must come from this REPL host...not from the interpreter itself.
    {
        // First the scanned code is bound into the user context with a
        // fallback to the lib context.
        //
        // !!! This code is very old, and is how the REPL has bound since
        // R3-Alpha.  It comes from RL_Do_String, but should receive a modern
        // review of why it's written exactly this way.
        //
        REBCTX *user_ctx = VAL_CONTEXT(Get_System(SYS_CONTEXTS, CTX_USER));

        REBVAL vali;
        SET_INTEGER(&vali, CTX_LEN(user_ctx) + 1);

        Bind_Values_All_Deep(ARR_HEAD(code), user_ctx);
        Resolve_Context(user_ctx, Lib_Context, &vali, FALSE, FALSE);

        // If we're stopped at a breakpoint, the REPL should have a concept
        // of what stack level it is inspecting (conveyed by the |#|>> in the
        // prompt).  This does a binding pass using the function for that
        // stack level, just the way a body is bound during Make_Function()
        //
        if (at_breakpoint) {
            REBVAL level;
            SET_INTEGER(&level, HG_Stack_Level);

            REBFRM *frame = Frame_For_Stack_Level(NULL, &level, FALSE);
            assert(frame);

            // Need to manage because it may be no words get bound into it,
            // and we're not putting it into a FRAME! value, so it might leak
            // otherwise if it's reified.
            //
            REBCTX *frame_ctx = Context_For_Frame_May_Reify_Managed(frame);

            Bind_Values_Deep(ARR_HEAD(code), frame_ctx);
        }

        // !!! This was unused code that used to be in Do_String from
        // RL_Api.  It was an alternative path under `flags` which said
        // "Bind into lib or user spaces?" and then "Top words will be
        // added to lib".  Is it relevant in any way?
        //
        /* Bind_Values_Set_Midstream_Shallow(ARR_HEAD(code), Lib_Context);
        Bind_Values_Deep(ARR_HEAD(code), Lib_Context); */
    }

    if (Do_At_Throws(out, code, 0, SPECIFIED)) { // `code` will be GC protected
        if (at_breakpoint) {
            if (
                IS_FUNCTION(out)
                && VAL_FUNC_DISPATCHER(out) == &N_resume
            ) {
                //
                // This means we're done with the embedded REPL.  We want to
                // resume and may be returning a piece of code that will be
                // run by the finishing BREAKPOINT command in the target
                // environment.
                //
                // We'll never return a halt, so we reuse -1 (in this very
                // temporary scheme built on the very clunky historical REPL,
                // which will not last much longer...fingers crossed.)
                //
                DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);
                CATCH_THROWN(out, out);
                *exit_status = -1;
                return -1;
            }

            if (
                IS_FUNCTION(out)
                && VAL_FUNC_DISPATCHER(out) == &N_quit
            ) {
                //
                // It would be frustrating if the system did not respond to
                // a QUIT and forced you to do `resume/with [quit]`.  So
                // this is *not* caught, rather passed back up with the
                // special -2 status code.
                //
                DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);
                CATCH_THROWN(out, out);
                *exit_status = -2;
                return -2;
            }
        }
        else {
            // We are at the top level REPL, where we catch QUIT and for
            // now, also EXIT as meaning you want to leave.
            //
            if (
                IS_FUNCTION(out)
                && (
                    VAL_FUNC_DISPATCHER(out) == &N_quit
                    || VAL_FUNC_DISPATCHER(out) == &N_exit
                )
            ) {
                DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);
                CATCH_THROWN(out, out);
                *exit_status = Exit_Status_From_Value(out);
                return -2; // Revisit hardcoded #
            }
        }

        fail (Error_No_Catch_For_Throw(out));
    }

    DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state);

    return 0;
}