mirror of
https://github.com/rubyjs/mini_racer
synced 2023-03-27 23:21:28 -04:00
Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b05d6914c5 | ||
![]() |
719abdb1a4 | ||
![]() |
9935d5a65a | ||
![]() |
3d110768ee | ||
![]() |
e0f5a7ac66 | ||
![]() |
7419fd154e | ||
![]() |
4414ea40f9 | ||
![]() |
9cf4ed6d92 | ||
![]() |
bdb0977d81 | ||
![]() |
5266fec597 | ||
![]() |
8b4d03eb76 | ||
![]() |
493e1bc1fc | ||
![]() |
79f1379ae2 | ||
![]() |
15051f2298 | ||
![]() |
38820e54fd | ||
![]() |
b8638e8899 | ||
![]() |
4487f11202 | ||
![]() |
9a409cf792 | ||
![]() |
746bfddfea |
14 changed files with 597 additions and 180 deletions
98
.github/workflows/ci.yml
vendored
98
.github/workflows/ci.yml
vendored
|
@ -1,48 +1,92 @@
|
|||
name: Test
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
- push
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
test-truffleruby:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- "macos-11"
|
||||
- "macos-12"
|
||||
- "ubuntu-20.04"
|
||||
ruby:
|
||||
- "truffleruby+graalvm-head"
|
||||
|
||||
name: ${{ matrix.os }} - ${{ matrix.ruby }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
TRUFFLERUBYOPT: "--jvm --polyglot"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
- name: Install GraalVM JS component
|
||||
run: gu install js
|
||||
- name: Compile
|
||||
run: bundle exec rake compile
|
||||
- name: Test
|
||||
run: bundle exec rake test
|
||||
|
||||
test-darwin:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- '10.15'
|
||||
- '11.0'
|
||||
platform:
|
||||
- x86_64
|
||||
# arm64
|
||||
name: Test (darwin)
|
||||
runs-on: macos-${{ matrix.os }}
|
||||
- "macos-11"
|
||||
- "macos-12"
|
||||
ruby:
|
||||
- "ruby-2.6"
|
||||
- "ruby-2.7"
|
||||
- "ruby-3.0"
|
||||
- "ruby-3.1"
|
||||
|
||||
name: ${{ matrix.os }} - ${{ matrix.ruby }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Bundle
|
||||
run: bundle install
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
- name: Compile
|
||||
run: bundle exec rake compile
|
||||
- name: Test
|
||||
run: bundle exec rake test
|
||||
|
||||
test-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby:
|
||||
- '2.6'
|
||||
- '2.7'
|
||||
- '3.0'
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "3.0"
|
||||
- "3.1"
|
||||
platform:
|
||||
- amd64
|
||||
- arm64
|
||||
# arm
|
||||
# ppc64le
|
||||
# s390x
|
||||
- "amd64"
|
||||
- "arm64"
|
||||
libc:
|
||||
- gnu
|
||||
- musl
|
||||
name: Test (linux)
|
||||
- "gnu"
|
||||
- "musl"
|
||||
exclude:
|
||||
# there's no libv8-node (v16) for aarch64-linux-musl at the moment
|
||||
- platform: "arm64"
|
||||
libc: "musl"
|
||||
|
||||
name: linux-${{ matrix.platform }} - ruby-${{ matrix.ruby }} - ${{ matrix.libc }}
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Enable ${{ matrix.platform }} platform
|
||||
id: qemu
|
||||
|
@ -67,9 +111,11 @@ jobs:
|
|||
echo "::set-output name=id::$(cat container_id)"
|
||||
- name: Install Alpine system dependencies
|
||||
if: ${{ matrix.libc == 'musl' }}
|
||||
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base linux-headers bash python2 python3 git curl tar clang binutils-gold
|
||||
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} apk add --no-cache build-base bash git
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Update Rubygems
|
||||
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} gem update --system
|
||||
- name: Bundle
|
||||
run: docker exec -w "${PWD}" ${{ steps.container.outputs.id }} bundle install
|
||||
- name: Compile
|
||||
|
|
23
.travis.yml
23
.travis.yml
|
@ -1,23 +0,0 @@
|
|||
language: ruby
|
||||
os: linux
|
||||
rvm:
|
||||
- 2.6
|
||||
- 2.7
|
||||
- 3.0
|
||||
- ruby-head
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
jobs:
|
||||
include:
|
||||
- rvm: 2.6
|
||||
os: osx
|
||||
osx_image: xcode11.3
|
||||
- rvm: 2.6
|
||||
os: osx
|
||||
osx_image: xcode12.2
|
||||
- rvm: 2.7
|
||||
os: osx
|
||||
osx_image: xcode12.2
|
||||
dist: xenial
|
||||
cache: bundler
|
12
CHANGELOG
12
CHANGELOG
|
@ -1,4 +1,14 @@
|
|||
- 17-01-2021
|
||||
- 16-08-2022
|
||||
|
||||
- 0.6.3
|
||||
|
||||
- Truffle ruby support! Thanks to Brandon Fish and the truffle team
|
||||
- Hide libv8 symbols on ELF targets
|
||||
- Slightly shrunk binary size
|
||||
- Simplified timeout implementation
|
||||
- Some stability fixes
|
||||
|
||||
- 17-01-2022
|
||||
|
||||
- 0.6.2
|
||||
|
||||
|
|
38
README.md
38
README.md
|
@ -1,6 +1,6 @@
|
|||
# MiniRacer
|
||||
|
||||
[](https://travis-ci.org/rubyjs/mini_racer)
|
||||
[](https://github.com/rubyjs/mini_racer/actions/workflows/ci.yml)
|
||||
|
||||
Minimal, modern embedded V8 for Ruby.
|
||||
|
||||
|
@ -10,12 +10,14 @@ It was created as an alternative to the excellent [therubyracer](https://github.
|
|||
|
||||
MiniRacer has an adapter for [execjs](https://github.com/rails/execjs) so it can be used directly with Rails projects to minify assets, run babel or compile CoffeeScript.
|
||||
|
||||
### A note about Ruby version Support
|
||||
### Supported Ruby Versions
|
||||
|
||||
MiniRacer only supports non-EOL versions of Ruby. See [Ruby](https://www.ruby-lang.org/en/downloads) to see the list of non-EOL Rubies.
|
||||
MiniRacer only supports non-EOL versions of Ruby. See [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/) for the list of non-EOL Rubies.
|
||||
|
||||
If you require support for older versions of Ruby install an older version of the gem.
|
||||
|
||||
MiniRacer **does not support** [Ruby built on MinGW](https://github.com/rubyjs/mini_racer/issues/252#issuecomment-1201172236, "pure windows" no Cygwin, no WSL2) (see https://github.com/rubyjs/libv8-node/issues/9).
|
||||
|
||||
## Features
|
||||
|
||||
### Simple eval for JavaScript
|
||||
|
@ -112,16 +114,21 @@ context.eval('bar()', filename: 'a/bar.js')
|
|||
|
||||
### Fork safety
|
||||
|
||||
Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe.
|
||||
Sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
|
||||
Some Ruby web servers employ forking (for example unicorn or puma in clustered mode). V8 is not fork safe by default and sadly Ruby does not have support for fork notifications per [#5446](https://bugs.ruby-lang.org/issues/5446).
|
||||
|
||||
Since 0.6.1 mini_racer does support V8 single threaded platform mode which should remove most forking related issues. To enable run this before using `MiniRacer::Context`:
|
||||
|
||||
```ruby
|
||||
MiniRacer::Platform.set_flags!(:single_threaded)
|
||||
```
|
||||
|
||||
If you want to ensure your application does not leak memory after fork either:
|
||||
|
||||
1. Ensure no MiniRacer::Context objects are created in the master process
|
||||
1. Ensure no `MiniRacer::Context` objects are created in the master process
|
||||
|
||||
Or
|
||||
|
||||
2. Dispose manually of all MiniRacer::Context objects prior to forking
|
||||
2. Dispose manually of all `MiniRacer::Context` objects prior to forking
|
||||
|
||||
```ruby
|
||||
# before fork
|
||||
|
@ -419,18 +426,15 @@ Or install it yourself as:
|
|||
**Note** using v8.h and compiling MiniRacer requires a C++11 standard compiler, more specifically clang 3.5 (or later) or GCC 6.3 (or later).
|
||||
|
||||
|
||||
## Travis-ci
|
||||
### Troubleshooting
|
||||
|
||||
To install `mini-racer` you will need a version of GCC that supports C++11 (GCC 6.3) this is included by default in ubuntu trusty based images.
|
||||
If you have a problem installing mini_racer, please consider the following steps:
|
||||
|
||||
Travis today ships by default with a precise based image. Precise Pangolin (12.04 LTS) was first released in August 2012. Even though you can install GCC 6.3 on precise the simpler approach is to opt for the trusty based image.
|
||||
|
||||
Add this to your .travis.yml file:
|
||||
|
||||
```
|
||||
- sudo: required
|
||||
- dist: trusty
|
||||
```
|
||||
* make sure you try the latest released version of mini_racer
|
||||
* make sure you have Rubygems >= 3.2.13 and bundler >= 2.2.13 installed via `gem update --system`
|
||||
* if you are using bundler, make sure to have `PLATFORMS` set correctly in `Gemfile.lock` via `bundle lock --add-platform`
|
||||
* make sure to recompile/reinstall `mini_racer` and `libv8-node` after system upgrades (for example via `gem uninstall --all mini_racer libv8-node`)
|
||||
* make sure you are on the latest patch/teeny version of a supported Ruby branch
|
||||
|
||||
## Similar Projects
|
||||
|
||||
|
|
18
Rakefile
18
Rakefile
|
@ -1,6 +1,5 @@
|
|||
require "bundler/gem_tasks"
|
||||
require "rake/testtask"
|
||||
require "rake/extensiontask"
|
||||
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << "test"
|
||||
|
@ -11,8 +10,21 @@ end
|
|||
task :default => [:compile, :test]
|
||||
|
||||
gem = Gem::Specification.load( File.dirname(__FILE__) + '/mini_racer.gemspec' )
|
||||
Rake::ExtensionTask.new( 'mini_racer_loader', gem )
|
||||
Rake::ExtensionTask.new( 'mini_racer_extension', gem )
|
||||
|
||||
if RUBY_ENGINE == "truffleruby"
|
||||
task :compile do
|
||||
# noop
|
||||
end
|
||||
|
||||
task :clean do
|
||||
# noop
|
||||
end
|
||||
else
|
||||
require 'rake/extensiontask'
|
||||
Rake::ExtensionTask.new( 'mini_racer_loader', gem )
|
||||
Rake::ExtensionTask.new( 'mini_racer_extension', gem )
|
||||
end
|
||||
|
||||
|
||||
|
||||
# via http://blog.flavorjon.es/2009/06/easily-valgrind-gdb-your-ruby-c.html
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
require 'mkmf'
|
||||
|
||||
if RUBY_ENGINE == "truffleruby"
|
||||
File.write("Makefile", dummy_makefile($srcdir).join(""))
|
||||
return
|
||||
end
|
||||
|
||||
require_relative '../../lib/mini_racer/version'
|
||||
gem 'libv8-node', MiniRacer::LIBV8_NODE_VERSION
|
||||
require 'libv8-node'
|
||||
|
@ -70,6 +76,9 @@ end
|
|||
|
||||
Libv8::Node.configure_makefile
|
||||
|
||||
# --exclude-libs is only for i386 PE and ELF targeted ports
|
||||
append_ldflags("-Wl,--exclude-libs=ALL ")
|
||||
|
||||
if enable_config('asan')
|
||||
$CXXFLAGS.insert(0, " -fsanitize=address ")
|
||||
$LDFLAGS.insert(0, " -fsanitize=address ")
|
||||
|
|
|
@ -305,8 +305,7 @@ static std::unique_ptr<Platform> current_platform = NULL;
|
|||
static std::mutex platform_lock;
|
||||
|
||||
static pthread_attr_t *thread_attr_p;
|
||||
static pthread_rwlock_t exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
static bool ruby_exiting = false; // guarded by exit_lock
|
||||
static std::atomic_int ruby_exiting(0);
|
||||
static bool single_threaded = false;
|
||||
|
||||
static void mark_context(void *);
|
||||
|
@ -335,10 +334,7 @@ static const rb_data_type_t isolate_type = {
|
|||
static VALUE rb_platform_set_flag_as_str(VALUE _klass, VALUE flag_as_str) {
|
||||
bool platform_already_initialized = false;
|
||||
|
||||
if(TYPE(flag_as_str) != T_STRING) {
|
||||
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE" (should be a string)",
|
||||
rb_obj_class(flag_as_str));
|
||||
}
|
||||
Check_Type(flag_as_str, T_STRING);
|
||||
|
||||
platform_lock.lock();
|
||||
|
||||
|
@ -664,11 +660,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|||
v8::String::Utf8Value symbol_name(isolate,
|
||||
Local<Symbol>::Cast(value)->Name());
|
||||
|
||||
VALUE str_symbol = rb_enc_str_new(
|
||||
*symbol_name,
|
||||
symbol_name.length(),
|
||||
rb_enc_find("utf-8")
|
||||
);
|
||||
VALUE str_symbol = rb_utf8_str_new(*symbol_name, symbol_name.length());
|
||||
|
||||
return rb_str_intern(str_symbol);
|
||||
}
|
||||
|
@ -679,7 +671,7 @@ static VALUE convert_v8_to_ruby(Isolate* isolate, Local<Context> context,
|
|||
return Qnil;
|
||||
} else {
|
||||
Local<String> rstr = rstr_maybe.ToLocalChecked();
|
||||
return rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
||||
return rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -861,7 +853,7 @@ StartupData warm_up_snapshot_data_blob(StartupData cold_snapshot_blob,
|
|||
return result;
|
||||
}
|
||||
|
||||
static VALUE rb_snapshot_size(VALUE self, VALUE str) {
|
||||
static VALUE rb_snapshot_size(VALUE self) {
|
||||
SnapshotInfo* snapshot_info;
|
||||
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
||||
|
||||
|
@ -872,10 +864,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|||
SnapshotInfo* snapshot_info;
|
||||
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
||||
|
||||
if(TYPE(str) != T_STRING) {
|
||||
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
||||
rb_obj_class(str));
|
||||
}
|
||||
Check_Type(str, T_STRING);
|
||||
|
||||
init_v8();
|
||||
|
||||
|
@ -891,7 +880,7 @@ static VALUE rb_snapshot_load(VALUE self, VALUE str) {
|
|||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE rb_snapshot_dump(VALUE self, VALUE str) {
|
||||
static VALUE rb_snapshot_dump(VALUE self) {
|
||||
SnapshotInfo* snapshot_info;
|
||||
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
||||
|
||||
|
@ -902,10 +891,7 @@ static VALUE rb_snapshot_warmup_unsafe(VALUE self, VALUE str) {
|
|||
SnapshotInfo* snapshot_info;
|
||||
TypedData_Get_Struct(self, SnapshotInfo, &snapshot_type, snapshot_info);
|
||||
|
||||
if(TYPE(str) != T_STRING) {
|
||||
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
||||
rb_obj_class(str));
|
||||
}
|
||||
Check_Type(str, T_STRING);
|
||||
|
||||
init_v8();
|
||||
|
||||
|
@ -1084,8 +1070,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|||
// If we were terminated or have the memory softlimit flag set
|
||||
if (marshal_stack_maxdepth_reached) {
|
||||
ruby_exception = rb_eScriptRuntimeError;
|
||||
std::string msg = std::string("Marshal object depth too deep. Script terminated.");
|
||||
message = rb_enc_str_new(msg.c_str(), msg.length(), rb_enc_find("utf-8"));
|
||||
message = rb_utf8_str_new_literal("Marshal object depth too deep. Script terminated.");
|
||||
} else if (result.terminated || mem_softlimit_reached) {
|
||||
ruby_exception = mem_softlimit_reached ? rb_eV8OutOfMemoryError : rb_eScriptTerminatedError;
|
||||
} else {
|
||||
|
@ -1120,7 +1105,7 @@ static VALUE convert_result_to_ruby(VALUE self /* context */,
|
|||
|
||||
if (result.json) {
|
||||
Local<String> rstr = tmp->ToString(p_ctx->Get(isolate)).ToLocalChecked();
|
||||
VALUE json_string = rb_enc_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate), rb_enc_find("utf-8"));
|
||||
VALUE json_string = rb_utf8_str_new(*String::Utf8Value(isolate, rstr), rstr->Utf8Length(isolate));
|
||||
ret = rb_funcall(rb_mJSON, rb_intern("parse"), 1, json_string);
|
||||
} else {
|
||||
StackCounter::Reset(isolate);
|
||||
|
@ -1149,13 +1134,10 @@ static VALUE rb_context_eval_unsafe(VALUE self, VALUE str, VALUE filename) {
|
|||
TypedData_Get_Struct(self, ContextInfo, &context_type, context_info);
|
||||
Isolate* isolate = context_info->isolate_info->isolate;
|
||||
|
||||
if(TYPE(str) != T_STRING) {
|
||||
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be a string)",
|
||||
rb_obj_class(str));
|
||||
}
|
||||
if(filename != Qnil && TYPE(filename) != T_STRING) {
|
||||
rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be nil or a string)",
|
||||
rb_obj_class(filename));
|
||||
Check_Type(str, T_STRING);
|
||||
|
||||
if (!NIL_P(filename)) {
|
||||
Check_Type(filename, T_STRING);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1474,42 +1456,33 @@ static void free_context_raw(void *arg) {
|
|||
if (isolate_info) {
|
||||
isolate_info->release();
|
||||
}
|
||||
|
||||
xfree(context_info);
|
||||
}
|
||||
|
||||
static void *free_context_thr(void* arg) {
|
||||
if (pthread_rwlock_tryrdlock(&exit_lock) != 0) {
|
||||
return NULL;
|
||||
if (ruby_exiting.load() == 0) {
|
||||
free_context_raw(arg);
|
||||
xfree(arg);
|
||||
}
|
||||
if (ruby_exiting) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free_context_raw(arg);
|
||||
|
||||
pthread_rwlock_unlock(&exit_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// destroys everything except freeing the ContextInfo struct (see deallocate())
|
||||
static void free_context(ContextInfo* context_info) {
|
||||
|
||||
IsolateInfo* isolate_info = context_info->isolate_info;
|
||||
|
||||
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
||||
context_info_copy->isolate_info = context_info->isolate_info;
|
||||
context_info_copy->context = context_info->context;
|
||||
|
||||
if (isolate_info && isolate_info->refs() > 1) {
|
||||
pthread_t free_context_thread;
|
||||
ContextInfo* context_info_copy = ALLOC(ContextInfo);
|
||||
|
||||
context_info_copy->isolate_info = context_info->isolate_info;
|
||||
context_info_copy->context = context_info->context;
|
||||
if (pthread_create(&free_context_thread, thread_attr_p,
|
||||
free_context_thr, (void*)context_info_copy)) {
|
||||
fprintf(stderr, "WARNING failed to release memory in MiniRacer, thread to release could not be created, process will leak memory\n");
|
||||
xfree(context_info_copy);
|
||||
}
|
||||
} else {
|
||||
free_context_raw(context_info_copy);
|
||||
free_context_raw(context_info);
|
||||
}
|
||||
|
||||
context_info->context = NULL;
|
||||
|
@ -1782,9 +1755,7 @@ static VALUE rb_context_call_unsafe(int argc, VALUE *argv, VALUE self) {
|
|||
}
|
||||
|
||||
VALUE function_name = argv[0];
|
||||
if (TYPE(function_name) != T_STRING) {
|
||||
rb_raise(rb_eTypeError, "first argument should be a String");
|
||||
}
|
||||
Check_Type(function_name, T_STRING);
|
||||
|
||||
char *fname = RSTRING_PTR(function_name);
|
||||
if (!fname) {
|
||||
|
@ -1870,12 +1841,7 @@ static VALUE rb_context_create_isolate_value(VALUE self) {
|
|||
static void set_ruby_exiting(VALUE value) {
|
||||
(void)value;
|
||||
|
||||
int res = pthread_rwlock_wrlock(&exit_lock);
|
||||
|
||||
ruby_exiting = true;
|
||||
if (res == 0) {
|
||||
pthread_rwlock_unlock(&exit_lock);
|
||||
}
|
||||
ruby_exiting.store(1);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
@ -1941,9 +1907,5 @@ extern "C" {
|
|||
thread_attr_p = &attr;
|
||||
}
|
||||
}
|
||||
auto on_fork_for_child = []() {
|
||||
exit_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
};
|
||||
pthread_atfork(nullptr, nullptr, on_fork_for_child);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
require 'mkmf'
|
||||
|
||||
if RUBY_ENGINE == "truffleruby"
|
||||
File.write("Makefile", dummy_makefile($srcdir).join(""))
|
||||
return
|
||||
end
|
||||
|
||||
extension_name = 'mini_racer_loader'
|
||||
dir_config extension_name
|
||||
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
require "mini_racer/version"
|
||||
require "mini_racer_loader"
|
||||
require "pathname"
|
||||
|
||||
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
||||
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
||||
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
||||
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
||||
if RUBY_ENGINE == "truffleruby"
|
||||
require "mini_racer/truffleruby"
|
||||
else
|
||||
require "mini_racer_loader"
|
||||
ext_filename = "mini_racer_extension.#{RbConfig::CONFIG['DLEXT']}"
|
||||
ext_path = Gem.loaded_specs['mini_racer'].require_paths
|
||||
.map { |p| (p = Pathname.new(p)).absolute? ? p : Pathname.new(__dir__).parent + p }
|
||||
ext_found = ext_path.map { |p| p + ext_filename }.find { |p| p.file? }
|
||||
|
||||
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
||||
MiniRacer::Loader.load(ext_found.to_s)
|
||||
raise LoadError, "Could not find #{ext_filename} in #{ext_path.map(&:to_s)}" unless ext_found
|
||||
MiniRacer::Loader.load(ext_found.to_s)
|
||||
end
|
||||
|
||||
require "thread"
|
||||
require "json"
|
||||
require "io/wait"
|
||||
|
||||
module MiniRacer
|
||||
|
||||
|
@ -202,7 +207,7 @@ module MiniRacer
|
|||
end
|
||||
|
||||
if !(File === f)
|
||||
raise ArgumentError("file_or_io")
|
||||
raise ArgumentError, "file_or_io"
|
||||
end
|
||||
|
||||
write_heap_snapshot_unsafe(f)
|
||||
|
@ -349,7 +354,7 @@ module MiniRacer
|
|||
|
||||
t = Thread.new do
|
||||
begin
|
||||
result = IO.select([rp],[],[],(@timeout/1000.0))
|
||||
result = rp.wait_readable(@timeout/1000.0)
|
||||
if !result
|
||||
mutex.synchronize do
|
||||
stop unless done
|
||||
|
@ -366,7 +371,7 @@ module MiniRacer
|
|||
done = true
|
||||
end
|
||||
|
||||
wp.write("done")
|
||||
wp.close
|
||||
|
||||
# ensure we do not leak a thread in state
|
||||
t.join
|
||||
|
@ -375,12 +380,9 @@ module MiniRacer
|
|||
rval
|
||||
ensure
|
||||
# exceptions need to be handled
|
||||
if t && wp
|
||||
wp.write("done")
|
||||
t.join
|
||||
end
|
||||
wp.close if wp
|
||||
rp.close if rp
|
||||
wp&.close
|
||||
t&.join
|
||||
rp&.close
|
||||
end
|
||||
|
||||
def check_init_options!(isolate:, snapshot:, max_memory:, marshal_stack_depth:, ensure_gc_after_idle:, timeout:)
|
||||
|
|
355
lib/mini_racer/truffleruby.rb
Normal file
355
lib/mini_racer/truffleruby.rb
Normal file
|
@ -0,0 +1,355 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module MiniRacer
|
||||
|
||||
class Context
|
||||
|
||||
class ExternalFunction
|
||||
private
|
||||
|
||||
def notify_v8
|
||||
name = @name.encode(::Encoding::UTF_8)
|
||||
wrapped = lambda do |*args|
|
||||
converted = @parent.send(:convert_js_to_ruby, args)
|
||||
begin
|
||||
result = @callback.call(*converted)
|
||||
rescue Polyglot::ForeignException => e
|
||||
e = RuntimeError.new(e.message)
|
||||
e.set_backtrace(e.backtrace)
|
||||
@parent.instance_variable_set(:@current_exception, e)
|
||||
raise e
|
||||
rescue => e
|
||||
@parent.instance_variable_set(:@current_exception, e)
|
||||
raise e
|
||||
end
|
||||
@parent.send(:convert_ruby_to_js, result)
|
||||
end
|
||||
|
||||
if @parent_object.nil?
|
||||
# set global name to proc
|
||||
result = @parent.eval_in_context('this')
|
||||
result[name] = wrapped
|
||||
else
|
||||
parent_object_eval = @parent_object_eval.encode(::Encoding::UTF_8)
|
||||
begin
|
||||
result = @parent.eval_in_context(parent_object_eval)
|
||||
rescue Polyglot::ForeignException, StandardError => e
|
||||
raise ParseError, "Was expecting #{@parent_object} to be an object", e.backtrace
|
||||
end
|
||||
result[name] = wrapped
|
||||
# set evaluated object results name to proc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def heap_stats
|
||||
{
|
||||
total_physical_size: 0,
|
||||
total_heap_size_executable: 0,
|
||||
total_heap_size: 0,
|
||||
used_heap_size: 0,
|
||||
heap_size_limit: 0,
|
||||
}
|
||||
end
|
||||
|
||||
def stop
|
||||
if @entered
|
||||
@context.stop
|
||||
@stopped = true
|
||||
stop_attached
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@context_initialized = false
|
||||
@use_strict = false
|
||||
|
||||
def init_unsafe(isolate, snapshot)
|
||||
unless defined?(Polyglot::InnerContext)
|
||||
raise "TruffleRuby #{RUBY_ENGINE_VERSION} does not have support for inner contexts, use a more recent version"
|
||||
end
|
||||
|
||||
unless Polyglot.languages.include? "js"
|
||||
raise "The language 'js' is not available, you likely need to `export TRUFFLERUBYOPT='--jvm --polyglot'`\n" \
|
||||
"You also need to install the 'js' component with 'gu install js' on GraalVM 22.2+\n" \
|
||||
"Note that you need TruffleRuby+GraalVM and not just the TruffleRuby standalone to use MiniRacer"
|
||||
end
|
||||
|
||||
@context = Polyglot::InnerContext.new(on_cancelled: -> {
|
||||
raise ScriptTerminatedError, 'JavaScript was terminated (either by timeout or explicitly)'
|
||||
})
|
||||
Context.instance_variable_set(:@context_initialized, true)
|
||||
@js_object = @context.eval('js', 'Object')
|
||||
@isolate_mutex = Mutex.new
|
||||
@stopped = false
|
||||
@entered = false
|
||||
@has_entered = false
|
||||
@current_exception = nil
|
||||
if isolate && snapshot
|
||||
isolate.instance_variable_set(:@snapshot, snapshot)
|
||||
end
|
||||
if snapshot
|
||||
@snapshot = snapshot
|
||||
elsif isolate
|
||||
@snapshot = isolate.instance_variable_get(:@snapshot)
|
||||
else
|
||||
@snapshot = nil
|
||||
end
|
||||
@is_object_or_array_func, @is_time_func, @js_date_to_time_func, @is_symbol_func, @js_symbol_to_symbol_func, @js_new_date_func, @js_new_array_func = eval_in_context <<-CODE
|
||||
[
|
||||
(x) => { return (x instanceof Object || x instanceof Array) && !(x instanceof Date) && !(x instanceof Function) },
|
||||
(x) => { return x instanceof Date },
|
||||
(x) => { return x.getTime(x) },
|
||||
(x) => { return typeof x === 'symbol' },
|
||||
(x) => { var r = x.description; return r === undefined ? 'undefined' : r },
|
||||
(x) => { return new Date(x) },
|
||||
(x) => { return new Array(x) },
|
||||
]
|
||||
CODE
|
||||
end
|
||||
|
||||
def dispose_unsafe
|
||||
@context.close
|
||||
end
|
||||
|
||||
def eval_unsafe(str, filename)
|
||||
@entered = true
|
||||
if !@has_entered && @snapshot
|
||||
snapshot_src = encode(@snapshot.instance_variable_get(:@source))
|
||||
begin
|
||||
eval_in_context(snapshot_src, filename)
|
||||
rescue Polyglot::ForeignException => e
|
||||
raise RuntimeError, e.message, e.backtrace
|
||||
end
|
||||
end
|
||||
@has_entered = true
|
||||
raise RuntimeError, "TruffleRuby does not support eval after stop" if @stopped
|
||||
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
||||
raise TypeError, "wrong type argument #{filename.class} (should be a string)" unless filename.nil? || filename.is_a?(String)
|
||||
|
||||
str = encode(str)
|
||||
begin
|
||||
translate do
|
||||
eval_in_context(str, filename)
|
||||
end
|
||||
rescue Polyglot::ForeignException => e
|
||||
raise RuntimeError, e.message, e.backtrace
|
||||
rescue ::RuntimeError => e
|
||||
if @current_exception
|
||||
e = @current_exception
|
||||
@current_exception = nil
|
||||
raise e
|
||||
else
|
||||
raise e, e.message
|
||||
end
|
||||
end
|
||||
ensure
|
||||
@entered = false
|
||||
end
|
||||
|
||||
def call_unsafe(function_name, *arguments)
|
||||
@entered = true
|
||||
if !@has_entered && @snapshot
|
||||
src = encode(@snapshot.instance_variable_get(:source))
|
||||
begin
|
||||
eval_in_context(src)
|
||||
rescue Polyglot::ForeignException => e
|
||||
raise RuntimeError, e.message, e.backtrace
|
||||
end
|
||||
end
|
||||
@has_entered = true
|
||||
raise RuntimeError, "TruffleRuby does not support call after stop" if @stopped
|
||||
begin
|
||||
translate do
|
||||
function = eval_in_context(function_name)
|
||||
function.call(*convert_ruby_to_js(arguments))
|
||||
end
|
||||
rescue Polyglot::ForeignException => e
|
||||
raise RuntimeError, e.message, e.backtrace
|
||||
end
|
||||
ensure
|
||||
@entered = false
|
||||
end
|
||||
|
||||
def create_isolate_value
|
||||
# Returning a dummy object since TruffleRuby does not have a 1-1 concept with isolate.
|
||||
# However, code and ASTs are shared between contexts.
|
||||
Isolate.new
|
||||
end
|
||||
|
||||
def isolate_mutex
|
||||
@isolate_mutex
|
||||
end
|
||||
|
||||
def translate
|
||||
convert_js_to_ruby yield
|
||||
rescue Object => e
|
||||
message = e.message
|
||||
if @current_exception
|
||||
raise @current_exception
|
||||
elsif e.message && e.message.start_with?('SyntaxError:')
|
||||
error_class = MiniRacer::ParseError
|
||||
elsif e.is_a?(MiniRacer::ScriptTerminatedError)
|
||||
error_class = MiniRacer::ScriptTerminatedError
|
||||
else
|
||||
error_class = MiniRacer::RuntimeError
|
||||
end
|
||||
|
||||
if error_class == MiniRacer::RuntimeError
|
||||
bls = e.backtrace_locations&.select { |bl| bl&.source_location&.language == 'js' }
|
||||
if bls && !bls.empty?
|
||||
if '(eval)' != bls[0].path
|
||||
message = "#{e.message}\n at #{bls[0]}\n" + bls[1..].map(&:to_s).join("\n")
|
||||
else
|
||||
message = "#{e.message}\n" + bls.map(&:to_s).join("\n")
|
||||
end
|
||||
end
|
||||
raise error_class, message
|
||||
else
|
||||
raise error_class, message, e.backtrace
|
||||
end
|
||||
end
|
||||
|
||||
def convert_js_to_ruby(value)
|
||||
case value
|
||||
when true, false, Integer, Float
|
||||
value
|
||||
else
|
||||
if value.nil?
|
||||
nil
|
||||
elsif value.respond_to?(:call)
|
||||
MiniRacer::JavaScriptFunction.new
|
||||
elsif value.respond_to?(:to_str)
|
||||
value.to_str.dup
|
||||
elsif value.respond_to?(:to_ary)
|
||||
value.to_ary.map do |e|
|
||||
if e.respond_to?(:call)
|
||||
nil
|
||||
else
|
||||
convert_js_to_ruby(e)
|
||||
end
|
||||
end
|
||||
elsif time?(value)
|
||||
js_date_to_time(value)
|
||||
elsif symbol?(value)
|
||||
js_symbol_to_symbol(value)
|
||||
else
|
||||
object = value
|
||||
h = {}
|
||||
object.instance_variables.each do |member|
|
||||
v = object[member]
|
||||
unless v.respond_to?(:call)
|
||||
h[member.to_s] = convert_js_to_ruby(v)
|
||||
end
|
||||
end
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def object_or_array?(val)
|
||||
@is_object_or_array_func.call(val)
|
||||
end
|
||||
|
||||
def time?(value)
|
||||
@is_time_func.call(value)
|
||||
end
|
||||
|
||||
def js_date_to_time(value)
|
||||
millis = @js_date_to_time_func.call(value)
|
||||
Time.at(Rational(millis, 1000))
|
||||
end
|
||||
|
||||
def symbol?(value)
|
||||
@is_symbol_func.call(value)
|
||||
end
|
||||
|
||||
def js_symbol_to_symbol(value)
|
||||
@js_symbol_to_symbol_func.call(value).to_s.to_sym
|
||||
end
|
||||
|
||||
def js_new_date(value)
|
||||
@js_new_date_func.call(value)
|
||||
end
|
||||
|
||||
def js_new_array(size)
|
||||
@js_new_array_func.call(size)
|
||||
end
|
||||
|
||||
def convert_ruby_to_js(value)
|
||||
case value
|
||||
when nil, true, false, Integer, Float
|
||||
value
|
||||
when Array
|
||||
ary = js_new_array(value.size)
|
||||
value.each_with_index do |v, i|
|
||||
ary[i] = convert_ruby_to_js(v)
|
||||
end
|
||||
ary
|
||||
when Hash
|
||||
h = @js_object.new
|
||||
value.each_pair do |k, v|
|
||||
h[convert_ruby_to_js(k.to_s)] = convert_ruby_to_js(v)
|
||||
end
|
||||
h
|
||||
when String, Symbol
|
||||
Truffle::Interop.as_truffle_string value
|
||||
when Time
|
||||
js_new_date(value.to_f * 1000)
|
||||
when DateTime
|
||||
js_new_date(value.to_time.to_f * 1000)
|
||||
else
|
||||
"Undefined Conversion"
|
||||
end
|
||||
end
|
||||
|
||||
def encode(string)
|
||||
raise ArgumentError unless string
|
||||
string.encode(::Encoding::UTF_8)
|
||||
end
|
||||
|
||||
class_eval <<-'RUBY', "(mini_racer)", 1
|
||||
def eval_in_context(code, file = nil); code = ('"use strict";' + code) if Context.instance_variable_get(:@use_strict); @context.eval('js', code, file || '(mini_racer)'); end
|
||||
RUBY
|
||||
|
||||
end
|
||||
|
||||
class Isolate
|
||||
def init_with_snapshot(snapshot)
|
||||
# TruffleRuby does not have a 1-1 concept with isolate.
|
||||
# However, isolate can hold a snapshot, and code and ASTs are shared between contexts.
|
||||
@snapshot = snapshot
|
||||
end
|
||||
|
||||
def low_memory_notification
|
||||
GC.start
|
||||
end
|
||||
|
||||
def idle_notification(idle_time)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class Platform
|
||||
def self.set_flag_as_str!(flag)
|
||||
raise TypeError, "wrong type argument #{flag.class} (should be a string)" unless flag.is_a?(String)
|
||||
raise MiniRacer::PlatformAlreadyInitialized, "The platform is already initialized." if Context.instance_variable_get(:@context_initialized)
|
||||
Context.instance_variable_set(:@use_strict, true) if "--use_strict" == flag
|
||||
end
|
||||
end
|
||||
|
||||
class Snapshot
|
||||
def load(str)
|
||||
raise TypeError, "wrong type argument #{str.class} (should be a string)" unless str.is_a?(String)
|
||||
# Intentionally noop since TruffleRuby mocks the snapshot API
|
||||
end
|
||||
|
||||
def warmup_unsafe!(src)
|
||||
raise TypeError, "wrong type argument #{src.class} (should be a string)" unless src.is_a?(String)
|
||||
# Intentionally noop since TruffleRuby mocks the snapshot API
|
||||
# by replaying snapshot source before the first eval/call
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module MiniRacer
|
||||
VERSION = "0.6.2"
|
||||
VERSION = "0.6.3"
|
||||
LIBV8_NODE_VERSION = "~> 16.10.0.0"
|
||||
end
|
||||
|
|
|
@ -35,7 +35,8 @@ class MiniRacerFunctionTest < Minitest::Test
|
|||
context.call('f', 1)
|
||||
end
|
||||
assert_equal err.message, 'Error: foo bar'
|
||||
assert_match(/1:23/, err.backtrace[0])
|
||||
assert_match(/1:23/, err.backtrace[0]) unless RUBY_ENGINE == "truffleruby"
|
||||
assert_match(/1:/, err.backtrace[0]) if RUBY_ENGINE == "truffleruby"
|
||||
end
|
||||
|
||||
def test_args_types
|
||||
|
|
|
@ -8,8 +8,8 @@ class MiniRacerTest < Minitest::Test
|
|||
# see `test_platform_set_flags_works` below
|
||||
MiniRacer::Platform.set_flags! :use_strict
|
||||
|
||||
|
||||
def test_locale
|
||||
skip "TruffleRuby does not have all js timezone by default" if RUBY_ENGINE == "truffleruby"
|
||||
val = MiniRacer::Context.new.eval("new Date('April 28 2021').toLocaleDateString('es-MX');")
|
||||
assert_equal '28/4/2021', val
|
||||
|
||||
|
@ -46,7 +46,7 @@ class MiniRacerTest < Minitest::Test
|
|||
|
||||
def test_compile_nil_context
|
||||
context = MiniRacer::Context.new
|
||||
assert_raises(ArgumentError) do
|
||||
assert_raises(TypeError) do
|
||||
assert_equal 2, context.eval(nil)
|
||||
end
|
||||
end
|
||||
|
@ -88,7 +88,7 @@ class MiniRacerTest < Minitest::Test
|
|||
|
||||
begin
|
||||
Thread.new do
|
||||
sleep 0.001
|
||||
sleep 0.01
|
||||
context.stop
|
||||
end
|
||||
context.eval('while(true){}')
|
||||
|
@ -102,6 +102,7 @@ class MiniRacerTest < Minitest::Test
|
|||
end
|
||||
|
||||
def test_it_can_timeout_during_serialization
|
||||
skip "TruffleRuby needs a fix for timing out during translation" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(timeout: 500)
|
||||
|
||||
assert_raises(MiniRacer::ScriptTerminatedError) do
|
||||
|
@ -302,12 +303,14 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_max_memory
|
||||
skip "TruffleRuby does not yet implement max_memory" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(max_memory: 200_000_000)
|
||||
|
||||
assert_raises(MiniRacer::V8OutOfMemoryError) { context.eval('let s = 1000; var a = new Array(s); a.fill(0); while(true) {s *= 1.1; let n = new Array(Math.floor(s)); n.fill(0); a = a.concat(n); };') }
|
||||
end
|
||||
|
||||
def test_max_memory_for_call
|
||||
skip "TruffleRuby does not yet implement max_memory" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(max_memory: 100_000_000)
|
||||
context.eval(<<~JS)
|
||||
let s;
|
||||
|
@ -399,6 +402,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_snapshot_size
|
||||
skip "TruffleRuby does not yet implement snapshots" if RUBY_ENGINE == "truffleruby"
|
||||
snapshot = MiniRacer::Snapshot.new('var foo = "bar";')
|
||||
|
||||
# for some reason sizes seem to change across runs, so we just
|
||||
|
@ -407,6 +411,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_snapshot_dump
|
||||
skip "TruffleRuby does not yet implement snapshots" if RUBY_ENGINE == "truffleruby"
|
||||
snapshot = MiniRacer::Snapshot.new('var foo = "bar";')
|
||||
dump = snapshot.dump
|
||||
|
||||
|
@ -433,7 +438,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_snapshots_can_be_warmed_up_with_no_side_effects
|
||||
# shamelessly insipired by https://github.com/v8/v8/blob/5.3.254/test/cctest/test-serialize.cc#L792-L854
|
||||
# shamelessly inspired by https://github.com/v8/v8/blob/5.3.254/test/cctest/test-serialize.cc#L792-L854
|
||||
snapshot_source = <<-JS
|
||||
function f() { return Math.sin(1); }
|
||||
var a = 5;
|
||||
|
@ -463,7 +468,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_invalid_warmup_sources_throw_an_exception_2
|
||||
assert_raises(ArgumentError) do
|
||||
assert_raises(TypeError) do
|
||||
MiniRacer::Snapshot.new('function f() { return 1 }').warmup!([])
|
||||
end
|
||||
end
|
||||
|
@ -581,13 +586,11 @@ raise FooError, "I like foos"
|
|||
def test_concurrent_access_over_the_same_isolate_2
|
||||
isolate = MiniRacer::Isolate.new
|
||||
|
||||
equals_after_sleep = {}
|
||||
|
||||
# workaround Rubies prior to commit 475c8701d74ebebe
|
||||
# (Make SecureRandom support Ractor, 2020-09-04)
|
||||
SecureRandom.hex
|
||||
|
||||
(1..10).map do |i|
|
||||
equals_after_sleep = (1..10).map do |i|
|
||||
Thread.new {
|
||||
random = SecureRandom.hex
|
||||
context = MiniRacer::Context.new(isolate: isolate)
|
||||
|
@ -598,12 +601,12 @@ raise FooError, "I like foos"
|
|||
|
||||
# cruby hashes are thread safe as long as you don't mess with the
|
||||
# same key in different threads
|
||||
equals_after_sleep[i] = context.eval('a') == random
|
||||
context.eval('a') == random
|
||||
}
|
||||
end.each(&:join)
|
||||
end.map(&:value)
|
||||
|
||||
assert_equal 10, equals_after_sleep.size
|
||||
assert equals_after_sleep.values.all?
|
||||
assert equals_after_sleep.all?
|
||||
end
|
||||
|
||||
def test_platform_set_flags_raises_an_exception_if_already_initialized
|
||||
|
@ -645,7 +648,7 @@ raise FooError, "I like foos"
|
|||
|
||||
def test_timeout_in_ruby_land
|
||||
context = MiniRacer::Context.new(timeout: 50)
|
||||
context.attach('sleep', proc{ sleep 0.1 })
|
||||
context.attach('sleep', proc{ sleep 0.5 })
|
||||
assert_raises(MiniRacer::ScriptTerminatedError) do
|
||||
context.eval('sleep(); "hi";')
|
||||
end
|
||||
|
@ -711,6 +714,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_estimated_size
|
||||
skip "TruffleRuby does not yet implement heap_stats" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(timeout: 5)
|
||||
context.eval("let a='testing';")
|
||||
|
||||
|
@ -754,7 +758,7 @@ raise FooError, "I like foos"
|
|||
|
||||
context.eval("'#{"x" * 10_000_000}'")
|
||||
|
||||
sleep 0.005
|
||||
sleep 0.01
|
||||
|
||||
end_heap = context.heap_stats[:used_heap_size]
|
||||
|
||||
|
@ -780,7 +784,7 @@ raise FooError, "I like foos"
|
|||
|
||||
def test_estimated_size_when_disposed
|
||||
|
||||
context = MiniRacer::Context.new(timeout: 5)
|
||||
context = MiniRacer::Context.new(timeout: 50)
|
||||
context.eval("let a='testing';")
|
||||
context.dispose
|
||||
|
||||
|
@ -805,7 +809,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_attached_recursion
|
||||
context = MiniRacer::Context.new(timeout: 20)
|
||||
context = MiniRacer::Context.new(timeout: 200)
|
||||
context.attach("a", proc{|a| a})
|
||||
context.attach("b", proc{|a| a})
|
||||
|
||||
|
@ -842,6 +846,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_heap_dump
|
||||
skip "TruffleRuby does not yet implement heap_dump" if RUBY_ENGINE == "truffleruby"
|
||||
f = Tempfile.new("heap")
|
||||
path = f.path
|
||||
f.unlink
|
||||
|
@ -873,6 +878,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_cyclical_object_js
|
||||
skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(marshal_stack_depth: 5)
|
||||
context.attach("a", proc{|a| a})
|
||||
|
||||
|
@ -880,6 +886,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_cyclical_array_js
|
||||
skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(marshal_stack_depth: 5)
|
||||
context.attach("a", proc{|a| a})
|
||||
|
||||
|
@ -887,6 +894,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_cyclical_elem_in_array_js
|
||||
skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(marshal_stack_depth: 5)
|
||||
context.attach("a", proc{|a| a})
|
||||
|
||||
|
@ -894,9 +902,10 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_infinite_object_js
|
||||
skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(marshal_stack_depth: 5)
|
||||
context.attach("a", proc{|a| a})
|
||||
|
||||
|
||||
js = <<~JS
|
||||
var d=0;
|
||||
function get(z) {
|
||||
|
@ -911,6 +920,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_deep_object_js
|
||||
skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new(marshal_stack_depth: 5)
|
||||
context.attach("a", proc{|a| a})
|
||||
|
||||
|
@ -966,6 +976,7 @@ raise FooError, "I like foos"
|
|||
end
|
||||
|
||||
def test_webassembly
|
||||
skip "TruffleRuby does not enable WebAssembly by default" if RUBY_ENGINE == "truffleruby"
|
||||
context = MiniRacer::Context.new()
|
||||
context.eval("let instance = null;")
|
||||
filename = File.expand_path("../support/add.wasm", __FILE__)
|
||||
|
@ -973,16 +984,16 @@ raise FooError, "I like foos"
|
|||
context.attach("print", proc {|f| puts f})
|
||||
|
||||
context.eval <<~JS
|
||||
WebAssembly
|
||||
.instantiate(new Uint8Array(loadwasm()), {
|
||||
wasi_snapshot_preview1: {
|
||||
proc_exit: function() { print("exit"); },
|
||||
args_get: function() { return 0 },
|
||||
args_sizes_get: function() { return 0 }
|
||||
}
|
||||
})
|
||||
.then(i => { instance = i["instance"];})
|
||||
.catch(e => print(e.toString()));
|
||||
WebAssembly
|
||||
.instantiate(new Uint8Array(loadwasm()), {
|
||||
wasi_snapshot_preview1: {
|
||||
proc_exit: function() { print("exit"); },
|
||||
args_get: function() { return 0 },
|
||||
args_sizes_get: function() { return 0 }
|
||||
}
|
||||
})
|
||||
.then(i => { instance = i["instance"];})
|
||||
.catch(e => print(e.toString()));
|
||||
JS
|
||||
|
||||
while !context.eval("instance") do
|
||||
|
@ -1022,4 +1033,27 @@ raise FooError, "I like foos"
|
|||
JS
|
||||
end
|
||||
end
|
||||
|
||||
def test_eval_returns_unfrozen_string
|
||||
context = MiniRacer::Context.new
|
||||
result = context.eval("'Hello George!'")
|
||||
assert_equal("Hello George!", result)
|
||||
assert_equal(false, result.frozen?)
|
||||
end
|
||||
|
||||
def test_call_returns_unfrozen_string
|
||||
context = MiniRacer::Context.new
|
||||
context.eval('function hello(name) { return "Hello " + name + "!" }')
|
||||
result = context.call('hello', 'George')
|
||||
assert_equal("Hello George!", result)
|
||||
assert_equal(false, result.frozen?)
|
||||
end
|
||||
|
||||
def test_callback_string_arguments_are_not_frozen
|
||||
context = MiniRacer::Context.new
|
||||
context.attach("test", proc{ |text| text.frozen? })
|
||||
|
||||
frozen = context.eval("test('Hello George!')")
|
||||
assert_equal(false, frozen)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,6 +23,6 @@ trigger_gc
|
|||
|
||||
MiniRacer::Context.new.dispose
|
||||
|
||||
Process.wait fork { puts @ctx.eval("a"); @ctx.dispose; puts Process.pid; trigger_gc; puts "done #{Process.pid}" }
|
||||
|
||||
|
||||
if Process.respond_to?(:fork)
|
||||
Process.wait fork { puts @ctx.eval("a"); @ctx.dispose; puts Process.pid; trigger_gc; puts "done #{Process.pid}" }
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue