diff --git a/ext/herb/extension.c b/ext/herb/extension.c index 1c0b35420..ae24e0e04 100644 --- a/ext/herb/extension.c +++ b/ext/herb/extension.c @@ -1,6 +1,7 @@ #include #include "../../src/include/util/hb_allocator.h" +#include "../../src/include/util/hb_arena_debug.h" #include "error_helpers.h" #include "extension.h" @@ -80,8 +81,18 @@ static VALUE buffer_cleanup(VALUE arg) { return Qnil; } -static VALUE Herb_lex(VALUE self, VALUE source) { +static VALUE Herb_lex(int argc, VALUE* argv, VALUE self) { + VALUE source, options; + rb_scan_args(argc, argv, "1:", &source, &options); + char* string = (char*) check_string(source); + bool print_arena_stats = false; + + if (!NIL_P(options)) { + VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats")); + if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); } + if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; } + } lex_args_T args = { 0 }; args.source = source; @@ -90,11 +101,23 @@ static VALUE Herb_lex(VALUE self, VALUE source) { args.tokens = herb_lex(string, &args.allocator); + if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); } + return rb_ensure(lex_convert_body, (VALUE) &args, lex_cleanup, (VALUE) &args); } -static VALUE Herb_lex_file(VALUE self, VALUE path) { +static VALUE Herb_lex_file(int argc, VALUE* argv, VALUE self) { + VALUE path, options; + rb_scan_args(argc, argv, "1:", &path, &options); + char* file_path = (char*) check_string(path); + bool print_arena_stats = false; + + if (!NIL_P(options)) { + VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats")); + if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); } + if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; } + } lex_args_T args = { 0 }; args.source = read_file_to_ruby_string(file_path); @@ -103,6 +126,8 @@ static VALUE Herb_lex_file(VALUE self, VALUE path) { args.tokens = herb_lex_file(file_path, &args.allocator); + if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); } + return rb_ensure(lex_convert_body, (VALUE) &args, lex_cleanup, (VALUE) &args); } @@ -111,6 +136,7 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) { rb_scan_args(argc, argv, "1:", &source, &options); char* string = (char*) check_string(source); + bool print_arena_stats = false; parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS; @@ -126,6 +152,10 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) { VALUE strict = rb_hash_lookup(options, rb_utf8_str_new_cstr("strict")); if (NIL_P(strict)) { strict = rb_hash_lookup(options, ID2SYM(rb_intern("strict"))); } if (!NIL_P(strict)) { parser_options.strict = RTEST(strict); } + + VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats")); + if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); } + if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; } } parse_args_T args = { 0 }; @@ -136,6 +166,8 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) { args.root = herb_parse(string, &parser_options, &args.allocator); + if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); } + return rb_ensure(parse_convert_body, (VALUE) &args, parse_cleanup, (VALUE) &args); } @@ -144,6 +176,7 @@ static VALUE Herb_parse_file(int argc, VALUE* argv, VALUE self) { rb_scan_args(argc, argv, "1:", &path, &options); char* file_path = (char*) check_string(path); + bool print_arena_stats = false; VALUE source_value = read_file_to_ruby_string(file_path); char* string = (char*) check_string(source_value); @@ -162,6 +195,10 @@ static VALUE Herb_parse_file(int argc, VALUE* argv, VALUE self) { VALUE strict = rb_hash_lookup(options, rb_utf8_str_new_cstr("strict")); if (NIL_P(strict)) { strict = rb_hash_lookup(options, ID2SYM(rb_intern("strict"))); } if (!NIL_P(strict)) { parser_options.strict = RTEST(strict); } + + VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats")); + if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); } + if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; } } parse_args_T args = { 0 }; @@ -172,6 +209,8 @@ static VALUE Herb_parse_file(int argc, VALUE* argv, VALUE self) { args.root = herb_parse(string, &parser_options, &args.allocator); + if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); } + return rb_ensure(parse_convert_body, (VALUE) &args, parse_cleanup, (VALUE) &args); } @@ -256,9 +295,9 @@ __attribute__((__visibility__("default"))) void Init_herb(void) { rb_init_error_classes(); rb_define_singleton_method(mHerb, "parse", Herb_parse, -1); - rb_define_singleton_method(mHerb, "lex", Herb_lex, 1); + rb_define_singleton_method(mHerb, "lex", Herb_lex, -1); rb_define_singleton_method(mHerb, "parse_file", Herb_parse_file, -1); - rb_define_singleton_method(mHerb, "lex_file", Herb_lex_file, 1); + rb_define_singleton_method(mHerb, "lex_file", Herb_lex_file, -1); rb_define_singleton_method(mHerb, "extract_ruby", Herb_extract_ruby, -1); rb_define_singleton_method(mHerb, "extract_html", Herb_extract_html, 1); rb_define_singleton_method(mHerb, "version", Herb_version, 0); diff --git a/lib/herb/cli.rb b/lib/herb/cli.rb index 5803d5558..40d561c5c 100644 --- a/lib/herb/cli.rb +++ b/lib/herb/cli.rb @@ -8,7 +8,7 @@ class Herb::CLI include Herb::Colors - attr_accessor :json, :silent, :log_file, :no_timing, :local, :escape, :no_escape, :freeze, :debug, :tool, :strict, :analyze, :track_whitespace, :verbose, :isolate + attr_accessor :json, :silent, :log_file, :no_timing, :local, :escape, :no_escape, :freeze, :debug, :tool, :strict, :analyze, :track_whitespace, :verbose, :isolate, :arena_stats def initialize(args) @args = args @@ -158,13 +158,13 @@ def result show_config exit(0) when "parse" - Herb.parse(file_content, strict: strict.nil? || strict, analyze: analyze.nil? || analyze, track_whitespace: track_whitespace || false) + Herb.parse(file_content, strict: strict.nil? || strict, analyze: analyze.nil? || analyze, track_whitespace: track_whitespace || false, arena_stats: arena_stats) when "compile" compile_template when "render" render_template when "lex" - Herb.lex(file_content) + Herb.lex(file_content, arena_stats: arena_stats) when "ruby" puts Herb.extract_ruby(file_content) exit(0) @@ -299,6 +299,10 @@ def option_parser parser.on("--tool TOOL", "Show config for specific tool: linter, formatter (for config command)") do |t| self.tool = t.to_sym end + + parser.on("--arena-stats", "Print arena memory statistics (for lex/parse commands)") do + self.arena_stats = true + end end end