binaryen 1.1.6.6 → 1.1.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44bf5b373ce186bdee3006d16e8129bde2be3e952c1211d3ee550fb529cffbfe
4
- data.tar.gz: c3ef7a60460cdc937e51b538ef4e6d94ebfba2167ff14821292881a9b91d6f1f
3
+ metadata.gz: 4217d4168d453c8e5316e9c66929d5e09057ec356dc6f80802c41521de6fd612
4
+ data.tar.gz: 2cb4b068c0456d72dcc55cf0d945a5357df0d3ad8c82166fca8a57dcd2908ec4
5
5
  SHA512:
6
- metadata.gz: 290897e4df9079e57df51b4b684fa5ae496ea414c44d801ed8d2be5c167d5347812533511ee26b8f59520d645df1072df5c6368d218ab824d89b38e07373e7b5
7
- data.tar.gz: c30ed5adcb899a8d938d211b3aa3e98e875ec061f19054efd5d7a803b9be0392f9215cbac61283147841e23171ead48391f06695aa06841a64a6be05d5ba57aa
6
+ metadata.gz: 9e3fd947316b466a8d592014bc57b1c34148da5b3042e0630b2bc5f56e54c287ab4c6416e78c1ef392e88429cbbcba0c0c3549bdd8b99d7068972c1adeb0c698
7
+ data.tar.gz: 2cdfe8e57ab692f16e911e7a819cb6cfecbf9b31027a04c8863eb919fd952cb6d92832d3884768cf05704507c7c408bcfb19eed3c47b90dfcbb6f1e7eaefc679
@@ -1,131 +1,108 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "English"
4
- require "shellwords"
5
- require "timeout"
6
3
  require "posix/spawn"
4
+ require "timeout"
5
+ require "tempfile"
7
6
 
8
7
  module Binaryen
9
- # Wrapper around a binaryen executable command with a timeout and streaming IO.
10
- #
11
- # @example Running wasm-opt
12
- #
13
- # ```ruby
14
- # command = Binaryen::Command.new("wasm-opt", timeout: 10)
15
- # optimized_wasm = command.run("-O4", stdin: "(module)")
16
- # ```
17
8
  class Command
18
9
  include POSIX::Spawn
19
-
10
+ DEFAULT_MAX_OUTPUT_SIZE = 256 * 1024 * 1024
11
+ DEFAULT_TIMEOUT = 10
20
12
  DEFAULT_ARGS_FOR_COMMAND = {
21
13
  "wasm-opt" => ["--output=-"],
22
14
  }.freeze
23
15
 
24
- class TimeoutChecker
25
- def initialize(end_time:, pid:)
26
- @end_time = end_time
27
- @pid = pid
28
- end
29
-
30
- def check!
31
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
32
- if now >= @end_time
33
- Process.kill("TERM", @pid)
34
- raise Timeout::Error, "Command timed out"
35
- end
36
- remaining_time = @end_time - now
37
- remaining_time
38
- end
39
- end
40
-
41
- def initialize(cmd, timeout: 10, ignore_missing: false)
16
+ def initialize(cmd, timeout: DEFAULT_TIMEOUT, max_output_size: DEFAULT_MAX_OUTPUT_SIZE, ignore_missing: false)
42
17
  @cmd = command_path(cmd, ignore_missing) || raise(ArgumentError, "command not found: #{cmd}")
43
18
  @timeout = timeout
44
19
  @default_args = DEFAULT_ARGS_FOR_COMMAND.fetch(cmd, [])
20
+ @max_output_size = max_output_size
45
21
  end
46
22
 
47
23
  def run(*arguments, stdin: nil, stderr: nil)
48
- end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
49
- command = build_command(*arguments)
50
- pid, iwr, ord, erd = popen4(*command)
51
- timeout_checker = TimeoutChecker.new(end_time: end_time, pid: pid)
52
-
53
- write_to_pipe(iwr, stdin, timeout_checker, pid) if stdin
24
+ args = [@cmd] + arguments + @default_args
54
25
 
55
- if stderr
56
- err_output = read_from_pipe(erd, timeout_checker)
57
- write_to_pipe(stderr, err_output, timeout_checker, pid, close_write: false)
26
+ if stdin
27
+ with_stdin_tempfile(stdin) { |path| spawn_command(*args, path, stderr: stderr) }
28
+ else
29
+ spawn_command(*args, stderr: stderr)
58
30
  end
59
- output = read_from_pipe(ord, timeout_checker)
60
- wait_or_kill(pid, timeout_checker)
61
-
62
- output
63
31
  end
64
32
 
65
33
  private
66
34
 
