嵌套传递PostScript过程时同名参数导致Ghostscript挂起问题
问题根因
这不是Ghostscript的故障,是代码逻辑触发了无限递归,本质是PostScript作为动态作用域语言采用执行时名字动态查找规则,加上未绑定过程的延迟求值特性,刚好和你两层过程使用同名参数的写法冲突导致的。
先拆解正常运行的a/b版本执行逻辑:
- 执行
{c} a时,栈顶为过程c,进入a后创建局部字典,将栈顶的c绑定到局部名Aproc - 构造未绑定的过程对象
{Aproc}压入栈,作为参数传给b - 进入
b后创建局部字典,将栈顶的{Aproc}绑定到局部名Bproc,随后执行Bproc - 执行
{Aproc}时需要查找名字Aproc:当前字典栈最顶端是b的局部字典,其中仅定义了Bproc,不存在Aproc,因此顺着字典栈向下查找,命中a局部字典中Aproc绑定的c过程,正常执行c,流程顺利结束。
再看挂起的a2/b2版本,问题就出在同名参数:
- 执行
{c} a2时,栈顶为过程c,进入a2后创建局部字典,将栈顶的c绑定到局部名proc - 构造未绑定的过程对象
{proc}压入栈,作为参数传给b2 - 进入
b2后创建局部字典,将栈顶传入的{proc}过程对象,绑定到了b2自己的局部名proc上——这是触发死循环的核心节点 - 随后执行
proc,首先在b2的局部字典查找,命中的就是刚绑定的{proc}过程对象,于是开始执行这个过程 - 执行
{proc}时又需要查找名字proc,此时字典栈最顶端仍是b2的局部字典,找到的proc还是这个{proc}过程本身,于是反复执行“查找proc→执行proc→查找proc”的逻辑,形成无限递归,Ghostscript会一直占用CPU无响应,直到被手动中断。
可落地方案
所有方案都保留了你通过exch def捕获栈参数作为局部变量的写法,可根据业务需求任选:
- 直接传值不做延迟包装:如果不需要延迟执行传入的过程,直接把局部变量绑定的值压栈传参即可,将
a2中的{proc} b2改为proc b2。此时b2收到的栈参数直接就是c过程,绑定到自己的局部proc后执行,会直接运行c,不会触发递归。 - 给包装过程加bind固化引用:如果确实需要包装过程实现延迟执行/闭包效果,将
{proc} b2改为{proc} bind b2。bind操作会在构造过程时,直接把过程内的proc名字替换为当前字典栈中查到的proc的直接引用(也就是a2局部绑定的c),后续哪怕b2中有同名的proc定义,执行这个包装过程时也不会再做动态名字查找,直接运行c。 - 规避同名(不推荐):保证内层过程的局部参数名,和传入的包装过程里引用的自由变量不重名即可正常运行。但这种写法维护成本极高,后续修改参数名时很容易再次触发同类问题,业务代码里不要用。
内容的提问来源于stack exchange,提问作者ggg
相关产品推荐
相关产品推荐

