Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions encoding.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_enc
struct rb_encoding_entry *ent = &enc_table->list[index];
rb_raw_encoding *encoding;

if (ent->loaded) {
RUBY_ASSERT(ent->base == base_encoding);
RUBY_ASSERT(!strcmp(name, ent->name));
return index;
}

if (!valid_encoding_name_p(name)) return -1;
if (!ent->name) {
ent->name = name = strdup(name);
Expand All @@ -369,14 +375,20 @@ enc_register_at(struct enc_table *enc_table, int index, const char *name, rb_enc
encoding = xmalloc(sizeof(rb_encoding));
}

rb_raw_encoding tmp_encoding;
if (base_encoding) {
*encoding = *base_encoding;
tmp_encoding = *base_encoding;
}
else {
memset(encoding, 0, sizeof(*ent->enc));
memset(&tmp_encoding, 0, sizeof(*ent->enc));
}
encoding->name = name;
encoding->ruby_encoding_index = index;
tmp_encoding.name = name;
tmp_encoding.ruby_encoding_index = index;

// FIXME: If encoding already existed, it may be concurrently accessed
// It's technically invalid to write to this memory as it's read, but as all
// values are set up it _probably_ works.
*encoding = tmp_encoding;
ent->enc = encoding;
st_insert(enc_table->names, (st_data_t)name, (st_data_t)index);

Expand Down Expand Up @@ -408,7 +420,9 @@ enc_from_index(struct enc_table *enc_table, int index)
if (UNLIKELY(index < 0 || enc_table->count <= (index &= ENC_INDEX_MASK))) {
return 0;
}
return enc_table->list[index].enc;
rb_encoding *enc = enc_table->list[index].enc;
RUBY_ASSERT(ENC_TO_ENCINDEX(enc) == index);
return enc;
}

rb_encoding *
Expand Down Expand Up @@ -827,7 +841,7 @@ enc_autoload_body(rb_encoding *enc)
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
i = enc->ruby_encoding_index;
enc_register_at(enc_table, i & ENC_INDEX_MASK, rb_enc_name(enc), base);
((rb_raw_encoding *)enc)->ruby_encoding_index = i;
RUBY_ASSERT(((rb_raw_encoding *)enc)->ruby_encoding_index == i);
}
}

Expand Down
68 changes: 49 additions & 19 deletions lib/erb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -511,24 +511,32 @@
#
# ## Encodings
#
# In general, an \ERB result string (or Ruby code generated by \ERB)
# has the same encoding as the string originally passed to ERB.new;
# see [Encoding][encoding].
# An \ERB template has an [encoding][encoding],
# which is by default the encoding of the source string;
# the result string will also have that encoding.
#
# You can specify the output encoding by adding a [magic comment][magic comments]
# ```
# s = <<EOT
# <%# Comment. %>
# EOT
# template = ERB.new(s)
# s.encoding # => #<Encoding:UTF-8>
# template.encoding # => #<Encoding:UTF-8>
# template.result.encoding # => #<Encoding:UTF-8>
# ```
#
# You can specify a different encoding by adding a [magic comment][magic comments]
# at the top of the given string:
#
# ```
# s = <<EOF
# s = <<EOT
# <%#-*- coding: Big5 -*-%>
#
# Some text.
# EOF
# # => "<%#-*- coding: Big5 -*-%>\n\nSome text.\n"
# s.encoding
# # => #<Encoding:UTF-8>
# ERB.new(s).result.encoding
# # => #<Encoding:Big5>
# <%# Comment. %>
# EOT
# template = ERB.new(s)
# s.encoding # => #<Encoding:UTF-8>
# template.encoding # => #<Encoding:Big5>
# template.result.encoding # => #<Encoding:Big5>
# ```
#
# ## Error Reporting
Expand Down Expand Up @@ -873,7 +881,12 @@ def make_compiler(trim_mode)
# The Ruby code generated by ERB
attr_reader :src

# The encoding to eval
# :markup: markdown
#
# Returns the encoding of `self`;
# see [encoding][encoding].
#
# [encoding]: https://docs.ruby-lang.org/en/master/Encoding.html
attr_reader :encoding

# :markup: markdown
Expand Down Expand Up @@ -919,7 +932,13 @@ def set_eoutvar(compiler, eoutvar = '_erbout')
compiler.post_cmd = [eoutvar]
end

# Generate results and print them. (see ERB#result)
# :markup: markdown
#
# :call-seq:
# run(binding = new_toplevel) -> nil
#
# Like #result, but prints the result string (instead of returning it);
# returns `nil`.
def run(b=new_toplevel)
print self.result(b)
end
Expand Down Expand Up @@ -969,10 +988,21 @@ def result_with_hash(hash)
result(b)
end

##
# Returns a new binding each time *near* TOPLEVEL_BINDING for runs that do
# not specify a binding.

# :markup: markdown
#
# :call-seq:
# new_toplevel(symbols) -> new_binding
#
# Returns a new binding based on `TOPLEVEL_BINDING`;
# used to create a default binding for a call to #result.
#
# See [Default Binding][default binding].
#
# Argument `symbols` is an array of symbols;
# each symbol `symbol` is used to define (unless already defined) a variable in the binding
# whose name is `symbol` and whose value is `nil`.
#
# [default binding]: rdoc-ref:ERB@Default+Binding
def new_toplevel(vars = nil)
b = TOPLEVEL_BINDING
if vars
Expand Down
12 changes: 12 additions & 0 deletions object.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ rb_class_allocate_instance(VALUE klass)
for (size_t i = 0; i < ROBJECT_FIELDS_CAPACITY(obj); i++) {
ptr[i] = Qundef;
}
if (rb_obj_class(obj) != rb_class_real(klass)) {
rb_bug("Expected rb_class_allocate_instance to set the class correctly");
}
#endif

