[PRISM] Support for compiling builtins

This commit is contained in:
Kevin Newton 2024-05-30 14:33:12 -04:00
Родитель 4558abec02
Коммит 4e36abbab3
3 изменённых файлов: 377 добавлений и 37 удалений

Просмотреть файл

@ -12,22 +12,16 @@
static struct st_table *loaded_builtin_table;
#endif
bool pm_builtin_ast_value(pm_parse_result_t *result, const char *feature_name, VALUE *name_str);
VALUE rb_builtin_ast_value(const char *feature_name, VALUE *name_str);
static const rb_iseq_t *
builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *table)
{
VALUE name_str = 0;
rb_ast_t *ast;
VALUE ast_value = rb_builtin_ast_value(feature_name, &name_str);
rb_vm_t *vm = GET_VM();
const rb_iseq_t *iseq;
if (NIL_P(ast_value)) {
rb_fatal("builtin_iseq_load: can not find %s; "
"probably miniprelude.c is out of date",
feature_name);
}
vm->builtin_function_table = table;
rb_vm_t *vm = GET_VM();
static const rb_compile_option_t optimization = {
.inline_const_cache = TRUE,
.peephole_optimization = TRUE,
@ -40,11 +34,38 @@ builtin_iseq_load(const char *feature_name, const struct rb_builtin_function *ta
.coverage_enabled = FALSE,
.debug_level = 0,
};
ast = rb_ruby_ast_data_get(ast_value);
const rb_iseq_t *iseq = rb_iseq_new_with_opt(ast_value, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization, Qnil);
GET_VM()->builtin_function_table = NULL;
rb_ast_dispose(ast);
if (*rb_ruby_prism_ptr()) {
pm_parse_result_t result = { 0 };
if (!pm_builtin_ast_value(&result, feature_name, &name_str)) {
rb_fatal("builtin_iseq_load: can not find %s; "
"probably miniprelude.c is out of date",
feature_name);
}
vm->builtin_function_table = table;
iseq = pm_iseq_new_with_opt(&result.node, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization);
GET_VM()->builtin_function_table = NULL;
pm_parse_result_free(&result);
}
else {
VALUE ast_value = rb_builtin_ast_value(feature_name, &name_str);
if (NIL_P(ast_value)) {
rb_fatal("builtin_iseq_load: can not find %s; "
"probably miniprelude.c is out of date",
feature_name);
}
rb_ast_t *ast = rb_ruby_ast_data_get(ast_value);
vm->builtin_function_table = table;
iseq = rb_iseq_new_with_opt(ast_value, name_str, name_str, Qnil, 0, NULL, 0, ISEQ_TYPE_TOP, &optimization, Qnil);
GET_VM()->builtin_function_table = NULL;
rb_ast_dispose(ast);
}
// for debug
if (0 && strcmp("prelude", feature_name) == 0) {

Просмотреть файл

@ -987,7 +987,10 @@ pm_compile_conditional(rb_iseq_t *iseq, const pm_line_column_t *line_column, pm_
LABEL *else_label = NEW_LABEL(location.line);
LABEL *end_label = NULL;
pm_compile_branch_condition(iseq, ret, predicate, then_label, else_label, false, scope_node);
DECL_ANCHOR(cond_seq);
INIT_ANCHOR(cond_seq);
pm_compile_branch_condition(iseq, cond_seq, predicate, then_label, else_label, false, scope_node);
PUSH_SEQ(ret, cond_seq);
rb_code_location_t conditional_location;
VALUE branches = Qfalse;
@ -2642,7 +2645,7 @@ pm_compile_pattern(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_node_t
const char *name = rb_id2name(id);
if (name && strlen(name) > 0 && name[0] != '_') {
COMPILE_ERROR(ERROR_ARGS "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
COMPILE_ERROR(iseq, location.line, "illegal variable in alternative pattern (%"PRIsVALUE")", rb_id2str(id));
return COMPILE_NG;
}
}
@ -2990,6 +2993,264 @@ pm_compile_retry_end_label(rb_iseq_t *iseq, LINK_ANCHOR *const ret, LABEL *retry
}
}
static const char *
pm_iseq_builtin_function_name(const pm_scope_node_t *scope_node, const pm_node_t *receiver, ID method_id)
{
const char *name = rb_id2name(method_id);
static const char prefix[] = "__builtin_";
const size_t prefix_len = sizeof(prefix) - 1;
if (receiver == NULL) {
if (UNLIKELY(strncmp(prefix, name, prefix_len) == 0)) {
// __builtin_foo
return &name[prefix_len];
}
}
else if (PM_NODE_TYPE_P(receiver, PM_CALL_NODE)) {
if (PM_NODE_FLAG_P(receiver, PM_CALL_NODE_FLAGS_VARIABLE_CALL)) {
const pm_call_node_t *cast = (const pm_call_node_t *) receiver;
if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("__builtin")) {
// __builtin.foo
return name;
}
}
}
else if (PM_NODE_TYPE_P(receiver, PM_CONSTANT_READ_NODE)) {
const pm_constant_read_node_t *cast = (const pm_constant_read_node_t *) receiver;
if (pm_constant_id_lookup(scope_node, cast->name) == rb_intern_const("Primitive")) {
// Primitive.foo
return name;
}
}
return NULL;
}
// Compile Primitive.attr! :leaf, ...
static int
pm_compile_builtin_attr(rb_iseq_t *iseq, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_line_column_t *node_location)
{
if (arguments == NULL) {
COMPILE_ERROR(iseq, node_location->line, "attr!: no argument");
return COMPILE_NG;
}
const pm_node_t *argument;
PM_NODE_LIST_FOREACH(&arguments->arguments, index, argument) {
if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
COMPILE_ERROR(iseq, node_location->line, "non symbol argument to attr!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
return COMPILE_NG;
}
VALUE symbol = pm_static_literal_value(iseq, argument, scope_node);
VALUE string = rb_sym_to_s(symbol);
if (strcmp(RSTRING_PTR(string), "leaf") == 0) {
ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_LEAF;
}
else if (strcmp(RSTRING_PTR(string), "inline_block") == 0) {
ISEQ_BODY(iseq)->builtin_attrs |= BUILTIN_ATTR_INLINE_BLOCK;
}
else if (strcmp(RSTRING_PTR(string), "use_block") == 0) {
iseq_set_use_block(iseq);
}
else {
COMPILE_ERROR(iseq, node_location->line, "unknown argument to attr!: %s", RSTRING_PTR(string));
return COMPILE_NG;
}
}
return COMPILE_OK;
}
static int
pm_compile_builtin_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const pm_scope_node_t *scope_node, const pm_arguments_node_t *arguments, const pm_line_column_t *node_location, int popped)
{
if (arguments == NULL) {
COMPILE_ERROR(iseq, node_location->line, "arg!: no argument");
return COMPILE_NG;
}
if (arguments->arguments.size != 1) {
COMPILE_ERROR(iseq, node_location->line, "arg!: too many argument");
return COMPILE_NG;
}
const pm_node_t *argument = arguments->arguments.nodes[0];
if (!PM_NODE_TYPE_P(argument, PM_SYMBOL_NODE)) {
COMPILE_ERROR(iseq, node_location->line, "non symbol argument to arg!: %s", pm_node_type_to_str(PM_NODE_TYPE(argument)));
return COMPILE_NG;
}
if (!popped) {
ID name = parse_string_symbol(scope_node, ((const pm_symbol_node_t *) argument));
int index = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq)->local_table_size - get_local_var_idx(iseq, name);
debugs("id: %s idx: %d\n", rb_id2name(name), index);
PUSH_GETLOCAL(ret, *node_location, index, get_lvar_level(iseq));
}
return COMPILE_OK;
}
static int
pm_compile_builtin_mandatory_only_method(rb_iseq_t *iseq, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_line_column_t *node_location)
{
const pm_node_t *ast_node = scope_node->ast_node;
if (!PM_NODE_TYPE_P(ast_node, PM_DEF_NODE)) {
rb_bug("mandatory_only?: not in method definition");
return COMPILE_NG;
}
const pm_def_node_t *def_node = (const pm_def_node_t *) ast_node;
const pm_parameters_node_t *parameters_node = def_node->parameters;
if (parameters_node == NULL) {
rb_bug("mandatory_only?: in method definition with no parameters");
return COMPILE_NG;
}
const pm_node_t *body_node = def_node->body;
if (body_node == NULL || !PM_NODE_TYPE_P(body_node, PM_STATEMENTS_NODE) || (((const pm_statements_node_t *) body_node)->body.size != 1) || !PM_NODE_TYPE_P(((const pm_statements_node_t *) body_node)->body.nodes[0], PM_IF_NODE)) {
rb_bug("mandatory_only?: not in method definition with plain statements");
return COMPILE_NG;
}
const pm_if_node_t *if_node = (const pm_if_node_t *) ((const pm_statements_node_t *) body_node)->body.nodes[0];
if (if_node->predicate != ((const pm_node_t *) call_node)) {
rb_bug("mandatory_only?: can't find mandatory node");
return COMPILE_NG;
}
pm_parameters_node_t parameters = {
.base = parameters_node->base,
.requireds = parameters_node->requireds
};
const pm_def_node_t def = {
.base = def_node->base,
.name = def_node->name,
.receiver = def_node->receiver,
.parameters = &parameters,
.body = (pm_node_t *) if_node->statements,
.locals = {
.ids = def_node->locals.ids,
.size = parameters_node->requireds.size,
.capacity = def_node->locals.capacity
}
};
pm_scope_node_t next_scope_node;
pm_scope_node_init(&def.base, &next_scope_node, scope_node);
ISEQ_BODY(iseq)->mandatory_only_iseq = pm_iseq_new_with_opt(
&next_scope_node,
rb_iseq_base_label(iseq),
rb_iseq_path(iseq),
rb_iseq_realpath(iseq),
node_location->line,
NULL,
0,
ISEQ_TYPE_METHOD,
ISEQ_COMPILE_DATA(iseq)->option
);
pm_scope_node_destroy(&next_scope_node);
return COMPILE_OK;
}
static int
pm_compile_builtin_function_call(rb_iseq_t *iseq, LINK_ANCHOR *const ret, pm_scope_node_t *scope_node, const pm_call_node_t *call_node, const pm_line_column_t *node_location, int popped, const rb_iseq_t *parent_block, const char *builtin_func)
{
const pm_arguments_node_t *arguments = call_node->arguments;
if (parent_block != NULL) {
COMPILE_ERROR(iseq, node_location->line, "should not call builtins here.");
return COMPILE_NG;
}
#define BUILTIN_INLINE_PREFIX "_bi"
char inline_func[sizeof(BUILTIN_INLINE_PREFIX) + DECIMAL_SIZE_OF(int)];
bool cconst = false;
retry:;
const struct rb_builtin_function *bf = iseq_builtin_function_lookup(iseq, builtin_func);
if (bf == NULL) {
if (strcmp("cstmt!", builtin_func) == 0 || strcmp("cexpr!", builtin_func) == 0) {
// ok
}
else if (strcmp("cconst!", builtin_func) == 0) {
cconst = true;
}
else if (strcmp("cinit!", builtin_func) == 0) {
// ignore
return COMPILE_OK;
}
else if (strcmp("attr!", builtin_func) == 0) {
return pm_compile_builtin_attr(iseq, scope_node, arguments, node_location);
}
else if (strcmp("arg!", builtin_func) == 0) {
return pm_compile_builtin_arg(iseq, ret, scope_node, arguments, node_location, popped);
}
else if (strcmp("mandatory_only?", builtin_func) == 0) {
if (popped) {
rb_bug("mandatory_only? should be in if condition");
}
else if (!LIST_INSN_SIZE_ZERO(ret)) {
rb_bug("mandatory_only? should be put on top");
}
PUSH_INSN1(ret, *node_location, putobject, Qfalse);
return pm_compile_builtin_mandatory_only_method(iseq, scope_node, call_node, node_location);
}
else if (1) {
rb_bug("can't find builtin function:%s", builtin_func);
}
else {
COMPILE_ERROR(iseq, node_location->line, "can't find builtin function:%s", builtin_func);
return COMPILE_NG;
}
int inline_index = node_location->line;
snprintf(inline_func, sizeof(inline_func), BUILTIN_INLINE_PREFIX "%d", inline_index);
builtin_func = inline_func;
arguments = NULL;
goto retry;
}
if (cconst) {
typedef VALUE(*builtin_func0)(void *, VALUE);
VALUE const_val = (*(builtin_func0)bf->func_ptr)(NULL, Qnil);
PUSH_INSN1(ret, *node_location, putobject, const_val);
return COMPILE_OK;
}
// fprintf(stderr, "func_name:%s -> %p\n", builtin_func, bf->func_ptr);
DECL_ANCHOR(args_seq);
INIT_ANCHOR(args_seq);
int flags = 0;
struct rb_callinfo_kwarg *keywords = NULL;
int argc = pm_setup_args(arguments, call_node->block, &flags, &keywords, iseq, args_seq, scope_node, node_location);
if (argc != bf->argc) {
COMPILE_ERROR(iseq, node_location->line, "argc is not match for builtin function:%s (expect %d but %d)", builtin_func, bf->argc, argc);
return COMPILE_NG;
}
unsigned int start_index;
if (delegate_call_p(iseq, argc, args_seq, &start_index)) {
PUSH_INSN2(ret, *node_location, opt_invokebuiltin_delegate, bf, INT2FIX(start_index));
}
else {
PUSH_SEQ(ret, args_seq);
PUSH_INSN1(ret, *node_location, invokebuiltin, bf);
}
if (popped) PUSH_INSN(ret, *node_location, pop);
return COMPILE_OK;
}
/**
* Compile a call node into the given iseq.
*/
@ -3041,6 +3302,7 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c
struct rb_callinfo_kwarg *kw_arg = NULL;
int orig_argc = pm_setup_args(call_node->arguments, call_node->block, &flags, &kw_arg, iseq, ret, scope_node, &location);
const rb_iseq_t *previous_block = ISEQ_COMPILE_DATA(iseq)->current_block;
const rb_iseq_t *block_iseq = NULL;
if (call_node->block != NULL && PM_NODE_TYPE_P(call_node->block, PM_BLOCK_NODE)) {
@ -3111,6 +3373,7 @@ pm_compile_call(rb_iseq_t *iseq, const pm_call_node_t *call_node, LINK_ANCHOR *c
}
if (popped) PUSH_INSN(ret, location, pop);
ISEQ_COMPILE_DATA(iseq)->current_block = previous_block;
}
static void
@ -4329,7 +4592,6 @@ pm_compile_ensure(rb_iseq_t *iseq, const pm_begin_node_t *cast, const pm_line_co
);
pm_scope_node_destroy(&next_scope_node);
ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
if (estart->link.next != &eend->link) {
@ -4668,7 +4930,7 @@ pm_compile_shareable_constant_value(rb_iseq_t *iseq, const pm_node_t *node, cons
const pm_node_t *element = cast->elements.nodes[index];
if (!PM_NODE_TYPE_P(element, PM_ASSOC_NODE)) {
COMPILE_ERROR(ERROR_ARGS "Ractor constant writes do not support **");
COMPILE_ERROR(iseq, location.line, "Ractor constant writes do not support **");
}
const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
@ -5395,7 +5657,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
throw_flag = 0;
}
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
COMPILE_ERROR(iseq, location.line, "Can't escape from eval with break");
return;
}
else {
@ -5417,7 +5679,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
return;
}
COMPILE_ERROR(ERROR_ARGS "Invalid break");
COMPILE_ERROR(iseq, location.line, "Invalid break");
}
return;
}
@ -5431,13 +5693,24 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
// foo.bar() {}
// ^^^^^^^^^^^^
const pm_call_node_t *cast = (const pm_call_node_t *) node;
LABEL *start = NEW_LABEL(location.line);
ID method_id = pm_constant_id_lookup(scope_node, cast->name);
if (cast->block) {
PUSH_LABEL(ret, start);
const pm_location_t *message_loc = &cast->message_loc;
if (message_loc->start == NULL) message_loc = &cast->base.location;
const pm_line_column_t location = PM_LOCATION_START_LINE_COLUMN(scope_node->parser, message_loc);
const char *builtin_func;
if (UNLIKELY(iseq_has_builtin_function_table(iseq)) && (builtin_func = pm_iseq_builtin_function_name(scope_node, cast->receiver, method_id)) != NULL) {
const pm_string_t *filepath = &scope_node->parser->filepath;
fprintf(stderr, "COMPILING %.*s:%d:%d builtin_func:%s\n", (int) pm_string_length(filepath), pm_string_source(filepath), location.line, location.column, builtin_func);
pm_compile_builtin_function_call(iseq, ret, scope_node, cast, &location, popped, ISEQ_COMPILE_DATA(iseq)->current_block, builtin_func);
return;
}
ID method_id = pm_constant_id_lookup(scope_node, cast->name);
LABEL *start = NEW_LABEL(location.line);
if (cast->block) PUSH_LABEL(ret, start);
switch (method_id) {
case idUMinus: {
@ -7389,7 +7662,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
break;
}
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
COMPILE_ERROR(iseq, location.line, "Can't escape from eval with next");
return;
}
@ -7407,7 +7680,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
if (popped) PUSH_INSN(ret, location, pop);
}
else {
COMPILE_ERROR(ERROR_ARGS "Invalid next");
COMPILE_ERROR(iseq, location.line, "Invalid next");
return;
}
}
@ -7642,7 +7915,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
break;
}
else if (ISEQ_BODY(ip)->type == ISEQ_TYPE_EVAL) {
COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
COMPILE_ERROR(iseq, location.line, "Can't escape from eval with redo");
return;
}
@ -7655,7 +7928,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
if (popped) PUSH_INSN(ret, location, pop);
}
else {
COMPILE_ERROR(ERROR_ARGS "Invalid redo");
COMPILE_ERROR(iseq, location.line, "Invalid redo");
return;
}
}
@ -7883,7 +8156,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
if (popped) PUSH_INSN(ret, location, pop);
}
else {
COMPILE_ERROR(ERROR_ARGS "Invalid retry");
COMPILE_ERROR(iseq, location.line, "Invalid retry");
return;
}
return;
@ -8721,7 +8994,9 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
return;
}
case ISEQ_TYPE_METHOD: {
ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
PUSH_TRACE(ret, RUBY_EVENT_CALL);
if (scope_node->body) {
PM_COMPILE((const pm_node_t *) scope_node->body);
}
@ -8729,9 +9004,10 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
PUSH_INSN(ret, location, putnil);
}
ISEQ_COMPILE_DATA(iseq)->root_node = (const void *) scope_node->body;
PUSH_TRACE(ret, RUBY_EVENT_RETURN);
ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
ISEQ_COMPILE_DATA(iseq)->last_line = body->location.code_location.end_pos.lineno;
break;
}
case ISEQ_TYPE_RESCUE: {
@ -9075,7 +9351,7 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
case ISEQ_TYPE_TOP:
case ISEQ_TYPE_MAIN:
case ISEQ_TYPE_CLASS:
COMPILE_ERROR(ERROR_ARGS "Invalid yield");
COMPILE_ERROR(iseq, location.line, "Invalid yield");
return;
default: /* valid */;
}

