sq_mini_racer 0.2.4.sqreen3 → 0.3.1.0.1
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 +206 -101
- data/ext/mini_racer_extension/mini_racer_extension.cc +472 -154
- 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 +65 -23
- data/.travis.yml +0 -73
- data/ext/prv_ext_loader/extconf.rb +0 -5
@@ -1,33 +1,226 @@
|
|
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_requirement
|
33
|
+
'~> 14.14.0.0.beta1'
|
34
|
+
end
|
35
|
+
|
36
|
+
def libv8_basename(version)
|
37
|
+
"#{libv8_gem_name}-#{version}-#{ruby_platform}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def libv8_gemspec(version)
|
41
|
+
"#{libv8_basename(version)}.gemspec"
|
42
|
+
end
|
43
|
+
|
44
|
+
def libv8_local_path(path=Gem.path)
|
45
|
+
name_glob = "#{libv8_gem_name}-*-#{ruby_platform}"
|
46
|
+
|
47
|
+
puts "looking for #{name_glob} in #{path.inspect}"
|
48
|
+
|
49
|
+
paths = path.map { |p| Dir.glob(File.join(p, 'specifications', name_glob + '.gemspec')) }.flatten
|
50
|
+
|
51
|
+
if paths.empty?
|
52
|
+
puts "#{name_glob} not found in #{path.inspect}"
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
specs = paths.map { |p| [p, eval(File.read(p))] }
|
57
|
+
.select { |_, spec| Gem::Requirement.new(libv8_requirement).satisfied_by?(spec.version) }
|
58
|
+
found_path, found_spec = specs.sort_by { |_, spec| spec.version }.last
|
59
|
+
|
60
|
+
unless found_path && found_spec
|
61
|
+
puts "not found in specs: no '#{libv8_requirement}' in #{paths.inspect}"
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
puts "found in specs: #{found_path}"
|
66
|
+
|
67
|
+
gemdir = File.basename(found_path, '.gemspec')
|
68
|
+
dir = File.expand_path(File.join(found_path, '..', '..', 'gems', gemdir))
|
69
|
+
|
70
|
+
unless Dir.exist?(dir)
|
71
|
+
puts "not found in gems: #{dir}"
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "found in gems: #{dir}"
|
76
|
+
|
77
|
+
[dir, found_spec]
|
78
|
+
end
|
79
|
+
|
80
|
+
def vendor_path
|
81
|
+
File.join(Dir.pwd, 'vendor')
|
82
|
+
end
|
83
|
+
|
84
|
+
def libv8_vendor_path
|
85
|
+
libv8_local_path([vendor_path])
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_platform(str)
|
89
|
+
Gem::Platform.new(str).tap do |p|
|
90
|
+
p.instance_eval { @version = 'musl' } if str =~ /-musl/ && p.version.nil?
|
91
|
+
p.instance_eval { @cpu = 'x86_64' } if str =~ /universal.*darwin/
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def ruby_platform
|
96
|
+
parse_platform(RUBY_PLATFORM)
|
97
|
+
end
|
98
|
+
|
99
|
+
def http_get(uri)
|
100
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
101
|
+
res = http.get(uri.path)
|
102
|
+
|
103
|
+
abort("HTTP error #{res.code}: #{uri}") unless res.code == '200'
|
104
|
+
|
105
|
+
return res.body
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def libv8_remote_search
|
110
|
+
body = http_get(URI("https://rubygems.org/api/v1/versions/#{libv8_gem_name}.json"))
|
111
|
+
json = JSON.parse(body)
|
112
|
+
|
113
|
+
versions = json.select do |v|
|
114
|
+
Gem::Requirement.new(libv8_requirement).satisfied_by?(Gem::Version.new(v['number']))
|
115
|
+
end
|
116
|
+
abort(<<-ERROR) if versions.empty?
|
117
|
+
ERROR: could not find #{libv8_gem_name} (requirement #{libv8_requirement}) in rubygems.org
|
118
|
+
ERROR
|
119
|
+
|
120
|
+
platform_versions = versions.select do |v|
|
121
|
+
parse_platform(v['platform']) == ruby_platform unless v['platform'] =~ /universal.*darwin/
|
122
|
+
end
|
123
|
+
abort(<<-ERROR) if platform_versions.empty?
|
124
|
+
ERROR: found gems matching #{libv8_gem_name}:'#{libv8_requirement}', but no binary for #{ruby_platform}
|
125
|
+
try "gem install #{libv8_gem_name}:'#{libv8_requirement}'" to attempt to build #{libv8_gem_name} from source
|
126
|
+
ERROR
|
127
|
+
|
128
|
+
puts "found #{libv8_gem_name} for #{ruby_platform} on rubygems: #{platform_versions.map { |v| v['number'] }.join(', ')}"
|
129
|
+
|
130
|
+
platform_versions.sort_by { |v| Gem::Version.new(v['number']) }.last
|
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(File.join(vendor_path, 'cache'))
|
143
|
+
body = http_get(libv8_download_uri(name, version, platform))
|
144
|
+
File.open(File.join(vendor_path, 'cache', libv8_downloaded_gem(name, version, platform)), 'wb') { |f| f.write(body) }
|
145
|
+
end
|
146
|
+
|
147
|
+
def libv8_install!
|
148
|
+
cmd = "gem install #{libv8_gem_name} --version '#{libv8_requirement}' --install-dir '#{vendor_path}'"
|
149
|
+
puts "installing #{libv8_gem_name} using `#{cmd}`"
|
150
|
+
rc = system(cmd)
|
151
|
+
|
152
|
+
abort(<<-ERROR) unless rc
|
153
|
+
ERROR: could not install #{libv8_gem_name}:#{libv8_requirement}
|
154
|
+
try "gem install #{libv8_gem_name} -v '#{libv8_requirement}'" to attempt to build libv8 from source
|
155
|
+
ERROR
|
156
|
+
|
157
|
+
libv8_local_path([vendor_path])
|
158
|
+
end
|
159
|
+
|
160
|
+
def libv8_vendor!
|
161
|
+
return libv8_install! if Gem::VERSION < '2.0'
|
162
|
+
|
163
|
+
version = libv8_remote_search
|
164
|
+
|
165
|
+
puts "downloading #{libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])} to #{vendor_path}"
|
166
|
+
libv8_download(libv8_gem_name, version['number'], version['platform'])
|
167
|
+
|
168
|
+
package = Gem::Package.new(File.join(vendor_path, 'cache', libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform'])))
|
169
|
+
package.extract_files(File.join(vendor_path, 'gems', File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem')))
|
170
|
+
FileUtils.mkdir_p(File.join(vendor_path, 'specifications'))
|
171
|
+
File.open(File.join(vendor_path, 'specifications', File.basename(libv8_downloaded_gem(libv8_gem_name, version['number'], version['platform']), '.gem') + '.gemspec'), 'wb') { |f| f.write(package.spec.to_ruby) }
|
172
|
+
|
173
|
+
libv8_vendor_path
|
174
|
+
end
|
175
|
+
|
176
|
+
def ensure_libv8_load_path
|
177
|
+
puts "platform ruby:#{RUBY_PLATFORM} rubygems:#{Gem::Platform.new(RUBY_PLATFORM)} detected:#{ruby_platform}"
|
178
|
+
|
179
|
+
libv8_path, spec = libv8_local_path
|
180
|
+
if !ENV['ONLY_INSTALLED_LIBV8_GEM'] && !libv8_path
|
181
|
+
libv8_path, spec = libv8_vendor_path || libv8_vendor!
|
182
|
+
end
|
183
|
+
|
184
|
+
abort(<<-ERROR) unless libv8_path
|
185
|
+
ERROR: could not find #{libv8_gem_name}
|
186
|
+
ERROR
|
187
|
+
|
188
|
+
$LOAD_PATH.unshift(File.join(libv8_path, 'ext'))
|
189
|
+
$LOAD_PATH.unshift(File.join(libv8_path, 'lib'))
|
190
|
+
end
|
191
|
+
|
192
|
+
ensure_libv8_load_path
|
193
|
+
|
194
|
+
require 'libv8-node'
|
195
|
+
|
196
|
+
IS_DARWIN = RUBY_PLATFORM =~ /darwin/
|
6
197
|
|
7
198
|
have_library('pthread')
|
8
199
|
have_library('objc') if IS_DARWIN
|
9
|
-
|
200
|
+
cppflags_clear_std!
|
10
201
|
$CPPFLAGS += " -Wall" unless $CPPFLAGS.split.include? "-Wall"
|
11
202
|
$CPPFLAGS += " -g" unless $CPPFLAGS.split.include? "-g"
|
12
203
|
$CPPFLAGS += " -rdynamic" unless $CPPFLAGS.split.include? "-rdynamic"
|
13
204
|
$CPPFLAGS += " -fPIC" unless $CPPFLAGS.split.include? "-rdynamic" or IS_DARWIN
|
14
205
|
$CPPFLAGS += " -std=c++0x"
|
15
206
|
$CPPFLAGS += " -fpermissive"
|
16
|
-
$CPPFLAGS += " -
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
$CPPFLAGS += " -mssse3"
|
21
|
-
end
|
207
|
+
$CPPFLAGS += " -DV8_COMPRESS_POINTERS"
|
208
|
+
$CPPFLAGS += " -fvisibility=hidden "
|
209
|
+
cppflags_add_frame_pointer!
|
210
|
+
cppflags_add_cpu_extension!
|
22
211
|
|
23
212
|
$CPPFLAGS += " -Wno-reserved-user-defined-literal" if IS_DARWIN
|
24
213
|
|
25
214
|
$LDFLAGS.insert(0, " -stdlib=libc++ ") if IS_DARWIN
|
215
|
+
$LDFLAGS += " -Wl,--no-undefined " unless IS_DARWIN
|
26
216
|
|
27
217
|
if ENV['CXX']
|
28
218
|
puts "SETTING CXX"
|
29
219
|
CONFIG['CXX'] = ENV['CXX']
|
30
220
|
end
|
221
|
+
# 1.9 has no $CXXFLAGS
|
222
|
+
$CPPFLAGS += " #{ENV['CPPFLAGS']}" if ENV['CPPFLAGS']
|
223
|
+
$LDFLAGS += " #{ENV['LDFLAGS']}" if ENV['LDFLAGS']
|
31
224
|
|
32
225
|
CXX11_TEST = <<EOS
|
33
226
|
#if __cplusplus <= 199711L
|
@@ -57,103 +250,15 @@ if CONFIG['warnflags']
|
|
57
250
|
CONFIG['warnflags'].gsub!('-Wimplicit-function-declaration', '')
|
58
251
|
end
|
59
252
|
|
60
|
-
if enable_config('debug')
|
253
|
+
if enable_config('debug') || enable_config('asan')
|
61
254
|
CONFIG['debugflags'] << ' -ggdb3 -O0'
|
62
255
|
end
|
63
256
|
|
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
|
257
|
+
Libv8::Node.configure_makefile
|
151
258
|
|
152
|
-
if
|
153
|
-
|
154
|
-
|
259
|
+
if enable_config('asan')
|
260
|
+
$CPPFLAGS.insert(0, " -fsanitize=address ")
|
261
|
+
$LDFLAGS.insert(0, " -fsanitize=address ")
|
155
262
|
end
|
156
263
|
|
157
|
-
require 'libv8'
|
158
|
-
Libv8.configure_makefile
|
159
264
|
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>
|
@@ -34,6 +36,8 @@
|
|
34
36
|
#include "compat.hpp"
|
35
37
|
#include "simdutf8check.h"
|
36
38
|
|
39
|
+
#include <time.h>
|
40
|
+
|
37
41
|
using namespace v8;
|
38
42
|
|
39
43
|
typedef struct {
|
@@ -47,6 +51,7 @@ public:
|
|
47
51
|
ArrayBuffer::Allocator* allocator;
|
48
52
|
StartupData* startup_data;
|
49
53
|
bool interrupted;
|
54
|
+
bool added_gc_cb;
|
50
55
|
pid_t pid;
|
51
56
|
VALUE mutex;
|
52
57
|
|
@@ -64,15 +69,12 @@ public:
|
|
64
69
|
|
65
70
|
|
66
71
|
IsolateInfo() : isolate(nullptr), allocator(nullptr), startup_data(nullptr),
|
67
|
-
interrupted(false), pid(getpid()), refs_count(0) {
|
72
|
+
interrupted(false), added_gc_cb(false), pid(getpid()), refs_count(0) {
|
68
73
|
VALUE cMutex = rb_const_get(rb_cThread, rb_intern("Mutex"));
|
69
74
|
mutex = rb_class_new_instance(0, nullptr, cMutex);
|
70
75
|
}
|
71
76
|
|
72
|
-
~IsolateInfo()
|
73
|
-
void free_isolate(IsolateInfo*);
|
74
|
-
free_isolate(this);
|
75
|
-
}
|
77
|
+
~IsolateInfo();
|
76
78
|
|
77
79
|
void init(SnapshotInfo* snapshot_info = nullptr);
|
78
80
|
|
@@ -149,6 +151,7 @@ typedef struct {
|
|
149
151
|
Local<Function> fun;
|
150
152
|
Local<Value> *argv;
|
151
153
|
EvalResult result;
|
154
|
+
size_t max_memory;
|
152
155
|
} FunctionCall;
|
153
156
|
|
154
157
|
enum IsolateFlags {
|
@@ -174,9 +177,14 @@ static VALUE rb_mJSON;
|
|
174
177
|
static VALUE rb_cFailedV8Conversion;
|
175
178
|
static VALUE rb_cDateTime = Qnil;
|
176
179
|
|
177
|
-
static Platform
|
180
|
+
static std::unique_ptr<Platform> current_platform = NULL;
|
178
181
|
static std::mutex platform_lock;
|
179
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
|
+
|
180
188
|
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
181
189
|
bool platform_already_initialized = false;
|
182
190
|
|
@@ -188,6 +196,9 @@ static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
|
188
196
|
platform_lock.lock();
|
189
197
|
|
190
198
|
if (current_platform == NULL) {
|
199
|
+
if (!strcmp(RSTRING_PTR(flag_as_str), "--single_threaded")) {
|
200
|
+
single_threaded = true;
|
201
|
+
}
|
191
202
|
V8::SetFlagsFromString(RSTRING_PTR(flag_as_str), (int)RSTRING_LEN(flag_as_str));
|
192
203
|
} else {
|
193
204
|
platform_already_initialized = true;
|
@@ -211,8 +222,8 @@ static void init_v8() {
|
|
211
222
|
|
212
223
|
if (current_platform == NULL) {
|
213
224
|
V8::InitializeICU();
|
214
|
-
current_platform = platform::
|
215
|
-
V8::InitializePlatform(current_platform);
|
225
|
+
current_platform = platform::NewDefaultPlatform();
|
226
|
+
V8::InitializePlatform(current_platform.get());
|
216
227
|
V8::Initialize();
|
217
228
|
}
|
218
229
|
|
@@ -255,15 +266,17 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
255
266
|
if ((local_value->IsObject() || local_value->IsArray()) &&
|
256
267
|
!local_value->IsDate() && !local_value->IsFunction()) {
|
257
268
|
Local<Object> JSON = context->Global()->Get(
|
258
|
-
String::
|
269
|
+
context, String::NewFromUtf8Literal(isolate, "JSON"))
|
270
|
+
.ToLocalChecked().As<Object>();
|
259
271
|
|
260
|
-
Local<Function> stringify = JSON->Get(
|
261
|
-
|
272
|
+
Local<Function> stringify = JSON->Get(
|
273
|
+
context, v8::String::NewFromUtf8Literal(isolate, "stringify"))
|
274
|
+
.ToLocalChecked().As<Function>();
|
262
275
|
|
263
|
-
Local<Object> object = local_value->ToObject();
|
276
|
+
Local<Object> object = local_value->ToObject(context).ToLocalChecked();
|
264
277
|
const unsigned argc = 1;
|
265
278
|
Local<Value> argv[argc] = { object };
|
266
|
-
MaybeLocal<Value> json = stringify->Call(JSON, argc, argv);
|
279
|
+
MaybeLocal<Value> json = stringify->Call(context, JSON, argc, argv);
|
267
280
|
|
268
281
|
if (json.IsEmpty()) {
|
269
282
|
evalRes.executed = false;
|
@@ -287,11 +300,21 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
287
300
|
evalRes.message = new Persistent<Value>();
|
288
301
|
Local<Message> message = trycatch.Message();
|
289
302
|
char buf[1000];
|
290
|
-
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
|
+
|
291
313
|
len = snprintf(buf, sizeof(buf), "%s at %s:%i:%i", *String::Utf8Value(isolate, message->Get()),
|
292
|
-
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString()),
|
293
|
-
|
294
|
-
|
314
|
+
*String::Utf8Value(isolate, message->GetScriptResourceName()->ToString(context).ToLocalChecked()),
|
315
|
+
line,
|
316
|
+
column);
|
317
|
+
|
295
318
|
if ((size_t) len >= sizeof(buf)) {
|
296
319
|
len = sizeof(buf) - 1;
|
297
320
|
buf[len] = '\0';
|
@@ -302,12 +325,13 @@ static void prepare_result(MaybeLocal<Value> v8res,
|
|
302
325
|
} else if(trycatch.HasTerminated()) {
|
303
326
|
evalRes.terminated = true;
|
304
327
|
evalRes.message = new Persistent<Value>();
|
305
|
-
Local<String> tmp = String::
|
328
|
+
Local<String> tmp = String::NewFromUtf8Literal(isolate, "JavaScript was terminated (either by timeout or explicitly)");
|
306
329
|
evalRes.message->Reset(isolate, tmp);
|
307
330
|
}
|
308
331
|
if (!trycatch.StackTrace(context).IsEmpty()) {
|
309
332
|
evalRes.backtrace = new Persistent<Value>();
|
310
|
-
evalRes.backtrace->Reset(isolate,
|
333
|
+
evalRes.backtrace->Reset(isolate,
|
334
|
+
trycatch.StackTrace(context).ToLocalChecked()->ToString(context).ToLocalChecked());
|
311
335
|
}
|
312
336
|
}
|
313
337
|
}
|
@@ -322,7 +346,8 @@ nogvl_context_eval(void* arg) {
|
|
322
346
|
|
323
347
|
EvalParams* eval_params = (EvalParams*)arg;
|
324
348
|
EvalResult* result = eval_params->result;
|
325
|
-
|
349
|
+
IsolateInfo* isolate_info = eval_params->context_info->isolate_info;
|
350
|
+
Isolate* isolate = isolate_info->isolate;
|
326
351
|
|
327
352
|
Isolate::Scope isolate_scope(isolate);
|
328
353
|
HandleScope handle_scope(isolate);
|
@@ -366,7 +391,10 @@ nogvl_context_eval(void* arg) {
|
|
366
391
|
// parsing successful
|
367
392
|
if (eval_params->max_memory > 0) {
|
368
393
|
isolate->SetData(MEM_SOFTLIMIT_VALUE, &eval_params->max_memory);
|
394
|
+
if (!isolate_info->added_gc_cb) {
|
369
395
|
isolate->AddGCEpilogueCallback(gc_callback);
|
396
|
+
isolate_info->added_gc_cb = true;
|
397
|
+
}
|
370
398
|
}
|
371
399
|
|
372
400
|
maybe_value = parsed_script.ToLocalChecked()->Run(context);
|
@@ -379,6 +407,12 @@ nogvl_context_eval(void* arg) {
|
|
379
407
|
return 0;
|
380
408
|
}
|
381
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
|
+
|
382
416
|
// assumes isolate locking is in place
|
383
417
|
static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
384
418
|
Local<Value> value) {
|
@@ -391,11 +425,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
391
425
|
}
|
392
426
|
|
393
427
|
if (value->IsInt32()) {
|
394
|
-
return INT2FIX(value->Int32Value());
|
428
|
+
return INT2FIX(value->Int32Value(context).ToChecked());
|
395
429
|
}
|
396
430
|
|
397
431
|
if (value->IsNumber()) {
|
398
|
-
return rb_float_new(value->NumberValue());
|
432
|
+
return rb_float_new(value->NumberValue(context).ToChecked());
|
399
433
|
}
|
400
434
|
|
401
435
|
if (value->IsTrue()) {
|
@@ -410,8 +444,11 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
410
444
|
VALUE rb_array = rb_ary_new();
|
411
445
|
Local<Array> arr = Local<Array>::Cast(value);
|
412
446
|
for(uint32_t i=0; i < arr->Length(); i++) {
|
413
|
-
|
414
|
-
|
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());
|
415
452
|
if (rb_funcall(rb_elem, rb_intern("class"), 0) == rb_cFailedV8Conversion) {
|
416
453
|
return rb_elem;
|
417
454
|
}
|
@@ -436,31 +473,52 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|
436
473
|
VALUE rb_hash = rb_hash_new();
|
437
474
|
TryCatch trycatch(isolate);
|
438
475
|
|
439
|
-
Local<Object> object = value->ToObject();
|
476
|
+
Local<Object> object = value->ToObject(context).ToLocalChecked();
|
440
477
|
auto maybe_props = object->GetOwnPropertyNames(context);
|
441
478
|
if (!maybe_props.IsEmpty()) {
|
442
479
|
Local<Array> props = maybe_props.ToLocalChecked();
|
443
480
|
for(uint32_t i=0; i < props->Length(); i++) {
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
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());
|
448
486
|
|
449
|
-
|
450
|
-
//
|
451
|
-
|
452
|
-
|
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();
|
453
491
|
}
|
454
492
|
|
455
|
-
VALUE rb_value = convert_v8_to_ruby(
|
493
|
+
VALUE rb_value = convert_v8_to_ruby(
|
494
|
+
isolate, context, prop_value.ToLocalChecked());
|
456
495
|
rb_hash_aset(rb_hash, rb_key, rb_value);
|
457
496
|
}
|
458
497
|
}
|
459
498
|
return rb_hash;
|
460
499
|
}
|
461
500
|
|
462
|
-
|
463
|
-
|
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
|
+
}
|
464
522
|
}
|
465
523
|
|
466
524
|
static VALUE convert_v8_to_ruby(Isolate* isolate,
|
@@ -557,7 +615,7 @@ treat_as_latin1:
|
|
557
615
|
return v8str.ToLocalChecked();
|
558
616
|
}
|
559
617
|
|
560
|
-
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)
|
561
619
|
{
|
562
620
|
EscapableHandleScope scope(isolate);
|
563
621
|
|
@@ -571,87 +629,87 @@ static Local<Value> convert_ruby_to_v8(Isolate* isolate, VALUE value)
|
|
571
629
|
VALUE klass;
|
572
630
|
|
573
631
|
switch (TYPE(value)) {
|
574
|
-
|
632
|
+
case T_FIXNUM:
|
575
633
|
{
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
}
|
581
|
-
return scope.Escape(Integer::New(isolate, (int)fixnum));
|
634
|
+
fixnum = NUM2LONG(value);
|
635
|
+
if (fixnum > INT_MAX)
|
636
|
+
{
|
637
|
+
return scope.Escape(Number::New(isolate, (double)fixnum));
|
582
638
|
}
|
583
|
-
|
584
|
-
|
585
|
-
|
639
|
+
return scope.Escape(Integer::New(isolate, (int)fixnum));
|
640
|
+
}
|
641
|
+
case T_FLOAT:
|
642
|
+
return scope.Escape(Number::New(isolate, NUM2DBL(value)));
|
643
|
+
case T_STRING:
|
586
644
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
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:
|
594
652
|
{
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
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);
|
601
659
|
}
|
602
|
-
|
660
|
+
case T_HASH:
|
603
661
|
{
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
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);
|
613
671
|
}
|
614
|
-
|
672
|
+
case T_SYMBOL:
|
615
673
|
{
|
616
|
-
|
674
|
+
value = rb_funcall(value, rb_intern("to_s"), 0);
|
617
675
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
618
676
|
}
|
619
|
-
|
677
|
+
case T_DATA:
|
620
678
|
{
|
621
|
-
|
622
|
-
|
679
|
+
klass = rb_funcall(value, rb_intern("class"), 0);
|
680
|
+
if (klass == rb_cTime || klass == rb_cDateTime)
|
681
|
+
{
|
682
|
+
if (klass == rb_cDateTime)
|
623
683
|
{
|
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));
|
684
|
+
value = rb_funcall(value, rb_intern("to_time"), 0);
|
630
685
|
}
|
686
|
+
value = rb_funcall(value, rb_intern("to_f"), 0);
|
687
|
+
return scope.Escape(Date::New(context, NUM2DBL(value) * 1000).ToLocalChecked());
|
688
|
+
}
|
631
689
|
// break intentionally missing
|
632
690
|
}
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
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:
|
645
703
|
{
|
646
704
|
if (rb_respond_to(value, rb_intern("to_s"))) {
|
647
705
|
// TODO: if this throws we're screwed
|
648
706
|
value = rb_funcall(value, rb_intern("to_s"), 0);
|
649
707
|
return scope.Escape(convert_ruby_str_to_v8(scope, isolate, value));
|
650
708
|
}
|
651
|
-
return scope.Escape(
|
709
|
+
return scope.Escape(
|
710
|
+
String::NewFromUtf8Literal(isolate, "Undefined Conversion"));
|
652
711
|
}
|
653
712
|
}
|
654
|
-
|
655
713
|
}
|
656
714
|
|
657
715
|
static void unblock_eval(void *ptr) {
|
@@ -659,6 +717,81 @@ static void unblock_eval(void *ptr) {
|
|
659
717
|
eval->context_info->isolate_info->interrupted = true;
|
660
718
|
}
|
661
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
|
+
|
662
795
|
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
663
796
|
SnapshotInfo* snapshot_info;
|
664
797
|
Data_Get_Struct(self, SnapshotInfo, snapshot_info);
|
@@ -677,7 +810,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|
677
810
|
|
678
811
|
init_v8();
|
679
812
|
|
680
|
-
StartupData startup_data =
|
813
|
+
StartupData startup_data = create_snapshot_data_blob(RSTRING_PTR(str));
|
681
814
|
|
682
815
|
if (startup_data.data == NULL && startup_data.raw_size == 0) {
|
683
816
|
rb_raise(rb_eSnapshotError, "Could not create snapshot, most likely the source is incorrect");
|
@@ -708,7 +841,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|
708
841
|
init_v8();
|
709
842
|
|
710
843
|
StartupData cold_startup_data = {snapshot_info->data, snapshot_info->raw_size};
|
711
|
-
StartupData warm_startup_data =
|
844
|
+
StartupData warm_startup_data = warm_up_snapshot_data_blob(cold_startup_data, RSTRING_PTR(str));
|
712
845
|
|
713
846
|
if (warm_startup_data.data == NULL && warm_startup_data.raw_size == 0) {
|
714
847
|
rb_raise(rb_eSnapshotError, "Could not warm up snapshot, most likely the source is incorrect");
|
@@ -771,6 +904,29 @@ static VALUE rb_isolate_idle_notification(VALUE self, VALUE idle_time_in_ms) {
|
|
771
904
|
return isolate_info->isolate->IdleNotificationDeadline(now + duration) ? Qtrue : Qfalse;
|
772
905
|
}
|
773
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
|
+
|
774
930
|
static VALUE rb_context_init_unsafe(VALUE self, VALUE isolate, VALUE snap) {
|
775
931
|
ContextInfo* context_info;
|
776
932
|
Data_Get_Struct(self, ContextInfo, context_info);
|
@@ -887,8 +1043,8 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|
887
1043
|
Local<Value> tmp = Local<Value>::New(isolate, *result.value);
|
888
1044
|
|
889
1045
|
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"));
|
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"));
|
892
1048
|
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
893
1049
|
} else {
|
894
1050
|
ret = convert_v8_to_ruby(isolate, *p_ctx, tmp);
|
@@ -1009,6 +1165,8 @@ gvl_ruby_callback(void* data) {
|
|
1009
1165
|
VALUE result;
|
1010
1166
|
VALUE self;
|
1011
1167
|
VALUE parent;
|
1168
|
+
ContextInfo* context_info;
|
1169
|
+
|
1012
1170
|
{
|
1013
1171
|
HandleScope scope(args->GetIsolate());
|
1014
1172
|
Local<External> external = Local<External>::Cast(args->Data());
|
@@ -1021,7 +1179,6 @@ gvl_ruby_callback(void* data) {
|
|
1021
1179
|
return NULL;
|
1022
1180
|
}
|
1023
1181
|
|
1024
|
-
ContextInfo* context_info;
|
1025
1182
|
Data_Get_Struct(parent, ContextInfo, context_info);
|
1026
1183
|
|
1027
1184
|
if (length > 0) {
|
@@ -1031,7 +1188,7 @@ gvl_ruby_callback(void* data) {
|
|
1031
1188
|
for (int i = 0; i < length; i++) {
|
1032
1189
|
Local<Value> value = ((*args)[i]).As<Value>();
|
1033
1190
|
VALUE tmp = convert_v8_to_ruby(args->GetIsolate(),
|
1034
|
-
|
1191
|
+
*context_info->context, value);
|
1035
1192
|
rb_ary_push(ruby_args, tmp);
|
1036
1193
|
}
|
1037
1194
|
}
|
@@ -1044,7 +1201,9 @@ gvl_ruby_callback(void* data) {
|
|
1044
1201
|
callback_data.failed = false;
|
1045
1202
|
|
1046
1203
|
if ((bool)args->GetIsolate()->GetData(DO_TERMINATE) == true) {
|
1047
|
-
args->GetIsolate()->ThrowException(
|
1204
|
+
args->GetIsolate()->ThrowException(
|
1205
|
+
String::NewFromUtf8Literal(args->GetIsolate(),
|
1206
|
+
"Terminated execution during transition from Ruby to JS"));
|
1048
1207
|
args->GetIsolate()->TerminateExecution();
|
1049
1208
|
if (length > 0) {
|
1050
1209
|
rb_ary_clear(ruby_args);
|
@@ -1058,11 +1217,11 @@ gvl_ruby_callback(void* data) {
|
|
1058
1217
|
|
1059
1218
|
if(callback_data.failed) {
|
1060
1219
|
rb_iv_set(parent, "@current_exception", result);
|
1061
|
-
args->GetIsolate()->ThrowException(String::
|
1220
|
+
args->GetIsolate()->ThrowException(String::NewFromUtf8Literal(args->GetIsolate(), "Ruby exception"));
|
1062
1221
|
}
|
1063
1222
|
else {
|
1064
1223
|
HandleScope scope(args->GetIsolate());
|
1065
|
-
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);
|
1066
1225
|
args->GetReturnValue().Set(v8_result);
|
1067
1226
|
}
|
1068
1227
|
|
@@ -1119,8 +1278,10 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1119
1278
|
Local<Context> context = context_info->context->Get(isolate);
|
1120
1279
|
Context::Scope context_scope(context);
|
1121
1280
|
|
1122
|
-
Local<String> v8_str =
|
1123
|
-
|
1281
|
+
Local<String> v8_str =
|
1282
|
+
String::NewFromUtf8(isolate, RSTRING_PTR(name),
|
1283
|
+
NewStringType::kNormal, (int)RSTRING_LEN(name))
|
1284
|
+
.ToLocalChecked();
|
1124
1285
|
|
1125
1286
|
// copy self so we can access from v8 external
|
1126
1287
|
VALUE* self_copy;
|
@@ -1130,24 +1291,38 @@ static VALUE rb_external_function_notify_v8(VALUE self) {
|
|
1130
1291
|
Local<Value> external = External::New(isolate, self_copy);
|
1131
1292
|
|
1132
1293
|
if (parent_object == Qnil) {
|
1133
|
-
context->Global()->Set(
|
1134
|
-
|
1294
|
+
context->Global()->Set(
|
1295
|
+
context,
|
1296
|
+
v8_str,
|
1297
|
+
FunctionTemplate::New(isolate, ruby_callback, external)
|
1298
|
+
->GetFunction(context)
|
1299
|
+
.ToLocalChecked());
|
1135
1300
|
|
1136
|
-
|
1137
|
-
|
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();
|
1138
1307
|
|
1139
1308
|
MaybeLocal<Script> parsed_script = Script::Compile(context, eval);
|
1140
1309
|
if (parsed_script.IsEmpty()) {
|
1141
|
-
|
1310
|
+
parse_error = true;
|
1142
1311
|
} else {
|
1143
|
-
MaybeLocal<Value> maybe_value =
|
1312
|
+
MaybeLocal<Value> maybe_value =
|
1313
|
+
parsed_script.ToLocalChecked()->Run(context);
|
1144
1314
|
attach_error = true;
|
1145
1315
|
|
1146
1316
|
if (!maybe_value.IsEmpty()) {
|
1147
1317
|
Local<Value> value = maybe_value.ToLocalChecked();
|
1148
|
-
if (value->IsObject()){
|
1149
|
-
|
1150
|
-
|
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;
|
1151
1326
|
}
|
1152
1327
|
}
|
1153
1328
|
}
|
@@ -1177,35 +1352,39 @@ static VALUE rb_context_isolate_mutex(VALUE self) {
|
|
1177
1352
|
return context_info->isolate_info->mutex;
|
1178
1353
|
}
|
1179
1354
|
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
if (isolate_info->isolate) {
|
1187
|
-
if (isolate_info->interrupted) {
|
1188
|
-
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");
|
1189
1361
|
} else {
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
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
|
+
);
|
1193
1372
|
} else {
|
1194
|
-
|
1373
|
+
isolate->Dispose();
|
1195
1374
|
}
|
1196
1375
|
}
|
1197
|
-
|
1376
|
+
isolate = nullptr;
|
1198
1377
|
}
|
1199
1378
|
|
1200
|
-
if (
|
1201
|
-
delete[]
|
1202
|
-
delete
|
1379
|
+
if (startup_data) {
|
1380
|
+
delete[] startup_data->data;
|
1381
|
+
delete startup_data;
|
1203
1382
|
}
|
1204
1383
|
|
1205
|
-
delete
|
1384
|
+
delete allocator;
|
1206
1385
|
}
|
1207
1386
|
|
1208
|
-
static void
|
1387
|
+
static void free_context_raw(void *arg) {
|
1209
1388
|
ContextInfo* context_info = (ContextInfo*)arg;
|
1210
1389
|
IsolateInfo* isolate_info = context_info->isolate_info;
|
1211
1390
|
Persistent<Context>* context = context_info->context;
|
@@ -1222,6 +1401,20 @@ static void *free_context_raw(void* arg) {
|
|
1222
1401
|
}
|
1223
1402
|
|
1224
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
|
+
|
1225
1418
|
return NULL;
|
1226
1419
|
}
|
1227
1420
|
|
@@ -1236,22 +1429,17 @@ static void free_context(ContextInfo* context_info) {
|
|
1236
1429
|
|
1237
1430
|
if (isolate_info && isolate_info->refs() > 1) {
|
1238
1431
|
pthread_t free_context_thread;
|
1239
|
-
|
1432
|
+
if (pthread_create(&free_context_thread, thread_attr_p,
|
1433
|
+
free_context_thr, (void*)context_info_copy)) {
|
1240
1434
|
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
1241
1435
|
}
|
1242
|
-
|
1243
1436
|
} else {
|
1244
1437
|
free_context_raw(context_info_copy);
|
1245
1438
|
}
|
1246
1439
|
|
1247
|
-
if (context_info->context && isolate_info && isolate_info->isolate) {
|
1248
1440
|
context_info->context = NULL;
|
1249
|
-
}
|
1250
|
-
|
1251
|
-
if (isolate_info) {
|
1252
1441
|
context_info->isolate_info = NULL;
|
1253
1442
|
}
|
1254
|
-
}
|
1255
1443
|
|
1256
1444
|
static void deallocate_isolate(void* data) {
|
1257
1445
|
|
@@ -1265,7 +1453,7 @@ static void mark_isolate(void* data) {
|
|
1265
1453
|
isolate_info->mark();
|
1266
1454
|
}
|
1267
1455
|
|
1268
|
-
void deallocate(void* data) {
|
1456
|
+
static void deallocate(void* data) {
|
1269
1457
|
ContextInfo* context_info = (ContextInfo*)data;
|
1270
1458
|
|
1271
1459
|
free_context(context_info);
|
@@ -1280,22 +1468,22 @@ static void mark_context(void* data) {
|
|
1280
1468
|
}
|
1281
1469
|
}
|
1282
1470
|
|
1283
|
-
void deallocate_external_function(void * data) {
|
1471
|
+
static void deallocate_external_function(void * data) {
|
1284
1472
|
xfree(data);
|
1285
1473
|
}
|
1286
1474
|
|
1287
|
-
void deallocate_snapshot(void * data) {
|
1475
|
+
static void deallocate_snapshot(void * data) {
|
1288
1476
|
SnapshotInfo* snapshot_info = (SnapshotInfo*)data;
|
1289
1477
|
delete[] snapshot_info->data;
|
1290
1478
|
xfree(snapshot_info);
|
1291
1479
|
}
|
1292
1480
|
|
1293
|
-
VALUE allocate_external_function(VALUE klass) {
|
1481
|
+
static VALUE allocate_external_function(VALUE klass) {
|
1294
1482
|
VALUE* self = ALLOC(VALUE);
|
1295
1483
|
return Data_Wrap_Struct(klass, NULL, deallocate_external_function, (void*)self);
|
1296
1484
|
}
|
1297
1485
|
|
1298
|
-
VALUE allocate(VALUE klass) {
|
1486
|
+
static VALUE allocate(VALUE klass) {
|
1299
1487
|
ContextInfo* context_info = ALLOC(ContextInfo);
|
1300
1488
|
context_info->isolate_info = NULL;
|
1301
1489
|
context_info->context = NULL;
|
@@ -1303,7 +1491,7 @@ VALUE allocate(VALUE klass) {
|
|
1303
1491
|
return Data_Wrap_Struct(klass, mark_context, deallocate, (void*)context_info);
|
1304
1492
|
}
|
1305
1493
|
|
1306
|
-
VALUE allocate_snapshot(VALUE klass) {
|
1494
|
+
static VALUE allocate_snapshot(VALUE klass) {
|
1307
1495
|
SnapshotInfo* snapshot_info = ALLOC(SnapshotInfo);
|
1308
1496
|
snapshot_info->data = NULL;
|
1309
1497
|
snapshot_info->raw_size = 0;
|
@@ -1311,7 +1499,7 @@ VALUE allocate_snapshot(VALUE klass) {
|
|
1311
1499
|
return Data_Wrap_Struct(klass, NULL, deallocate_snapshot, (void*)snapshot_info);
|
1312
1500
|
}
|
1313
1501
|
|
1314
|
-
VALUE allocate_isolate(VALUE klass) {
|
1502
|
+
static VALUE allocate_isolate(VALUE klass) {
|
1315
1503
|
IsolateInfo* isolate_info = new IsolateInfo();
|
1316
1504
|
|
1317
1505
|
return Data_Wrap_Struct(klass, mark_isolate, deallocate_isolate, (void*)isolate_info);
|
@@ -1350,6 +1538,71 @@ rb_heap_stats(VALUE self) {
|
|
1350
1538
|
return rval;
|
1351
1539
|
}
|
1352
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
|
+
|
1353
1606
|
static VALUE
|
1354
1607
|
rb_context_stop(VALUE self) {
|
1355
1608
|
|
@@ -1402,13 +1655,23 @@ nogvl_context_call(void *args) {
|
|
1402
1655
|
if (!call) {
|
1403
1656
|
return 0;
|
1404
1657
|
}
|
1405
|
-
|
1658
|
+
IsolateInfo *isolate_info = call->context_info->isolate_info;
|
1659
|
+
Isolate* isolate = isolate_info->isolate;
|
1406
1660
|
|
1407
1661
|
// in gvl flag
|
1408
1662
|
isolate->SetData(IN_GVL, (void*)false);
|
1409
1663
|
// terminate ASAP
|
1410
1664
|
isolate->SetData(DO_TERMINATE, (void*)false);
|
1411
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
|
+
|
1412
1675
|
Isolate::Scope isolate_scope(isolate);
|
1413
1676
|
EscapableHandleScope handle_scope(isolate);
|
1414
1677
|
TryCatch trycatch(isolate);
|
@@ -1466,6 +1729,13 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1466
1729
|
call_argv = argv + 1;
|
1467
1730
|
}
|
1468
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
|
+
|
1469
1739
|
bool missingFunction = false;
|
1470
1740
|
{
|
1471
1741
|
Locker lock(isolate);
|
@@ -1477,8 +1747,11 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1477
1747
|
|
1478
1748
|
// examples of such usage can be found in
|
1479
1749
|
// https://github.com/v8/v8/blob/36b32aa28db5e993312f4588d60aad5c8330c8a5/test/cctest/test-api.cc#L15711
|
1480
|
-
|
1481
|
-
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
|
+
}
|
1482
1755
|
|
1483
1756
|
if (val.IsEmpty() || !val.ToLocalChecked()->IsFunction()) {
|
1484
1757
|
missingFunction = true;
|
@@ -1494,7 +1767,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|
1494
1767
|
return Qnil;
|
1495
1768
|
}
|
1496
1769
|
for(int i=0; i < fun_argc; i++) {
|
1497
|
-
call.argv[i] = convert_ruby_to_v8(isolate, call_argv[i]);
|
1770
|
+
call.argv[i] = convert_ruby_to_v8(isolate, context, call_argv[i]);
|
1498
1771
|
}
|
1499
1772
|
}
|
1500
1773
|
#if RUBY_API_VERSION_MAJOR > 1
|
@@ -1527,12 +1800,44 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|
1527
1800
|
return Data_Wrap_Struct(rb_cIsolate, NULL, &deallocate_isolate, isolate_info);
|
1528
1801
|
}
|
1529
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
|
+
|
1530
1824
|
extern "C" {
|
1531
1825
|
|
1532
|
-
void Init_sq_mini_racer_extension ( void )
|
1826
|
+
__attribute__((visibility("default"))) void Init_sq_mini_racer_extension ( void )
|
1533
1827
|
{
|
1534
|
-
|
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
|
+
}
|
1535
1839
|
VALUE rb_mMiniRacer = rb_define_module_under(rb_mSqreen, "MiniRacer");
|
1840
|
+
rb_define_module_function(rb_mMiniRacer, "monotime", (VALUE(*)(...))&rb_monotime, 0);
|
1536
1841
|
rb_cContext = rb_define_class_under(rb_mMiniRacer, "Context", rb_cObject);
|
1537
1842
|
rb_cSnapshot = rb_define_class_under(rb_mMiniRacer, "Snapshot", rb_cObject);
|
1538
1843
|
rb_cIsolate = rb_define_class_under(rb_mMiniRacer, "Isolate", rb_cObject);
|
@@ -1558,6 +1863,8 @@ extern "C" {
|
|
1558
1863
|
rb_define_method(rb_cContext, "dispose_unsafe", (VALUE(*)(...))&rb_context_dispose, 0);
|
1559
1864
|
rb_define_method(rb_cContext, "low_memory_notification", (VALUE(*)(...))&rb_context_low_memory_notification, 0);
|
1560
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
|
+
|
1561
1868
|
rb_define_private_method(rb_cContext, "create_isolate_value",(VALUE(*)(...))&rb_context_create_isolate_value, 0);
|
1562
1869
|
rb_define_private_method(rb_cContext, "eval_unsafe",(VALUE(*)(...))&rb_context_eval_unsafe, 2);
|
1563
1870
|
rb_define_private_method(rb_cContext, "call_unsafe", (VALUE(*)(...))&rb_context_call_unsafe, -1);
|
@@ -1577,8 +1884,19 @@ extern "C" {
|
|
1577
1884
|
rb_define_private_method(rb_cSnapshot, "load", (VALUE(*)(...))&rb_snapshot_load, 1);
|
1578
1885
|
|
1579
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);
|
1580
1889
|
rb_define_private_method(rb_cIsolate, "init_with_snapshot",(VALUE(*)(...))&rb_isolate_init_with_snapshot, 1);
|
1581
1890
|
|
1582
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
|
+
}
|
1583
1901
|
}
|
1584
1902
|
}
|