return obj;
Expand Down Expand Up @@ -2192,6 +2195,15 @@ class_get_alloc_func(VALUE klass)
return allocator;
}

// Might return NULL.
rb_alloc_func_t
rb_zjit_class_get_alloc_func(VALUE klass)
{
assert(RCLASS_INITIALIZED_P(klass));
assert(!RCLASS_SINGLETON_P(klass));
return rb_get_alloc_func(klass);
}

static VALUE
class_call_alloc_func(rb_alloc_func_t allocator, VALUE klass)
{
Expand Down
35 changes: 35 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,41 @@ def test = Foo.new
}, insns: [:opt_new]
end

def test_opt_new_invalidate_new
assert_compiles '["Foo", "foo"]', %q{
class Foo; end
def test = Foo.new
test; test
result = [test.class.name]
def Foo.new = "foo"
result << test
result
}, insns: [:opt_new], call_threshold: 2
end

def test_opt_new_with_custom_allocator
assert_compiles '"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"', %q{
require "digest"
def test = Digest::SHA256.new.hexdigest
test; test
}, insns: [:opt_new], call_threshold: 2
end

def test_opt_new_with_custom_allocator_raises
assert_compiles '[42, 42]', %q{
require "digest"
class C < Digest::Base; end
def test
begin
Digest::Base.new
rescue NotImplementedError
42
end
end
[test, test]
}, insns: [:opt_new], call_threshold: 2
end

def test_new_hash_empty
assert_compiles '{}', %q{
def test = {}
Expand Down
12 changes: 12 additions & 0 deletions vm_insnhelper.c
Original file line number Diff line number Diff line change
Expand Up @@ -2353,6 +2353,12 @@ vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv)
return vm_cc_cme(cc);
}

const struct rb_callable_method_entry_struct *
rb_zjit_vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv)
{
return vm_search_method(cd_owner, cd, recv);
}

#if __has_attribute(transparent_union)
typedef union {
VALUE (*anyargs)(ANYARGS);
Expand Down Expand Up @@ -2417,6 +2423,12 @@ vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, cfunc_type f
return check_cfunc(cme, func);
}

bool
rb_zjit_cme_is_cfunc(const rb_callable_method_entry_t *me, const cfunc_type func)
{
return check_cfunc(me, func);
}

int
rb_vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, cfunc_type func)
{
Expand Down
24 changes: 24 additions & 0 deletions zjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,30 @@ rb_zjit_local_id(const rb_iseq_t *iseq, unsigned idx)
return ISEQ_BODY(iseq)->local_table[idx];
}

bool rb_zjit_cme_is_cfunc(const rb_callable_method_entry_t *me, const void *func);

const struct rb_callable_method_entry_struct *
rb_zjit_vm_search_method(VALUE cd_owner, struct rb_call_data *cd, VALUE recv);

bool
rb_zjit_class_initialized_p(VALUE klass)
{
return RCLASS_INITIALIZED_P(klass);
}

rb_alloc_func_t rb_zjit_class_get_alloc_func(VALUE klass);

VALUE rb_class_allocate_instance(VALUE klass);

bool
rb_zjit_class_has_default_allocator(VALUE klass)
{
assert(RCLASS_INITIALIZED_P(klass));
assert(!RCLASS_SINGLETON_P(klass));
rb_alloc_func_t alloc = rb_zjit_class_get_alloc_func(klass);
return alloc == rb_class_allocate_instance;
}

// Primitives used by zjit.rb. Don't put other functions below, which wouldn't use them.
VALUE rb_zjit_assert_compiles(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
Expand Down
1 change: 1 addition & 0 deletions zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def stats_string
print_counters_with_prefix(prefix: 'compile_error_', prompt: 'compile error reasons', buf:, stats:, limit: 20)
print_counters_with_prefix(prefix: 'exit_', prompt: 'side exit reasons', buf:, stats:, limit: 20)
print_counters_with_prefix(prefix: 'dynamic_send_type_', prompt: 'dynamic send types', buf:, stats:, limit: 20)
print_counters_with_prefix(prefix: 'send_fallback_', prompt: 'send fallback def_types', buf:, stats:, limit: 20)

# Show the most important stats ratio_in_zjit at the end
print_counters([
Expand Down
4 changes: 4 additions & 0 deletions zjit/bindgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@ fn main() {
.allowlist_function("rb_insn_name")
.allowlist_function("rb_insn_len")
.allowlist_function("rb_yarv_class_of")
.allowlist_function("rb_zjit_class_initialized_p")
.allowlist_function("rb_zjit_class_has_default_allocator")
.allowlist_function("rb_get_ec_cfp")
.allowlist_function("rb_get_cfp_iseq")
.allowlist_function("rb_get_cfp_pc")
Expand All @@ -342,6 +344,8 @@ fn main() {
.allowlist_function("rb_get_cfp_ep_level")
.allowlist_function("rb_get_cme_def_type")
.allowlist_function("rb_zjit_constcache_shareable")
.allowlist_function("rb_zjit_vm_search_method")
.allowlist_function("rb_zjit_cme_is_cfunc")
.allowlist_function("rb_get_cme_def_body_attr_id")
.allowlist_function("rb_get_symbol_id")
.allowlist_function("rb_get_cme_def_body_optimized_type")
Expand Down
Loading