sq_mini_racer 0.2.4.sqreen2 → 0.3.1.0.0
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/.dockerignore +12 -0
- data/.gitignore +9 -0
- data/CHANGELOG +88 -0
- data/Dockerfile +22 -0
- data/README.md +42 -2
- data/Rakefile +66 -43
- data/azure-pipelines.yml +69 -0
- data/azure-template.yml +101 -0
- data/ext/mini_racer_extension/extconf.rb +212 -92
- data/ext/mini_racer_extension/mini_racer_extension.cc +483 -155
- data/ext/mini_racer_loader/extconf.rb +8 -0
- data/ext/{prv_ext_loader/prv_ext_loader.c → mini_racer_loader/mini_racer_loader.c} +24 -5
- data/lib/sq_mini_racer.rb +3 -0
- data/lib/sqreen/mini_racer.rb +112 -24
- data/lib/sqreen/mini_racer/version.rb +3 -3
- data/mini_racer.gemspec +16 -8
- data/valgrind.supp +74 -0
- metadata +62 -20
- data/.travis.yml +0 -73
- data/ext/prv_ext_loader/extconf.rb +0 -5
@@ -1,33 +1,232 @@
|
|
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
|
+
'libv8-node'
|
30
|
+
end
|
31
|
+
|
32
|
+
def libv8_version
|
33
|
+
'14.14.0.0.beta2'
|
34
|
+
end
|
35
|
+
|
36
|
+
def libv8_basename
|
37
|
+
"#{libv8_gem_name}-#{libv8_version}-#{ruby_platform}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def libv8_gemspec_no_libc
|
41
|
+
platform_no_libc = ruby_platform.to_s.split('-')[0..1].join('-')
|
42
|
+
"#{libv8_gem_name}-#{libv8_version}-#{platform_no_libc}.gemspec"
|
43
|
+
end
|
44
|
+
|
45
|
+
def libv8_gemspec
|
46
|
+
"#{libv8_basename}.gemspec"
|
47
|
+
end
|
48
|
+
|
49
|
+
def libv8_local_path(path=Gem.path)
|
50
|
+
gemspecs = [libv8_gemspec, libv8_gemspec_no_libc].uniq
|
51
|
+
puts "looking for #{gemspecs.join(', ')} in installed gems"
|
52
|
+
candidates = path.product(gemspecs)
|
53
|
+
.map { |(p, gemspec)| File.join(p, 'specifications', gemspec) }
|
54
|
+
p candidates
|
55
|
+
found = candidates.select { |f| File.exist?(f) }.first
|
56
|
+
|
57
|
+
unless found
|
58
|
+
puts "#{gemspecs.join(', ')} not found in installed gems"
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
puts "found in installed specs: #{found}"
|
63
|
+
|
64
|
+
gemdir = File.basename(found, '.gemspec')
|
65
|
+
dir = File.expand_path(File.join(found, '..', '..', 'gems', gemdir))
|
66
|
+
|
67
|
+
unless Dir.exist?(dir)
|
68
|
+
puts "not found in installed gems: #{dir}"
|
69
|
+
return
|
70
|
+
end
|
71
|
+
|
72
|
+
puts "found in installed gems: #{dir}"
|
73
|
+
|
74
|
+
dir
|
75
|
+
end
|
76
|
+
|
77
|
+
def vendor_path
|
78
|
+
File.join(Dir.pwd, 'vendor')
|
79
|
+
end
|
80
|
+
|
81
|
+
def libv8_vendor_path
|
82
|
+
puts "looking for #{libv8_basename} in #{vendor_path}"
|
83
|
+
path = Dir.glob("#{vendor_path}/#{libv8_basename}").first
|
84
|
+
|
85
|
+
unless path
|
86
|
+
puts "#{libv8_basename} not found in #{vendor_path}"
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
90
|
+
puts "looking for #{libv8_basename}/lib/libv8-node.rb in #{vendor_path}"
|
91
|
+
unless Dir.glob(File.join(vendor_path, libv8_basename, 'lib', 'libv8-node.rb')).first
|
92
|
+
puts "#{libv8_basename}/lib/libv8.rb not found in #{vendor_path}"
|
93
|
+
return
|
94
|
+
end
|
95
|
+
|
96
|
+
path
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_platform(str)
|
100
|
+
Gem::Platform.new(str).tap do |p|
|
101
|
+
p.instance_eval { @cpu = 'x86_64' } if str =~ /universal.*darwin/
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def ruby_platform
|
106
|
+
parse_platform(RUBY_PLATFORM)
|
107
|
+
end
|
108
|
+
|
109
|
+
def http_get(uri)
|
110
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
111
|
+
res = http.get(uri.path)
|
112
|
+
|
113
|
+
abort("HTTP error #{res.code}: #{uri}") unless res.code == '200'
|
114
|
+
|
115
|
+
return res.body
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def libv8_remote_search
|
120
|
+
body = http_get(URI("https://rubygems.org/api/v1/versions/#{libv8_gem_name}.json"))
|
121
|
+
json = JSON.parse(body)
|
122
|
+
|
123
|
+
versions = json.select do |v|
|
124
|
+
Gem::Version.new(v['number']) == Gem::Version.new(libv8_version)
|
125
|
+
end
|
126
|
+
abort(<<-ERROR) if versions.empty?
|
127
|
+
ERROR: could not find #{libv8_gem_name} (version #{libv8_version}) in rubygems.org
|
128
|
+
ERROR
|
129
|
+
|
130
|
+
platform_versions = versions.select do |v|
|
131
|
+
parse_platform(v['platform']) == ruby_platform unless v['platform'] =~ /universal.*darwin/
|
132
|
+
end
|
133
|
+
abort(<<-ERROR) if platform_versions.empty?
|
134
|
+
ERROR: found #{libv8_gem_name}-#{libv8_version}, but no binary for #{ruby_platform}
|
135
|
+
try "gem install #{libv8_gem_name} -v '#{libv8_version}'" to attempt to build libv8 from source
|
136
|
+
ERROR
|
137
|
+
|
138
|
+
platform_versions.first
|
139
|
+
end
|
140
|
+
|
141
|
+
def libv8_download_uri(name, version, platform)
|
142
|
+
URI("https://rubygems.org/downloads/#{name}-#{version}-#{platform}.gem")
|
143
|
+
end
|
144
|
+
|
145
|
+
def libv8_downloaded_gem(name, version, platform)
|
146
|
+
"#{name}-#{version}-#{platform}.gem"
|
147
|
+
end
|
148
|
+
|
149
|
+
def libv8_download(name, version, platform)
|
150
|
+
FileUtils.mkdir_p(vendor_path)
|
151
|
+
body = http_get(libv8_download_uri(name, version, platform))
|
152
|
+
File.open(File.join(vendor_path, libv8_downloaded_gem(name, version, platform)), 'wb') { |f| f.write(body) }
|
153
|
+
end
|
154
|
+
|
155
|
+
def libv8_install!
|
156
|
+
cmd = "gem install #{libv8_gem_name} --version '#{libv8_version}' --install-dir '#{vendor_path}'"
|
157
|
+
puts "installing #{libv8_gem_name} using `#{cmd}`"
|
158
|
+
rc = system(cmd)
|
159
|
+
|
160
|
+
abort(<<-ERROR) unless rc
|
161
|
+
ERROR: could not install #{libv8_gem_name} #{libv8_version}
|
162
|
+
try "gem install #{libv8_gem_name} -v '#{libv8_version}'" to attempt to build libv8 from source
|
163
|
+
ERROR
|
164
|
+
|
165
|
+
libv8_local_path([vendor_path])
|
166
|
+
end
|
167
|
+
|
168
|
+
def libv8_vendor!
|
169
|
+
return libv8_install! if Gem::VERSION < '2.0'
|
170
|
+
|
171
|
+
version = libv8_remote_search
|
172
|
+
|
173
|
+
puts "downloading #{libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])} to #{vendor_path}"
|
174
|
+
libv8_download(libv8_gem_name, version['number'], version['platform'])
|
175
|
+
|
176
|
+
package = Gem::Package.new(File.join(vendor_path, libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])))
|
177
|
+
package.extract_files(File.join(vendor_path, File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem')))
|
178
|
+
|
179
|
+
libv8_vendor_path
|
180
|
+
end
|
181
|
+
|
182
|
+
def ensure_libv8_load_path
|
183
|
+
puts "detected platform #{RUBY_PLATFORM} => #{ruby_platform}"
|
184
|
+
|
185
|
+
libv8_path = libv8_local_path
|
186
|
+
unless ENV['ONLY_INSTALLED_LIBV8_GEM']
|
187
|
+
libv8_path ||= libv8_vendor_path || libv8_vendor!
|
188
|
+
end
|
189
|
+
|
190
|
+
abort(<<-ERROR) unless libv8_path
|
191
|
+
ERROR: could not find #{libv8_gem_name}
|
192
|
+
ERROR
|
193
|
+
|
194
|
+
$LOAD_PATH.unshift(File.join(libv8_path, 'ext'))
|
195
|
+
$LOAD_PATH.unshift(File.join(libv8_path, 'lib'))
|
196
|
+
end
|
197
|
+
|
198
|
+
ensure_libv8_load_path
|
199
|
+
|
200
|
+
require 'libv8-node'
|
201
|
+
|
202
|
+
IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
6
203
|
|
7
204
|
have_library('pthread')
|
8
205
|
have_library('objc') if IS_DARWIN
|
9
|
-
|
206
|
+
cppflags_clear_std!
|
10
207
|
$CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
11
208
|
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
12
209
|
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
13
210
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
14
211
|
$CPPFLAGS += " -std=c++0x"
|
15
212
|
$CPPFLAGS += " -fpermissive"
|
16
|
-
$CPPFLAGS += " -
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
$CPPFLAGS += " -mssse3"
|
21
|
-
end
|
213
|
+
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
214
|
+
$CPPFLAGS += " -fvisibility=hidden "
|
215
|
+
cppflags_add_frame_pointer!
|
216
|
+
cppflags_add_cpu_extension!
|
22
217
|
|
23
218
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
24
219
|
|
25
220
|
$LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
|
221
|
+
$LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
|
26
222
|
|
27
223
|
if ENV['CXX']
|
28
224
|
puts "SETTING CXX"
|
29
225
|
CONFIG['CXX'] = ENV['CXX']
|
30
226
|
end
|
227
|
+
# 1.9 has no $CXXFLAGS
|
228
|
+
$CPPFLAGS += " #{ENV['CPPFLAGS']}" if ENV['CPPFLAGS']
|
229
|
+
$LDFLAGS += " #{ENV['LDFLAGS']}" if ENV['LDFLAGS']
|
31
230
|
|
32
231
|
CXX11_TEST = <<EOS
|
33
232
|
#if __cplusplus <= 199711L
|
@@ -57,94 +256,15 @@ if CONFIG['warnflags']
|
|
57
256
|
CONFIG['warnflags'].gsub!('-Wimplicit-function-declaration', '')
|
58
257
|
end
|
59
258
|
|
60
|
-
if enable_config('debug')
|
259
|
+
if enable_config('debug') || enable_config('asan')
|
61
260
|
CONFIG['debugflags'] << ' -ggdb3 -O0'
|
62
261
|
end
|
63
262
|
|
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
|
-
# 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
|
-
def force_platform_gem
|
89
|
-
gem_version = `gem --version`
|
90
|
-
return 'gem' unless $?.success?
|
91
|
-
|
92
|
-
return 'gem' if gem_version.to_f.zero? || gem_version.to_f >= 2.3
|
93
|
-
return 'gem' if RUBY_PLATFORM != 'x86_64-linux'
|
94
|
-
|
95
|
-
gem_binary = `which gem`
|
96
|
-
return 'gem' unless $?.success?
|
97
|
-
|
98
|
-
ruby = File.foreach(gem_binary.strip).first.sub(/^#!/, '').strip
|
99
|
-
unless File.file? ruby
|
100
|
-
warn "No valid ruby: #{ruby}"
|
101
|
-
return 'gem'
|
102
|
-
end
|
103
|
-
|
104
|
-
require 'tempfile'
|
105
|
-
file = Tempfile.new('sq_mini_racer')
|
106
|
-
file << <<EOS
|
107
|
-
require 'rubygems'
|
108
|
-
Gem.platforms.reject! { |it| it == 'ruby' }
|
109
|
-
#{IO.read(gem_binary.strip)}
|
110
|
-
EOS
|
111
|
-
file.close
|
112
|
-
"#{ruby} '#{file.path}'"
|
113
|
-
end
|
114
|
-
|
115
|
-
LIBV8_VERSION = '6.7.288.46.1'
|
116
|
-
libv8_rb = Dir.glob('**/libv8.rb').first
|
117
|
-
FileUtils.mkdir_p('gemdir')
|
118
|
-
unless libv8_rb
|
119
|
-
gem_name = libv8_gem_name
|
120
|
-
cmd = "#{fixup_libtinfo} #{force_platform_gem} install --version '= #{LIBV8_VERSION}' --install-dir gemdir #{gem_name}"
|
121
|
-
puts "Will try downloading #{gem_name} gem: #{cmd}"
|
122
|
-
`#{cmd}`
|
123
|
-
unless $?.success?
|
124
|
-
warn <<EOS
|
125
|
-
|
126
|
-
WARNING: Could not download a private copy of the libv8 gem. Please make
|
127
|
-
sure that you have internet access and that the `gem` binary is available.
|
128
|
-
|
129
|
-
EOS
|
130
|
-
end
|
131
|
-
|
132
|
-
libv8_rb = Dir.glob('**/libv8.rb').first
|
133
|
-
unless libv8_rb
|
134
|
-
warn <<EOS
|
135
|
-
|
136
|
-
WARNING: Could not find libv8 after the local copy of libv8 having supposedly
|
137
|
-
been installed.
|
138
|
-
|
139
|
-
EOS
|
140
|
-
end
|
141
|
-
end
|
263
|
+
Libv8::Node.configure_makefile
|
142
264
|
|
143
|
-
if
|
144
|
-
|
145
|
-
|
265
|
+
if enable_config('asan')
|
266
|
+
$CPPFLAGS.insert(0, " -fsanitize=address ")
|
267
|
+
$LDFLAGS.insert(0, " -fsanitize=address ")
|
146
268
|
end
|
147
269
|
|
148
|
-
require 'libv8'
|
149
|
-
Libv8.configure_makefile
|
150
270
|
create_makefile 'sq_mini_racer_extension'
|
@@ -8,12 +8,24 @@
|
|
8
8
|
# undef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
|
9
9
|
# undef HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P
|
10
10
|
|
11
|
-
#include <ruby.h>
|
12
11
|
#include <ruby/version.h>
|
12
|
+
|
13
|
+
// workaround for Ruby 2.3.0 on macOS
|
14
|
+
#if RUBY_API_VERSION_CODE == 20300
|
15
|
+
#ifdef _DARWIN_C_SOURCE
|
16
|
+
#ifndef __STDC_LIB_EXT1__
|
17
|
+
#undef HAVE_MEMSET_S
|
18
|
+
#endif
|
19
|
+
#endif
|
20
|
+
#endif
|
21
|
+
|
22
|
+
#include <ruby.h>
|
13
23
|
#if RUBY_API_VERSION_MAJOR > 1
|
14
24
|
#include <ruby/thread.h>
|
15
25
|
#endif
|
26
|
+
#include <ruby/io.h>
|
16
27
|
#include <v8.h>
|
28
|
+
#include <v8-profiler.h>
|
17
29
|
#include <libplatform/libplatform.h>
|
18
30
|
#include <ruby/encoding.h>
|
19
31
|
#include <pthread.h>
|
@@ -24,6 +36,8 @@
|
|
24
36
|
#include "compat.hpp"
|
25
37
|
#include "simdutf8check.h"
|
26
38
|
|
39
|
+
#include <time.h>
|
40
|
+
|
27
41
|
using namespace v8;
|
28
42
|
|
29
43
|
typedef struct {
|
@@ -37,6 +51,7 @@ public:
|
|
37
51
|
ArrayBuffer::Allocator* allocator;
|
38
52
|
StartupData* startup_data;
|
39
53
|
bool interrupted;
|
54
|
+
bool added_gc_cb;
|
40
55
|
pid_t pid;
|
41
56
|
VALUE mutex;
|
42
57
|
|
@@ -54,15 +69,12 @@ public:
|
|
54
69
|
|
55
70
|
|
56
71
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
57
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
72
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
58
73
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
59
74
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
60
75
|
}
|
61
76
|
|
62
|
-
~IsolateInfo()
|
63
|
-
void free_isolate(IsolateInfo*);
|
64
|
-
free_isolate(this);
|
65
|
-
}
|
77
|
+
~IsolateInfo();
|
66
78
|
|
67
79
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
68
80
|
|
@@ -139,6 +151,7 @@ typedef struct {
|
|
139
151
|
Local<Function> fun;
|
140
152
|
Local<Value> *argv;
|
141
153
|
EvalResult result;
|
154
|
+
size_t max_memory;
|
142
155
|
} FunctionCall;
|
143
156
|
|
144
157
|
enum IsolateFlags {
|
@@ -164,9 +177,14 @@ static VALUE rb_mJSON;
|
|
164
177
|
static VALUE rb_cFailedV8Conversion;
|
165
178
|
static VALUE rb_cDateTime = Qnil;
|
166
179
|
|
167
|
-
static Platform
|
180
|
+
static std::unique_ptr<Platform> current_platform = NULL;
|
168
181
|
static std::mutex platform_lock;
|
169
182
|
|
183
|
+
static pthread_attr_t *thread_attr_p;
|
184
|
+
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
185
|
+
static bool ruby_exiting = false; // guarded by exit_lock
|
186
|
+
static bool single_threaded = false;
|
187
|
+
|
170
188
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
171
189
|
bool platform_already_initialized = false;
|
172
190
|
|
@@ -178,6 +196,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
178
196
|
platform_lock.lock();
|
179
197
|
|
180
198
|
if (current_platform == NULL) {
|
199
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
200
|
+
single_threaded = true;
|
201
|
+
}
|
181
202
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
182
203
|
} else {
|
183
204
|
platform_already_initialized = true;
|
@@ -201,8 +222,8 @@ static void init_v8() {
|
|
201
222
|
|
202
223
|
if (current_platform == NULL) {
|
203
224
|
V8::InitializeICU();
|
204
|
-
current_platform = platform::
|
205
|
-
V8::InitializePlatform(current_platform);
|
225
|
+
current_platform = platform::NewDefaultPlatform();
|
226
|
+
V8::InitializePlatform(current_platform.get());
|
206
227
|
V8::Initialize();
|
207
228
|
}
|
208
229
|
|
@@ -245,15 +266,17 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
245
266
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
246
267
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
247
268
|
Local<Object> JSON = context->Global()->Get(
|
248
|
-
String::
|
269
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
270
|
+
.ToLocalChecked().As<Object>();
|
249
271
|
|
250
|
-
Local<Function> stringify = JSON->Get(
|
251
|
-
|
272
|
+
Local<Function> stringify = JSON->Get(
|
273
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
274
|
+
.ToLocalChecked().As<Function>();
|
252
275
|
|
253
|
-
Local<Object> object = local_value->ToObject();
|
276
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
254
277
|
const unsigned argc = 1;
|
255
278
|
Local<Value> argv[argc] = { object };
|
256
|
-
MaybeLocal<Value> json = stringify->Call(JSON, argc, argv);
|
279
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
257
280
|
|
258
281
|
if (json.IsEmpty()) {
|
259
282
|
evalRes.executed = false;
|
@@ -277,11 +300,21 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
277
300
|
evalRes.message = new Persistent<Value>();
|
278
301
|
Local<Message> message = trycatch.Message();
|
279
302
|
char buf[1000];
|
280
|
-
int len;
|
303
|
+
int len, line, column;
|
304
|
+
|
305
|
+
if (!message->GetLineNumber(context).To(&line)) {
|
306
|
+
line = 0;
|
307
|
+
}
|
308
|
+
|
309
|
+
if (!message->GetStartColumn(context).To(&column)) {
|
310
|
+
column = 0;
|
311
|
+
}
|
312
|
+
|
281
313
|
len = snprintf(buf, sizeof(buf), "%s at %s:%i:%i", *String::Utf8Value(isolate, message->Get()),
|
282
|
-
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString()),
|
283
|
-
|
284
|
-
|
314
|
+
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString(context).ToLocalChecked()),
|
315
|
+
line,
|
316
|
+
column);
|
317
|
+
|
285
318
|
if ((size_t) len >= sizeof(buf)) {
|
286
319
|
len = sizeof(buf) - 1;
|
287
320
|
buf[len] = '\0';
|
@@ -292,12 +325,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
292
325
|
} else if(trycatch.HasTerminated()) {
|
293
326
|
evalRes.terminated = true;
|
294
327
|
evalRes.message = new Persistent<Value>();
|
295
|
-
Local<String> tmp = String::
|
328
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
296
329
|
evalRes.message->Reset(isolate, tmp);
|
297
330
|
}
|
298
331
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
299
332
|
evalRes.backtrace = new Persistent<Value>();
|
300
|
-
evalRes.backtrace->Reset(isolate,
|
333
|
+
evalRes.backtrace->Reset(isolate,
|
334
|
+
trycatch.StackTrace(context).ToLocalChecked()->ToString(context).ToLocalChecked());
|
301
335
|
}
|
302
336
|
}
|
303
337
|
}
|
@@ -312,7 +346,8 @@ nogvl_context_eval(void* arg) {
|
|
312
346
|
|
313
347
|
EvalParams* eval_params = (EvalParams*)arg;
|
314
348
|
EvalResult* result = eval_params->result;
|
315
|
-
|
349
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
350
|
+
Isolate* isolate = isolate_info->isolate;
|
316
351
|
|
317
352
|
Isolate::Scope isolate_scope(isolate);
|
318
353
|
HandleScope handle_scope(isolate);
|
@@ -356,7 +391,10 @@ nogvl_context_eval(void* arg) {
|
|
356
391
|
// parsing successful
|
357
392
|
if (eval_params->max_memory > 0) {
|
358
393
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
394
|
+
if (!isolate_info->added_gc_cb) {
|
359
395
|
isolate->AddGCEpilogueCallback(gc_callback);
|
396
|
+
isolate_info->added_gc_cb = true;
|
397
|
+
}
|
360
398
|
}
|
361
399
|
|
362
400
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -369,6 +407,12 @@ nogvl_context_eval(void* arg) {
|
|
369
407
|
return 0;
|
370
408
|
}
|
371
409
|
|
410
|
+
static VALUE new_empty_failed_conv_obj() {
|
411
|
+
// TODO isolate code that translates execption to ruby
|
412
|
+
// exception so we can properly return it
|
413
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
414
|
+
}
|
415
|
+
|
372
416
|
// assumes isolate locking is in place
|
373
417
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
374
418
|
Local<Value> value) {
|
@@ -381,11 +425,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
381
425
|
}
|
382
426
|
|
383
427
|
if (value->IsInt32()) {
|
384
|
-
return INT2FIX(value->Int32Value());
|
428
|
+
return INT2FIX(value->Int32Value(context).ToChecked());
|
385
429
|
}
|
386
430
|
|
387
431
|
if (value->IsNumber()) {
|
388
|
-
return rb_float_new(value->NumberValue());
|
432
|
+
return rb_float_new(value->NumberValue(context).ToChecked());
|
389
433
|
}
|
390
434
|
|
391
435
|
if (value->IsTrue()) {
|
@@ -400,8 +444,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
400
444
|
VALUE rb_array = rb_ary_new();
|
401
445
|
Local<Array> arr = Local<Array>::Cast(value);
|
402
446
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
403
|
-
|
404
|
-
|
447
|
+
MaybeLocal<Value> element = arr->Get(context, i);
|
448
|
+
if (element.IsEmpty()) {
|
449
|
+
continue;
|
450
|
+
}
|
451
|
+
VALUE rb_elem = convert_v8_to_ruby(isolate, context, element.ToLocalChecked());
|
405
452
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
406
453
|
return rb_elem;
|
407
454
|
}
|
@@ -426,31 +473,52 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
426
473
|
VALUE rb_hash = rb_hash_new();
|
427
474
|
TryCatch trycatch(isolate);
|
428
475
|
|
429
|
-
Local<Object> object = value->ToObject();
|
476
|
+
Local<Object> object = value->ToObject(context).ToLocalChecked();
|
430
477
|
auto maybe_props = object->GetOwnPropertyNames(context);
|
431
478
|
if (!maybe_props.IsEmpty()) {
|
432
479
|
Local<Array> props = maybe_props.ToLocalChecked();
|
433
480
|
for(uint32_t i=0; i < props->Length(); i++) {
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
481
|
+
MaybeLocal<Value> key = props->Get(context, i);
|
482
|
+
if (key.IsEmpty()) {
|
483
|
+
return rb_funcall(rb_cFailedV8Conversion, rb_intern("new"), 1, rb_str_new2(""));
|
484
|
+
}
|
485
|
+
VALUE rb_key = convert_v8_to_ruby(isolate, context, key.ToLocalChecked());
|
438
486
|
|
439
|
-
|
440
|
-
//
|
441
|
-
|
442
|
-
|
487
|
+
MaybeLocal<Value> prop_value = object->Get(context, key.ToLocalChecked());
|
488
|
+
// this may have failed due to Get raising
|
489
|
+
if (prop_value.IsEmpty() || trycatch.HasCaught()) {
|
490
|
+
return new_empty_failed_conv_obj();
|
443
491
|
}
|
444
492
|
|
445
|
-
VALUE rb_value = convert_v8_to_ruby(
|
493
|
+
VALUE rb_value = convert_v8_to_ruby(
|
494
|
+
isolate, context, prop_value.ToLocalChecked());
|
446
495
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
447
496
|
}
|
448
497
|
}
|
449
498
|
return rb_hash;
|
450
499
|
}
|
451
500
|
|
452
|
-
|
453
|
-
|
501
|
+
if (value->IsSymbol()) {
|
502
|
+
v8::String::Utf8Value symbol_name(isolate,
|
503
|
+
Local<Symbol>::Cast(value)->Name());
|
504
|
+
|
505
|
+
VALUE str_symbol = rb_enc_str_new(
|
506
|
+
*symbol_name,
|
507
|
+
symbol_name.length(),
|
508
|
+
rb_enc_find("utf-8")
|
509
|
+
);
|
510
|
+
|
511
|
+
return ID2SYM(rb_intern_str(str_symbol));
|
512
|
+
}
|
513
|
+
|
514
|
+
MaybeLocal<String> rstr_maybe = value->ToString(context);
|
515
|
+
|
516
|
+
if (rstr_maybe.IsEmpty()) {
|
517
|
+
return Qnil;
|
518
|
+
} else {
|
519
|
+
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
520
|
+
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
521
|
+
}
|
454
522
|
}
|
455
523
|
|
456
524
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -547,7 +615,7 @@ treat_as_latin1:
|
|
547
615
|
return v8str.ToLocalChecked();
|
548
616
|
}
|
549
617
|
|
550
|
-
static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value)
|
618
|
+
static Local<Value> convert_ruby_to_v8(Isolate* isolate, Local<Context> context, VALUE value)
|
551
619
|
{
|
552
620
|
EscapableHandleScope scope(isolate);
|
553
621
|
|
@@ -561,87 +629,87 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value)
|
|
561
629
|
VALUE klass;
|
562
630
|
|
563
631
|
switch (TYPE(value)) {
|
564
|
-
|
632
|
+
case T_FIXNUM:
|
565
633
|
{
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
634
|
+
fixnum = NUM2LONG(value);
|
635
|
+
if (fixnum > INT_MAX)
|
636
|
+
{
|
637
|
+
return scope.Escape(Number::New(isolate, (double)fixnum));
|
638
|
+
}
|
639
|
+
return scope.Escape(Integer::New(isolate, (int)fixnum));
|
572
640
|
}
|
573
|
-
|
574
|
-
|
575
|
-
|
641
|
+
case T_FLOAT:
|
642
|
+
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
643
|
+
case T_STRING:
|
576
644
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
645
|
+
case T_NIL:
|
646
|
+
return scope.Escape(Null(isolate));
|
647
|
+
case T_TRUE:
|
648
|
+
return scope.Escape(True(isolate));
|
649
|
+
case T_FALSE:
|
650
|
+
return scope.Escape(False(isolate));
|
651
|
+
case T_ARRAY:
|
584
652
|
{
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
653
|
+
length = RARRAY_LEN(value);
|
654
|
+
array = Array::New(isolate, (int)length);
|
655
|
+
for(i=0; i<length; i++) {
|
656
|
+
array->Set(context, i, convert_ruby_to_v8(isolate, context, rb_ary_entry(value, i)));
|
657
|
+
}
|
658
|
+
return scope.Escape(array);
|
591
659
|
}
|
592
|
-
|
660
|
+
case T_HASH:
|
593
661
|
{
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
662
|
+
object = Object::New(isolate);
|
663
|
+
hash_as_array = rb_funcall(value, rb_intern("to_a"), 0);
|
664
|
+
length = RARRAY_LEN(hash_as_array);
|
665
|
+
for(i=0; i<length; i++) {
|
666
|
+
pair = rb_ary_entry(hash_as_array, i);
|
667
|
+
object->Set(context, convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 0)),
|
668
|
+
convert_ruby_to_v8(isolate, context, rb_ary_entry(pair, 1)));
|
669
|
+
}
|
670
|
+
return scope.Escape(object);
|
603
671
|
}
|
604
|
-
|
672
|
+
case T_SYMBOL:
|
605
673
|
{
|
606
|
-
|
674
|
+
value = rb_funcall(value, rb_intern("to_s"), 0);
|
607
675
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
608
676
|
}
|
609
|
-
|
677
|
+
case T_DATA:
|
610
678
|
{
|
611
|
-
|
612
|
-
|
679
|
+
klass = rb_funcall(value, rb_intern("class"), 0);
|
680
|
+
if (klass == rb_cTime || klass == rb_cDateTime)
|
681
|
+
{
|
682
|
+
if (klass == rb_cDateTime)
|
613
683
|
{
|
614
|
-
|
615
|
-
{
|
616
|
-
value = rb_funcall(value, rb_intern("to_time"), 0);
|
617
|
-
}
|
618
|
-
value = rb_funcall(value, rb_intern("to_f"), 0);
|
619
|
-
return scope.Escape(Date::New(isolate, NUM2DBL(value) * 1000));
|
684
|
+
value = rb_funcall(value, rb_intern("to_time"), 0);
|
620
685
|
}
|
686
|
+
value = rb_funcall(value, rb_intern("to_f"), 0);
|
687
|
+
return scope.Escape(Date::New(context, NUM2DBL(value) * 1000).ToLocalChecked());
|
688
|
+
}
|
621
689
|
// break intentionally missing
|
622
690
|
}
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
691
|
+
case T_OBJECT:
|
692
|
+
case T_CLASS:
|
693
|
+
case T_ICLASS:
|
694
|
+
case T_MODULE:
|
695
|
+
case T_REGEXP:
|
696
|
+
case T_MATCH:
|
697
|
+
case T_STRUCT:
|
698
|
+
case T_BIGNUM:
|
699
|
+
case T_FILE:
|
700
|
+
case T_UNDEF:
|
701
|
+
case T_NODE:
|
702
|
+
default:
|
635
703
|
{
|
636
704
|
if (rb_respond_to(value, rb_intern("to_s"))) {
|
637
705
|
// TODO: if this throws we're screwed
|
638
706
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
639
707
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
640
708
|
}
|
641
|
-
return scope.Escape(
|
709
|
+
return scope.Escape(
|
710
|
+
String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
642
711
|
}
|
643
712
|
}
|
644
|
-
|
645
713
|
}
|
646
714
|
|
647
715
|
static void unblock_eval(void *ptr) {
|
@@ -649,6 +717,81 @@ static void unblock_eval(void *ptr) {
|
|
649
717
|
eval->context_info->isolate_info->interrupted = true;
|
650
718
|
}
|
651
719
|
|
720
|
+
/*
|
721
|
+
* The implementations of the run_extra_code(), create_snapshot_data_blob() and
|
722
|
+
* warm_up_snapshot_data_blob() functions have been derived from V8's test suite.
|
723
|
+
*/
|
724
|
+
static bool run_extra_code(Isolate *isolate, Local<v8::Context> context,
|
725
|
+
const char *utf8_source, const char *name) {
|
726
|
+
Context::Scope context_scope(context);
|
727
|
+
TryCatch try_catch(isolate);
|
728
|
+
Local<String> source_string;
|
729
|
+
if (!String::NewFromUtf8(isolate, utf8_source).ToLocal(&source_string)) {
|
730
|
+
return false;
|
731
|
+
}
|
732
|
+
Local<String> resource_name =
|
733
|
+
String::NewFromUtf8(isolate, name).ToLocalChecked();
|
734
|
+
ScriptOrigin origin(resource_name);
|
735
|
+
ScriptCompiler::Source source(source_string, origin);
|
736
|
+
Local<Script> script;
|
737
|
+
if (!ScriptCompiler::Compile(context, &source).ToLocal(&script))
|
738
|
+
return false;
|
739
|
+
if (script->Run(context).IsEmpty()) return false;
|
740
|
+
return true;
|
741
|
+
}
|
742
|
+
|
743
|
+
static StartupData
|
744
|
+
create_snapshot_data_blob(const char *embedded_source = nullptr) {
|
745
|
+
Isolate *isolate = Isolate::Allocate();
|
746
|
+
|
747
|
+
// Optionally run a script to embed, and serialize to create a snapshot blob.
|
748
|
+
SnapshotCreator snapshot_creator(isolate);
|
749
|
+
{
|
750
|
+
HandleScope scope(isolate);
|
751
|
+
Local<v8::Context> context = v8::Context::New(isolate);
|
752
|
+
if (embedded_source != nullptr &&
|
753
|
+
!run_extra_code(isolate, context, embedded_source, "<embedded>")) {
|
754
|
+
return {};
|
755
|
+
}
|
756
|
+
snapshot_creator.SetDefaultContext(context);
|
757
|
+
}
|
758
|
+
return snapshot_creator.CreateBlob(
|
759
|
+
SnapshotCreator::FunctionCodeHandling::kClear);
|
760
|
+
}
|
761
|
+
|
762
|
+
StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
763
|
+
const char *warmup_source) {
|
764
|
+
// Use following steps to create a warmed up snapshot blob from a cold one:
|
765
|
+
// - Create a new isolate from the cold snapshot.
|
766
|
+
// - Create a new context to run the warmup script. This will trigger
|
767
|
+
// compilation of executed functions.
|
768
|
+
// - Create a new context. This context will be unpolluted.
|
769
|
+
// - Serialize the isolate and the second context into a new snapshot blob.
|
770
|
+
StartupData result = {nullptr, 0};
|
771
|
+
|
772
|
+
if (cold_snapshot_blob.raw_size > 0 && cold_snapshot_blob.data != nullptr &&
|
773
|
+
warmup_source != NULL) {
|
774
|
+
SnapshotCreator snapshot_creator(nullptr, &cold_snapshot_blob);
|
775
|
+
Isolate *isolate = snapshot_creator.GetIsolate();
|
776
|
+
{
|
777
|
+
HandleScope scope(isolate);
|
778
|
+
Local<Context> context = Context::New(isolate);
|
779
|
+
if (!run_extra_code(isolate, context, warmup_source, "<warm-up>")) {
|
780
|
+
return result;
|
781
|
+
}
|
782
|
+
}
|
783
|
+
{
|
784
|
+
HandleScope handle_scope(isolate);
|
785
|
+
isolate->ContextDisposedNotification(false);
|
786
|
+
Local<Context> context = Context::New(isolate);
|
787
|
+
snapshot_creator.SetDefaultContext(context);
|
788
|
+
}
|
789
|
+
result = snapshot_creator.CreateBlob(
|
790
|
+
SnapshotCreator::FunctionCodeHandling::kKeep);
|
791
|
+
}
|
792
|
+
return result;
|
793
|
+
}
|
794
|
+
|
652
795
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
653
796
|
SnapshotInfo* snapshot_info;
|
654
797
|
Data_Get_Struct(self, SnapshotInfo, snapshot_info);
|
@@ -667,7 +810,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
667
810
|
|
668
811
|
init_v8();
|
669
812
|
|
670
|
-
StartupData startup_data =
|
813
|
+
StartupData startup_data = create_snapshot_data_blob(RSTRING_PTR(str));
|
671
814
|
|
672
815
|
if (startup_data.data == NULL && startup_data.raw_size == 0) {
|
673
816
|
rb_raise(rb_eSnapshotError, "Could not create snapshot, most likely the source is incorrect");
|
@@ -698,7 +841,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
698
841
|
init_v8();
|
699
842
|
|
700
843
|
StartupData cold_startup_data = {snapshot_info->data, snapshot_info->raw_size};
|
701
|
-
StartupData warm_startup_data =
|
844
|
+
StartupData warm_startup_data = warm_up_snapshot_data_blob(cold_startup_data, RSTRING_PTR(str));
|
702
845
|
|
703
846
|
if (warm_startup_data.data == NULL && warm_startup_data.raw_size == 0) {
|
704
847
|
rb_raise(rb_eSnapshotError, "Could not warm up snapshot, most likely the source is incorrect");
|
@@ -761,6 +904,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
761
904
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
762
905
|
}
|
763
906
|
|
907
|
+
static VALUE rb_isolate_low_memory_notification(VALUE self) {
|
908
|
+
IsolateInfo* isolate_info;
|
909
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
910
|
+
|
911
|
+
if (current_platform == NULL) return Qfalse;
|
912
|
+
|
913
|
+
isolate_info->isolate->LowMemoryNotification();
|
914
|
+
return Qnil;
|
915
|
+
}
|
916
|
+
|
917
|
+
static VALUE rb_isolate_pump_message_loop(VALUE self) {
|
918
|
+
IsolateInfo* isolate_info;
|
919
|
+
Data_Get_Struct(self, IsolateInfo, isolate_info);
|
920
|
+
|
921
|
+
if (current_platform == NULL) return Qfalse;
|
922
|
+
|
923
|
+
if (platform::PumpMessageLoop(current_platform.get(), isolate_info->isolate)){
|
924
|
+
return Qtrue;
|
925
|
+
} else {
|
926
|
+
return Qfalse;
|
927
|
+
}
|
928
|
+
}
|
929
|
+
|
764
930
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
765
931
|
ContextInfo* context_info;
|
766
932
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -877,8 +1043,8 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
877
1043
|
Local<Value> tmp = Local<Value>::New(isolate, *result.value);
|
878
1044
|
|
879
1045
|
if (result.json) {
|
880
|
-
Local<String> rstr = tmp->ToString();
|
881
|
-
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(), rb_enc_find("utf-8"));
|
1046
|
+
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
1047
|
+
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
882
1048
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
883
1049
|
} else {
|
884
1050
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
@@ -999,6 +1165,8 @@ gvl_ruby_callback(void* data) {
|
|
999
1165
|
VALUE result;
|
1000
1166
|
VALUE self;
|
1001
1167
|
VALUE parent;
|
1168
|
+
ContextInfo* context_info;
|
1169
|
+
|
1002
1170
|
{
|
1003
1171
|
HandleScope scope(args->GetIsolate());
|
1004
1172
|
Local<External> external = Local<External>::Cast(args->Data());
|
@@ -1011,7 +1179,6 @@ gvl_ruby_callback(void* data) {
|
|
1011
1179
|
return NULL;
|
1012
1180
|
}
|
1013
1181
|
|
1014
|
-
ContextInfo* context_info;
|
1015
1182
|
Data_Get_Struct(parent, ContextInfo, context_info);
|
1016
1183
|
|
1017
1184
|
if (length > 0) {
|
@@ -1021,7 +1188,7 @@ gvl_ruby_callback(void* data) {
|
|
1021
1188
|
for (int i = 0; i < length; i++) {
|
1022
1189
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1023
1190
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1024
|
-
|
1191
|
+
*context_info->context, value);
|
1025
1192
|
rb_ary_push(ruby_args, tmp);
|
1026
1193
|
}
|
1027
1194
|
}
|
@@ -1034,7 +1201,9 @@ gvl_ruby_callback(void* data) {
|
|
1034
1201
|
callback_data.failed = false;
|
1035
1202
|
|
1036
1203
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1037
|
-
args->GetIsolate()->ThrowException(
|
1204
|
+
args->GetIsolate()->ThrowException(
|
1205
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1206
|
+
"Terminated execution during transition from Ruby to JS"));
|
1038
1207
|
args->GetIsolate()->TerminateExecution();
|
1039
1208
|
if (length > 0) {
|
1040
1209
|
rb_ary_clear(ruby_args);
|
@@ -1048,11 +1217,11 @@ gvl_ruby_callback(void* data) {
|
|
1048
1217
|
|
1049
1218
|
if(callback_data.failed) {
|
1050
1219
|
rb_iv_set(parent, "@current_exception", result);
|
1051
|
-
args->GetIsolate()->ThrowException(String::
|
1220
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1052
1221
|
}
|
1053
1222
|
else {
|
1054
1223
|
HandleScope scope(args->GetIsolate());
|
1055
|
-
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), result);
|
1224
|
+
Handle<Value> v8_result = convert_ruby_to_v8(args->GetIsolate(), context_info->context->Get(args->GetIsolate()), result);
|
1056
1225
|
args->GetReturnValue().Set(v8_result);
|
1057
1226
|
}
|
1058
1227
|
|
@@ -1109,8 +1278,10 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1109
1278
|
Local<Context> context = context_info->context->Get(isolate);
|
1110
1279
|
Context::Scope context_scope(context);
|
1111
1280
|
|
1112
|
-
Local<String> v8_str =
|
1113
|
-
|
1281
|
+
Local<String> v8_str =
|
1282
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1283
|
+
NewStringType::kNormal, (int)RSTRING_LEN(name))
|
1284
|
+
.ToLocalChecked();
|
1114
1285
|
|
1115
1286
|
// copy self so we can access from v8 external
|
1116
1287
|
VALUE* self_copy;
|
@@ -1120,24 +1291,38 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1120
1291
|
Local<Value> external = External::New(isolate, self_copy);
|
1121
1292
|
|
1122
1293
|
if (parent_object == Qnil) {
|
1123
|
-
context->Global()->Set(
|
1124
|
-
|
1294
|
+
context->Global()->Set(
|
1295
|
+
context,
|
1296
|
+
v8_str,
|
1297
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1298
|
+
->GetFunction(context)
|
1299
|
+
.ToLocalChecked());
|
1125
1300
|
|
1126
|
-
|
1127
|
-
|
1301
|
+
} else {
|
1302
|
+
Local<String> eval =
|
1303
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(parent_object_eval),
|
1304
|
+
NewStringType::kNormal,
|
1305
|
+
(int)RSTRING_LEN(parent_object_eval))
|
1306
|
+
.ToLocalChecked();
|
1128
1307
|
|
1129
1308
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1130
1309
|
if (parsed_script.IsEmpty()) {
|
1131
|
-
|
1310
|
+
parse_error = true;
|
1132
1311
|
} else {
|
1133
|
-
MaybeLocal<Value> maybe_value =
|
1312
|
+
MaybeLocal<Value> maybe_value =
|
1313
|
+
parsed_script.ToLocalChecked()->Run(context);
|
1134
1314
|
attach_error = true;
|
1135
1315
|
|
1136
1316
|
if (!maybe_value.IsEmpty()) {
|
1137
1317
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1138
|
-
if (value->IsObject()){
|
1139
|
-
|
1140
|
-
|
1318
|
+
if (value->IsObject()) {
|
1319
|
+
value.As<Object>()->Set(
|
1320
|
+
context,
|
1321
|
+
v8_str,
|
1322
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1323
|
+
->GetFunction(context)
|
1324
|
+
.ToLocalChecked());
|
1325
|
+
attach_error = false;
|
1141
1326
|
}
|
1142
1327
|
}
|
1143
1328
|
}
|
@@ -1167,35 +1352,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1167
1352
|
return context_info->isolate_info->mutex;
|
1168
1353
|
}
|
1169
1354
|
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
if (isolate_info->isolate) {
|
1177
|
-
if (isolate_info->interrupted) {
|
1178
|
-
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, it can not be disposed and memory will not be reclaimed till the Ruby process exits.\n");
|
1355
|
+
IsolateInfo::~IsolateInfo() {
|
1356
|
+
if (isolate) {
|
1357
|
+
if (this->interrupted) {
|
1358
|
+
fprintf(stderr, "WARNING: V8 isolate was interrupted by Ruby, "
|
1359
|
+
"it can not be disposed and memory will not be "
|
1360
|
+
"reclaimed till the Ruby process exits.\n");
|
1179
1361
|
} else {
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1362
|
+
if (this->pid != getpid() && !single_threaded) {
|
1363
|
+
fprintf(stderr, "WARNING: V8 isolate was forked, "
|
1364
|
+
"it can not be disposed and "
|
1365
|
+
"memory will not be reclaimed "
|
1366
|
+
"till the Ruby process exits.\n"
|
1367
|
+
"It is VERY likely your process will hang.\n"
|
1368
|
+
"If you wish to use v8 in forked environment "
|
1369
|
+
"please ensure the platform is initialized with:\n"
|
1370
|
+
"MiniRacer::Platform.set_flags! :single_threaded\n"
|
1371
|
+
);
|
1183
1372
|
} else {
|
1184
|
-
|
1373
|
+
isolate->Dispose();
|
1185
1374
|
}
|
1186
1375
|
}
|
1187
|
-
|
1376
|
+
isolate = nullptr;
|
1188
1377
|
}
|
1189
1378
|
|
1190
|
-
if (
|
1191
|
-
delete[]
|
1192
|
-
delete
|
1379
|
+
if (startup_data) {
|
1380
|
+
delete[] startup_data->data;
|
1381
|
+
delete startup_data;
|
1193
1382
|
}
|
1194
1383
|
|
1195
|
-
delete
|
1384
|
+
delete allocator;
|
1196
1385
|
}
|
1197
1386
|
|
1198
|
-
static void
|
1387
|
+
static void free_context_raw(void *arg) {
|
1199
1388
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1200
1389
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1201
1390
|
Persistent<Context>* context = context_info->context;
|
@@ -1212,6 +1401,20 @@ static void *free_context_raw(void* arg) {
|
|
1212
1401
|
}
|
1213
1402
|
|
1214
1403
|
xfree(context_info);
|
1404
|
+
}
|
1405
|
+
|
1406
|
+
static void *free_context_thr(void* arg) {
|
1407
|
+
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
1408
|
+
return NULL;
|
1409
|
+
}
|
1410
|
+
if (ruby_exiting) {
|
1411
|
+
return NULL;
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
free_context_raw(arg);
|
1415
|
+
|
1416
|
+
pthread_rwlock_unlock(&exit_lock);
|
1417
|
+
|
1215
1418
|
return NULL;
|
1216
1419
|
}
|
1217
1420
|
|
@@ -1226,22 +1429,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1226
1429
|
|
1227
1430
|
if (isolate_info && isolate_info->refs() > 1) {
|
1228
1431
|
pthread_t free_context_thread;
|
1229
|
-
|
1432
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1433
|
+
free_context_thr, (void*)context_info_copy)) {
|
1230
1434
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1231
1435
|
}
|
1232
|
-
|
1233
1436
|
} else {
|
1234
1437
|
free_context_raw(context_info_copy);
|
1235
1438
|
}
|
1236
1439
|
|
1237
|
-
if (context_info->context && isolate_info && isolate_info->isolate) {
|
1238
1440
|
context_info->context = NULL;
|
1239
|
-
}
|
1240
|
-
|
1241
|
-
if (isolate_info) {
|
1242
1441
|
context_info->isolate_info = NULL;
|
1243
1442
|
}
|
1244
|
-
}
|
1245
1443
|
|
1246
1444
|
static void deallocate_isolate(void* data) {
|
1247
1445
|
|
@@ -1255,7 +1453,7 @@ static void mark_isolate(void* data) {
|
|
1255
1453
|
isolate_info->mark();
|
1256
1454
|
}
|
1257
1455
|
|
1258
|
-
void deallocate(void* data) {
|
1456
|
+
static void deallocate(void* data) {
|
1259
1457
|
ContextInfo* context_info = (ContextInfo*)data;
|
1260
1458
|
|
1261
1459
|
free_context(context_info);
|
@@ -1270,22 +1468,22 @@ static void mark_context(void* data) {
|
|
1270
1468
|
}
|
1271
1469
|
}
|
1272
1470
|
|
1273
|
-
void deallocate_external_function(void * data) {
|
1471
|
+
static void deallocate_external_function(void * data) {
|
1274
1472
|
xfree(data);
|
1275
1473
|
}
|
1276
1474
|
|
1277
|
-
void deallocate_snapshot(void * data) {
|
1475
|
+
static void deallocate_snapshot(void * data) {
|
1278
1476
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1279
1477
|
delete[] snapshot_info->data;
|
1280
1478
|
xfree(snapshot_info);
|
1281
1479
|
}
|
1282
1480
|
|
1283
|
-
VALUE allocate_external_function(VALUE klass) {
|
1481
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1284
1482
|
VALUE* self = ALLOC(VALUE);
|
1285
1483
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1286
1484
|
}
|
1287
1485
|
|
1288
|
-
VALUE allocate(VALUE klass) {
|
1486
|
+
static VALUE allocate(VALUE klass) {
|
1289
1487
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1290
1488
|
context_info->isolate_info = NULL;
|
1291
1489
|
context_info->context = NULL;
|
@@ -1293,7 +1491,7 @@ VALUE allocate(VALUE klass) {
|
|
1293
1491
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1294
1492
|
}
|
1295
1493
|
|
1296
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1494
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1297
1495
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1298
1496
|
snapshot_info->data = NULL;
|
1299
1497
|
snapshot_info->raw_size = 0;
|
@@ -1301,7 +1499,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1301
1499
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1302
1500
|
}
|
1303
1501
|
|
1304
|
-
VALUE allocate_isolate(VALUE klass) {
|
1502
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1305
1503
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1306
1504
|
|
1307
1505
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1340,6 +1538,71 @@ rb_heap_stats(VALUE self) {
|
|
1340
1538
|
return rval;
|
1341
1539
|
}
|
1342
1540
|
|
1541
|
+
// https://github.com/bnoordhuis/node-heapdump/blob/master/src/heapdump.cc
|
1542
|
+
class FileOutputStream : public OutputStream {
|
1543
|
+
public:
|
1544
|
+
FileOutputStream(FILE* stream) : stream_(stream) {}
|
1545
|
+
|
1546
|
+
virtual int GetChunkSize() {
|
1547
|
+
return 65536;
|
1548
|
+
}
|
1549
|
+
|
1550
|
+
virtual void EndOfStream() {}
|
1551
|
+
|
1552
|
+
virtual WriteResult WriteAsciiChunk(char* data, int size) {
|
1553
|
+
const size_t len = static_cast<size_t>(size);
|
1554
|
+
size_t off = 0;
|
1555
|
+
|
1556
|
+
while (off < len && !feof(stream_) && !ferror(stream_))
|
1557
|
+
off += fwrite(data + off, 1, len - off, stream_);
|
1558
|
+
|
1559
|
+
return off == len ? kContinue : kAbort;
|
1560
|
+
}
|
1561
|
+
|
1562
|
+
private:
|
1563
|
+
FILE* stream_;
|
1564
|
+
};
|
1565
|
+
|
1566
|
+
|
1567
|
+
static VALUE
|
1568
|
+
rb_heap_snapshot(VALUE self, VALUE file) {
|
1569
|
+
|
1570
|
+
rb_io_t *fptr;
|
1571
|
+
|
1572
|
+
fptr = RFILE(file)->fptr;
|
1573
|
+
|
1574
|
+
if (!fptr) return Qfalse;
|
1575
|
+
|
1576
|
+
FILE* fp;
|
1577
|
+
fp = fdopen(fptr->fd, "w");
|
1578
|
+
if (fp == NULL) return Qfalse;
|
1579
|
+
|
1580
|
+
|
1581
|
+
ContextInfo* context_info;
|
1582
|
+
Data_Get_Struct(self, ContextInfo, context_info);
|
1583
|
+
Isolate* isolate;
|
1584
|
+
isolate = context_info->isolate_info ? context_info->isolate_info->isolate : NULL;
|
1585
|
+
|
1586
|
+
if (!isolate) return Qfalse;
|
1587
|
+
|
1588
|
+
Locker lock(isolate);
|
1589
|
+
Isolate::Scope isolate_scope(isolate);
|
1590
|
+
HandleScope handle_scope(isolate);
|
1591
|
+
|
1592
|
+
HeapProfiler* heap_profiler = isolate->GetHeapProfiler();
|
1593
|
+
|
1594
|
+
const HeapSnapshot* const snap = heap_profiler->TakeHeapSnapshot();
|
1595
|
+
|
1596
|
+
FileOutputStream stream(fp);
|
1597
|
+
snap->Serialize(&stream, HeapSnapshot::kJSON);
|
1598
|
+
|
1599
|
+
fflush(fp);
|
1600
|
+
|
1601
|
+
const_cast<HeapSnapshot*>(snap)->Delete();
|
1602
|
+
|
1603
|
+
return Qtrue;
|
1604
|
+
}
|
1605
|
+
|
1343
1606
|
static VALUE
|
1344
1607
|
rb_context_stop(VALUE self) {
|
1345
1608
|
|
@@ -1392,13 +1655,23 @@ nogvl_context_call(void *args) {
|
|
1392
1655
|
if (!call) {
|
1393
1656
|
return 0;
|
1394
1657
|
}
|
1395
|
-
|
1658
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1659
|
+
Isolate* isolate = isolate_info->isolate;
|
1396
1660
|
|
1397
1661
|
// in gvl flag
|
1398
1662
|
isolate->SetData(IN_GVL, (void*)false);
|
1399
1663
|
// terminate ASAP
|
1400
1664
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1401
1665
|
|
1666
|
+
if (call->max_memory > 0) {
|
1667
|
+
isolate->SetData(MEM_SOFTLIMIT_VALUE, &call->max_memory);
|
1668
|
+
isolate->SetData(MEM_SOFTLIMIT_REACHED, (void*)false);
|
1669
|
+
if (!isolate_info->added_gc_cb) {
|
1670
|
+
isolate->AddGCEpilogueCallback(gc_callback);
|
1671
|
+
isolate_info->added_gc_cb = true;
|
1672
|
+
}
|
1673
|
+
}
|
1674
|
+
|
1402
1675
|
Isolate::Scope isolate_scope(isolate);
|
1403
1676
|
EscapableHandleScope handle_scope(isolate);
|
1404
1677
|
TryCatch trycatch(isolate);
|
@@ -1456,6 +1729,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1456
1729
|
call_argv = argv + 1;
|
1457
1730
|
}
|
1458
1731
|
|
1732
|
+
call.max_memory = 0;
|
1733
|
+
VALUE mem_softlimit = rb_iv_get(self, "@max_memory");
|
1734
|
+
if (mem_softlimit != Qnil) {
|
1735
|
+
unsigned long sl_int = NUM2ULONG(mem_softlimit);
|
1736
|
+
call.max_memory = (size_t)sl_int;
|
1737
|
+
}
|
1738
|
+
|
1459
1739
|
bool missingFunction = false;
|
1460
1740
|
{
|
1461
1741
|
Locker lock(isolate);
|
@@ -1467,8 +1747,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1467
1747
|
|
1468
1748
|
// examples of such usage can be found in
|
1469
1749
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1470
|
-
|
1471
|
-
MaybeLocal<v8::Value> val
|
1750
|
+
MaybeLocal<String> fname = String::NewFromUtf8(isolate, call.function_name);
|
1751
|
+
MaybeLocal<v8::Value> val;
|
1752
|
+
if (!fname.IsEmpty()) {
|
1753
|
+
val = context->Global()->Get(context, fname.ToLocalChecked());
|
1754
|
+
}
|
1472
1755
|
|
1473
1756
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1474
1757
|
missingFunction = true;
|
@@ -1484,7 +1767,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1484
1767
|
return Qnil;
|
1485
1768
|
}
|
1486
1769
|
for(int i=0; i < fun_argc; i++) {
|
1487
|
-
call.argv[i] = convert_ruby_to_v8(isolate, call_argv[i]);
|
1770
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1488
1771
|
}
|
1489
1772
|
}
|
1490
1773
|
#if RUBY_API_VERSION_MAJOR > 1
|
@@ -1517,12 +1800,44 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1517
1800
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1518
1801
|
}
|
1519
1802
|
|
1803
|
+
static void set_ruby_exiting(VALUE value) {
|
1804
|
+
(void)value;
|
1805
|
+
|
1806
|
+
int res = pthread_rwlock_wrlock(&exit_lock);
|
1807
|
+
|
1808
|
+
ruby_exiting = true;
|
1809
|
+
if (res == 0) {
|
1810
|
+
pthread_rwlock_unlock(&exit_lock);
|
1811
|
+
}
|
1812
|
+
}
|
1813
|
+
|
1814
|
+
static VALUE rb_monotime(VALUE self) {
|
1815
|
+
struct timespec ts;
|
1816
|
+
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
|
1817
|
+
return INT2FIX(-1);
|
1818
|
+
}
|
1819
|
+
|
1820
|
+
return DBL2NUM(
|
1821
|
+
(double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0);
|
1822
|
+
}
|
1823
|
+
|
1520
1824
|
extern "C" {
|
1521
1825
|
|
1522
|
-
void Init_sq_mini_racer_extension ( void )
|
1826
|
+
__attribute__((visibility("default"))) void Init_sq_mini_racer_extension ( void )
|
1523
1827
|
{
|
1524
|
-
|
1828
|
+
ID sqreen_id = rb_intern("Sqreen");
|
1829
|
+
VALUE rb_mSqreen;
|
1830
|
+
if (rb_const_defined(rb_cObject, sqreen_id)) {
|
1831
|
+
rb_mSqreen = rb_const_get(rb_cObject, sqreen_id);
|
1832
|
+
if (TYPE(rb_mSqreen) != T_MODULE) {
|
1833
|
+
rb_raise(rb_eTypeError, "Sqreen is not a module");
|
1834
|
+
return;
|
1835
|
+
}
|
1836
|
+
} else {
|
1837
|
+
rb_mSqreen = rb_define_module("Sqreen");
|
1838
|
+
}
|
1525
1839
|
VALUE rb_mMiniRacer = rb_define_module_under(rb_mSqreen, "MiniRacer");
|
1840
|
+
rb_define_module_function(rb_mMiniRacer, "monotime", (VALUE(*)(...))&rb_monotime, 0);
|
1526
1841
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1527
1842
|
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1528
1843
|
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
@@ -1548,6 +1863,8 @@ extern "C" {
|
|
1548
1863
|
rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0);
|
1549
1864
|
rb_define_method(rb_cContext, "low_memory_notification", (VALUE(*)(...))&rb_context_low_memory_notification, 0);
|
1550
1865
|
rb_define_method(rb_cContext, "heap_stats", (VALUE(*)(...))&rb_heap_stats, 0);
|
1866
|
+
rb_define_method(rb_cContext, "write_heap_snapshot_unsafe", (VALUE(*)(...))&rb_heap_snapshot, 1);
|
1867
|
+
|
1551
1868
|
rb_define_private_method(rb_cContext, "create_isolate_value",(VALUE(*)(...))&rb_context_create_isolate_value, 0);
|
1552
1869
|
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
|
1553
1870
|
rb_define_private_method(rb_cContext, "call_unsafe", (VALUE(*)(...))&rb_context_call_unsafe, -1);
|
@@ -1567,8 +1884,19 @@ extern "C" {
|
|
1567
1884
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1568
1885
|
|
1569
1886
|
rb_define_method(rb_cIsolate, "idle_notification", (VALUE(*)(...))&rb_isolate_idle_notification, 1);
|
1887
|
+
rb_define_method(rb_cIsolate, "low_memory_notification", (VALUE(*)(...))&rb_isolate_low_memory_notification, 0);
|
1888
|
+
rb_define_method(rb_cIsolate, "pump_message_loop", (VALUE(*)(...))&rb_isolate_pump_message_loop, 0);
|
1570
1889
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1571
1890
|
|
1572
1891
|
rb_define_singleton_method(rb_cPlatform, "set_flag_as_str!", (VALUE(*)(...))&rb_platform_set_flag_as_str, 1);
|
1892
|
+
|
1893
|
+
rb_set_end_proc(set_ruby_exiting, Qnil);
|
1894
|
+
|
1895
|
+
static pthread_attr_t attr;
|
1896
|
+
if (pthread_attr_init(&attr) == 0) {
|
1897
|
+
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0) {
|
1898
|
+
thread_attr_p = &attr;
|
1899
|
+
}
|
1900
|
+
}
|
1573
1901
|
}
|
1574
1902
|
}
|