static REBOOL Same_Func(REBVAL *val, REBVAL *arg) { if (VAL_TYPE(val) == VAL_TYPE(arg) && VAL_FUNC_SPEC(val) == VAL_FUNC_SPEC(arg) && VAL_FUNC_ARGS(val) == VAL_FUNC_ARGS(arg) && VAL_FUNC_CODE(val) == VAL_FUNC_CODE(arg)) return TRUE; return FALSE; }
*/ void Make_Native(REBVAL *value, REBSER *spec, REBFUN func, REBINT type) /* ***********************************************************************/ { //Print("Make_Native: %s spec %d", Get_Sym_Name(type+1), SERIES_TAIL(spec)); VAL_FUNC_SPEC(value) = spec; VAL_FUNC_ARGS(value) = Check_Func_Spec(spec); VAL_FUNC_CODE(value) = func; VAL_SET(value, type); }
*/ void Do_Native(REBVAL *func) /* ***********************************************************************/ { REBVAL *ds; REBINT n; #if !defined(NDEBUG) const REBYTE *fname = Get_Word_Name(DSF_LABEL(DSF)); #endif Eval_Natives++; if ((n = VAL_FUNC_CODE(func)(DS_OUT))) { ds = DS_OUT; switch (n) { case R_OUT: // for compiler opt break; case R_TOS: *ds = *DS_TOP; break; case R_TOS1: *ds = *DS_NEXT; break; case R_NONE: SET_NONE(ds); break; case R_UNSET: SET_UNSET(ds); break; case R_TRUE: SET_TRUE(ds); break; case R_FALSE: SET_FALSE(ds); break; case R_ARG1: *ds = *D_ARG(1); break; case R_ARG2: *ds = *D_ARG(2); break; case R_ARG3: *ds = *D_ARG(3); break; } } }
*/ void Do_Native(REBVAL *func) /* ***********************************************************************/ { REBVAL *ds; REBINT n; #ifdef DEBUGGING REBYTE *fname = Get_Word_Name(DSF_WORD(DSF)); // for DEBUG Debug_Str(fname); #endif Eval_Natives++; if (NZ(n = VAL_FUNC_CODE(func)(DS_RETURN))) { ds = DS_RETURN; switch (n) { case R_RET: // for compiler opt break; case R_TOS: *ds = *DS_TOP; break; case R_TOS1: *ds = *DS_NEXT; break; case R_NONE: SET_NONE(ds); break; case R_UNSET: SET_UNSET(ds); break; case R_TRUE: SET_TRUE(ds); break; case R_FALSE: SET_FALSE(ds); break; case R_ARG1: *ds = *D_ARG(1); break; case R_ARG2: *ds = *D_ARG(2); break; case R_ARG3: *ds = *D_ARG(3); break; } } }
*/ int Do_Port_Action(struct Reb_Call *call_, REBSER *port, REBCNT action) /* ** Call a PORT actor (action) value. Search PORT actor ** first. If not found, search the PORT scheme actor. ** ** NOTE: stack must already be setup correctly for action, and ** the caller must cleanup the stack. ** ***********************************************************************/ { REBVAL *actor; REBCNT n = 0; assert(action < A_MAX_ACTION); // Verify valid port (all of these must be false): if ( // Must be = or larger than std port: (SERIES_TAIL(port) < STD_PORT_MAX) || // Must be an object series: !IS_FRAME(BLK_HEAD(port)) || // Must have a spec object: !IS_OBJECT(BLK_SKIP(port, STD_PORT_SPEC)) ) { raise Error_0(RE_INVALID_PORT); } // Get actor for port, if it has one: actor = BLK_SKIP(port, STD_PORT_ACTOR); if (IS_NONE(actor)) return R_NONE; // If actor is a native function: if (IS_NATIVE(actor)) return cast(REBPAF, VAL_FUNC_CODE(actor))(call_, port, action); // actor must be an object: if (!IS_OBJECT(actor)) raise Error_0(RE_INVALID_ACTOR); // Dispatch object function: n = Find_Action(actor, action); actor = Obj_Value(actor, n); if (!n || !actor || !ANY_FUNC(actor)) raise Error_1(RE_NO_PORT_ACTION, Get_Action_Word(action)); if (Redo_Func_Throws(actor)) { // No special handling needed, as we are just going to return // the output value in D_OUT anyway. } return R_OUT; // If not in PORT actor, use the SCHEME actor: #ifdef no_longer_used if (n == 0) { actor = Obj_Value(scheme, STD_SCHEME_actor); if (!actor) goto err; if (IS_NATIVE(actor)) goto fun; if (!IS_OBJECT(actor)) goto err; //vTrap_Expect(value, STD_PORT_actor, REB_OBJECT); n = Find_Action(actor, action); if (n == 0) goto err; } #endif }
*/ 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)]; }
*/ 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; }
*/ REBINT Cmp_Value(REBVAL *s, REBVAL *t, REBFLG is_case) /* ** Compare two values and return the difference. ** ** is_case TRUE for case sensitive compare ** ***********************************************************************/ { REBDEC d1, d2; if (VAL_TYPE(t) != VAL_TYPE(s) && !(IS_NUMBER(s) && IS_NUMBER(t))) return VAL_TYPE(s) - VAL_TYPE(t); switch(VAL_TYPE(s)) { case REB_INTEGER: if (IS_DECIMAL(t)) { d1 = (REBDEC)VAL_INT64(s); d2 = VAL_DECIMAL(t); goto chkDecimal; } return THE_SIGN(VAL_INT64(s) - VAL_INT64(t)); case REB_LOGIC: return VAL_LOGIC(s) - VAL_LOGIC(t); case REB_CHAR: if (is_case) return THE_SIGN(VAL_CHAR(s) - VAL_CHAR(t)); return THE_SIGN((REBINT)(UP_CASE(VAL_CHAR(s)) - UP_CASE(VAL_CHAR(t)))); case REB_DECIMAL: case REB_MONEY: d1 = VAL_DECIMAL(s); if (IS_INTEGER(t)) d2 = (REBDEC)VAL_INT64(t); else d2 = VAL_DECIMAL(t); chkDecimal: if (Eq_Decimal(d1, d2)) return 0; if (d1 < d2) return -1; return 1; case REB_PAIR: return Cmp_Pair(s, t); case REB_EVENT: return Cmp_Event(s, t); case REB_GOB: return Cmp_Gob(s, t); case REB_TUPLE: return Cmp_Tuple(s, t); case REB_TIME: return Cmp_Time(s, t); case REB_DATE: return Cmp_Date(s, t); case REB_BLOCK: case REB_PAREN: case REB_MAP: case REB_PATH: case REB_SET_PATH: case REB_GET_PATH: case REB_LIT_PATH: return Cmp_Block(s, t, is_case); case REB_STRING: case REB_FILE: case REB_EMAIL: case REB_URL: case REB_TAG: return Compare_String_Vals(s, t, (REBOOL)!is_case); case REB_BITSET: case REB_BINARY: case REB_IMAGE: return Compare_Binary_Vals(s, t); case REB_VECTOR: return Compare_Vector(s, t); case REB_DATATYPE: return VAL_DATATYPE(s) - VAL_DATATYPE(t); case REB_WORD: case REB_SET_WORD: case REB_GET_WORD: case REB_LIT_WORD: case REB_REFINEMENT: case REB_ISSUE: return Compare_Word(s,t,is_case); case REB_ERROR: return VAL_ERR_NUM(s) - VAL_ERR_NUM(s); case REB_OBJECT: case REB_MODULE: case REB_PORT: return VAL_OBJ_FRAME(s) - VAL_OBJ_FRAME(t); case REB_NATIVE: return &VAL_FUNC_CODE(s) - &VAL_FUNC_CODE(t); case REB_ACTION: case REB_COMMAND: case REB_OP: case REB_FUNCTION: return VAL_FUNC_BODY(s) - VAL_FUNC_BODY(t); case REB_NONE: case REB_UNSET: case REB_END: default: break; } return 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)]; }
// // 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; }