haplo 2.5.2-java → 2.5.6-java

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: f505d46f7f3766ebd5d2504fd378696ff474a7548c92aec99e1e7f9203fb10ff
4
- data.tar.gz: ff8150dcb45f36c3063aa2dada681143400a35b4394ced5d3538d17fa216515a
3
+ metadata.gz: d3ac1f799b715528ab010c1721a1e2639291b8bd9fccc3a14991fddea9fabcc1
4
+ data.tar.gz: 237162280dc08152e3caffb7d343a70e8daea50e31cd2dc85ea202180ce32427
5
5
  SHA512:
6
- metadata.gz: 1fd5e45d04bd5b63c3fdd3edeb1f7bec265bdd8b1d17ef33588baa6d58d8bac58e19689f1f448f76d8f977bd2399d0aff592da49f81f785e5c8d97ba3b4f6a8d
7
- data.tar.gz: 0aaa4154cea48097385dfd9e93f229284fd62736ccd35f9220e54b8f6eb944c5d27f6535bc508df86fb4fb5b9ed272f9c1bfd0730ca7956d82f7270fe43ace6b
6
+ metadata.gz: 22d5a789c3dbf38036d97dc8a55b07cf92272a7f5adfd86dec50bcf24321d857212efc2e810b27822b8607695562b90ba46b90d37ef9cfc38c09e690a092d72e
7
+ data.tar.gz: cc022ec4afd70fdee1d5f27f253e3b83a8e67ee63c6987b0689220262cb2dd4536dec705bff9bb76a0ed5643e1152210be456ecbe734273e9f829f6546048f16
data/haplo.gemspec CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  files = Dir.glob("#{root_dir}/**/*.*").map { |x| x[root_dir.length + 1, x.length]}
4
4
 
5
5
  s.name = 'haplo'
6
- s.version = '2.5.2'
7
- s.date = '2021-05-14'
6
+ s.version = '2.5.6'
7
+ s.date = '2021-08-25'
8
8
  s.summary = "Haplo Plugin Tool"
9
9
  s.description = "Development tools for developing Haplo plugins, see https://haplo.org"
10
10
  s.licenses = ["MPL-2.0"]
