interp是核心函数,理解此函数要下极大的功夫。
对有些及难懂的注释了一些。
交流分享是一种学习的好方法。
/* Main interpreter. */ /* If execution terminates normally, return e_InterpreterExit. */ /* If an error occurs, leave the current object in *perror_object */ /* and return a (negative) error code. */ static int interp(i_ctx_t **pi_ctx_p /* context for execution, updated if resched */, const ref * pref /* object to interpret */, ref * perror_object) { i_ctx_t *i_ctx_p = *pi_ctx_p; /* * Note that iref may actually be either a ref * or a ref_packed *. * Certain DEC compilers assume that a ref * is ref-aligned even if it * is cast to a short *, and generate code on this assumption, leading * to "unaligned access" errors. For this reason, we declare * iref_packed, and use a macro to cast it to the more aligned type * where necessary (which is almost everywhere it is used). This may * lead to compiler warnings about "cast increases alignment * requirements", but this is less harmful than expensive traps at run * time. */ /* typedef ushort ref_packed; ref_packed实际上和ref第一数据成员对应的: ushort type_attrs; 而这个type_attrs这个重要的值在iscan.c的scan_token()中设置, 例如: #define make_bool(pref,bval)/ make_tv(pref, t_boolean, boolval, bval) #define make_false(pref)/ make_bool(pref, 0) #define make_true(pref)/ make_bool(pref, 1) #define make_int(pref,ival)/ make_tv(pref, t_integer, intval, ival) #define make_int_new(pref,ival)/ make_tv_new(pref, t_integer, intval, ival) #define make_tv(pref,newtype,valfield,newvalue)/ make_tav(pref,newtype,0,valfield,newvalue) #define make_ta(pref,newtype,newattrs)/ (r_set_type_attrs(pref, newtype, newattrs) and_fill_sv(pref)) #define r_set_type_attrs(rp,typ,mask)/ ((rp)->tas.type_attrs = ((typ) << r_type_shift) + (mask)) 这些代码在store.h中 */ register const ref_packed *iref_packed = (const ref_packed *)pref; /* * To make matters worse, some versions of gcc/egcs have a bug that * leads them to assume that if iref_packed is EVER cast to a ref *, * it is ALWAYS ref-aligned. We detect this in stdpre.h and provide * the following workaround: */ #ifdef ALIGNMENT_ALIASING_BUG const ref *iref_temp; # define IREF (iref_temp = (const ref *)iref_packed, iref_temp) #else # define IREF ((const ref *)iref_packed) #endif #define SET_IREF(rp) (iref_packed = (const ref_packed *)(rp)) register int icount = 0; /* # of consecutive tokens at iref */ /* 从函数看来,icount用于数组(过程)的个数 */ register os_ptr iosp = osp; /* private copy of osp */ /* 操作数栈 */ register es_ptr iesp = esp; /* private copy of esp */ /* 执行栈 */ int code; ref token; /* token read from file or string, */ /* must be declared in this scope */ register const ref *pvalue = 0; /*name的查找结果,比如add,查找对应函数zadd实现*/ uint opindex; /* needed for oparrays */ os_ptr whichp; /* * We have to make the error information into a struct; * otherwise, the Watcom compiler will assign it to registers * strictly on the basis of textual frequency. * We also have to use ref_assign_inline everywhere, and * avoid direct assignments of refs, so that esi and edi * will remain available on Intel processors. */ struct interp_error_s { int code; int line; const ref *obj; ref full; } ierror; /* * Get a pointer to the name table so that we can use the * inline version of name_index_ref. */ const name_table *const int_nt = imemory->gs_lib_ctx->gs_name_table; /*出错处理,这些代码并不复杂*/ #define set_error(ecode)/ { ierror.code = ecode; ierror.line = __LINE__; } #define return_with_error(ecode, objp)/ { set_error(ecode); ierror.obj = objp; goto rwe; } #define return_with_error_iref(ecode)/ { set_error(ecode); goto rwei; } #define return_with_code_iref()/ { ierror.line = __LINE__; goto rweci; } #define return_with_stackoverflow(objp)/ { o_stack.requested = 1; return_with_error(e_stackoverflow, objp); } #define return_with_stackoverflow_iref()/ { o_stack.requested = 1; return_with_error_iref(e_stackoverflow); } /* * If control reaches the special operators (x_add, etc.) as a result of * interpreting an executable name, iref points to the name, not the * operator, so the name rather than the operator becomes the error object, * which is wrong. We detect and handle this case explicitly when an error * occurs, so as not to slow down the non-error case. */ #define return_with_error_tx_op(err_code)/ { if (r_has_type(IREF, t_name)) {/ return_with_error(err_code, pvalue);/ } else {/ return_with_error_iref(err_code);/ }/ } /* garbage collect,这个功能以后在有时间在研究。 */ int ticks_left = gs_interp_time_slice_ticks; /* * If we exceed the VMThreshold, set ticks_left to -100 * to alert the interpreter that we need to garbage collect. */ set_gc_signal(i_ctx_p, &ticks_left, -100); esfile_clear_cache(); /* * From here on, if icount > 0, iref and icount correspond * to the top entry on the execution stack: icount is the count * of sequential entries remaining AFTER the current one. */ /* 这种形式能正确执行,因为向"过程"这种形式*/ #define IREF_NEXT(ip)/ ((const ref_packed *)((const ref *)(ip) + 1)) #define IREF_NEXT_EITHER(ip)/ ( r_is_packed(ip) ? (ip) + 1 : IREF_NEXT(ip) ) /* store 意味保存状态之意, short意味packed, 而either两者皆有(packed, ref) */ #define store_state(ep)/ ( icount > 0 ? (ep->value.const_refs = IREF + 1, r_set_size(ep, icount)) : 0 ) #define store_state_short(ep)/ ( icount > 0 ? (ep->value.packed = iref_packed + 1, r_set_size(ep, icount)) : 0 ) #define store_state_either(ep)/ ( icount > 0 ? (ep->value.packed = IREF_NEXT_EITHER(iref_packed), r_set_size(ep, icount)) : 0 ) #define next()/ if ( --icount > 0 ) { iref_packed = IREF_NEXT(iref_packed); goto top; } else goto out #define next_short()/ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }/ ++iref_packed; goto top #define next_either()/ if ( --icount <= 0 ) { if ( icount < 0 ) goto up; iesp--; }/ iref_packed = IREF_NEXT_EITHER(iref_packed); goto top #if !PACKED_SPECIAL_OPS # undef next_either # define next_either() next() # undef store_state_either # define store_state_either(ep) store_state(ep) #endif /* We want to recognize executable arrays here, */ /* so we push the argument on the estack and enter */ /* the loop at the bottom. */ if (iesp >= estop) return_with_error(e_execstackoverflow, pref); ++iesp; ref_assign_inline(iesp, pref); goto bot; top: /* * This is the top of the interpreter loop. * iref points to the ref being interpreted. * Note that this might be an element of a packed array, * not a real ref: we carefully arranged the first 16 bits of * a ref and of a packed array element so they could be distinguished * from each other. (See ghost.h and packed.h for more detail.) */ INCR(top); #ifdef DEBUG /* Do a little validation on the top o-stack entry. */ if (iosp >= osbot && (r_type(iosp) == t__invalid || r_type(iosp) >= tx_next_op) ) { lprintf("Invalid value on o-stack!/n"); return_with_error_iref(e_Fatal); } if (gs_debug['I'] || (gs_debug['i'] && (r_is_packed(iref_packed) ? r_packed_is_name(iref_packed) : r_has_type(IREF, t_name))) ) { os_ptr save_osp = osp; /* avoid side-effects */ es_ptr save_esp = esp; osp = iosp; esp = iesp; dlprintf5("d%u,e%u<%u>0x%lx(%d): ", ref_stack_count(&d_stack), ref_stack_count(&e_stack), ref_stack_count(&o_stack), (ulong)IREF, icount); debug_print_ref(imemory, IREF); if (iosp >= osbot) { dputs(" // "); debug_print_ref(imemory, iosp); } dputc('/n'); osp = save_osp; esp = save_esp; dflush(); } #endif /* Objects that have attributes (arrays, dictionaries, files, and strings) */ /* use lit and exec; other objects use plain and plain_exec. */ #define lit(t) type_xe_value(t, a_execute) #define exec(t) type_xe_value(t, a_execute + a_executable) #define nox(t) type_xe_value(t, 0) #define nox_exec(t) type_xe_value(t, a_executable) #define plain(t) type_xe_value(t, 0) #define plain_exec(t) type_xe_value(t, a_executable) /* * We have to populate enough cases of the switch statement to force * some compilers to use a dispatch rather than a testing loop. * What a nuisance! */ switch (r_type_xe(iref_packed)) {//这儿能够行原因在于:iscan.c的scan_token函数对每个对象设定了属性。 /* Access errors. */ #define cases_invalid()/ case plain(t__invalid): case plain_exec(t__invalid) cases_invalid(): return_with_error_iref(e_Fatal); #define cases_nox()/ case nox_exec(t_array): case nox_exec(t_dictionary):/ case nox_exec(t_file): case nox_exec(t_string):/ case nox_exec(t_mixedarray): case nox_exec(t_shortarray) cases_nox(): return_with_error_iref(e_invalidaccess); /* * Literal objects. We have to enumerate all the types. * In fact, we have to include some extra plain_exec entries * just to populate the switch. We break them up into groups * to avoid overflowing some preprocessors. */ /* lit 意味着压栈,罗列了各种各样的形式 */ #define cases_lit_1()/ case lit(t_array): case nox(t_array):/ case plain(t_boolean): case plain_exec(t_boolean):/ case lit(t_dictionary): case nox(t_dictionary) #define cases_lit_2()/ case lit(t_file): case nox(t_file):/ case plain(t_fontID): case plain_exec(t_fontID):/ case plain(t_integer): case plain_exec(t_integer):/ case plain(t_mark): case plain_exec(t_mark) #define cases_lit_3()/ case plain(t_name):/ case plain(t_null):/ case plain(t_oparray):/ case plain(t_operator) #define cases_lit_4()/ case plain(t_real): case plain_exec(t_real):/ case plain(t_save): case plain_exec(t_save):/ case lit(t_string): case nox(t_string) #define cases_lit_5()/ case lit(t_mixedarray): case nox(t_mixedarray):/ case lit(t_shortarray): case nox(t_shortarray):/ case plain(t_device): case plain_exec(t_device):/ case plain(t_struct): case plain_exec(t_struct):/ case plain(t_astruct): case plain_exec(t_astruct) /* Executable arrays are treated as literals in direct execution. */ #define cases_lit_array()/ case exec(t_array): case exec(t_mixedarray): case exec(t_shortarray) cases_lit_1(): cases_lit_2(): cases_lit_3(): cases_lit_4(): cases_lit_5(): INCR(lit); break; cases_lit_array(): INCR(lit_array); break; /* Special operators. */ /* 把这些常用操作符罗列出来,能提高解析效率 */ case plain_exec(tx_op_add): x_add: INCR(x_add);/* num1 num2 add sum */ if ((code = zop_add(iosp)) < 0) return_with_error_tx_op(code); iosp--; /* iosp代表操作数栈,add需要两个操作数,这儿只减一,因为其中一个用来存放add之后的结果 */ next_either(); case plain_exec(tx_op_def): x_def: INCR(x_def);/* key value def */ osp = iosp; /* sync o_stack */ if ((code = zop_def(i_ctx_p)) < 0) return_with_error_tx_op(code); iosp -= 2; next_either(); case plain_exec(tx_op_dup): x_dup: INCR(x_dup);/* any dup any any */ if (iosp < osbot) return_with_error_tx_op(e_stackunderflow); if (iosp >= ostop) { o_stack.requested = 1; return_with_error_tx_op(e_stackoverflow); } iosp++; ref_assign_inline(iosp, iosp - 1); next_either(); case plain_exec(tx_op_exch): x_exch: INCR(x_exch);/* any1 any2 exch any2 any1 */ if (iosp <= osbot) return_with_error_tx_op(e_stackunderflow); ref_assign_inline(&token, iosp); ref_assign_inline(iosp, iosp - 1); ref_assign_inline(iosp - 1, &token); next_either(); case plain_exec(tx_op_if): x_if: INCR(x_if); /* bool proc if */ if (!r_is_proc(iosp)) return_with_error_tx_op(check_proc_failed(iosp)); if (!r_has_type(iosp - 1, t_boolean)) return_with_error_tx_op((iosp <= osbot ? e_stackunderflow : e_typecheck)); if (!iosp[-1].value.boolval) { iosp -= 2; next_either(); } if (iesp >= estop) return_with_error_tx_op(e_execstackoverflow); store_state_either(iesp); whichp = iosp; iosp -= 2; goto ifup; case plain_exec(tx_op_ifelse): x_ifelse: INCR(x_ifelse); /* bool proc1 proc2 ifelse */ if (!r_is_proc(iosp)) return_with_error_tx_op(check_proc_failed(iosp)); if (!r_is_proc(iosp - 1)) return_with_error_tx_op(check_proc_failed(iosp - 1)); if (!r_has_type(iosp - 2, t_boolean))/*这个一定要布尔变量,因为:bool proc1 proc2 ifelse*/ return_with_error_tx_op((iosp < osbot + 2 ? e_stackunderflow : e_typecheck)); if (iesp >= estop) return_with_error_tx_op(e_execstackoverflow); /*下面要执行whichp过程,所以要保存当前状态。store_state_either()。*/ store_state_either(iesp); whichp = (iosp[-2].value.boolval ? iosp - 1 : iosp); iosp -= 3; /* Open code "up" for the array case(s) */ /* icount是过程的元素个数,下面减1 如果等于0,那么就有一个元素,如果小于0,就无元素。*/ ifup:if ((icount = r_size(whichp) - 1) <= 0) { if (icount < 0) goto up; /* 0-element proc */ SET_IREF(whichp->value.refs); /* 1-element proc */ if (--ticks_left > 0) goto top; } ++iesp; /* Do a ref_assign, but also set iref. */ iesp->tas = whichp->tas; /* 这儿是过程的第一个元素 */ /* 例如:{ pop userdict maxlength 0 eq },便是pop */ SET_IREF(iesp->value.refs = whichp->value.refs); if (--ticks_left > 0) goto top; goto slice; case plain_exec(tx_op_index): x_index: INCR(x_index); /* anyn … any0 n index anyn … any0 anyn */ osp = iosp; /* zindex references o_stack */ if ((code = zindex(i_ctx_p)) < 0) return_with_error_tx_op(code); next_either(); case plain_exec(tx_op_pop): x_pop: INCR(x_pop);/* any pop – */ if (iosp < osbot) return_with_error_tx_op(e_stackunderflow); iosp--; next_either(); case plain_exec(tx_op_roll): x_roll: INCR(x_roll); /* anyn-1 … any0 n j roll any(j-1) mod n … any0 anyn-1 … anyj mod n */ osp = iosp; /* zroll references o_stack */ if ((code = zroll(i_ctx_p)) < 0) return_with_error_tx_op(code); iosp -= 2; next_either(); case plain_exec(tx_op_sub): x_sub: INCR(x_sub); /* num1 num2 sub difference */ if ((code = zop_sub(iosp)) < 0) return_with_error_tx_op(code); iosp--; next_either(); /* Executable types. */ case plain_exec(t_null): goto bot; case plain_exec(t_oparray): /* Replace with the definition and go again. */ INCR(exec_array); opindex = op_index(IREF); pvalue = IREF->value.const_refs; /* array的形式 */ opst: /* Prepare to call a t_oparray procedure in *pvalue. */ store_state(iesp); /* 保存当前的执行栈的状态,因为下面的状态和当前的转态有区别 */ oppr: /* Record the stack depths in case of failure. */ if (iesp >= estop - 4) return_with_error_iref(e_execstackoverflow); iesp += 5; osp = iosp; /* ref_stack_count_inline needs this */ make_mark_estack(iesp - 4, es_other, oparray_cleanup); make_int(iesp - 3, opindex); /* for .errorexec effect */ make_int(iesp - 2, ref_stack_count_inline(&o_stack)); make_int(iesp - 1, ref_stack_count_inline(&d_stack)); make_op_estack(iesp, oparray_pop); goto pr; prst: /* Prepare to call the procedure (array) in *pvalue. */ store_state(iesp); /* 同上,但是并不重叠,因为上面有goto pr */ pr: /* Call the array in *pvalue. State has been stored. */ if ((icount = r_size(pvalue) - 1) <= 0) { /* 执行元素的个数 */ if (icount < 0) goto up; /* 0-element proc */ SET_IREF(pvalue->value.refs); /* 1-element proc */ if (--ticks_left > 0) goto top; } if (iesp >= estop) return_with_error_iref(e_execstackoverflow); ++iesp; /* Do a ref_assign, but also set iref. */ iesp->tas = pvalue->tas; SET_IREF(iesp->value.refs = pvalue->value.refs); if (--ticks_left > 0) goto top; goto slice; case plain_exec(t_operator): INCR(exec_operator); if (--ticks_left <= 0) { /* The following doesn't work, */ /* and I can't figure out why. */ /****** goto sst; ******/ } esp = iesp; /* save for operator */ osp = iosp; /* ditto */ /* Operator routines take osp as an argument. */ /* This is just a convenience, since they adjust */ /* osp themselves to reflect the results. */ /* Operators that (net) push information on the */ /* operand stack must check for overflow: */ /* this normally happens automatically through */ /* the push macro (in oper.h). */ /* Operators that do not typecheck their operands, */ /* or take a variable number of arguments, */ /* must check explicitly for stack underflow. */ /* (See oper.h for more detail.) */ /* Note that each case must set iosp = osp: */ /* this is so we can switch on code without having to */ /* store it and reload it (for dumb compilers). */ /* 调用操作符,code 返回值有不同的处理。0 这种情况是很多的。*/ switch (code = call_operator(real_opproc(IREF), i_ctx_p)) { case 0: /* normal case */ case 1: /* alternative success case */ iosp = osp; next(); case o_push_estack: /* store the state and go to up */ store_state(iesp); opush:iosp = osp; iesp = esp; if (--ticks_left > 0) goto up; goto slice; case o_pop_estack: /* just go to up */ opop:iosp = osp; if (esp == iesp) goto bot; iesp = esp; goto up; case o_reschedule: store_state(iesp); goto res; case e_RemapColor: oe_remap: store_state(iesp); remap: if (iesp + 2 >= estop) { esp = iesp; code = ref_stack_extend(&e_stack, 2); if (code < 0) return_with_error_iref(code); iesp = esp; } packed_get(imemory, iref_packed, iesp + 1); make_oper(iesp + 2, 0, r_ptr(&istate->remap_color_info, int_remap_color_info_t)->proc); iesp += 2; goto up; } iosp = osp; iesp = esp; return_with_code_iref(); // struct name_s { /* pvalue specifies the definition status of the name: */ /* pvalue == pv_no_defn: no definitions */ //#define pv_no_defn ((ref *)0) /* pvalue == pv_other: other status */ //#define pv_other ((ref *)1) /* pvalue != pv_no_defn, pvalue != pv_other: pvalue is valid */ //#define pv_valid(pvalue) ((unsigned long)(pvalue) > 1) // ref *pvalue; /* if only defined in systemdict or */ /* userdict, this points to the value */ // }; case plain_exec(t_name): INCR(exec_name); pvalue = IREF->value.pname->pvalue; /* 这步能直接关联到操作符,否则在下面的字典里面查找 */ if (!pv_valid(pvalue)) { uint nidx = names_index(int_nt, IREF); uint htemp; INCR(find_name); if ((pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0) return_with_error_iref(e_undefined); } /* Dispatch on the type of the value. */ /* Again, we have to over-populate the switch. */ switch (r_type_xe(pvalue)) { /* 操作符将以何种形式执行,同上面很相似。 */ cases_invalid(): return_with_error_iref(e_Fatal); cases_nox(): /* access errors */ return_with_error_iref(e_invalidaccess); cases_lit_1(): cases_lit_2(): cases_lit_3(): cases_lit_4(): cases_lit_5(): INCR(name_lit); /* Just push the value */ if (iosp >= ostop) return_with_stackoverflow(pvalue); ++iosp; ref_assign_inline(iosp, pvalue); next(); case exec(t_array): case exec(t_mixedarray): case exec(t_shortarray): INCR(name_proc); /* This is an executable procedure, execute it. */ goto prst; case plain_exec(tx_op_add): goto x_add; case plain_exec(tx_op_def): goto x_def; case plain_exec(tx_op_dup): goto x_dup; case plain_exec(tx_op_exch): goto x_exch; case plain_exec(tx_op_if): goto x_if; case plain_exec(tx_op_ifelse): goto x_ifelse; case plain_exec(tx_op_index): goto x_index; case plain_exec(tx_op_pop): goto x_pop; case plain_exec(tx_op_roll): goto x_roll; case plain_exec(tx_op_sub): goto x_sub; case plain_exec(t_null): goto bot; case plain_exec(t_oparray): INCR(name_oparray); opindex = op_index(pvalue); pvalue = (const ref *)pvalue->value.const_refs; goto opst; case plain_exec(t_operator): INCR(name_operator); { /* Shortcut for operators. */ /* See above for the logic. */ if (--ticks_left <= 0) { /* The following doesn't work, */ /* and I can't figure out why. */ /****** goto sst; ******/ } esp = iesp; osp = iosp; switch (code = call_operator(real_opproc(pvalue), i_ctx_p) ) { case 0: /* normal case */ case 1: /* alternative success case */ iosp = osp; next(); case o_push_estack: store_state(iesp); goto opush; case o_pop_estack: goto opop; case o_reschedule: store_state(iesp); goto res; case e_RemapColor: goto oe_remap; } iosp = osp; iesp = esp; return_with_error(code, pvalue); } case plain_exec(t_name): case exec(t_file): case exec(t_string): default: /* Not a procedure, reinterpret it. */ store_state(iesp); icount = 0; SET_IREF(pvalue); goto top; } case exec(t_file): { /* Executable file. Read the next token and interpret it. */ stream *s; scanner_state sstate; check_read_known_file(s, IREF, return_with_error_iref); rt: if (iosp >= ostop) /* check early */ return_with_stackoverflow_iref(); osp = iosp; /* scan_token uses ostack */ scanner_init_options(&sstate, IREF, i_ctx_p->scanner_options); again: code = scan_token(i_ctx_p, &token, &sstate); iosp = osp; /* ditto */ switch (code) { case 0: /* read a token */ /* It's worth checking for literals, which make up */ /* the majority of input tokens, before storing the */ /* state on the e-stack. Note that because of //, */ /* the token may have *any* type and attributes. */ /* Note also that executable arrays aren't executed */ /* at the top level -- they're treated as literals. */ if (!r_has_attr(&token, a_executable) || r_is_array(&token) ) { /* If scan_token used the o-stack, */ /* we know we can do a push now; if not, */ /* the pre-check is still valid. */ iosp++; ref_assign_inline(iosp, &token); goto rt; } store_state(iesp); /* 保存当前执行栈的状态 */ /* Push the file on the e-stack */ if (iesp >= estop) return_with_error_iref(e_execstackoverflow); esfile_set_cache(++iesp); ref_assign_inline(iesp, IREF); /* 保存exec file 状态 */ SET_IREF(&token); /* 读取的token,设置为下一次执行 */ icount = 0; goto top; case e_undefined: /* //name undefined */ scanner_error_object(i_ctx_p, &sstate, &token); return_with_error(code, &token); case scan_EOF: /* end of file */ esfile_clear_cache(); goto bot; case scan_BOS: /* Binary object sequences */ /* ARE executed at the top level. */ store_state(iesp); /* Push the file on the e-stack */ if (iesp >= estop) return_with_error_iref(e_execstackoverflow); esfile_set_cache(++iesp); ref_assign_inline(iesp, IREF); pvalue = &token; goto pr; case scan_Refill: store_state(iesp); /* iref may point into the exec stack; */ /* save its referent now. */ ref_assign_inline(&token, IREF); /* Push the file on the e-stack */ if (iesp >= estop) return_with_error_iref(e_execstackoverflow); ++iesp; ref_assign_inline(iesp, &token); esp = iesp; osp = iosp; code = scan_handle_refill(i_ctx_p, &sstate, true, ztokenexec_continue); scan_cont: iosp = osp; iesp = esp; switch (code) { case 0: iesp--; /* don't push the file */ goto again; /* stacks are unchanged */ case o_push_estack: esfile_clear_cache(); if (--ticks_left > 0) goto up; goto slice; } /* must be an error */ iesp--; /* don't push the file */ return_with_code_iref(); case scan_Comment://注释的处理方法 case scan_DSC_Comment: { /* See scan_Refill above for comments. */ ref file_token; store_state(iesp); ref_assign_inline(&file_token, IREF); if (iesp >= estop) return_with_error_iref(e_execstackoverflow); ++iesp; ref_assign_inline(iesp, &file_token); esp = iesp; osp = iosp; code = ztoken_handle_comment(i_ctx_p, &sstate, &token, code, true, true, ztokenexec_continue); } goto scan_cont; default: /* error */ ref_assign_inline(&token, IREF); scanner_error_object(i_ctx_p, &sstate, &token); return_with_error(code, &token); } } case exec(t_string): { /* Executable string. Read a token and interpret it. */ stream ss; scanner_state sstate; s_init(&ss, NULL); sread_string(&ss, IREF->value.bytes, r_size(IREF)); scanner_init_stream_options(&sstate, &ss, SCAN_FROM_STRING); osp = iosp; /* scan_token uses ostack */ code = scan_token(i_ctx_p, &token, &sstate); iosp = osp; /* ditto */ switch (code) { case 0: /* read a token */ case scan_BOS: /* binary object sequence */ store_state(iesp); /* If the updated string isn't empty, push it back */ /* on the e-stack. */ { uint size = sbufavailable(&ss); if (size) { if (iesp >= estop) return_with_error_iref(e_execstackoverflow); ++iesp; iesp->tas.type_attrs = IREF->tas.type_attrs; iesp->value.const_bytes = sbufptr(&ss); r_set_size(iesp, size); } } if (code == 0) { SET_IREF(&token); icount = 0; goto top; } /* Handle BOS specially */ pvalue = &token; goto pr; case scan_EOF: /* end of string */ goto bot; case scan_Refill: /* error */ code = gs_note_error(e_syntaxerror); default: /* error */ ref_assign_inline(&token, IREF); scanner_error_object(i_ctx_p, &sstate, &token); return_with_error(code, &token); } } /* Handle packed arrays here by re-dispatching. */ /* This also picks up some anomalous cases of non-packed arrays. */ default: { /* packed_array数组处理: 00tttttt exrwsfnm full-size ref 010mjjjj jjjjjjjj executable operator (so bind can work) 011mvvvv vvvvvvvv integer (biased by packed_min_intval) 100m---- -------- (not used) 101m---- -------- (not used) 110miiii iiiiiiii literal name 111miiii iiiiiiii executable name 下面的*iref_packed >> r_packed_type_shift, r_packed_type_shift为 13, 向左移13位,留上面3位。 typedef enum { pt_full_ref = 0, #define pt_min_packed 2 pt_executable_operator = 2, pt_integer = 3, pt_unused1 = 4, pt_unused2 = 5, #define pt_min_name 6 pt_literal_name = 6, #define pt_min_exec_name 7 pt_executable_name = 7 } packed_type; int make_packed_array(ref * parr, ref_stack_t * pstack, uint size, gs_dual_memory_t *idmemory, client_name_t cname) { .................... ................................ ..................... for (i = size; i != 0; i--) { pref = ref_stack_index(pstack, i - 1); switch (r_btype(pref)) { case t_name: { uint nidx = name_index(imem, pref); if (nidx >= packed_name_max_index) break; *pdest++ = nidx + (r_has_attr(pref, a_executable) ? pt_tag(pt_executable_name) : pt_tag(pt_literal_name)); } continue; case t_integer: if (pref->value.intval < packed_min_intval || pref->value.intval > packed_max_intval ) break; *pdest++ = pt_tag(pt_integer) + ((short)pref->value.intval - packed_min_intval); continue; case t_oparray: case t_operator: { uint oidx; if (!r_has_attr(pref, a_executable)) break; oidx = op_index(pref); if (oidx == 0 || oidx > packed_int_mask) break; *pdest++ = pt_tag(pt_executable_operator) + oidx; } continue; ............................. } 上面的t_operator等的处理方法,表明为什么要向下面这样处理。 */ uint index; switch (*iref_packed >> r_packed_type_shift) { case pt_full_ref: case pt_full_ref + 1: INCR(p_full); if (iosp >= ostop) return_with_stackoverflow_iref(); /* We know this can't be an executable object */ /* requiring special handling, so we just push it. */ ++iosp; /* We know that refs are properly aligned: */ /* see packed.h for details. */ ref_assign_inline(iosp, IREF); next(); case pt_executable_operator: index = *iref_packed & packed_value_mask; /* packed_value_mask为: 111111111111 */ if (--ticks_left <= 0) { /* The following doesn't work, */ /* and I can't figure out why. */ /****** goto sst_short; ******/ } if (!op_index_is_operator(index)) { INCR(p_exec_oparray); store_state_short(iesp); opindex = index; /* Call the operator procedure. */ index -= op_def_count; pvalue = (const ref *) (index < r_size(&op_array_table_global.table) ? op_array_table_global.table.value.const_refs + index : op_array_table_local.table.value.const_refs + (index - r_size(&op_array_table_global.table))); goto oppr; } INCR(p_exec_operator); /* See the main plain_exec(t_operator) case */ /* for details of what happens here. */ #if PACKED_SPECIAL_OPS /* * We arranged in iinit.c that the special ops * have operator indices starting at 1. * * The (int) cast in the next line is required * because some compilers don't allow arithmetic * involving two different enumerated types. */ # define case_xop(xop) case xop - (int)tx_op + 1 switch (index) { case_xop(tx_op_add):goto x_add; case_xop(tx_op_def):goto x_def; case_xop(tx_op_dup):goto x_dup; case_xop(tx_op_exch):goto x_exch; case_xop(tx_op_if):goto x_if; case_xop(tx_op_ifelse):goto x_ifelse; case_xop(tx_op_index):goto x_index; case_xop(tx_op_pop):goto x_pop; case_xop(tx_op_roll):goto x_roll; case_xop(tx_op_sub):goto x_sub; case 0: /* for dumb compilers */ default: ; } # undef case_xop #endif INCR(p_exec_non_x_operator); esp = iesp; osp = iosp; switch (code = call_operator(op_index_proc(index), i_ctx_p)) { case 0: case 1: iosp = osp; next_short(); case o_push_estack: store_state_short(iesp); goto opush; case o_pop_estack: iosp = osp; if (esp == iesp) { next_short(); } iesp = esp; goto up; case o_reschedule: store_state_short(iesp); goto res; case e_RemapColor: store_state_short(iesp); goto remap; } iosp = osp; iesp = esp; return_with_code_iref(); case pt_integer: INCR(p_integer); if (iosp >= ostop) return_with_stackoverflow_iref(); ++iosp; make_int(iosp, ((int)*iref_packed & packed_int_mask) + packed_min_intval); next_short(); case pt_literal_name: INCR(p_lit_name); { uint nidx = *iref_packed & packed_value_mask; if (iosp >= ostop) return_with_stackoverflow_iref(); ++iosp; name_index_ref_inline(int_nt, nidx, iosp); next_short(); } case pt_executable_name: INCR(p_exec_name); { uint nidx = *iref_packed & packed_value_mask; pvalue = name_index_ptr_inline(int_nt, nidx)->pvalue; if (!pv_valid(pvalue)) { uint htemp; INCR(p_find_name); if ((pvalue = dict_find_name_by_index_inline(nidx, htemp)) == 0) { names_index_ref(int_nt, nidx, &token); return_with_error(e_undefined, &token); } } if (r_has_masked_attrs(pvalue, a_execute, a_execute + a_executable)) { /* Literal, push it. */ INCR(p_name_lit); if (iosp >= ostop) return_with_stackoverflow_iref(); ++iosp; ref_assign_inline(iosp, pvalue); next_short(); } if (r_is_proc(pvalue)) { /* This is an executable procedure, */ /* execute it. */ INCR(p_name_proc); store_state_short(iesp); goto pr; } /* Not a literal or procedure, reinterpret it. */ store_state_short(iesp); icount = 0; SET_IREF(pvalue); goto top; } /* default can't happen here */ } } } /* Literal type, just push it. */ if (iosp >= ostop) return_with_stackoverflow_iref(); ++iosp; ref_assign_inline(iosp, IREF); /* 赋值给操作符数栈 */ bot:next(); out: /* At most 1 more token in the current procedure. */ /* (We already decremented icount.) */ if (!icount) {//icount为0,但前面--icount,也就是从1变成0,说明有一个元素。 /* Pop the execution stack for tail recursion. */ iesp--; iref_packed = IREF_NEXT(iref_packed); goto top; } up:if (--ticks_left < 0) goto slice;//初始化的时候调用 /* See if there is anything left on the execution stack. */ if (!r_is_proc(iesp)) { SET_IREF(iesp--);//非过程的处理方法 icount = 0; goto top; } /*过程的处理方法,ifelse处理方法与此类似。*/ SET_IREF(iesp->value.refs); /* next element of array */ icount = r_size(iesp) - 1; if (icount <= 0) { /* <= 1 more elements */ iesp--; /* pop, or tail recursion */ if (icount < 0) goto up; } goto top; res: /* Some operator has asked for context rescheduling. */ /* We've done a store_state. */ *pi_ctx_p = i_ctx_p; code = (*gs_interp_reschedule_proc)(pi_ctx_p); i_ctx_p = *pi_ctx_p; sched: /* We've just called a scheduling procedure. */ /* The interpreter state is in memory; iref is not current. */ if (code < 0) { set_error(code); /* * We need a real object to return as the error object. * (It only has to last long enough to store in * *perror_object.) */ make_null_proc(&ierror.full); SET_IREF(ierror.obj = &ierror.full); goto error_exit; } /* Reload state information from memory. */ iosp = osp; iesp = esp; goto up; #if 0 /****** ****** ***** */ sst: /* Time-slice, but push the current object first. */ store_state(iesp); if (iesp >= estop) return_with_error_iref(e_execstackoverflow); iesp++; ref_assign_inline(iesp, iref); #endif /****** ****** ***** */ slice: /* It's time to time-slice or garbage collect. */ /* iref is not live, so we don't need to do a store_state. */ osp = iosp; esp = iesp; /* If ticks_left <= -100, we need to GC now. */ if (ticks_left <= -100) { /* We need to garbage collect now. */ *pi_ctx_p = i_ctx_p; code = interp_reclaim(pi_ctx_p, -1); i_ctx_p = *pi_ctx_p; } else if (gs_interp_time_slice_proc) { *pi_ctx_p = i_ctx_p; code = (*gs_interp_time_slice_proc)(pi_ctx_p); i_ctx_p = *pi_ctx_p; } else code = 0; ticks_left = gs_interp_time_slice_ticks; set_code_on_interrupt(imemory, &code); goto sched; /* Error exits. */ rweci: ierror.code = code; rwei: ierror.obj = IREF; rwe: if (!r_is_packed(iref_packed)) store_state(iesp); else { /* * We need a real object to return as the error object. * (It only has to last long enough to store in *perror_object.) */ packed_get(imemory, (const ref_packed *)ierror.obj, &ierror.full); store_state_short(iesp); if (IREF == ierror.obj) SET_IREF(&ierror.full); ierror.obj = &ierror.full; } error_exit: if (ERROR_IS_INTERRUPT(ierror.code)) { /* We must push the current object being interpreted */ /* back on the e-stack so it will be re-executed. */ /* Currently, this is always an executable operator, */ /* but it might be something else someday if we check */ /* for interrupts in the interpreter loop itself. */ if (iesp >= estop) code = e_execstackoverflow; else { iesp++; ref_assign_inline(iesp, IREF); } } esp = iesp; osp = iosp; ref_assign_inline(perror_object, ierror.obj); #ifdef DEBUG if (ierror.code == e_InterpreterExit) { /* Do not call gs_log_error to reduce the noise. */ return e_InterpreterExit; } #endif return gs_log_error(ierror.code, __FILE__, ierror.line); }