sq_mini_racer 0.2.4.sqreen3 → 0.2.5.0.1.beta1
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 +4 -4
- data/CHANGELOG +15 -0
- data/README.md +25 -0
- data/Rakefile +41 -0
- data/ext/mini_racer_extension/extconf.rb +180 -101
- data/ext/mini_racer_extension/mini_racer_extension.cc +270 -94
- data/lib/sqreen/mini_racer.rb +22 -0
- data/lib/sqreen/mini_racer/version.rb +1 -1
- data/mini_racer.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4deece72e6d02478b4381a59ab9b1988a2fbff6a30e811596183240699fd018
|
4
|
+
data.tar.gz: ac83f077c9b56423cd8b8e716d8102bec73050c8a4ae0e525fda931f43ec6762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cad6011c1066ccc51e1c3bfe3064c50e7dbdc6dd04127ce24f855a97f9ba8fd492a5de3b1af54b810b4a112d74f03bcd746c8d95af822a5f9d59e8cd9cdce8a2
|
7
|
+
data.tar.gz: f985a398202455acfadd914606306d5469928bfbf69394fd39b5a79b097f81a6994bf4875bb7765d01b4fd2797b00378a5e3d23de4a05b4d3588216e3ac59cc9
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
- 14-05-2019
|
2
|
+
|
3
|
+
- 0.2.6
|
4
|
+
|
5
|
+
- FEATURE: add support for write_heap_snapshot which helps you analyze memory
|
6
|
+
|
7
|
+
- 25-04-2019
|
8
|
+
|
9
|
+
- 0.2.5
|
10
|
+
|
11
|
+
- FIX: Compatiblity fixes for V8 7 and above @ignisf
|
12
|
+
- FIX: Memory leak in gc_callback @messense
|
13
|
+
- IMPROVEMENT: Added example of sourcemap support @ianks
|
14
|
+
- URGENT: you will need this release for latest version of libv8 to work
|
15
|
+
|
1
16
|
- 02-11-2018
|
2
17
|
|
3
18
|
- 0.2.4
|
data/README.md
CHANGED
@@ -101,6 +101,27 @@ context.eval('bar()', filename: 'a/bar.js')
|
|
101
101
|
|
102
102
|
```
|
103
103
|
|
104
|
+
### Fork safety
|
105
|
+
|
106
|
+
Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe.
|
107
|
+
Sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
|
108
|
+
|
109
|
+
If you want to ensure your application does not leak memory after fork either:
|
110
|
+
|
111
|
+
1. Ensure no MiniRacer::Context objects are created in the master process
|
112
|
+
|
113
|
+
Or
|
114
|
+
|
115
|
+
2. Dispose manually of all MiniRacer::Context objects prior to forking
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
# before fork
|
119
|
+
|
120
|
+
require 'objspace'
|
121
|
+
ObjectSpace.each_object(MiniRacer::Context){|c| c.dispose}
|
122
|
+
|
123
|
+
# fork here
|
124
|
+
```
|
104
125
|
|
105
126
|
### Threadsafe
|
106
127
|
|
@@ -350,6 +371,10 @@ Note how the global interpreter lock release leads to 2 threads doing the same w
|
|
350
371
|
|
351
372
|
As a rule MiniRacer strives to always support and depend on the latest stable version of libv8.
|
352
373
|
|
374
|
+
## Source Maps
|
375
|
+
|
376
|
+
MiniRacer can fully support source maps but must be configured correctly to do so. [Check out this example](./examples/source-map-support/) for a working implementation.
|
377
|
+
|
353
378
|
## Installation
|
354
379
|
|
355
380
|
Add this line to your application's Gemfile:
|
data/Rakefile
CHANGED
@@ -19,6 +19,22 @@ Rake::ExtensionTask.new('prv_ext_loader', gem)
|
|
19
19
|
|
20
20
|
# via http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
|
21
21
|
namespace :test do
|
22
|
+
desc "run test suite with Address Sanitizer"
|
23
|
+
task :asan do
|
24
|
+
ENV["CONFIGURE_ARGS"] = [ENV["CONFIGURE_ARGS"], '--enable-asan'].compact.join(' ')
|
25
|
+
Rake::Task['compile'].invoke
|
26
|
+
|
27
|
+
asan_path = `ldconfig -N -p |grep libasan | grep -v 32 | sed 's/.* => \\(.*\\)$/\\1/'`.chomp.split("\n")[-1]
|
28
|
+
|
29
|
+
|
30
|
+
cmdline = "env LD_PRELOAD=\"#{asan_path}\" ruby test/test_leak.rb"
|
31
|
+
puts cmdline
|
32
|
+
system cmdline
|
33
|
+
|
34
|
+
cmdline = "env LD_PRELOAD=\"#{asan_path}\" rake test"
|
35
|
+
puts cmdline
|
36
|
+
system cmdline
|
37
|
+
end
|
22
38
|
# partial-loads-ok and undef-value-errors necessary to ignore
|
23
39
|
# spurious (and eminently ignorable) warnings from the ruby
|
24
40
|
# interpreter
|
@@ -55,3 +71,28 @@ namespace :test do
|
|
55
71
|
end
|
56
72
|
end
|
57
73
|
end
|
74
|
+
|
75
|
+
desc 'run clang-tidy linter on mini_racer_extension.cc'
|
76
|
+
task :lint do
|
77
|
+
require 'mkmf'
|
78
|
+
require 'libv8'
|
79
|
+
|
80
|
+
Libv8.configure_makefile
|
81
|
+
|
82
|
+
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote,
|
83
|
+
'arch_hdrdir' => $arch_hdrdir.quote,
|
84
|
+
'top_srcdir' => $top_srcdir.quote)
|
85
|
+
if $universal and (arch_flag = conf['ARCH_FLAG']) and !arch_flag.empty?
|
86
|
+
conf['ARCH_FLAG'] = arch_flag.gsub(/(?:\G|\s)-arch\s+\S+/, '')
|
87
|
+
end
|
88
|
+
|
89
|
+
checks = %W(bugprone-*
|
90
|
+
cert-*
|
91
|
+
cppcoreguidelines-*
|
92
|
+
clang-analyzer-*
|
93
|
+
performance-*
|
94
|
+
portability-*
|
95
|
+
readability-*).join(',')
|
96
|
+
|
97
|
+
sh RbConfig::expand("clang-tidy -checks='#{checks}' ext/mini_racer_extension/mini_racer_extension.cc -- #$INCFLAGS #$CPPFLAGS", conf)
|
98
|
+
end
|
@@ -1,24 +1,191 @@
|
|
1
1
|
require 'mkmf'
|
2
|
+
|
2
3
|
require 'fileutils'
|
4
|
+
require 'net/http'
|
5
|
+
require 'json'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rubygems/package'
|
3
8
|
|
4
|
-
IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
5
9
|
IS_SOLARIS = RUBY_PLATFORM =~ /solaris/
|
10
|
+
IS_LINUX_MUSL = RUBY_PLATFORM =~ /linux-musl/
|
11
|
+
|
12
|
+
def cppflags_clear_std!
|
13
|
+
$CPPFLAGS.gsub! /-std=[^\s]+/, ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def cppflags_add_frame_pointer!
|
17
|
+
$CPPFLAGS += " -fno-omit-frame-pointer"
|
18
|
+
end
|
19
|
+
|
20
|
+
def cppflags_add_cpu_extension!
|
21
|
+
if enable_config('avx2')
|
22
|
+
$CPPFLAGS += " -mavx2"
|
23
|
+
else
|
24
|
+
$CPPFLAGS += " -mssse3"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def libv8_gem_name
|
29
|
+
return "libv8-solaris" if IS_SOLARIS
|
30
|
+
return "libv8-alpine" if IS_LINUX_MUSL
|
31
|
+
|
32
|
+
'libv8'
|
33
|
+
end
|
34
|
+
|
35
|
+
def libv8_version
|
36
|
+
'7.3.492.27.1'
|
37
|
+
end
|
38
|
+
|
39
|
+
def libv8_basename
|
40
|
+
"#{libv8_gem_name}-#{libv8_version}-#{ruby_platform}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def libv8_gemspec
|
44
|
+
"#{libv8_basename}.gemspec"
|
45
|
+
end
|
46
|
+
|
47
|
+
def libv8_local_path
|
48
|
+
puts "looking for #{libv8_gemspec} in installed gems"
|
49
|
+
candidates = Gem.path.map { |p| File.join(p, 'specifications', libv8_gemspec) }
|
50
|
+
found = candidates.select { |f| File.exist?(f) }.first
|
51
|
+
|
52
|
+
unless found
|
53
|
+
puts "#{libv8_gemspec} not found in installed gems"
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "found in installed specs: #{found}"
|
58
|
+
|
59
|
+
dir = File.expand_path(File.join(found, '..', '..', 'gems', libv8_basename))
|
60
|
+
|
61
|
+
unless Dir.exist?(dir)
|
62
|
+
puts "not found in installed gems: #{dir}"
|
63
|
+
return
|
64
|
+
end
|
65
|
+
|
66
|
+
puts "found in installed gems: #{dir}"
|
67
|
+
|
68
|
+
dir
|
69
|
+
end
|
70
|
+
|
71
|
+
def vendor_path
|
72
|
+
File.join(Dir.pwd, 'vendor')
|
73
|
+
end
|
74
|
+
|
75
|
+
def libv8_vendor_path
|
76
|
+
puts "looking for #{libv8_basename} in #{vendor_path}"
|
77
|
+
path = Dir.glob("#{vendor_path}/#{libv8_basename}").first
|
78
|
+
|
79
|
+
unless path
|
80
|
+
puts "#{libv8_basename} not found in #{vendor_path}"
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
puts "looking for #{libv8_basename}/lib/libv8.rb in #{vendor_path}"
|
85
|
+
unless Dir.glob(File.join(vendor_path, libv8_basename, 'lib', 'libv8.rb')).first
|
86
|
+
puts "#{libv8_basename}/lib/libv8.rb not found in #{vendor_path}"
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
path
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_platform(str)
|
94
|
+
Gem::Platform.new(str).tap { |p| p.instance_eval { @os = 'linux-musl' } if str =~ /musl/ }
|
95
|
+
end
|
96
|
+
|
97
|
+
def ruby_platform
|
98
|
+
parse_platform(RUBY_PLATFORM)
|
99
|
+
end
|
100
|
+
|
101
|
+
def http_get(uri)
|
102
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
103
|
+
res = http.get(uri.path)
|
104
|
+
|
105
|
+
abort("HTTP error #{res.code}: #{uri}") unless res.code == '200'
|
106
|
+
|
107
|
+
return res.body
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def libv8_remote_search
|
112
|
+
body = http_get(URI("https://rubygems.org/api/v1/versions/#{libv8_gem_name}.json"))
|
113
|
+
json = JSON.parse(body)
|
114
|
+
|
115
|
+
versions = json.select do |v|
|
116
|
+
Gem::Version.new(v['number']) == Gem::Version.new(libv8_version)
|
117
|
+
end
|
118
|
+
abort(<<-ERROR) if versions.empty?
|
119
|
+
ERROR: could not find #{libv8_version}
|
120
|
+
ERROR
|
121
|
+
|
122
|
+
platform_versions = versions.select do |v|
|
123
|
+
parse_platform(v['platform']) == ruby_platform
|
124
|
+
end
|
125
|
+
abort(<<-ERROR) if platform_versions.empty?
|
126
|
+
ERROR: found #{libv8_gem_name}-#{libv8_version}, but no binary for #{ruby_platform}
|
127
|
+
try "gem install #{libv8_gem_name} -v '#{libv8_version}'" to attempt to build libv8 from source
|
128
|
+
ERROR
|
129
|
+
|
130
|
+
platform_versions.first
|
131
|
+
end
|
132
|
+
|
133
|
+
def libv8_download_uri(name, version, platform)
|
134
|
+
URI("https://rubygems.org/downloads/#{name}-#{version}-#{platform}.gem")
|
135
|
+
end
|
136
|
+
|
137
|
+
def libv8_downloaded_gem(name, version, platform)
|
138
|
+
"#{name}-#{version}-#{platform}.gem"
|
139
|
+
end
|
140
|
+
|
141
|
+
def libv8_download(name, version, platform)
|
142
|
+
FileUtils.mkdir_p(vendor_path)
|
143
|
+
body = http_get(libv8_download_uri(name, version, platform))
|
144
|
+
File.open(File.join(vendor_path, libv8_downloaded_gem(name, version, platform)), 'wb') { |f| f.write(body) }
|
145
|
+
end
|
146
|
+
|
147
|
+
def libv8_vendor!
|
148
|
+
version = libv8_remote_search
|
149
|
+
|
150
|
+
puts "downloading #{libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])} to #{vendor_path}"
|
151
|
+
libv8_download(libv8_gem_name, version['number'], version['platform'])
|
152
|
+
|
153
|
+
package = Gem::Package.new(File.join(vendor_path, libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])))
|
154
|
+
package.extract_files(File.join(vendor_path, File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem')))
|
155
|
+
|
156
|
+
libv8_vendor_path
|
157
|
+
end
|
158
|
+
|
159
|
+
def ensure_libv8_load_path
|
160
|
+
puts "detected platform #{RUBY_PLATFORM} => #{ruby_platform}"
|
161
|
+
|
162
|
+
libv8_path = libv8_local_path || libv8_vendor_path || libv8_vendor!
|
163
|
+
|
164
|
+
abort(<<-ERROR) unless libv8_path
|
165
|
+
ERROR: could not find #{libv8_gem_name}
|
166
|
+
ERROR
|
167
|
+
|
168
|
+
$LOAD_PATH.unshift(File.join(libv8_path, 'ext'))
|
169
|
+
$LOAD_PATH.unshift(File.join(libv8_path, 'lib'))
|
170
|
+
end
|
171
|
+
|
172
|
+
ensure_libv8_load_path
|
173
|
+
|
174
|
+
require 'libv8'
|
175
|
+
|
176
|
+
IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
6
177
|
|
7
178
|
have_library('pthread')
|
8
179
|
have_library('objc') if IS_DARWIN
|
9
|
-
|
180
|
+
cppflags_clear_std!
|
10
181
|
$CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
11
182
|
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
12
183
|
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
13
184
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
14
185
|
$CPPFLAGS += " -std=c++0x"
|
15
186
|
$CPPFLAGS += " -fpermissive"
|
16
|
-
|
17
|
-
|
18
|
-
$CPPFLAGS += " -mavx2"
|
19
|
-
else
|
20
|
-
$CPPFLAGS += " -mssse3"
|
21
|
-
end
|
187
|
+
cppflags_add_frame_pointer!
|
188
|
+
cppflags_add_cpu_extension!
|
22
189
|
|
23
190
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
24
191
|
|
@@ -57,103 +224,15 @@ if CONFIG['warnflags']
|
|
57
224
|
CONFIG['warnflags'].gsub!('-Wimplicit-function-declaration', '')
|
58
225
|
end
|
59
226
|
|
60
|
-
if enable_config('debug')
|
227
|
+
if enable_config('debug') || enable_config('asan')
|
61
228
|
CONFIG['debugflags'] << ' -ggdb3 -O0'
|
62
229
|
end
|
63
230
|
|
64
|
-
|
65
|
-
dirs = %w[/lib64 /usr/lib64 /lib /usr/lib]
|
66
|
-
found_v5 = dirs.map { |d| "#{d}/libtinfo.so.5" }.find &File.method(:file?)
|
67
|
-
return '' if found_v5
|
68
|
-
found_v6 = dirs.map { |d| "#{d}/libtinfo.so.6" }.find &File.method(:file?)
|
69
|
-
return '' unless found_v6
|
70
|
-
FileUtils.ln_s found_v6, 'gemdir/libtinfo.so.5', :force => true
|
71
|
-
"LD_LIBRARY_PATH='#{File.expand_path('gemdir')}:#{ENV['LD_LIBRARY_PATH']}'"
|
72
|
-
end
|
73
|
-
|
74
|
-
def libv8_gem_name
|
75
|
-
return "libv8-solaris" if IS_SOLARIS
|
76
|
-
|
77
|
-
is_musl = false
|
78
|
-
begin
|
79
|
-
is_musl = !!(File.read('/proc/self/maps') =~ /ld-musl-x86_64/)
|
80
|
-
rescue; end
|
81
|
-
|
82
|
-
is_musl ? 'libv8-alpine' : 'libv8'
|
83
|
-
end
|
84
|
-
|
85
|
-
# 1) old rubygem versions prefer source gems to binary ones
|
86
|
-
# ... and their --platform switch is broken too, as it leaves the 'ruby'
|
87
|
-
# platform in Gem.platforms.
|
88
|
-
# 2) the ruby binaries distributed with alpine (platform ending in -musl)
|
89
|
-
# refuse to load binary gems by default
|
90
|
-
def force_platform_gem
|
91
|
-
gem_version = `gem --version`
|
92
|
-
return 'gem' unless $?.success?
|
93
|
-
|
94
|
-
if RUBY_PLATFORM != 'x86_64-linux-musl'
|
95
|
-
return 'gem' if gem_version.to_f.zero? || gem_version.to_f >= 2.3
|
96
|
-
return 'gem' if RUBY_PLATFORM != 'x86_64-linux'
|
97
|
-
end
|
98
|
-
|
99
|
-
gem_binary = `which gem`
|
100
|
-
return 'gem' unless $?.success?
|
101
|
-
|
102
|
-
ruby = File.foreach(gem_binary.strip).first.sub(/^#!/, '').strip
|
103
|
-
unless File.file? ruby
|
104
|
-
warn "No valid ruby: #{ruby}"
|
105
|
-
return 'gem'
|
106
|
-
end
|
107
|
-
|
108
|
-
require 'tempfile'
|
109
|
-
file = Tempfile.new('sq_mini_racer')
|
110
|
-
file << <<EOS
|
111
|
-
require 'rubygems'
|
112
|
-
platforms = Gem.platforms
|
113
|
-
platforms.reject! { |it| it == 'ruby' }
|
114
|
-
if platforms.empty?
|
115
|
-
platforms << Gem::Platform.new('x86_64-linux')
|
116
|
-
end
|
117
|
-
Gem.send(:define_method, :platforms) { platforms }
|
118
|
-
#{IO.read(gem_binary.strip)}
|
119
|
-
EOS
|
120
|
-
file.close
|
121
|
-
"#{ruby} '#{file.path}'"
|
122
|
-
end
|
123
|
-
|
124
|
-
LIBV8_VERSION = '6.7.288.46.1'
|
125
|
-
libv8_rb = Dir.glob('**/libv8.rb').first
|
126
|
-
FileUtils.mkdir_p('gemdir')
|
127
|
-
unless libv8_rb
|
128
|
-
gem_name = libv8_gem_name
|
129
|
-
cmd = "#{fixup_libtinfo} #{force_platform_gem} install --version '= #{LIBV8_VERSION}' --install-dir gemdir #{gem_name}"
|
130
|
-
puts "Will try downloading #{gem_name} gem: #{cmd}"
|
131
|
-
`#{cmd}`
|
132
|
-
unless $?.success?
|
133
|
-
warn <<EOS
|
134
|
-
|
135
|
-
WARNING: Could not download a private copy of the libv8 gem. Please make
|
136
|
-
sure that you have internet access and that the `gem` binary is available.
|
137
|
-
|
138
|
-
EOS
|
139
|
-
end
|
140
|
-
|
141
|
-
libv8_rb = Dir.glob('**/libv8.rb').first
|
142
|
-
unless libv8_rb
|
143
|
-
warn <<EOS
|
144
|
-
|
145
|
-
WARNING: Could not find libv8 after the local copy of libv8 having supposedly
|
146
|
-
been installed.
|
147
|
-
|
148
|
-
EOS
|
149
|
-
end
|
150
|
-
end
|
231
|
+
Libv8.configure_makefile
|
151
232
|
|
152
|
-
if
|
153
|
-
|
154
|
-
|
233
|
+
if enable_config('asan')
|
234
|
+
$CPPFLAGS.insert(0, " -fsanitize=address ")
|
235
|
+
$LDFLAGS.insert(0, " -fsanitize=address ")
|
155
236
|
end
|
156
237
|
|
157
|
-
require 'libv8'
|
158
|
-
Libv8.configure_makefile
|
159
238
|
create_makefile 'sq_mini_racer_extension'
|
@@ -23,7 +23,9 @@
|
|
23
23
|
#if RUBY_API_VERSION_MAJOR > 1
|
24
24
|
#include <ruby/thread.h>
|
25
25
|
#endif
|
26
|
+
#include <ruby/io.h>
|
26
27
|
#include <v8.h>
|
28
|
+
#include <v8-profiler.h>
|
27
29
|
#include <libplatform/libplatform.h>
|
28
30
|
#include <ruby/encoding.h>
|
29
31
|
#include <pthread.h>
|
@@ -174,7 +176,7 @@ static VALUE rb_mJSON;
|
|
174
176
|
static VALUE rb_cFailedV8Conversion;
|
175
177
|
static VALUE rb_cDateTime = Qnil;
|
176
178
|
|
177
|
-
static Platform
|
179
|
+
static std::unique_ptr<Platform> current_platform = NULL;
|
178
180
|
static std::mutex platform_lock;
|
179
181
|
|
180
182
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
@@ -211,8 +213,8 @@ static void init_v8() {
|
|
211
213
|
|
212
214
|
if (current_platform == NULL) {
|
213
215
|
V8::InitializeICU();
|
214
|
-
current_platform = platform::
|
215
|
-
V8::InitializePlatform(current_platform);
|
216
|
+
current_platform = platform::NewDefaultPlatform();
|
217
|
+
V8::InitializePlatform(current_platform.get());
|
216
218
|
V8::Initialize();
|
217
219
|
}
|
218
220
|
|
@@ -254,16 +256,16 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
254
256
|
Local<Value> local_value = v8res.ToLocalChecked();
|
255
257
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
256
258
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
257
|
-
Local<Object> JSON = context->Global()->Get(
|
258
|
-
|
259
|
+
Local<Object> JSON = context->Global()->Get(String::NewFromUtf8(isolate, "JSON"))
|
260
|
+
->ToObject(context).ToLocalChecked();
|
259
261
|
|
260
262
|
Local<Function> stringify = JSON->Get(v8::String::NewFromUtf8(isolate, "stringify"))
|
261
263
|
.As<Function>();
|
262
264
|
|
263
|
-
Local<Object> object = local_value->ToObject();
|
265
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
264
266
|
const unsigned argc = 1;
|
265
267
|
Local<Value> argv[argc] = { object };
|
266
|
-
MaybeLocal<Value> json = stringify->Call(JSON, argc, argv);
|
268
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
267
269
|
|
268
270
|
if (json.IsEmpty()) {
|
269
271
|
evalRes.executed = false;
|
@@ -287,11 +289,21 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
287
289
|
evalRes.message = new Persistent<Value>();
|
288
290
|
Local<Message> message = trycatch.Message();
|
289
291
|
char buf[1000];
|
290
|
-
int len;
|
292
|
+
int len, line, column;
|
293
|
+
|
294
|
+
if (!message->GetLineNumber(context).To(&line)) {
|
295
|
+
line = 0;
|
296
|
+
}
|
297
|
+
|
298
|
+
if (!message->GetStartColumn(context).To(&column)) {
|
299
|
+
column = 0;
|
300
|
+
}
|
301
|
+
|
291
302
|
len = snprintf(buf, sizeof(buf), "%s at %s:%i:%i", *String::Utf8Value(isolate, message->Get()),
|
292
|
-
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString()),
|
293
|
-
|
294
|
-
|
303
|
+
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString(context).ToLocalChecked()),
|
304
|
+
line,
|
305
|
+
column);
|
306
|
+
|
295
307
|
if ((size_t) len >= sizeof(buf)) {
|
296
308
|
len = sizeof(buf) - 1;
|
297
309
|
buf[len] = '\0';
|
@@ -307,7 +319,8 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
307
319
|
}
|
308
320
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
309
321
|
evalRes.backtrace = new Persistent<Value>();
|
310
|
-
evalRes.backtrace->Reset(isolate,
|
322
|
+
evalRes.backtrace->Reset(isolate,
|
323
|
+
trycatch.StackTrace(context).ToLocalChecked()->ToString(context).ToLocalChecked());
|
311
324
|
}
|
312
325
|
}
|
313
326
|
}
|
@@ -391,11 +404,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
391
404
|
}
|
392
405
|
|
393
406
|
if (value->IsInt32()) {
|
394
|
-
return INT2FIX(value->Int32Value());
|
407
|
+
return INT2FIX(value->Int32Value(context).ToChecked());
|
395
408
|
}
|
396
409
|
|
397
410
|
if (value->IsNumber()) {
|
398
|
-
return rb_float_new(value->NumberValue());
|
411
|
+
return rb_float_new(value->NumberValue(context).ToChecked());
|
399
412
|
}
|
400
413
|
|
401
414
|
if (value->IsTrue()) {
|
@@ -436,7 +449,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
436
449
|
VALUE rb_hash = rb_hash_new();
|
437
450
|
TryCatch trycatch(isolate);
|
438
451
|
|
439
|
-
Local<Object> object = value->ToObject();
|
452
|
+
Local<Object> object = value->ToObject(context).ToLocalChecked();
|
440
453
|
auto maybe_props = object->GetOwnPropertyNames(context);
|
441
454
|
if (!maybe_props.IsEmpty()) {
|
442
455
|
Local<Array> props = maybe_props.ToLocalChecked();
|
@@ -459,8 +472,8 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
459
472
|
return rb_hash;
|
460
473
|
}
|
461
474
|
|
462
|
-
Local<String> rstr = value->ToString();
|
463
|
-
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(), rb_enc_find("utf-8"));
|
475
|
+
Local<String> rstr = value->ToString(context).ToLocalChecked();
|
476
|
+
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
464
477
|
}
|
465
478
|
|
466
479
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -557,7 +570,7 @@ treat_as_latin1:
|
|
557
570
|
return v8str.ToLocalChecked();
|
558
571
|
}
|
559
572
|
|
560
|
-
static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value)
|
573
|
+
static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context, VALUE value)
|
561
574
|
{
|
562
575
|
EscapableHandleScope scope(isolate);
|
563
576
|
|
@@ -571,87 +584,86 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value)
|
|
571
584
|
VALUE klass;
|
572
585
|
|
573
586
|
switch (TYPE(value)) {
|
574
|
-
|
587
|
+
case T_FIXNUM:
|
575
588
|
{
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
589
|
+
fixnum = NUM2LONG(value);
|
590
|
+
if (fixnum > INT_MAX)
|
591
|
+
{
|
592
|
+
return scope.Escape(Number::New(isolate, (double)fixnum));
|
593
|
+
}
|
594
|
+
return scope.Escape(Integer::New(isolate, (int)fixnum));
|
582
595
|
}
|
583
|
-
|
584
|
-
|
585
|
-
|
596
|
+
case T_FLOAT:
|
597
|
+
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
598
|
+
case T_STRING:
|
586
599
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
600
|
+
case T_NIL:
|
601
|
+
return scope.Escape(Null(isolate));
|
602
|
+
case T_TRUE:
|
603
|
+
return scope.Escape(True(isolate));
|
604
|
+
case T_FALSE:
|
605
|
+
return scope.Escape(False(isolate));
|
606
|
+
case T_ARRAY:
|
594
607
|
{
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
608
|
+
length = RARRAY_LEN(value);
|
609
|
+
array = Array::New(isolate, (int)length);
|
610
|
+
for(i=0; i<length; i++) {
|
611
|
+
array->Set(i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
612
|
+
}
|
613
|
+
return scope.Escape(array);
|
601
614
|
}
|
602
|
-
|
615
|
+
case T_HASH:
|
603
616
|
{
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
617
|
+
object = Object::New(isolate);
|
618
|
+
hash_as_array = rb_funcall(value, rb_intern("to_a"), 0);
|
619
|
+
length = RARRAY_LEN(hash_as_array);
|
620
|
+
for(i=0; i<length; i++) {
|
621
|
+
pair = rb_ary_entry(hash_as_array, i);
|
622
|
+
object->Set(convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
623
|
+
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
624
|
+
}
|
625
|
+
return scope.Escape(object);
|
613
626
|
}
|
614
|
-
|
627
|
+
case T_SYMBOL:
|
615
628
|
{
|
616
|
-
|
629
|
+
value = rb_funcall(value, rb_intern("to_s"), 0);
|
617
630
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
618
631
|
}
|
619
|
-
|
632
|
+
case T_DATA:
|
633
|
+
{
|
634
|
+
klass = rb_funcall(value, rb_intern("class"), 0);
|
635
|
+
if (klass == rb_cTime || klass == rb_cDateTime)
|
620
636
|
{
|
621
|
-
|
622
|
-
if (klass == rb_cTime || klass == rb_cDateTime)
|
637
|
+
if (klass == rb_cDateTime)
|
623
638
|
{
|
624
|
-
|
625
|
-
{
|
626
|
-
value = rb_funcall(value, rb_intern("to_time"), 0);
|
627
|
-
}
|
628
|
-
value = rb_funcall(value, rb_intern("to_f"), 0);
|
629
|
-
return scope.Escape(Date::New(isolate, NUM2DBL(value) * 1000));
|
639
|
+
value = rb_funcall(value, rb_intern("to_time"), 0);
|
630
640
|
}
|
641
|
+
value = rb_funcall(value, rb_intern("to_f"), 0);
|
642
|
+
return scope.Escape(Date::New(context, NUM2DBL(value) * 1000).ToLocalChecked());
|
643
|
+
}
|
631
644
|
// break intentionally missing
|
632
645
|
}
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
646
|
+
case T_OBJECT:
|
647
|
+
case T_CLASS:
|
648
|
+
case T_ICLASS:
|
649
|
+
case T_MODULE:
|
650
|
+
case T_REGEXP:
|
651
|
+
case T_MATCH:
|
652
|
+
case T_STRUCT:
|
653
|
+
case T_BIGNUM:
|
654
|
+
case T_FILE:
|
655
|
+
case T_UNDEF:
|
656
|
+
case T_NODE:
|
657
|
+
default:
|
645
658
|
{
|
646
659
|
if (rb_respond_to(value, rb_intern("to_s"))) {
|
647
660
|
// TODO: if this throws we're screwed
|
648
661
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
649
662
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
650
663
|
}
|
651
|
-
|
652
|
-
|
664
|
+
return scope.Escape(String::NewFromUtf8(isolate, "Undefined Conversion"));
|
665
|
+
}
|
653
666
|
}
|
654
|
-
|
655
667
|
}
|
656
668
|
|
657
669
|
static void unblock_eval(void *ptr) {
|
@@ -659,6 +671,91 @@ static void unblock_eval(void *ptr) {
|
|
659
671
|
eval->context_info->isolate_info->interrupted = true;
|
660
672
|
}
|
661
673
|
|
674
|
+
/*
|
675
|
+
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
676
|
+
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
677
|
+
*/
|
678
|
+
bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
679
|
+
const char *utf8_source, const char *name) {
|
680
|
+
Context::Scope context_scope(context);
|
681
|
+
TryCatch try_catch(isolate);
|
682
|
+
Local<String> source_string;
|
683
|
+
if (!String::NewFromUtf8(isolate, utf8_source,
|
684
|
+
NewStringType::kNormal)
|
685
|
+
.ToLocal(&source_string)) {
|
686
|
+
return false;
|
687
|
+
}
|
688
|
+
Local<v8::String> resource_name =
|
689
|
+
String::NewFromUtf8(isolate, name, NewStringType::kNormal)
|
690
|
+
.ToLocalChecked();
|
691
|
+
ScriptOrigin origin(resource_name);
|
692
|
+
ScriptCompiler::Source source(source_string, origin);
|
693
|
+
Local<Script> script;
|
694
|
+
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
695
|
+
return false;
|
696
|
+
if (script->Run(context).IsEmpty())
|
697
|
+
return false;
|
698
|
+
// CHECK(!try_catch.HasCaught());
|
699
|
+
return true;
|
700
|
+
}
|
701
|
+
|
702
|
+
StartupData
|
703
|
+
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
704
|
+
// Create a new isolate and a new context from scratch, optionally run
|
705
|
+
// a script to embed, and serialize to create a snapshot blob.
|
706
|
+
StartupData result = {nullptr, 0};
|
707
|
+
{
|
708
|
+
SnapshotCreator snapshot_creator;
|
709
|
+
Isolate *isolate = snapshot_creator.GetIsolate();
|
710
|
+
{
|
711
|
+
HandleScope scope(isolate);
|
712
|
+
Local<Context> context = Context::New(isolate);
|
713
|
+
if (embedded_source != nullptr &&
|
714
|
+
!run_extra_code(isolate, context, embedded_source,
|
715
|
+
"<embedded>")) {
|
716
|
+
return result;
|
717
|
+
}
|
718
|
+
snapshot_creator.SetDefaultContext(context);
|
719
|
+
}
|
720
|
+
result = snapshot_creator.CreateBlob(
|
721
|
+
SnapshotCreator::FunctionCodeHandling::kClear);
|
722
|
+
}
|
723
|
+
return result;
|
724
|
+
}
|
725
|
+
|
726
|
+
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
727
|
+
const char *warmup_source) {
|
728
|
+
// Use following steps to create a warmed up snapshot blob from a cold one:
|
729
|
+
// - Create a new isolate from the cold snapshot.
|
730
|
+
// - Create a new context to run the warmup script. This will trigger
|
731
|
+
// compilation of executed functions.
|
732
|
+
// - Create a new context. This context will be unpolluted.
|
733
|
+
// - Serialize the isolate and the second context into a new snapshot blob.
|
734
|
+
StartupData result = {nullptr, 0};
|
735
|
+
|
736
|
+
if (cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr &&
|
737
|
+
warmup_source != NULL) {
|
738
|
+
SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
|
739
|
+
Isolate *isolate = snapshot_creator.GetIsolate();
|
740
|
+
{
|
741
|
+
HandleScope scope(isolate);
|
742
|
+
Local<Context> context = Context::New(isolate);
|
743
|
+
if (!run_extra_code(isolate, context, warmup_source, "<warm-up>")) {
|
744
|
+
return result;
|
745
|
+
}
|
746
|
+
}
|
747
|
+
{
|
748
|
+
HandleScope handle_scope(isolate);
|
749
|
+
isolate->ContextDisposedNotification(false);
|
750
|
+
Local<Context> context = Context::New(isolate);
|
751
|
+
snapshot_creator.SetDefaultContext(context);
|
752
|
+
}
|
753
|
+
result = snapshot_creator.CreateBlob(
|
754
|
+
SnapshotCreator::FunctionCodeHandling::kKeep);
|
755
|
+
}
|
756
|
+
return result;
|
757
|
+
}
|
758
|
+
|
662
759
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
663
760
|
SnapshotInfo* snapshot_info;
|
664
761
|
Data_Get_Struct(self, SnapshotInfo, snapshot_info);
|
@@ -677,7 +774,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
677
774
|
|
678
775
|
init_v8();
|
679
776
|
|
680
|
-
StartupData startup_data =
|
777
|
+
StartupData startup_data = create_snapshot_data_blob(RSTRING_PTR(str));
|
681
778
|
|
682
779
|
if (startup_data.data == NULL && startup_data.raw_size == 0) {
|
683
780
|
rb_raise(rb_eSnapshotError, "Could not create snapshot, most likely the source is incorrect");
|
@@ -708,7 +805,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
708
805
|
init_v8();
|
709
806
|
|
710
807
|
StartupData cold_startup_data = {snapshot_info->data, snapshot_info->raw_size};
|
711
|
-
StartupData warm_startup_data =
|
808
|
+
StartupData warm_startup_data = warm_up_snapshot_data_blob(cold_startup_data, RSTRING_PTR(str));
|
712
809
|
|
713
810
|
if (warm_startup_data.data == NULL && warm_startup_data.raw_size == 0) {
|
714
811
|
rb_raise(rb_eSnapshotError, "Could not warm up snapshot, most likely the source is incorrect");
|
@@ -887,8 +984,8 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
887
984
|
Local<Value> tmp = Local<Value>::New(isolate, *result.value);
|
888
985
|
|
889
986
|
if (result.json) {
|
890
|
-
Local<String> rstr = tmp->ToString();
|
891
|
-
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(), rb_enc_find("utf-8"));
|
987
|
+
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
988
|
+
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
892
989
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
893
990
|
} else {
|
894
991
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
@@ -1009,6 +1106,8 @@ gvl_ruby_callback(void* data) {
|
|
1009
1106
|
VALUE result;
|
1010
1107
|
VALUE self;
|
1011
1108
|
VALUE parent;
|
1109
|
+
ContextInfo* context_info;
|
1110
|
+
|
1012
1111
|
{
|
1013
1112
|
HandleScope scope(args->GetIsolate());
|
1014
1113
|
Local<External> external = Local<External>::Cast(args->Data());
|
@@ -1021,7 +1120,6 @@ gvl_ruby_callback(void* data) {
|
|
1021
1120
|
return NULL;
|
1022
1121
|
}
|
1023
1122
|
|
1024
|
-
ContextInfo* context_info;
|
1025
1123
|
Data_Get_Struct(parent, ContextInfo, context_info);
|
1026
1124
|
|
1027
1125
|
if (length > 0) {
|
@@ -1031,7 +1129,7 @@ gvl_ruby_callback(void* data) {
|
|
1031
1129
|
for (int i = 0; i < length; i++) {
|
1032
1130
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1033
1131
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1034
|
-
|
1132
|
+
*context_info->context, value);
|
1035
1133
|
rb_ary_push(ruby_args, tmp);
|
1036
1134
|
}
|
1037
1135
|
}
|
@@ -1062,7 +1160,7 @@ gvl_ruby_callback(void* data) {
|
|
1062
1160
|
}
|
1063
1161
|
else {
|
1064
1162
|
HandleScope scope(args->GetIsolate());
|
1065
|
-
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), result);
|
1163
|
+
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), context_info->context->Get(args->GetIsolate()), result);
|
1066
1164
|
args->GetReturnValue().Set(v8_result);
|
1067
1165
|
}
|
1068
1166
|
|
@@ -1119,8 +1217,10 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1119
1217
|
Local<Context> context = context_info->context->Get(isolate);
|
1120
1218
|
Context::Scope context_scope(context);
|
1121
1219
|
|
1122
|
-
Local<String> v8_str =
|
1123
|
-
|
1220
|
+
Local<String> v8_str =
|
1221
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1222
|
+
NewStringType::kNormal, (int)RSTRING_LEN(name))
|
1223
|
+
.ToLocalChecked();
|
1124
1224
|
|
1125
1225
|
// copy self so we can access from v8 external
|
1126
1226
|
VALUE* self_copy;
|
@@ -1130,23 +1230,34 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1130
1230
|
Local<Value> external = External::New(isolate, self_copy);
|
1131
1231
|
|
1132
1232
|
if (parent_object == Qnil) {
|
1133
|
-
context->Global()->Set(
|
1134
|
-
|
1233
|
+
context->Global()->Set(
|
1234
|
+
v8_str, FunctionTemplate::New(isolate, ruby_callback, external)
|
1235
|
+
->GetFunction(context)
|
1236
|
+
.ToLocalChecked());
|
1135
1237
|
|
1136
|
-
|
1137
|
-
|
1238
|
+
} else {
|
1239
|
+
Local<String> eval =
|
1240
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1241
|
+
NewStringType::kNormal,
|
1242
|
+
(int)RSTRING_LEN(parent_object_eval))
|
1243
|
+
.ToLocalChecked();
|
1138
1244
|
|
1139
1245
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1140
1246
|
if (parsed_script.IsEmpty()) {
|
1141
1247
|
parse_error = true;
|
1142
1248
|
} else {
|
1143
|
-
MaybeLocal<Value> maybe_value =
|
1249
|
+
MaybeLocal<Value> maybe_value =
|
1250
|
+
parsed_script.ToLocalChecked()->Run(context);
|
1144
1251
|
attach_error = true;
|
1145
1252
|
|
1146
1253
|
if (!maybe_value.IsEmpty()) {
|
1147
1254
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1148
|
-
if (value->IsObject()){
|
1149
|
-
|
1255
|
+
if (value->IsObject()) {
|
1256
|
+
value.As<Object>()->Set(
|
1257
|
+
v8_str, FunctionTemplate::New(
|
1258
|
+
isolate, ruby_callback, external)
|
1259
|
+
->GetFunction(context)
|
1260
|
+
.ToLocalChecked());
|
1150
1261
|
attach_error = false;
|
1151
1262
|
}
|
1152
1263
|
}
|
@@ -1350,6 +1461,69 @@ rb_heap_stats(VALUE self) {
|
|
1350
1461
|
return rval;
|
1351
1462
|
}
|
1352
1463
|
|
1464
|
+
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1465
|
+
class FileOutputStream : public OutputStream {
|
1466
|
+
public:
|
1467
|
+
FileOutputStream(FILE* stream) : stream_(stream) {}
|
1468
|
+
|
1469
|
+
virtual int GetChunkSize() {
|
1470
|
+
return 65536;
|
1471
|
+
}
|
1472
|
+
|
1473
|
+
virtual void EndOfStream() {}
|
1474
|
+
|
1475
|
+
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1476
|
+
const size_t len = static_cast<size_t>(size);
|
1477
|
+
size_t off = 0;
|
1478
|
+
|
1479
|
+
while (off < len && !feof(stream_) && !ferror(stream_))
|
1480
|
+
off += fwrite(data + off, 1, len - off, stream_);
|
1481
|
+
|
1482
|
+
return off == len ? kContinue : kAbort;
|
1483
|
+
}
|
1484
|
+
|
1485
|
+
private:
|
1486
|
+
FILE* stream_;
|
1487
|
+
};
|
1488
|
+
|
1489
|
+
|
1490
|
+
static VALUE
|
1491
|
+
rb_heap_snapshot(VALUE self, VALUE file) {
|
1492
|
+
|
1493
|
+
rb_io_t *fptr;
|
1494
|
+
|
1495
|
+
fptr = RFILE(file)->fptr;
|
1496
|
+
|
1497
|
+
if (!fptr) return Qfalse;
|
1498
|
+
|
1499
|
+
FILE* fp;
|
1500
|
+
fp = fdopen(fptr->fd, "w");
|
1501
|
+
if (fp == NULL) return Qfalse;
|
1502
|
+
|
1503
|
+
|
1504
|
+
ContextInfo* context_info;
|
1505
|
+
Data_Get_Struct(self, ContextInfo, context_info);
|
1506
|
+
Isolate* isolate;
|
1507
|
+
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1508
|
+
|
1509
|
+
if (!isolate) return Qfalse;
|
1510
|
+
|
1511
|
+
Locker lock(isolate);
|
1512
|
+
Isolate::Scope isolate_scope(isolate);
|
1513
|
+
HandleScope handle_scope(isolate);
|
1514
|
+
|
1515
|
+
HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
1516
|
+
|
1517
|
+
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1518
|
+
|
1519
|
+
FileOutputStream stream(fp);
|
1520
|
+
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1521
|
+
|
1522
|
+
const_cast<HeapSnapshot*>(snap)->Delete();
|
1523
|
+
|
1524
|
+
return Qtrue;
|
1525
|
+
}
|
1526
|
+
|
1353
1527
|
static VALUE
|
1354
1528
|
rb_context_stop(VALUE self) {
|
1355
1529
|
|
@@ -1494,7 +1668,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1494
1668
|
return Qnil;
|
1495
1669
|
}
|
1496
1670
|
for(int i=0; i < fun_argc; i++) {
|
1497
|
-
call.argv[i] = convert_ruby_to_v8(isolate, call_argv[i]);
|
1671
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1498
1672
|
}
|
1499
1673
|
}
|
1500
1674
|
#if RUBY_API_VERSION_MAJOR > 1
|
@@ -1558,6 +1732,8 @@ extern "C" {
|
|
1558
1732
|
rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0);
|
1559
1733
|
rb_define_method(rb_cContext, "low_memory_notification", (VALUE(*)(...))&rb_context_low_memory_notification, 0);
|
1560
1734
|
rb_define_method(rb_cContext, "heap_stats", (VALUE(*)(...))&rb_heap_stats, 0);
|
1735
|
+
rb_define_method(rb_cContext, "write_heap_snapshot_unsafe", (VALUE(*)(...))&rb_heap_snapshot, 1);
|
1736
|
+
|
1561
1737
|
rb_define_private_method(rb_cContext, "create_isolate_value",(VALUE(*)(...))&rb_context_create_isolate_value, 0);
|
1562
1738
|
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
|
1563
1739
|
rb_define_private_method(rb_cContext, "call_unsafe", (VALUE(*)(...))&rb_context_call_unsafe, -1);
|
data/lib/sqreen/mini_racer.rb
CHANGED
@@ -178,6 +178,28 @@ module MiniRacer
|
|
178
178
|
eval(File.read(filename))
|
179
179
|
end
|
180
180
|
|
181
|
+
def write_heap_snapshot(file_or_io)
|
182
|
+
f = nil
|
183
|
+
implicit = false
|
184
|
+
|
185
|
+
|
186
|
+
if String === file_or_io
|
187
|
+
f = File.open(file_or_io, "w")
|
188
|
+
implicit = true
|
189
|
+
else
|
190
|
+
f = file_or_io
|
191
|
+
end
|
192
|
+
|
193
|
+
if !(File === f)
|
194
|
+
raise ArgumentError("file_or_io")
|
195
|
+
end
|
196
|
+
|
197
|
+
write_heap_snapshot_unsafe(f)
|
198
|
+
|
199
|
+
ensure
|
200
|
+
f.close if implicit
|
201
|
+
end
|
202
|
+
|
181
203
|
def eval(str, options=nil)
|
182
204
|
raise(ContextDisposedError, 'attempted to call eval on a disposed context!') if @disposed
|
183
205
|
|
data/mini_racer.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
17
17
|
|
18
|
-
REJECTS = %r{\A((benchmark|test|spec|features)/|bench\.rb|.+\.sh|Jenkinsfile)}
|
18
|
+
REJECTS = %r{\A((benchmark|test|spec|features|examples)/|bench\.rb|.+\.sh|Jenkinsfile)}
|
19
19
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(REJECTS) }
|
20
20
|
spec.bindir = "exe"
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sq_mini_racer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5.0.1.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|