data/lib/debuggers.rb ADDED
@@ -0,0 +1,263 @@
1
+ # Haplo Plugin Tool http://docs.haplo.org/dev/tool/plugin
2
+ # (c) Haplo Services Ltd 2006 - 2016 http://www.haplo-services.com
3
+ # This Source Code Form is subject to the terms of the Mozilla Public
4
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
5
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
+
7
+
8
+ module PluginTool
9
+
10
+ @@profile_output = nil
11
+
12
+ def self.request_profile(options)
13
+ output_file = $stdout
14
+ close_output_file = false
15
+ if options.profile_file
16
+ output_file = File.open(options.profile_file, "a")
17
+ close_output_file = true
18
+ end
19
+
20
+ formatter = nil
21
+ case options.profile_format || 'tree'
22
+ when 'tree'
23
+ @@profile_output = ProfilerFormatterTree.new(output_file)
24
+ when 'raw'
25
+ @@profile_output = ProfilerFormatterRaw.new(output_file)
26
+ else
27
+ puts "Unknown profiler format: #{options.profile_format}"
28
+ exit 1
29
+ end
30
+
31
+ if 'OK' == PluginTool.post("/api/development-plugin-loader/debugger-profile-start", {:min => options.profile})
32
+ puts "JavaScript profiler started."
33
+ else
34
+ puts "Error starting JavaScript profiler."
35
+ exit 1
36
+ end
37
+ at_exit do
38
+ puts
39
+ puts "Disabling JavaScript profiler..."
40
+ PluginTool.post("/api/development-plugin-loader/debugger-profile-stop")
41
+ puts "JavaScript profiler disabled."
42
+ output_file.close if close_output_file
43
+ end
44
+ end
45
+
46
+ class ProfilerFormatter
47
+ def initialize(output_file)
48
+ @output_file = output_file
49
+ end
50
+ def format(report)
51
+ _format(report, @output_file)
52
+ @output_file.flush
53
+ end
54
+ end
55
+
56
+ class ProfilerFormatterRaw < ProfilerFormatter
57
+ def _format(report, output_file)
58
+ output_file.write(report)
59
+ end
60
+ end
61
+
62
+ class ProfilerFormatterTree < ProfilerFormatter
63
+ def _format(report, output_file)
64
+ report.split("\n").each do |line|
65
+ depth, time, percent, count, position = line.split("\t")
66
+ if depth == 'REPORT'
67
+ output_file.write("PROFILE -- #{Time.new(time)}\n")
68
+ elsif depth == 'OMIT'
69
+ output_file.write((" "*time.to_i)+"... children omitted\n")
70
+ else
71
+ output_file.write((" "*depth.to_i)+"#{percent} #{count} #{time.to_i / 1000000} #{position}\n")
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ def self.profiler_handle_report(report)
78
+ if @@profile_output
79
+ @@profile_output.format(report)
80
+ end
81
+ end
82
+
83
+ # -------------------------------------------------------------------------
84
+
85
+ def self.request_coverage(options)
86
+
87
+ format = options.coverage_format || 'raw'
88
+ if format != 'raw'
89
+ puts "Unknown coverage format: #{format}"
90
+ exit 1
91
+ end
92
+
93
+ if 'OK' == PluginTool.post("/api/development-plugin-loader/debugger-coverage-start")
94
+ puts "Coverage capture started."
95
+ else
96
+ puts "Error starting coverage capture."
97
+ exit 1
98
+ end
99
+
100
+ at_exit do
101
+ coverage = PluginTool.post("/api/development-plugin-loader/debugger-coverage-stop")
102
+ # TODO: Check errors
103
+ File.open(options.coverage_file, "w") { |f| f.write coverage }
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+
110
+ # -------------------------------------------------------------------------
111
+
112
+ module DebugAdapterProtocolTunnel
113
+
114
+ @@dap_plugins = nil
115
+ @@dap_debugger_option = nil
116
+ @@dap_server = nil
117
+ @@dap_connection = nil
118
+
119
+ def self.prepare(plugins, options)
120
+ @@dap_plugins = plugins
121
+ @@dap_debugger_option = options.debugger
122
+ raise "BAD DEBUGGER OPTION #{@@dap_debugger_option}" unless @@dap_debugger_option =~ /\A(\d+)\z/
123
+ @@dap_server = TCPServer.new("127.0.0.1", @@dap_debugger_option.to_i)
124
+ Thread.new do
125
+ while true
126
+ connection = @@dap_server.accept
127
+ if connection
128
+ if @@dap_connection
129
+ connection.close
130
+ else
131
+ @@dap_connection = DAPConnection.new(connection, @@dap_plugins)
132
+ Thread.new do
133
+ @@dap_connection.run
134
+ _stop_remote_debugger()
135
+ @@dap_connection = nil
136
+ end
137
+ end
138
+ end
139
+ end
140
+ rescue => e
141
+ # ignore
142
+ end
143
+ at_exit do
144
+ @@dap_server.close
145
+ @@dap_connection.close if @@dap_connection
146
+ _stop_remote_debugger()
147
+ end
148
+ end
149
+
150
+ def self._stop_remote_debugger
151
+ puts "DEBUGGER: Stopping remote debugger..."
152
+ result = PluginTool.post("/api/development-plugin-loader/debugger-dap-stop")
153
+ if result == 'OK'
154
+ puts "DEBUGGER: Remote debugger stopped."
155
+ else
156
+ puts "DEBUGGER: Error stopping remote debugger, application server may be in non-functioning state."
157
+ end
158
+ end
159
+
160
+ def self.log_message_from_server(text)
161
+ if @@dap_connection
162
+ @@dap_connection._write({
163
+ 'type' => 'event',
164
+ 'event' => 'output',
165
+ 'body' => {
166
+ 'category' => 'console',
167
+ 'output' => text+"\n"
168
+ }
169
+ })
170
+ end
171
+ end
172
+
173
+ def self.dap_message_from_server(json)
174
+ if @@dap_connection
175
+ @@dap_connection._write(JSON.parse(json))
176
+ end
177
+ end
178
+
179
+ class DAPConnection
180
+ def initialize(connection, plugins)
181
+ @connection = connection
182
+ @plugins = plugins
183
+ @running = false
184
+ @next_seq = 1
185
+ @write_mutex = Mutex.new
186
+ @_dump_messages = (ENV['HAPLO_DAP_DEBUG'] == '1')
187
+ end
188
+
189
+ def close
190
+ begin
191
+ @connection.close
192
+ rescue => e
193
+ # ignore any errors
194
+ end
195
+ end
196
+
197
+ def run
198
+ begin
199
+ run2
200
+ rescue => e
201
+ puts "DEBUGGER: Local connection closed"
202
+ end
203
+ self.close
204
+ end
205
+
206
+ def run2
207
+ @running = true
208
+ have_initialized = false
209
+ while @running
210
+ header = @connection.readline
211
+ blank = @connection.readline
212
+ if header =~ /\Acontent-length:\s+(\d+)\r\n\z/i && blank == "\r\n"
213
+ body = @connection.read($1.to_i)
214
+ message = JSON.parse(body)
215
+ puts "DAP READ: #{JSON.pretty_generate(message)}\n" if @_dump_messages
216
+ unless have_initialized
217
+ if message['type'] == 'request' && message['command'] == 'initialize'
218
+ puts "DEBUGGER: Local connection from #{message['arguments']['clientName']} (#{message['arguments']['clientID']})\nDEBUGGER: Starting remote debugger..."
219
+ plugin_locations = {}
220
+ @plugins.each { |p| plugin_locations[p.name] = p.plugin_dir }
221
+ start_response = PluginTool.post("/api/development-plugin-loader/debugger-dap-start", {
222
+ :plugin_locations => JSON.generate(plugin_locations)
223
+ })
224
+ unless start_response =~ /\ATOKEN: (.+)\z/
225
+ raise "Remote debugger failed to start"
226
+ end
227
+ @token = $1
228
+ puts "DEBUGGER: Remote debugger started."
229
+ have_initialized = true
230
+ else
231
+ raise "Expected initialize message after DAP connection"
232
+ end
233
+ end
234
+ # TODO: Send messages in another thread, so that they can be batched together
235
+ message_response = PluginTool.post_with_json_response("/api/development-plugin-loader/debugger-dap-messages", {
236
+ :token => @token,
237
+ :messages => JSON.generate([message])
238
+ })
239
+ if message_response['error']
240
+ puts "DEBUGGER: Server responded with error: #{message_response['error']}"
241
+ else
242
+ message_response['messages'].each do |response|
243
+ _write(response) if response
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ def _write(message)
251
+ @write_mutex.synchronize do
252
+ m = {'seq' => @next_seq}
253
+ @next_seq += 1
254
+ m.merge!(message)
255
+ puts "DAP WRITE: #{JSON.pretty_generate(m)}\n" if @_dump_messages
256
+ msg_json = JSON.generate(m)
257
+ @connection.write("Content-Length: #{msg_json.bytesize}\r\n\r\n")
258
+ @connection.write(msg_json)
259
+ end
260
+ end
261
+ end
262
+
263
+ end
Binary file
data/lib/notifications.rb CHANGED
@@ -69,6 +69,12 @@ module PluginTool
69
69
  when 'log '
