Why does ghostscript hang when passing a procedure to a procedure to another procedure to invoke when using the same argument name? - arguments

I am passing a postscript procedure (c) as an argument on the stack to a procedure (a), which then passes this as an argument on the stack to another procedure (b) that invokes the procedure (c).
When I use a dictionary to localize variables and use the same name for the argument in a and b, ghostscript hangs (in the below code, these are procedures a1 and a2 and they both use proc). When I make them different names (in the below code these are procedures a and b which use Aproc and Bproc, respectively), it runs correctly.
Note that I am aware that I can just use the stack to pass down the original argument and avoid the whole exch def step, but in my real world code, I want to capture the stack argument to use locally.
Below is a minimal working example of the issue:
%!PS
/c{
(C START) =
(C output) =
(C END) =
}bind def
/b{
(B START) =
1 dict begin
/Bproc exch def
Bproc
end
(B END) =
}bind def
/a{
(A START) =
1 dict begin
/Aproc exch def
{Aproc} b
end
(A END) =
}bind def
/b2{
(B2 START) =
1 dict begin
/proc exch def
proc
end
(B2 END) =
}bind def
/a2{
(A2 START) =
1 dict begin
/proc exch def
{proc} b2
end
(A2 END) =
}bind def
{c} a
% {c} a2
Here is the output with the above code (different argument names, no issues):
$ gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=test2.pdf test2.ps
GPL Ghostscript 9.54.0 (2021-03-30)
Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
A START
B START
C START
C output
C END
B END
A END
$
Change the last 2 lines of the code to the following (comment out first, uncomment out the second):
...
% {c} a
{c} a2
Now ghostscript just hangs:
$ gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=test2.pdf test2.ps
GPL Ghostscript 9.54.0 (2021-03-30)
Copyright (C) 2021 Artifex Software, Inc. All rights reserved.
This software is supplied under the GNU AGPLv3 and comes with NO WARRANTY:
see the file COPYING for details.
C-c C-c: *** Interrupt
$
Environment: OpenSuse:
$ uname -a
Linux localhost.localdomain 5.18.4-1-default #1 SMP PREEMPT_DYNAMIC Wed Jun 15 06:00:33 UTC 2022 (ed6345d) x86_64 x86_64 x86_64 GNU/Linux
$ cat /etc/os-release
NAME="openSUSE Tumbleweed"
# VERSION="20220619"
ID="opensuse-tumbleweed"
ID_LIKE="opensuse suse"
VERSION_ID="20220619"
PRETTY_NAME="openSUSE Tumbleweed"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:tumbleweed:20220619"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed"
LOGO="distributor-logo-Tumbleweed"

I think you are being tripped up by early and late name binding, which is a subtle point in PostScript and often traps newcomers to the language.
Because of late name binding you can change the definition of a key/value pair during the course of execution, which can have unexpected effects. This is broadly similar to self-modifying code.
See section 3.12 Early Name Binding on page 117 of the 3rd Edition PostScript Language Reference Manual for details, but the basic point is right at the first paragraph:
"Normally, when the PostScript language scanner encounters an executable name in the program being scanned, it simply produces an executable name object; it does not look up the value of the name. It looks up the name only when the name object is executed by the interpreter. The lookup occurs in the dictionaries that are on the dictionary tack at the time of execution."
Also you aren't quite (I think) doing what you think you are with the executable array tokens '{' and '}'. That isn't putting the definition on the stack, exactly, it is defining an executable array on the stack. Not quite the same thing. In particular it means that the content of the array (between { and }) isn't being executed.
If you wanted to put the original function on the stack you would do '/proc load'. For example if you wanted to define a function to be the same as showpage you might do:
/my_showpage /showpage load def
Not:
/my_showpage {showpage} def
So looking at your failing case.
In function a2 You start by creating and opening a new dictionary. Said dictionary is only present on the stack, so if you ever pop it off it will go out of scope and be garbage collected (I'm sure you know this of course).
In that dictionary you create a key '/proc' with the associated value of an executable array. The array contains {c}.
Then you create a new executable array on the stack {proc}
You then execute function b2 with that executable array on the stack.
b2 starts another new dictionary, again on the stack, and then defines a key/value pair '/proc' with the associated value {proc}.
You then execute proc. That executes the array, the only thing in it is a reference to 'proc', so that is looked up starting in the current dictionary.
Oh look! We have a definition in the current dictionary, it's an executable array. So we push that array and execute it. The only thing in it is a reference to 'proc', so we look that up in the current dictionary, and we have one, so we execute that array. Round and round and round we go.....
You can avoid this by simply changing :
/a2{
(A2 START) =
1 dict begin
/proc exch def
{proc} b2
end
(A2 END) =
}bind def
To :
/a2{
(A2 START) =
1 dict begin
/proc exch def
/proc load b2
end
(A2 END) =
}bind def

Related

Debugging <<loop>> error message in haskell

Hello i am encountering this error message in a Haskell program and i do not know where is the loop coming from.There are almost no IO methods so that i can hook myself to them and print the partial result in the terminal.
I start with a file , i read it and then there are only pure methods.How can i debug this ?
Is there a way to attach to methods or create a helper that can do the following:
Having a method method::a->b how can i somehow wrap it in a iomethod::(a->b)->IO (a->b) to be able to test in in GHCI (i want to insert some putStrLn-s etc ?
P.S My data suffer transformations IO a(->b->c->d->......)->IO x and i do not know how to debug the part that is in the parathesis (that is the code that contains the pure methods)
Types and typeclass definitions and implementations
data TCPFile=Rfile (Maybe Readme) | Dfile Samples | Empty
data Header=Header { ftype::Char}
newtype Samples=Samples{values::[Maybe Double]}deriving(Show)
data Readme=Readme{ maxClients::Int, minClients::Int,stepClients::Int,maxDelay::Int,minDelay::Int,stepDelay::Int}deriving(Show)
data FileData=FileData{ header::Header,rawContent::Text}
(>>?)::Maybe a->(a->Maybe b)->Maybe b
(Just t) >>? f=f t
Nothing >>? _=Nothing
class TextEncode a where
fromText::Text-> a
getHeader::TCPFile->Header
getHeader (Rfile _ ) = Header { ftype='r'}
getHeader (Dfile _ )= Header{ftype='d'}
getHeader _ = Header {ftype='e'}
instance Show TCPFile where
show (Rfile t)="Rfile " ++"{"++content++"}" where
content=case t of
Nothing->""
Just c -> show c
show (Dfile c)="Dfile " ++"{"++show c ++ "}"
instance TextEncode Samples where
fromText text=Samples (map (readMaybe.unpack) cols) where
cols=splitOn (pack ",") text
instance TextEncode Readme where
fromText txt =let len= length dat
dat= case len of
6 ->Prelude.take 6 .readData $ txt
_ ->[0,0,0,0,0,0] in
Readme{maxClients=Prelude.head dat,minClients=dat!!1,stepClients=dat!!2,maxDelay=dat!!3,minDelay=dat!!4,stepDelay=dat!!5} where
instance TextEncode TCPFile where
fromText = textToFile
Main
module Main where
import Data.Text(Text,pack,unpack)
import Data.Text.IO(readFile,writeFile)
import TCPFile(TCPFile)
main::IO()
main=do
dat<-readTcpFile "test.txt"
print dat
readTcpFile::FilePath->IO TCPFile
readTcpFile path =fromText <$> Data.Text.IO.readFile path
textToFile::Text->TCPFile
textToFile input=case readHeader input >>? (\h -> Just (FileData h input)) >>? makeFile of
Just r -> r
Nothing ->Empty
readHeader::Text->Maybe Header
readHeader txt=case Data.Text.head txt of
'r' ->Just (Header{ ftype='r'})
'd' ->Just (Header {ftype ='d'})
_ -> Nothing
makeFile::FileData->Maybe TCPFile
makeFile fd= case ftype.header $ fd of
'r'->Just (Rfile (Just (fromText . rawContent $ fd)))
'd'->Just (Dfile (fromText . rawContent $ fd))
_ ->Nothing
readData::Text->[Int]
readData =catMaybes . maybeValues where
maybeValues=mvalues.split.filterText "{}"
#all the methods under this line are used in the above method
mvalues::[Text]->[Maybe Int]
mvalues arr=map (\x->(readMaybe::String->Maybe Int).unpack $ x) arr
split::Text->[Text]
split =splitOn (pack ",")
filterText::[Char]->Text->Text
filterText chars tx=Data.Text.filter (\x -> not (x `elem` chars)) tx
I want first to clean the Text from given characters , in our case }{ then split it by ,.After the text is split by commas i want to parse them, and create either a Rfile which contains 6 integers , either a Dfile (datafile) which contains any given number of integers.
Input
I have a file with the following content: r,1.22,3.45,6.66,5.55,6.33,2.32} and i am running runghc main 2>err.hs
Expected Output : Rfile (Just (Readme 1.22 3.45 6.66 5.55 6.33 2.32))
In the TextEncode Readme instance, len and dat depend on each other:
instance TextEncode Readme where
fromText txt =let len= length dat
dat= case len of
To debug this kind of thing, other than staring at the code, one thing you can do is compile with -prof -fprof-auto -rtsopts, and run your program with the cmd line options +RTS -xc. This should print a trace when the <<loop>> exception is raised (or if the program loops instead, when you kill it (Ctrl+C)). See the GHC manual https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/runtime_control.html#rts-flag--xc
As Li-yao Xia said part of the problem is the infinite recursion, but if you tried the following code, then the problem still remains.
instance TextEncode Readme where
fromText txt =let len= length [1,2,3,4,5,6] --dat
dat= case len of
The second issue is that the file contains decimal numbers but all the conversion function are expecting Maybe Int, changing the definitions of the following functions should give the expected results, on the other hand probably the correct fix is that the file should have integers and not decimal numbers.
readData::Text->[Double]
--readData xs = [1,2,3,4,5,6,6]
readData =catMaybes . maybeValues where
maybeValues = mvalues . split . filterText "{}"
--all the methods under this line are used in the above method
mvalues::[Text]->[Maybe Double]
mvalues arr=map (\x->(readMaybe::String->Maybe Double).unpack $ x) arr
data Readme=Readme{ maxClients::Double, minClients::Double,stepClients::Double,maxDelay::Double,minDelay::Double,stepDelay::Double}deriving(Show)

Julia: How to copy data to another processor in Julia

How do you move data from one processor to another in julia?
Say I have an array
a = [1:10]
Or some other data structure. What is the proper way to put it on all other available processors so that it will be available on those processors as the same variable name?
I didn't know how to do this at first, so I spent some time figuring it out.
Here are some functions I wrote to pass objects:
sendto
Send an arbitrary number of variables to specified processes.
New variables are created in the Main module on specified processes. The
name will be the key of the keyword argument and the value will be the
associated value.
function sendto(p::Int; args...)
for (nm, val) in args
#spawnat(p, eval(Main, Expr(:(=), nm, val)))
end
end
function sendto(ps::Vector{Int}; args...)
for p in ps
sendto(p; args...)
end
end
Examples
# creates an integer x and Matrix y on processes 1 and 2
sendto([1, 2], x=100, y=rand(2, 3))
# create a variable here, then send it everywhere else
z = randn(10, 10); sendto(workers(), z=z)
getfrom
Retrieve an object defined in an arbitrary module on an arbitrary
process. Defaults to the Main module.
The name of the object to be retrieved should be a symbol.
getfrom(p::Int, nm::Symbol; mod=Main) = fetch(#spawnat(p, getfield(mod, nm)))
Examples
# get an object from named x from Main module on process 2. Name it x
x = getfrom(2, :x)
passobj
Pass an arbitrary number of objects from one process to arbitrary
processes. The variable must be defined in the from_mod module of the
src process and will be copied under the same name to the to_mod
module on each target process.
function passobj(src::Int, target::Vector{Int}, nm::Symbol;
from_mod=Main, to_mod=Main)
r = RemoteRef(src)
#spawnat(src, put!(r, getfield(from_mod, nm)))
for to in target
#spawnat(to, eval(to_mod, Expr(:(=), nm, fetch(r))))
end
nothing
end
function passobj(src::Int, target::Int, nm::Symbol; from_mod=Main, to_mod=Main)
passobj(src, [target], nm; from_mod=from_mod, to_mod=to_mod)
end
function passobj(src::Int, target, nms::Vector{Symbol};
from_mod=Main, to_mod=Main)
for nm in nms
passobj(src, target, nm; from_mod=from_mod, to_mod=to_mod)
end
end
Examples
# pass variable named x from process 2 to all other processes
passobj(2, filter(x->x!=2, procs()), :x)
# pass variables t, u, v from process 3 to process 1
passobj(3, 1, [:t, :u, :v])
# Pass a variable from the `Foo` module on process 1 to Main on workers
passobj(1, workers(), [:foo]; from_mod=Foo)
use #eval #everywhere... and escape the local variable. like this:
julia> a=collect(1:3)
3-element Array{Int64,1}:
1
2
3
julia> addprocs(1)
1-element Array{Int64,1}:
2
julia> #eval #everywhere a=$a
julia> #fetchfrom 2 a
3-element Array{Int64,1}:
1
2
3
Just so everyone here knows, I put these ideas together into a package ParallelDataTransfer.jl for this. So you just need to do
using ParallelDataTransfer
(after installing) in order to use the functions mentioned in the answers here. Why? These functions are pretty useful! I added some testing, some new macros, and updated them a bit (they pass on v0.5, fail on v0.4.x). Feel free to put in pull requests to edit these and add more.
To supplement #spencerlyon2 's answer here are some macros:
function sendtosimple(p::Int, nm, val)
ref = #spawnat(p, eval(Main, Expr(:(=), nm, val)))
end
macro sendto(p, nm, val)
return :( sendtosimple($p, $nm, $val) )
end
macro broadcast(nm, val)
quote
#sync for p in workers()
#async sendtosimple(p, $nm, $val)
end
end
end
The #spawnat macro binds a value to a symbol on a particular process
julia> #sendto 2 :bip pi/3
RemoteRef{Channel{Any}}(9,1,5340)
julia> #fetchfrom 2 bip
1.0471975511965976
The #broadcast macro binds a value to a symbol in all processes except 1 (as I found doing so made future expressions using the name copy the version from process 1)
julia> #broadcast :bozo 5
julia> #fetchfrom 2 bozo
5
julia> bozo
ERROR: UndefVarError: bozo not defined
julia> bozo = 3 #these three lines are why I exclude pid 1
3
julia> #fetchfrom 7 bozo
3
julia> #fetchfrom 7 Main.bozo
5

'Segmentation Fault' while debugging expect script

I am debugging an expect program with the traditional debugging way by passing the -D 1 flag for the following script.
#!/usr/bin/expect
proc p3 {} {
set m 0
}
proc p2 {} {
set c 4
p3
set d 5
}
proc p1 {} {
set a 2
p2
set a 5
}
p1
With the debugger command w, I am trying to see the stack frame and got the following error.
dinesh#mypc:~/pgms/expect$ expect -D 1 stack.exp
1: proc p3 {} {
set m 0
}
dbg1.0> n
1: proc p2 {} {
set c 4
p3
set d 5
}
dbg1.1>
1: proc p1 {} {
set a 2
p2
set a 5
}
dbg1.2>
1: p1
dbg1.3> s
2: set a 2
dbg2.4>
2: p2
dbg2.5>
3: set c 4
dbg3.6> w
0: expect {-D} {1} {stack.exp}
Segmentation fault (core dumped)
dinesh#mypc:~/pgms/expect$
I am having the expect version 5.45.
Is there anything wrong in my way of command execution ?
In order to achieve the debugging trace, Expect pokes its fingers inside the implementation of Tcl. In particular, it has copies of the definitions of some of the internal structures used inside Tcl (e.g., the definition of the implementation of procedures and of stack frames). However, these structures change from time to time; we don't announce such internal implementation changes, as they shouldn't have any bearing on any other code, but that's obviously not the case.
Overall, this is a bug in Expect (and it might be that the fix is for a new C API function to be added to Tcl). In order to see about fixing this, we need to know not just the exact version of Expect but also the exact version of Tcl (use info patchlevel to get this).

Why is this unused string not garbage collected?

Why does unused_variable_2 and unused_variable_3 get garbage collected, but not unused_variable_1?
# leaky_boat.rb
require "memprof"
class Boat
def initialize(string)
unused_variable1 = string[0...100]
puts unused_variable1.object_id
#string = string
puts #string.object_id
end
end
class Rocket
def initialize(string)
unused_variable_2 = string.dup
puts unused_variable_2.object_id
unused_variable_3 = String.new(string)
puts unused_variable_3.object_id
#string = string
puts #string.object_id
end
end
Memprof.start
text = "a" * 100
object_id_message = "Object ids of unused_variable_1, #string, unused_variable_2, unused_variable_3, and another #string"
before_gc_message = "Before GC"
after_gc_message = "After GC"
puts object_id_message
boat = Boat.new(text)
rocket = Rocket.new(text)
puts before_gc_message
Memprof.stats
ObjectSpace.garbage_collect
puts after_gc_message
Memprof.stats
Memprof.stop
Running the program:
$ uname -a
Linux [redacted] 3.2.0-25-generic #40-Ubuntu SMP Wed May 23 20:30:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
$ ruby --version # Have to use Ruby 1.8 - memprof doesn't work on 1.9
ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux]
$ ruby -rubygems leaky_boat.rb
Object ids of unused_variable_1, #string, unused_variable_2, unused_variable_3, and another #string
70178323299180
70178323299320
70178323299100
70178323299060
70178323299320
Before GC
2 leaky_boat.rb:6:String
2 leaky_boat.rb:26:String
1 leaky_boat.rb:9:String
1 leaky_boat.rb:7:String
1 leaky_boat.rb:32:Rocket
1 leaky_boat.rb:31:Boat
1 leaky_boat.rb:29:String
1 leaky_boat.rb:28:String
1 leaky_boat.rb:27:String
1 leaky_boat.rb:20:String
1 leaky_boat.rb:18:String
1 leaky_boat.rb:17:String
1 leaky_boat.rb:16:String
1 leaky_boat.rb:15:String
After GC
1 leaky_boat.rb:6:String
1 leaky_boat.rb:32:Rocket
1 leaky_boat.rb:31:Boat
1 leaky_boat.rb:29:String
1 leaky_boat.rb:28:String
1 leaky_boat.rb:27:String
1 leaky_boat.rb:26:String
This behavior is because the string implementation of your version of ruby for substr has a special case to save memory allocations when you take a substr that is the tail of the source string and the string length is large enough to not store the string value in the base object structure.
If you trace the code, you see the range subscript string[0...100] will go through this clause in rb_str_substr. So the new string will be allocated via str_new3 which allocates a new object struct (hence the differing object_id), but sets the string value ptr field as a pointer into the source object's extended storage and sets the ELTS_SHARED flag to indicate that the new object shares storage with another object.
In your code you take this new substring object and assign it to instance var #string which is still a live reference when you run garbage collection. Since there's a live reference to the allocated storage of the original string, it can't be collected.
In ruby trunk, this optimization to share storage on compatible tail substrings appears to still exist.
The two other vars unused_variable_2 and unused_variable_3 don't have this extended storage sharing issue because they're set via mechanisms that assure distinct storage, so they get garbage collected as expected when their references pass out of scope.
String#dup runs rb_str_replace (via initialize_copy binding) which replaces the contents of the source string with a copy of the contents of the source string and assures that the storage is not shared.
String#new(source_str) runs through rb_str_init which similarly assures distinct storage with rb_str_replace on the supplied initial value.

Cucumber and variables internal to methods called indirectly

Please note: I am new to TDD & cucumber, so the answer may be very easy.
I am creating a basic image editor for a test (the image is just a sequence of letters).
I have written a Cucumber story:
Scenario Outline: edit commands
Given I start the editor
And a 3 x 3 image is created
When I type the command <command>
Then the image should look like <image>
The step
Scenarios: colour single pixel
| command | image |
| L 1 2 C | OOOCOOOOO |
always fails, returning
expected: "OOOCOOOOO"
got: " OOOOOOOO" (using ==) (RSpec::Expectations::ExpectationNotMetError)
This is the step code:
When /^I type the command (.*)$/ do |command|
#editor.exec_cmd(command).should be
end
The function exec_cmd in the program recognizes the command and launches the appropriate action. In this case it will launch the following
def colorize_pixel(x, y, color)
if !#image.nil?
x = x.to_i
y = y.to_i
pos = (y - 1) * #image[:columns] + x
#image[:content].insert(pos, color).slice!(pos - 1)
else
#messenger.puts "There's no image. Create one first!"
end
end
However, this always fails unless I hardcode the values of the two local variables (pos and color) in the function in the program itself.
Why? It doesn's seem I'm doing anything wrong in the program itself: the function does what it's supposed to do and those two variables are only useful locally. So I'd think this is a problem with my use of cucumber. How do I properly test this?
---edit---
def exec_cmd(cmd = nil)
if !cmd.nil?
case cmd.split.first
when "I" then create_image(cmd[1], cmd[2])
when "S" then show_image
when "C" then clear_table
when "L" then colorize_pixel(cmd[1], cmd[2], cmd[3])
else
#messenger.puts "Incorrect command. " + "Commands available: I C L V H F S X."
end
else
#messenger.puts "Please enter a command."
end
end
When /^I type the command (.*)$/ do |command|
#output = #editor.exec_cmd(command)
end
Then /^the image should look like (.)*$/ do |expected_image|
#output.should == expected_image
end
Hope this may help you.
It's not a cucumber issue.
The problem was that, in exec_cmd, split was called only in the "case" clause, not in the "when"s. This meant that, since the command's format was "a 1 2 b", cmd[1] in the "when" would call the second character of the string, a space, not the second value of the array, and the other functions would convert that to_i, returning 0.
I changed exec_cmd like this:
def exec_cmd(cmd = nil)
if !cmd.nil?
cmd = cmd.split
case cmd.first
when "I" then create_image(cmd[1], cmd[2])
[...]
end
which fixed the issue.

Resources