Просмотреть файл

@ -154,6 +154,18 @@ prelude_ast_value(VALUE name, VALUE code, int line)
return ast_value;
}
static void
pm_prelude_load(pm_parse_result_t *result, VALUE name, VALUE code, int line)
{
pm_options_line_set(&result->options, line);
VALUE error = pm_parse_string(result, code, name);
if (!NIL_P(error)) {
pm_parse_result_free(result);
rb_exc_raise(error);
}
}
% end
% if @builtin_count > 0
#define PRELUDE_VAST(n, name_str, start_line) \
@ -176,6 +188,28 @@ rb_builtin_ast_value(const char *feature_name, VALUE *name_str)
return ast_value;
}
bool
pm_builtin_ast_value(pm_parse_result_t *result, const char *feature_name, VALUE *name_str)
{
const size_t prefix_len = rb_strlen_lit("<internal:");
size_t namelen = strlen(feature_name);
% @preludes.each_value do |i, prelude, lines, sub, start_line|
% if sub
if (
(sizeof(prelude_name<%= i %>) - prefix_len - 2 == namelen) &&
(strncmp(prelude_name<%= i %> + prefix_len, feature_name, namelen) == 0)
) {
*name_str = PRELUDE_NAME(<%= i %>);
pm_prelude_load(result, *name_str, PRELUDE_CODE(<%= i %>), <%= start_line %>);
return true;
}
% end
% end
return false;
}
% end
% if @prelude_count > 0
COMPILER_WARNING_PUSH
@ -198,13 +232,22 @@ prelude_eval(VALUE code, VALUE name, int line)
0, /* int debug_level; */
};
rb_ast_t *ast;
VALUE ast_value = prelude_ast_value(name, code, line);
ast = rb_ruby_ast_data_get(ast_value);
rb_iseq_eval(rb_iseq_new_with_opt(ast_value, name, name, Qnil, line,
NULL, 0, ISEQ_TYPE_TOP, &optimization,
Qnil));
rb_ast_dispose(ast);
if (*rb_ruby_prism_ptr()) {
pm_parse_result_t result = { 0 };
pm_prelude_load(&result, name, code, line);
rb_iseq_eval(pm_iseq_new_with_opt(&result.node, name, name, Qnil, line,
NULL, 0, ISEQ_TYPE_TOP, &optimization));
pm_parse_result_free(&result);
}
else {
rb_ast_t *ast;
VALUE ast_value = prelude_ast_value(name, code, line);
ast = rb_ruby_ast_data_get(ast_value);
rb_iseq_eval(rb_iseq_new_with_opt(ast_value, name, name, Qnil, line,
NULL, 0, ISEQ_TYPE_TOP, &optimization,
Qnil));
rb_ast_dispose(ast);
}
}
COMPILER_WARNING_POP