70
70
  # Output from console.log()
71
71
  puts "LOG:#{data}"
72
+ DebugAdapterProtocolTunnel.log_message_from_server(data)
73
+ when 'DAP1'
74
+ DebugAdapterProtocolTunnel.dap_message_from_server(data)
75
+ when 'prof'
76
+ # Profiler report
77
+ PluginTool.profiler_handle_report(data)
72
78
  when 'audt'
73
79
  decoded = JSON.parse(data)
74
80
  kind = decoded.find { |name,value| name == 'auditEntryType' }.last
data/lib/plugin_tool.rb CHANGED
@@ -23,7 +23,7 @@ NO_DEPENDENCY_COMMANDS.delete('list')
23
23
  PLUGIN_SEARCH_PATH = ['.']
24
24
 
25
25
  # Options for passing to plugin objects
26
- options = Struct.new(:output, :minimiser, :no_dependency, :with_dependency, :exclude_with_prefix, :no_console, :show_system_audit, :args, :force, :turbo, :server_substring, :restrict_to_app_id).new
26
+ options = Struct.new(:output, :minimiser, :no_dependency, :with_dependency, :exclude_with_prefix, :no_console, :show_system_audit, :args, :force, :turbo, :debugger, :profile, :profile_file, :profile_format, :coverage_file, :coverage_format, :server_substring, :restrict_to_app_id).new
27
27
 
28
28
  # Parse arguments
29
29
  show_help = false
