From 97a31db097d571186c7740a585b6540013a15ee7 Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Mon, 29 Jul 2024 08:03:15 +1000 Subject: [PATCH 1/2] Version Bump - 0.13.0 - Syntax tree cleanup - Version bump - Test cleanup (fix invalid timeout test) --- .github/workflows/ci.yml | 1 - CHANGELOG | 5 + lib/mini_racer/version.rb | 4 +- mini_racer.gemspec | 58 +++-- test/mini_racer_test.rb | 531 ++++++++++++++++++++++---------------- 5 files changed, 353 insertions(+), 246 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4238d05..1618b78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,6 @@ jobs: - "macos-13" - "macos-14" # arm64 ruby: - - "ruby-3.0" # EOL as of 2024-04-23 - "ruby-3.1" - "ruby-3.2" - "ruby-3.3" diff --git a/CHANGELOG b/CHANGELOG index 6594582..ee65e75 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +- 0.13.0 - 29-07-2024 + + - Target Node to 22.5.1.0 0 - corrects segfault in earlier release + - Remove Ruby 3.0 which is EOL (use ealier version of gem if needed) + - 0.9.0 - 25-03-2024 - Target Node to 18.19.0.0 diff --git a/lib/mini_racer/version.rb b/lib/mini_racer/version.rb index 10f499f..9480dbf 100644 --- a/lib/mini_racer/version.rb +++ b/lib/mini_racer/version.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module MiniRacer - VERSION = "0.12.0" - LIBV8_NODE_VERSION = "~> 21.7.2.0" + VERSION = "0.13.0" + LIBV8_NODE_VERSION = "~> 22.5.1.0" end diff --git a/mini_racer.gemspec b/mini_racer.gemspec index e87bd08..8e5ed03 100644 --- a/mini_racer.gemspec +++ b/mini_racer.gemspec @@ -1,27 +1,38 @@ # coding: utf-8 -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'mini_racer/version' +require "mini_racer/version" Gem::Specification.new do |spec| - spec.name = "mini_racer" - spec.version = MiniRacer::VERSION - spec.authors = ["Sam Saffron"] - spec.email = ["sam.saffron@gmail.com"] - - spec.summary = %q{Minimal embedded v8 for Ruby} - spec.description = %q{Minimal embedded v8 engine for Ruby} - spec.homepage = "https://github.com/discourse/mini_racer" - spec.license = "MIT" - - spec.metadata = { - "bug_tracker_uri" => "https://github.com/discourse/mini_racer/issues", - "changelog_uri" => "https://github.com/discourse/mini_racer/blob/v#{spec.version}/CHANGELOG", - "documentation_uri" => "https://www.rubydoc.info/gems/mini_racer/#{spec.version}", - "source_code_uri" => "https://github.com/discourse/mini_racer/tree/v#{spec.version}", + spec.name = "mini_racer" + spec.version = MiniRacer::VERSION + spec.authors = ["Sam Saffron"] + spec.email = ["sam.saffron@gmail.com"] + + spec.summary = "Minimal embedded v8 for Ruby" + spec.description = "Minimal embedded v8 engine for Ruby" + spec.homepage = "https://github.com/discourse/mini_racer" + spec.license = "MIT" + + spec.metadata = { + "bug_tracker_uri" => "https://github.com/discourse/mini_racer/issues", + "changelog_uri" => + "https://github.com/discourse/mini_racer/blob/v#{spec.version}/CHANGELOG", + "documentation_uri" => + "https://www.rubydoc.info/gems/mini_racer/#{spec.version}", + "source_code_uri" => + "https://github.com/discourse/mini_racer/tree/v#{spec.version}" } - spec.files = Dir["lib/**/*.rb", "ext/**/*", "README.md", "LICENSE.txt", "CHANGELOG", "CODE_OF_CONDUCT.md"] + spec.files = + Dir[ + "lib/**/*.rb", + "ext/**/*", + "README.md", + "LICENSE.txt", + "CHANGELOG", + "CODE_OF_CONDUCT.md" + ] spec.require_paths = ["lib"] spec.add_development_dependency "bundler" @@ -30,10 +41,13 @@ Gem::Specification.new do |spec| spec.add_development_dependency "rake-compiler" spec.add_development_dependency "m" - spec.add_dependency 'libv8-node', MiniRacer::LIBV8_NODE_VERSION - spec.require_paths = ["lib", "ext"] + spec.add_dependency "libv8-node", MiniRacer::LIBV8_NODE_VERSION + spec.require_paths = %w[lib ext] - spec.extensions = ["ext/mini_racer_loader/extconf.rb", "ext/mini_racer_extension/extconf.rb"] + spec.extensions = %w[ + ext/mini_racer_loader/extconf.rb + ext/mini_racer_extension/extconf.rb + ] - spec.required_ruby_version = '>= 3.0' + spec.required_ruby_version = ">= 3.1" end diff --git a/test/mini_racer_test.rb b/test/mini_racer_test.rb index 6b139fd..c21e889 100644 --- a/test/mini_racer_test.rb +++ b/test/mini_racer_test.rb @@ -1,31 +1,46 @@ # frozen_string_literal: true -require 'securerandom' -require 'date' -require 'test_helper' +require "securerandom" +require "date" +require "test_helper" class MiniRacerTest < Minitest::Test # see `test_platform_set_flags_works` below MiniRacer::Platform.set_flags! :use_strict def test_locale_mx - 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 + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not have all js timezone by default" + end + val = + MiniRacer::Context.new.eval( + "new Date('April 28 2021').toLocaleDateString('es-MX');" + ) + assert_equal "28/4/2021", val end def test_locale_us - 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('en-US');") - assert_equal '4/28/2021', val + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not have all js timezone by default" + end + val = + MiniRacer::Context.new.eval( + "new Date('April 28 2021').toLocaleDateString('en-US');" + ) + assert_equal "4/28/2021", val end def test_locale_fr # TODO: this causes a segfault on Linux - 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('fr-FR');") - assert_equal '28/04/2021', val + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not have all js timezone by default" + end + val = + MiniRacer::Context.new.eval( + "new Date('April 28 2021').toLocaleDateString('fr-FR');" + ) + assert_equal "28/04/2021", val end def test_segfault @@ -46,31 +61,32 @@ def test_that_it_has_a_version_number def test_types context = MiniRacer::Context.new - assert_equal 2, context.eval('2') + assert_equal 2, context.eval("2") assert_equal "two", context.eval('"two"') - assert_equal 2.1, context.eval('2.1') - assert_equal true, context.eval('true') - assert_equal false, context.eval('false') - assert_nil context.eval('null') - assert_nil context.eval('undefined') + assert_equal 2.1, context.eval("2.1") + assert_equal true, context.eval("true") + assert_equal false, context.eval("false") + assert_nil context.eval("null") + assert_nil context.eval("undefined") end def test_compile_nil_context context = MiniRacer::Context.new - assert_raises(TypeError) do - assert_equal 2, context.eval(nil) - end + assert_raises(TypeError) { assert_equal 2, context.eval(nil) } end def test_array context = MiniRacer::Context.new - assert_equal [1,"two"], context.eval('[1,"two"]') + assert_equal [1, "two"], context.eval('[1,"two"]') end def test_object context = MiniRacer::Context.new # remember JavaScript is quirky {"1" : 1} magically turns to {1: 1} cause magic - assert_equal({"1" => 2, "two" => "two"}, context.eval('var a={"1" : 2, "two" : "two"}; a')) + assert_equal( + { "1" => 2, "two" => "two" }, + context.eval('var a={"1" : 2, "two" : "two"}; a') + ) end def test_it_returns_runtime_error @@ -78,7 +94,7 @@ def test_it_returns_runtime_error exp = nil begin - context.eval('var foo=function(){boom;}; foo()') + context.eval("var foo=function(){boom;}; foo()") rescue => e exp = e end @@ -90,7 +106,7 @@ def test_it_returns_runtime_error assert_match(/mini_racer/, exp.backtrace[2]) # context should not be dead - assert_equal 2, context.eval('1+1') + assert_equal 2, context.eval("1+1") end def test_it_can_stop @@ -102,42 +118,42 @@ def test_it_can_stop sleep 0.01 context.stop end - context.eval('while(true){}') + context.eval("while(true){}") rescue => e exp = e end assert_equal MiniRacer::ScriptTerminatedError, exp.class assert_match(/terminated/, exp.message) - end def test_it_can_timeout_during_serialization - skip "TruffleRuby needs a fix for timing out during translation" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby needs a fix for timing out during translation" + end context = MiniRacer::Context.new(timeout: 500) assert_raises(MiniRacer::ScriptTerminatedError) do - context.eval 'var a = {get a(){ while(true); }}; a' + context.eval "var a = {get a(){ while(true); }}; a" end end def test_it_can_automatically_time_out_context # 2 millisecs is a very short timeout but we don't want test running forever context = MiniRacer::Context.new(timeout: 2) - assert_raises do - context.eval('while(true){}') - end + assert_raises { context.eval("while(true){}") } end def test_returns_javascript_function context = MiniRacer::Context.new - assert_same MiniRacer::JavaScriptFunction, context.eval("var a = function(){}; a").class + assert_same MiniRacer::JavaScriptFunction, + context.eval("var a = function(){}; a").class end def test_it_handles_malformed_js context = MiniRacer::Context.new assert_raises MiniRacer::ParseError do - context.eval('I am not JavaScript {') + context.eval("I am not JavaScript {") end end @@ -156,31 +172,27 @@ def test_it_handles_malformed_js_with_backtrace def test_it_remembers_stuff_in_context context = MiniRacer::Context.new - context.eval('var x = function(){return 22;}') - assert_equal 22, context.eval('x()') + context.eval("var x = function(){return 22;}") + assert_equal 22, context.eval("x()") end def test_can_attach_functions context = MiniRacer::Context.new - context.eval 'var adder' - context.attach("adder", proc{|a,b| a+b}) - assert_equal 3, context.eval('adder(1,2)') + context.eval "var adder" + context.attach("adder", proc { |a, b| a + b }) + assert_equal 3, context.eval("adder(1,2)") end def test_es6_arrow_functions context = MiniRacer::Context.new - assert_equal 42, context.eval('var adder=(x,y)=>x+y; adder(21,21);') + assert_equal 42, context.eval("var adder=(x,y)=>x+y; adder(21,21);") end def test_concurrent_access context = MiniRacer::Context.new - context.eval('var counter=0; var plus=()=>counter++;') + context.eval("var counter=0; var plus=()=>counter++;") - (1..10).map do - Thread.new { - context.eval("plus()") - } - end.each(&:join) + (1..10).map { Thread.new { context.eval("plus()") } }.each(&:join) assert_equal 10, context.eval("counter") end @@ -193,14 +205,14 @@ def initialize(message) def test_attached_exceptions context = MiniRacer::Context.new - context.attach("adder", proc{ raise FooError, "I like foos" }) + context.attach("adder", proc { raise FooError, "I like foos" }) assert_raises do begin -raise FooError, "I like foos" - context.eval('adder()') + raise FooError, "I like foos" + context.eval("adder()") rescue => e assert_equal FooError, e.class - assert_match( /I like foos/, e.message) + assert_match(/I like foos/, e.message) # TODO backtrace splicing so js frames are injected raise end @@ -209,40 +221,49 @@ def test_attached_exceptions def test_attached_on_object context = MiniRacer::Context.new - context.eval 'var minion' - context.attach("minion.speak", proc{"banana"}) + context.eval "var minion" + context.attach("minion.speak", proc { "banana" }) assert_equal "banana", context.eval("minion.speak()") end def test_attached_on_nested_object context = MiniRacer::Context.new - context.eval 'var minion' - context.attach("minion.kevin.speak", proc{"banana"}) + context.eval "var minion" + context.attach("minion.kevin.speak", proc { "banana" }) assert_equal "banana", context.eval("minion.kevin.speak()") end def test_return_arrays context = MiniRacer::Context.new - context.eval 'var nose' - context.attach("nose.type", proc{["banana",["nose"]]}) + context.eval "var nose" + context.attach("nose.type", proc { ["banana", ["nose"]] }) assert_equal ["banana", ["nose"]], context.eval("nose.type()") end def test_return_hash context = MiniRacer::Context.new - context.attach("test", proc{{banana: :nose, "inner" => {42 => 42}}}) - assert_equal({"banana" => "nose", "inner" => {"42" => 42}}, context.eval("test()")) + context.attach( + "test", + proc { { :banana => :nose, "inner" => { 42 => 42 } } } + ) + assert_equal( + { "banana" => "nose", "inner" => { "42" => 42 } }, + context.eval("test()") + ) end def test_return_date context = MiniRacer::Context.new test_time = Time.new test_datetime = test_time.to_datetime - context.attach("test", proc{test_time}) - context.attach("test_datetime", proc{test_datetime}) + context.attach("test", proc { test_time }) + context.attach("test_datetime", proc { test_datetime }) # check that marshalling to JS creates a date object (getTime()) - assert_equal((test_time.to_f*1000).to_i, context.eval("var result = test(); result.getTime();").to_i) + assert_equal( + (test_time.to_f * 1000).to_i, + context.eval("var result = test(); result.getTime();").to_i + ) # check that marshalling to RB creates a Time object result = context.eval("test()") @@ -250,13 +271,19 @@ def test_return_date assert_equal(test_time.tv_sec, result.tv_sec) # check that no precision is lost in the marshalling (js only stores milliseconds) - assert_equal((test_time.tv_usec/1000.0).floor, (result.tv_usec/1000.0).floor) + assert_equal( + (test_time.tv_usec / 1000.0).floor, + (result.tv_usec / 1000.0).floor + ) # check that DateTime gets marshalled to js date and back out as rb Time result = context.eval("test_datetime()") assert_equal(test_time.class, result.class) assert_equal(test_time.tv_sec, result.tv_sec) - assert_equal((test_time.tv_usec/1000.0).floor, (result.tv_usec/1000.0).floor) + assert_equal( + (test_time.tv_usec / 1000.0).floor, + (result.tv_usec / 1000.0).floor + ) end def test_datetime_missing @@ -266,14 +293,20 @@ def test_datetime_missing # no exceptions should happen here, and non-datetime classes should marshall correctly still. context = MiniRacer::Context.new test_time = Time.new - context.attach("test", proc{test_time}) + context.attach("test", proc { test_time }) - assert_equal((test_time.to_f*1000).to_i, context.eval("var result = test(); result.getTime();").to_i) + assert_equal( + (test_time.to_f * 1000).to_i, + context.eval("var result = test(); result.getTime();").to_i + ) result = context.eval("test()") assert_equal(test_time.class, result.class) assert_equal(test_time.tv_sec, result.tv_sec) - assert_equal((test_time.tv_usec/1000.0).floor, (result.tv_usec/1000.0).floor) + assert_equal( + (test_time.tv_usec / 1000.0).floor, + (result.tv_usec / 1000.0).floor + ) ensure Object.const_set(:DateTime, date_time_backup) end @@ -282,7 +315,7 @@ def test_datetime_missing def test_return_large_number context = MiniRacer::Context.new test_num = 1_000_000_000_000_000 - context.attach("test", proc{test_num}) + context.attach("test", proc { test_num }) assert_equal(true, context.eval("test() === 1000000000000000")) assert_equal(test_num, context.eval("test()")) @@ -290,8 +323,8 @@ def test_return_large_number def test_return_int_max context = MiniRacer::Context.new - test_num = 2 ** (31) - 1 #last int32 number - context.attach("test", proc{test_num}) + test_num = 2**(31) - 1 #last int32 number + context.attach("test", proc { test_num }) assert_equal(true, context.eval("test() === 2147483647")) assert_equal(test_num, context.eval("test()")) @@ -300,7 +333,7 @@ def test_return_int_max def test_return_unknown context = MiniRacer::Context.new test_unknown = Date.new # hits T_DATA in convert_ruby_to_v8 - context.attach("test", proc{test_unknown}) + context.attach("test", proc { test_unknown }) assert_equal("Undefined Conversion", context.eval("test()")) # clean up and start up a new context @@ -309,19 +342,27 @@ def test_return_unknown context = MiniRacer::Context.new test_unknown = Date.new # hits T_DATA in convert_ruby_to_v8 - context.attach("test", proc{test_unknown}) + context.attach("test", proc { test_unknown }) assert_equal("Undefined Conversion", context.eval("test()")) end def test_max_memory - skip "TruffleRuby does not yet implement max_memory" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement max_memory" + end 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); };') } + assert_raises(MiniRacer::V8OutOfMemoryError) do + 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 end def test_max_memory_for_call - skip "TruffleRuby does not yet implement max_memory" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement max_memory" + end context = MiniRacer::Context.new(max_memory: 100_000_000) context.eval(<<~JS) let s; @@ -342,9 +383,9 @@ def test_max_memory_for_call s = val; } JS - context.call('set_s', 1000) - assert_raises(MiniRacer::V8OutOfMemoryError) { context.call('memory_test') } - s = context.eval('s') + context.call("set_s", 1000) + assert_raises(MiniRacer::V8OutOfMemoryError) { context.call("memory_test") } + s = context.eval("s") assert_operator(s, :>, 100_000) end @@ -353,9 +394,7 @@ def test_max_memory_bounds MiniRacer::Context.new(max_memory: -200_000_000) end - assert_raises(ArgumentError) do - MiniRacer::Context.new(max_memory: 2**32) - end + assert_raises(ArgumentError) { MiniRacer::Context.new(max_memory: 2**32) } end module Echo @@ -366,7 +405,7 @@ def self.say(thing) def test_can_attach_method context = MiniRacer::Context.new - context.eval 'var Echo' + context.eval "var Echo" context.attach("Echo.say", Echo.method(:say)) assert_equal "hello", context.eval("Echo.say('hello')") end @@ -378,23 +417,20 @@ def test_attach_error context.eval("var minion = 2") assert_raises do begin - context.attach("minion.kevin.speak", proc{"banana"}) + context.attach("minion.kevin.speak", proc { "banana" }) rescue => e assert_equal MiniRacer::ParseError, e.class assert_match(/expecting minion.kevin/, e.message) raise end end - end def test_load context = MiniRacer::Context.new context.load(File.dirname(__FILE__) + "/file.js") assert_equal "world", context.eval("hello") - assert_raises do - context.load(File.dirname(__FILE__) + "/missing.js") - end + assert_raises { context.load(File.dirname(__FILE__) + "/missing.js") } end def test_contexts_can_be_safely_GCed @@ -406,7 +442,10 @@ def test_contexts_can_be_safely_GCed end def test_it_can_use_snapshots - snapshot = MiniRacer::Snapshot.new('function hello() { return "world"; }; var foo = "bar";') + snapshot = + MiniRacer::Snapshot.new( + 'function hello() { return "world"; }; var foo = "bar";' + ) context = MiniRacer::Context.new(snapshot: snapshot) @@ -415,7 +454,9 @@ def test_it_can_use_snapshots end def test_snapshot_size - skip "TruffleRuby does not yet implement snapshots" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement snapshots" + end snapshot = MiniRacer::Snapshot.new('var foo = "bar";') # for some reason sizes seem to change across runs, so we just @@ -424,7 +465,9 @@ def test_snapshot_size end def test_snapshot_dump - skip "TruffleRuby does not yet implement snapshots" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement snapshots" + end snapshot = MiniRacer::Snapshot.new('var foo = "bar";') dump = snapshot.dump @@ -435,9 +478,9 @@ def test_snapshot_dump def test_invalid_snapshots_throw_an_exception begin - MiniRacer::Snapshot.new('var foo = bar;') + MiniRacer::Snapshot.new("var foo = bar;") rescue MiniRacer::SnapshotError => e - assert(e.backtrace[0].include? 'JavaScript') + assert(e.backtrace[0].include? "JavaScript") got_error = true end @@ -445,7 +488,7 @@ def test_invalid_snapshots_throw_an_exception end def test_an_empty_snapshot_is_valid - MiniRacer::Snapshot.new('') + MiniRacer::Snapshot.new("") MiniRacer::Snapshot.new GC.start end @@ -476,32 +519,32 @@ def test_snapshots_can_be_warmed_up_with_no_side_effects def test_invalid_warmup_sources_throw_an_exception assert_raises(MiniRacer::SnapshotError) do - MiniRacer::Snapshot.new('Math.sin = 1;').warmup!('var a = Math.sin(1);') + MiniRacer::Snapshot.new("Math.sin = 1;").warmup!("var a = Math.sin(1);") end end def test_invalid_warmup_sources_throw_an_exception_2 assert_raises(TypeError) do - MiniRacer::Snapshot.new('function f() { return 1 }').warmup!([]) + MiniRacer::Snapshot.new("function f() { return 1 }").warmup!([]) end end def test_warming_up_with_invalid_source_does_not_affect_the_snapshot_internal_state - snapshot = MiniRacer::Snapshot.new('Math.sin = 1;') + snapshot = MiniRacer::Snapshot.new("Math.sin = 1;") begin - snapshot.warmup!('var a = Math.sin(1);') - rescue + snapshot.warmup!("var a = Math.sin(1);") + rescue StandardError # do nothing end context = MiniRacer::Context.new(snapshot: snapshot) - assert_equal 1, context.eval('Math.sin') + assert_equal 1, context.eval("Math.sin") end def test_snapshots_can_be_GCed_without_affecting_contexts_created_from_them - snapshot = MiniRacer::Snapshot.new('Math.sin = 1;') + snapshot = MiniRacer::Snapshot.new("Math.sin = 1;") context = MiniRacer::Context.new(snapshot: snapshot) # force the snapshot to be GC'ed @@ -509,25 +552,25 @@ def test_snapshots_can_be_GCed_without_affecting_contexts_created_from_them GC.start # the context should still work fine - assert_equal 1, context.eval('Math.sin') + assert_equal 1, context.eval("Math.sin") end def test_it_can_re_use_isolates_for_multiple_contexts - snapshot = MiniRacer::Snapshot.new('Math.sin = 1;') + snapshot = MiniRacer::Snapshot.new("Math.sin = 1;") isolate = MiniRacer::Isolate.new(snapshot) context1 = MiniRacer::Context.new(isolate: isolate) - assert_equal 1, context1.eval('Math.sin') + assert_equal 1, context1.eval("Math.sin") - context1.eval('var a = 5;') + context1.eval("var a = 5;") context2 = MiniRacer::Context.new(isolate: isolate) - assert_equal 1, context2.eval('Math.sin') + assert_equal 1, context2.eval("Math.sin") assert_raises MiniRacer::RuntimeError do begin - context2.eval('a;') + context2.eval("a;") rescue => e - assert_equal('ReferenceError: a is not defined', e.message) + assert_equal("ReferenceError: a is not defined", e.message) raise end end @@ -581,19 +624,14 @@ def test_isolate_can_be_notified_of_idle_time assert(isolate.idle_notification(1000)) end - def test_concurrent_access_over_the_same_isolate_1 isolate = MiniRacer::Isolate.new context = MiniRacer::Context.new(isolate: isolate) - context.eval('var counter=0; var plus=()=>counter++;') + context.eval("var counter=0; var plus=()=>counter++;") - (1..10).map do - Thread.new { - context.eval("plus()") - } - end.each(&:join) + (1..10).map { Thread.new { context.eval("plus()") } }.each(&:join) - assert_equal 10, context.eval('counter') + assert_equal 10, context.eval("counter") end def test_concurrent_access_over_the_same_isolate_2 @@ -603,20 +641,27 @@ def test_concurrent_access_over_the_same_isolate_2 # (Make SecureRandom support Ractor, 2020-09-04) SecureRandom.hex - equals_after_sleep = (1..10).map do |i| - Thread.new { - random = SecureRandom.hex - context = MiniRacer::Context.new(isolate: isolate) - - context.eval('var now = new Date().getTime(); while(new Date().getTime() < now + 20) {}') - context.eval("var a='#{random}'") - context.eval('var now = new Date().getTime(); while(new Date().getTime() < now + 20) {}') - - # cruby hashes are thread safe as long as you don't mess with the - # same key in different threads - context.eval('a') == random - } - end.map(&:value) + equals_after_sleep = + (1..10) + .map do |i| + Thread.new do + random = SecureRandom.hex + context = MiniRacer::Context.new(isolate: isolate) + + context.eval( + "var now = new Date().getTime(); while(new Date().getTime() < now + 20) {}" + ) + context.eval("var a='#{random}'") + context.eval( + "var now = new Date().getTime(); while(new Date().getTime() < now + 20) {}" + ) + + # cruby hashes are thread safe as long as you don't mess with the + # same key in different threads + context.eval("a") == random + end + end + .map(&:value) assert_equal 10, equals_after_sleep.size assert equals_after_sleep.all? @@ -636,32 +681,37 @@ def test_platform_set_flags_works assert_raises(MiniRacer::RuntimeError) do # should fail because of strict mode set for all these tests - context.eval 'x = 28' + context.eval "x = 28" end end def test_error_on_return_val v8 = MiniRacer::Context.new assert_raises(MiniRacer::RuntimeError) do - v8.eval('var o = {}; o.__defineGetter__("bar", function() { return null(); }); o') + v8.eval( + 'var o = {}; o.__defineGetter__("bar", function() { return null(); }); o' + ) end end def test_ruby_based_property_in_rval v8 = MiniRacer::Context.new - v8.attach 'echo', proc{|x| x} - assert_equal({"bar" => 42}, v8.eval("var o = {get bar() { return echo(42); }}; o")) + v8.attach "echo", proc { |x| x } + assert_equal( + { "bar" => 42 }, + v8.eval("var o = {get bar() { return echo(42); }}; o") + ) end def test_function_rval context = MiniRacer::Context.new - context.attach("echo", proc{|msg| msg}) + context.attach("echo", proc { |msg| msg }) assert_equal("foo", context.eval("echo('foo')")) end def test_timeout_in_ruby_land context = MiniRacer::Context.new(timeout: 50) - context.attach('sleep', proc{ sleep 0.5 }) + context.attach("sleep", proc { sleep 0.5 }) assert_raises(MiniRacer::ScriptTerminatedError) do context.eval('sleep(); "hi";') end @@ -670,20 +720,28 @@ def test_timeout_in_ruby_land def test_undef_mem context = MiniRacer::Context.new(timeout: 5) - context.attach("marsh", proc do |a, b, c| - return [a,b,c] if a.is_a?(MiniRacer::FailedV8Conversion) || b.is_a?(MiniRacer::FailedV8Conversion) || c.is_a?(MiniRacer::FailedV8Conversion) - - a[rand(10000).to_s] = "a" - b[rand(10000).to_s] = "b" - c[rand(10000).to_s] = "c" - [a,b,c] - end) + context.attach( + "marsh", + proc do |a, b, c| + if a.is_a?(MiniRacer::FailedV8Conversion) || + b.is_a?(MiniRacer::FailedV8Conversion) || + c.is_a?(MiniRacer::FailedV8Conversion) + return a, b, c + end + + a[rand(10_000).to_s] = "a" + b[rand(10_000).to_s] = "b" + c[rand(10_000).to_s] = "c" + [a, b, c] + end + ) assert_raises do # TODO make it raise the correct exception! - context.eval("var a = [{},{},{}]; while(true) { a = marsh(a[0],a[1],a[2]); }") + context.eval( + "var a = [{},{},{}]; while(true) { a = marsh(a[0],a[1],a[2]); }" + ) end - end class TestPlatform < MiniRacer::Platform @@ -696,38 +754,37 @@ def test_platform_flags_to_strings flags = [ :flag1, [[[:flag2]]], - {key1: :value1}, - {key2: 42, - key3: 8.7}, - '--i_already_have_leading_hyphens', - [:'--me_too', - 'i_dont'] + { key1: :value1 }, + { key2: 42, key3: 8.7 }, + "--i_already_have_leading_hyphens", + [:"--me_too", "i_dont"] ] expected_string_flags = [ - '--flag1', - '--flag2', - '--key1 value1', - '--key2 42', - '--key3 8.7', - '--i_already_have_leading_hyphens', - '--me_too', - '--i_dont' + "--flag1", + "--flag2", + "--key1 value1", + "--key2 42", + "--key3 8.7", + "--i_already_have_leading_hyphens", + "--me_too", + "--i_dont" ] - assert_equal expected_string_flags, TestPlatform.public_flags_to_strings(flags) + assert_equal expected_string_flags, + TestPlatform.public_flags_to_strings(flags) end def test_can_dispose_context context = MiniRacer::Context.new(timeout: 5) context.dispose - assert_raises(MiniRacer::ContextDisposedError) do - context.eval("a") - end + assert_raises(MiniRacer::ContextDisposedError) { context.eval("a") } end def test_estimated_size - skip "TruffleRuby does not yet implement heap_stats" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement heap_stats" + end context = MiniRacer::Context.new(timeout: 500) context.eval(<<-JS) let a='testing'; @@ -740,11 +797,20 @@ def test_estimated_size stats = context.heap_stats # eg: {:total_physical_size=>1280640, :total_heap_size_executable=>4194304, :total_heap_size=>3100672, :used_heap_size=>1205376, :heap_size_limit=>1501560832} assert_equal( - [:total_physical_size, :total_heap_size_executable, :total_heap_size, :used_heap_size, :heap_size_limit].sort, + %i[ + total_physical_size + total_heap_size_executable + total_heap_size + used_heap_size + heap_size_limit + ].sort, stats.keys.sort ) - assert(stats.values.all?{|v| v > 0}, "expecting the isolate to have values for all the vals") + assert( + stats.values.all? { |v| v > 0 }, + "expecting the isolate to have values for all the vals" + ) end def test_releasing_memory @@ -760,13 +826,14 @@ def test_releasing_memory end_heap = context.heap_stats[:used_heap_size] - assert((end_heap - start_heap).abs < 1000, "expecting most of the 1_000_000 long string to be freed") + assert( + (end_heap - start_heap).abs < 1000, + "expecting most of the 1_000_000 long string to be freed" + ) end def test_bad_params - assert_raises do - MiniRacer::Context.new(random: :thing) - end + assert_raises { MiniRacer::Context.new(random: :thing) } end def test_ensure_gc @@ -781,16 +848,19 @@ def test_ensure_gc end_heap = context.heap_stats[:used_heap_size] - assert((end_heap - start_heap).abs < 1000, "expecting most of the 1_000_000 long string to be freed") + assert( + (end_heap - start_heap).abs < 1000, + "expecting most of the 1_000_000 long string to be freed" + ) end def test_eval_with_filename context = MiniRacer::Context.new() - context.eval("var foo = function(){baz();}", filename: 'b/c/foo1.js') + context.eval("var foo = function(){baz();}", filename: "b/c/foo1.js") got_error = false begin - context.eval("foo()", filename: 'baz1.js') + context.eval("foo()", filename: "baz1.js") rescue MiniRacer::RuntimeError => e assert_match(/foo1.js/, e.backtrace[0]) assert_match(/baz1.js/, e.backtrace[1]) @@ -798,17 +868,18 @@ def test_eval_with_filename end assert(got_error, "should raise") - end def test_estimated_size_when_disposed - context = MiniRacer::Context.new(timeout: 50) context.eval("let a='testing';") context.dispose stats = context.heap_stats - assert(stats.values.all?{|v| v==0}, "should have 0 values once disposed") + assert( + stats.values.all? { |v| v == 0 }, + "should have 0 values once disposed" + ) end def test_can_dispose @@ -829,10 +900,10 @@ def junk_it_up def test_attached_recursion context = MiniRacer::Context.new(timeout: 200) - context.attach("a", proc{|a| a}) - context.attach("b", proc{|a| a}) + context.attach("a", proc { |a| a }) + context.attach("b", proc { |a| a }) - context.eval('const obj = {get r(){ b() }}; a(obj);') + context.eval("const obj = {get r(){ b() }}; a(obj);") end def test_no_disposal_of_isolate_when_it_is_referenced @@ -844,7 +915,7 @@ def test_no_disposal_of_isolate_when_it_is_referenced def test_context_starts_with_no_isolate_value context = MiniRacer::Context.new - assert_equal context.instance_variable_get('@isolate'), false + assert_equal context.instance_variable_get("@isolate"), false end def test_context_isolate_value_is_kept @@ -865,13 +936,15 @@ def test_isolate_is_nil_after_disposal end def test_heap_dump - skip "TruffleRuby does not yet implement heap_dump" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement heap_dump" + end f = Tempfile.new("heap") path = f.path f.unlink context = MiniRacer::Context.new - context.eval('let x = 1000;') + context.eval("let x = 1000;") context.write_heap_snapshot(path) dump = File.read(path) @@ -886,9 +959,7 @@ def test_pipe_leak # make sure that we clean up early so pipe file # descriptors are not kept around context = MiniRacer::Context.new(timeout: 1000) - 10000.times do |i| - context.eval("'hello'") - end + 10_000.times { |i| context.eval("'hello'") } end def test_symbol_support @@ -897,33 +968,47 @@ def test_symbol_support end def test_cyclical_object_js - skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement marshal_stack_depth" + end context = MiniRacer::Context.new(marshal_stack_depth: 5) - context.attach("a", proc{|a| a}) + context.attach("a", proc { |a| a }) - assert_raises(MiniRacer::RuntimeError) { context.eval("var o={}; o.o=o; a(o)") } + assert_raises(MiniRacer::RuntimeError) do + context.eval("var o={}; o.o=o; a(o)") + end end def test_cyclical_array_js - skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement marshal_stack_depth" + end context = MiniRacer::Context.new(marshal_stack_depth: 5) - context.attach("a", proc{|a| a}) + context.attach("a", proc { |a| a }) - assert_raises(MiniRacer::RuntimeError) { context.eval("let arr = []; arr.push(arr); a(arr)") } + assert_raises(MiniRacer::RuntimeError) do + context.eval("let arr = []; arr.push(arr); a(arr)") + end end def test_cyclical_elem_in_array_js - skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement marshal_stack_depth" + end context = MiniRacer::Context.new(marshal_stack_depth: 5) - context.attach("a", proc{|a| a}) + context.attach("a", proc { |a| a }) - assert_raises(MiniRacer::RuntimeError) { context.eval("let arr = []; arr[0]=1; arr[1]=arr; a(arr)") } + assert_raises(MiniRacer::RuntimeError) do + context.eval("let arr = []; arr[0]=1; arr[1]=arr; a(arr)") + end end def test_infinite_object_js - skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement marshal_stack_depth" + end context = MiniRacer::Context.new(marshal_stack_depth: 5) - context.attach("a", proc{|a| a}) + context.attach("a", proc { |a| a }) js = <<~JS var d=0; @@ -939,15 +1024,19 @@ def test_infinite_object_js end def test_deep_object_js - skip "TruffleRuby does not yet implement marshal_stack_depth" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not yet implement marshal_stack_depth" + end context = MiniRacer::Context.new(marshal_stack_depth: 5) - context.attach("a", proc{|a| a}) + context.attach("a", proc { |a| a }) # stack depth should be enough to marshal the object assert_equal [[[]]], context.eval("let arr = [[[]]]; a(arr)") # too deep - assert_raises(MiniRacer::RuntimeError) { context.eval("let arr = [[[[[[[[]]]]]]]]; a(arr)") } + assert_raises(MiniRacer::RuntimeError) do + context.eval("let arr = [[[[[[[[]]]]]]]]; a(arr)") + end end def test_stackdepth_bounds @@ -956,7 +1045,9 @@ def test_stackdepth_bounds end assert_raises(ArgumentError) do - MiniRacer::Context.new(marshal_stack_depth: MiniRacer::MARSHAL_STACKDEPTH_MAX_VALUE+1) + MiniRacer::Context.new( + marshal_stack_depth: MiniRacer::MARSHAL_STACKDEPTH_MAX_VALUE + 1 + ) end end @@ -975,7 +1066,7 @@ def test_proxy_support (new MyProxy([])).function_call(new MyProxy([])-1) JS context = MiniRacer::Context.new() - context.attach('myFunctionLogger', ->(property) { }) + context.attach("myFunctionLogger", ->(property) {}) context.eval(js) end @@ -990,17 +1081,19 @@ def test_promise test().then(v => x = v); JS - v = context.eval("x"); + v = context.eval("x") assert_equal(v, 99) end def test_webassembly - skip "TruffleRuby does not enable WebAssembly by default" if RUBY_ENGINE == "truffleruby" + if RUBY_ENGINE == "truffleruby" + skip "TruffleRuby does not enable WebAssembly by default" + end context = MiniRacer::Context.new() context.eval("let instance = null;") filename = File.expand_path("../support/add.wasm", __FILE__) - context.attach("loadwasm", proc {|f| File.read(filename).each_byte.to_a}) - context.attach("print", proc {|f| puts f}) + context.attach("loadwasm", proc { |f| File.read(filename).each_byte.to_a }) + context.attach("print", proc { |f| puts f }) context.eval <<~JS WebAssembly @@ -1015,9 +1108,7 @@ def test_webassembly .catch(e => print(e.toString())); JS - while !context.eval("instance") do - context.isolate.pump_message_loop - end + context.isolate.pump_message_loop while !context.eval("instance") assert_equal(3, context.eval("instance.exports.add(1,2)")) end @@ -1032,25 +1123,18 @@ def initialize(response) def test_exception_objects context = MiniRacer::Context.new - context.attach('repro', lambda { - raise ReproError.new(Response.new(404)) - }) - assert_raises(ReproError) do - context.eval('repro();') - end + context.attach("repro", lambda { raise ReproError.new(Response.new(404)) }) + assert_raises(ReproError) { context.eval("repro();") } end def test_timeout context = MiniRacer::Context.new(timeout: 500, max_memory: 20_000_000) - assert_raises(MiniRacer::ScriptTerminatedError) do - context.eval <<~JS - var doit = async() => { - while (true) - await new Promise(resolve => resolve()) + assert_raises(MiniRacer::ScriptTerminatedError) { context.eval <<~JS } + var doit = () => { + while (true) {} } doit(); JS - end end def test_eval_returns_unfrozen_string @@ -1063,16 +1147,21 @@ def test_eval_returns_unfrozen_string def test_call_returns_unfrozen_string context = MiniRacer::Context.new context.eval('function hello(name) { return "Hello " + name + "!" }') - result = context.call('hello', 'George') + 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? }) + context.attach("test", proc { |text| text.frozen? }) frozen = context.eval("test('Hello George!')") assert_equal(false, frozen) end + + def test_threading_safety + Thread.new { MiniRacer::Context.new.eval("100") }.join + GC.start + end end From b2317e081c8a8de3da96fa678857ee7466bd458b Mon Sep 17 00:00:00 2001 From: Sam Saffron Date: Mon, 29 Jul 2024 08:06:31 +1000 Subject: [PATCH 2/2] drop 3.0 properly --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1618b78..44f1dfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,6 @@ jobs: fail-fast: false matrix: ruby: - - "3.0" # EOL as of 2024-04-23 - "3.1" - "3.2" - "3.3"