67
- def write_to_pipe(pipe, stdin, timeout_checker, pid, close_write: true)
68
- offset = 0
69
- length = stdin.bytesize
70
-
71
- while offset < length
72
- remaining_time = timeout_checker.check!
73
-
74
- next unless IO.select(nil, [pipe], nil, remaining_time)
75
-
76
- begin
77
- written = pipe.write_nonblock(stdin.byteslice(offset, length), exception: false)
78
- offset += written if written.is_a?(Integer)
79
- rescue Errno::EPIPE
80
- wait_or_kill(pid, timeout_checker, pid)
81
- end
35
+ def with_stdin_tempfile(content)
36
+ Tempfile.open("binaryen-stdin") do |f|
37
+ f.binmode
38
+ f.write(content)
39
+ f.close
40
+ yield f.path
82
41
  end
83
- ensure
84
- pipe.close_write if close_write
85
42
  end
86
43
 
87
- def read_from_pipe(pipe, timeout_checker, close_read: true)
88
- output = +""
89
-
90
- while (result = pipe.read_nonblock(8192, exception: false))
91
- remaining_time = timeout_checker.check!
92
-
93
- case result
94
- when :wait_readable
95
- IO.select([pipe], nil, nil, remaining_time)
96
- when nil
97
- break
98
- else
99
- output << result
100
- end
101
- end
102
-
103
- output
104
- ensure
105
- pipe.close_read if close_read
44
+ def command_path(cmd, ignore_missing)
45
+ Dir[File.join(Binaryen.bindir, cmd)].first || (ignore_missing && cmd)
106
46
  end
107
47
 
108
- def wait_or_kill(pid, timeout_checker)
109
- loop do
110
- timeout_checker.check!
48
+ def spawn_command(*args, stderr: nil)
49
+ out = "".b
50
+ data_buffer = "".b
51
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
52
+ pid, stdin, stdout, stderr_stream = popen4(*args)
53
+ stdin.close
54
+ @pid = pid
55
+ readers = [stdout, stderr_stream]
56
+
57
+ while readers.any?
58
+ elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
59
+ remaining_time = @timeout - elapsed_time
60
+ ready = IO.select(readers, nil, nil, remaining_time)
61
+ raise Timeout::Error, "command timed out after #{@timeout} seconds" if ready.nil?
62
+
63
+ ready[0].each do |io|
64
+ max_amount_to_read = @max_output_size - out.bytesize + 1
65
+ data = io.read_nonblock(max_amount_to_read, data_buffer, exception: false)
66
+
67
+ if data == :wait_readable
68
+ # If the IO object is not ready for reading, read_nonblock returns :wait_readable.
69
+ # This isn't an error, but a notification.
70
+ next
71
+ elsif data.nil?
72
+ # At EOF, read_nonblock returns nil instead of raising EOFError.
73
+ readers.delete(io)
74
+ elsif io == stdout
75
+ out << data_buffer
76
+ elsif io == stderr_stream && stderr
77
+ stderr << data_buffer
78
+ end
79
+ rescue Errno::EPIPE, Errno::EINTR
80
+ # Handle EPIPE and EINTR errors
81
+ readers.delete(io)
82
+ end
111
83
 
112
- if (_, status = Process.wait2(pid, Process::WNOHANG))
113
- raise Binaryen::NonZeroExitStatus,
114
- "command exited with status #{status.exitstatus}" if status.exitstatus != 0
84
+ if out.bytesize > @max_output_size
85
+ Process.kill("TERM", @pid)
86
+ raise Binaryen::MaximumOutputExceeded, "maximum output size exceeded (#{@max_output_size} bytes)"
87
+ end
115
88
 
116
- return true
117
- else
118
- sleep(0.1)
89
+ if remaining_time < 0
90
+ Process.kill("TERM", @pid)
91
+ raise Timeout::Error, "command timed out after #{@timeout} seconds"
119
92
  end
120
93
  end
121
- end
122
94
 
123
- def build_command(*arguments)
124
- [@cmd] + arguments + @default_args
125
- end
95
+ _, status = Process.waitpid2(pid, Process::WUNTRACED)
126
96
 
127
- def command_path(cmd, ignore_missing)
128
- Dir[File.join(Binaryen.bindir, cmd)].first || (ignore_missing && cmd)
97
+ raise Binaryen::NonZeroExitStatus, "command exited with status #{status.exitstatus}" unless status.success?
98
+
99
+ out
100
+ ensure
101
+ [stdin, stdout, stderr_stream].each do |io|
102
+ io.close
103
+ rescue
104
+ nil
105
+ end
129
106
  end
130
107
  end
131
108
  end
@@ -3,4 +3,5 @@
3
3
  module Binaryen
4
4
  class Error < StandardError; end
5
5
  class NonZeroExitStatus < Error; end
6
+ class MaximumOutputExceeded < Error; end
6
7
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Binaryen
4
- VERSION = "1.1.6.6"
4
+ VERSION = "1.1.6.8"
5
5
  BINARYEN_VERSION = "version_116"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: binaryen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.6.6
4
+ version: 1.1.6.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-18 00:00:00.000000000 Z
11
+ date: 2023-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: posix-spawn