@@ -37,6 +37,12 @@ opts = GetoptLong.new(
37
37
  ['--server', '-s', GetoptLong::REQUIRED_ARGUMENT],
38
38
  ['--force', GetoptLong::NO_ARGUMENT],
39
39
  ['--turbo', GetoptLong::NO_ARGUMENT],
40
+ ['--debugger', GetoptLong::REQUIRED_ARGUMENT],
41
+ ['--profile', GetoptLong::REQUIRED_ARGUMENT],
42
+ ['--profile-file', GetoptLong::REQUIRED_ARGUMENT],
43
+ ['--profile-format', GetoptLong::REQUIRED_ARGUMENT],
44
+ ['--coverage-file', GetoptLong::REQUIRED_ARGUMENT],
45
+ ['--coverage-format', GetoptLong::REQUIRED_ARGUMENT],
40
46
  ['--output', GetoptLong::REQUIRED_ARGUMENT],
41
47
  ['--pack-restrict-to-app-id', GetoptLong::REQUIRED_ARGUMENT],
42
48
  ['--no-console', '-n', GetoptLong::NO_ARGUMENT],
@@ -72,6 +78,18 @@ opts.each do |opt, argument|
72
78
  options.force = true
73
79
  when '--turbo'
74
80
  options.turbo = true
81
+ when '--debugger'
82
+ options.debugger = argument
83
+ when '--profile'
84
+ options.profile = argument.to_f
85
+ when '--profile-file'
86
+ options.profile_file = argument
87
+ when '--profile-format'
88
+ options.profile_format = argument
89
+ when '--coverage-file'
90
+ options.coverage_file = argument
91
+ when '--coverage-format'
92
+ options.coverage_format = argument
75
93
  end
76
94
  end
77
95
  # Handle rest of command line -- first arg is the command, the rest are passed on
@@ -325,6 +343,13 @@ end
325
343
  unless LOCAL_ONLY_COMMANDS[PLUGIN_TOOL_COMMAND]
326
344
  PluginTool.custom_behaviour.server_ready(plugins, PLUGIN_TOOL_COMMAND, options)
327
345
  plugins.each { |p| p.setup_for_server }
346
+
347
+ if options.profile
348
+ PluginTool.request_profile(options)
349
+ end
350
+ if options.coverage_file
351
+ PluginTool.request_coverage(options)
352
+ end
328
353
  end
329
354
 
330
355
  # Run the command
@@ -343,6 +368,11 @@ PluginTool.start_syntax_check
343
368
  # Notifications support (including console)
344
369
  PluginTool.start_notifications(options) unless options.no_console
345
370
 
371
+ # If the debugger is requested, open the listening socket. Debugger initialised on connection.
372
+ if options.debugger
373
+ DebugAdapterProtocolTunnel.prepare(plugins, options)
374
+ end
375
+
346
376
  # Open watcher
347
377
  watcher = PluginTool.make_watcher(plugins.map { |p| p.plugin_dir })
348
378
 
data/lib/run.rb CHANGED
@@ -65,6 +65,7 @@ require "#{PLUGIN_TOOL_ROOT_DIR}/lib/syntax_checking.rb"
65
65
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/notifications.rb"
66
66
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/plugin.rb"
67
67
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/new_plugin.rb"
68
+ require "#{PLUGIN_TOOL_ROOT_DIR}/lib/debuggers.rb"
68
69
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/misc.rb"
69
70
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/watchers.rb"
70
71
  require "#{PLUGIN_TOOL_ROOT_DIR}/lib/minimise.rb"
data/lib/server.rb CHANGED
@@ -11,6 +11,7 @@ module PluginTool
11
11
  @@server_hostname = hostname
12
12
  @@server_port = port
13
13
  @@server_key = key
14
+ @@request_mutex = Mutex.new
14
15
  end
15
16
 
16
17
  def self.get_server_hostname
@@ -76,7 +77,9 @@ module PluginTool
76
77
  http = get_http
77
78
  request = Net::HTTP::Get.new(path)
78
79
  setup_request(request)
79
- http.request(request).body
80
+ @@request_mutex.synchronize do
81
+ http.request(request).body
82
+ end
80
83
  end
81
84
 
82
85
  def self.get_with_json_response(path)
@@ -117,7 +120,9 @@ EOF
117
120
  body << "--#{boundary}--\r"
118
121
  request.body = body
119
122
  end
120
- http.request(request).body
123
+ @@request_mutex.synchronize do
124
+ http.request(request).body
125
+ end
121
126
  end
122
127
 
123
128
  def self.post_with_json_response(path, params = nil, files = nil)
data/lib/version.txt CHANGED
@@ -1 +1 @@
1
- 8864920
1
+ e0f744c
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haplo
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.2
4
+ version: 2.5.6
5
5
  platform: java
6
6
  authors:
7
7
  - Haplo Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-14 00:00:00.000000000 Z
11
+ date: 2021-08-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Development tools for developing Haplo plugins, see https://haplo.org
14
14
  email: client.services@haplo-services.com
@@ -24,6 +24,7 @@ files:
24
24
  - lib/auth.rb
25
25
  - lib/check.rb
26
26
  - lib/custom.rb
27
+ - lib/debuggers.rb
27
28
  - lib/haplo-templates.jar
28
29
  - lib/hmac.rb
29
30
  - lib/hsvt_parser_config.rb
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
71
  version: '0'
71
72
  requirements: []
72
73
  rubyforge_project:
73
- rubygems_version: 2.7.6
74
+ rubygems_version: 2.7.9
74
75
  signing_key:
75
76
  specification_version: 4
76
77
  summary: Haplo Plugin Tool