binaryen 1.1.6.6 → 1.1.6.8

Sign up to get free protection for your applications and to get access to all the features.
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