class SyntaxTree::YARV::Compiler
This class is an experiment in transforming Syntax Tree nodes into their corresponding YARV
instruction sequences. It attempts to mirror the behavior of RubyVM::InstructionSequence.compile.
You use this as with any other visitor. First you parse code into a tree, then you visit it with this compiler. Visiting the root node of the tree will return a SyntaxTree::YARV::Compiler::InstructionSequence object. With that object you can call to_a on it, which will return a serialized form of the instruction sequence as an array. This array should mirror the array given by RubyVM::InstructionSequence#to_a.
As an example, here is how you would compile a single expression:
program = SyntaxTree.parse("1 + 2") program.accept(SyntaxTree::YARV::Compiler.new).to_a [ "YARVInstructionSequence/SimpleDataFormat", 3, 1, 1, {:arg_size=>0, :local_size=>0, :stack_max=>2}, "<compiled>", "<compiled>", "<compiled>", 1, :top, [], {}, [], [ [:putobject_INT2FIX_1_], [:putobject, 2], [:opt_plus, {:mid=>:+, :flag=>16, :orig_argc=>1}], [:leave] ] ]
Note that this is the same output as calling:
RubyVM::InstructionSequence.compile("1 + 2").to_a
Attributes
The current instruction sequence that is being compiled.
A boolean to track if we’re currently compiling the last statement within a set of statements. This information is necessary to determine if we need to return the value of the last statement.
These options mirror the compilation options that we currently support that can be also passed to RubyVM::InstructionSequence.compile.
Public Class Methods
# File lib/syntax_tree/yarv/compiler.rb, line 293 def initialize(options = Options.new) @options = options @iseq = nil @last_statement = false end
Public Instance Methods
# File lib/syntax_tree/yarv/compiler.rb, line 299 def visit_BEGIN(node) visit(node.statements) end
# File lib/syntax_tree/yarv/compiler.rb, line 303 def visit_CHAR(node) if options.frozen_string_literal? iseq.putobject(node.value[1..]) else iseq.putstring(node.value[1..]) end end
# File lib/syntax_tree/yarv/compiler.rb, line 311 def visit_END(node) start_line = node.location.start_line once_iseq = with_child_iseq(iseq.block_child_iseq(start_line)) do postexe_iseq = with_child_iseq(iseq.block_child_iseq(start_line)) do iseq.event(:RUBY_EVENT_B_CALL) *statements, last_statement = node.statements.body visit_all(statements) with_last_statement { visit(last_statement) } iseq.event(:RUBY_EVENT_B_RETURN) iseq.leave end iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.send( YARV.calldata(:"core#set_postexe", 0, CallData::CALL_FCALL), postexe_iseq ) iseq.leave end iseq.once(once_iseq, iseq.inline_storage) iseq.pop end
# File lib/syntax_tree/yarv/compiler.rb, line 339 def visit_alias(node) iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.putspecialobject(PutSpecialObject::OBJECT_CBASE) visit(node.left) visit(node.right) iseq.send(YARV.calldata(:"core#set_method_alias", 3)) end
# File lib/syntax_tree/yarv/compiler.rb, line 347 def visit_aref(node) calldata = YARV.calldata(:[], 1) visit(node.collection) if !options.frozen_string_literal? && options.specialized_instruction? && (node.index.parts.length == 1) arg = node.index.parts.first if arg.is_a?(StringLiteral) && (arg.parts.length == 1) string_part = arg.parts.first if string_part.is_a?(TStringContent) iseq.opt_aref_with(string_part.value, calldata) return end end end visit(node.index) iseq.send(calldata) end
# File lib/syntax_tree/yarv/compiler.rb, line 369 def visit_arg_block(node) visit(node.value) end
# File lib/syntax_tree/yarv/compiler.rb, line 373 def visit_arg_paren(node) visit(node.arguments) end
# File lib/syntax_tree/yarv/compiler.rb, line 377 def visit_arg_star(node) visit(node.value) iseq.splatarray(false) end
# File lib/syntax_tree/yarv/compiler.rb, line 382 def visit_args(node) visit_all(node.parts) end
# File lib/syntax_tree/yarv/compiler.rb, line 386 def visit_array(node) if (compiled = RubyVisitor.compile(node)) iseq.duparray(compiled) elsif node.contents && node.contents.parts.length == 1 && node.contents.parts.first.is_a?(BareAssocHash) && node.contents.parts.first.assocs.length == 1 && node.contents.parts.first.assocs.first.is_a?(AssocSplat) iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.newhash(0) visit(node.contents.parts.first) iseq.send(YARV.calldata(:"core#hash_merge_kwd", 2)) iseq.newarraykwsplat(1) else length = 0 node.contents.parts.each do |part| if part.is_a?(ArgStar) if length > 0 iseq.newarray(length) length = 0 end visit(part.value) iseq.concatarray else visit(part) length += 1 end end iseq.newarray(length) if length > 0 iseq.concatarray if length > 0 && length != node.contents.parts.length end end
# File lib/syntax_tree/yarv/compiler.rb, line 421 def visit_aryptn(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 424 def visit_assign(node) case node.target when ARefField calldata = YARV.calldata(:[]=, 2) if !options.frozen_string_literal? && options.specialized_instruction? && (node.target.index.parts.length == 1) arg = node.target.index.parts.first if arg.is_a?(StringLiteral) && (arg.parts.length == 1) string_part = arg.parts.first if string_part.is_a?(TStringContent) visit(node.target.collection) visit(node.value) iseq.swap iseq.topn(1) iseq.opt_aset_with(string_part.value, calldata) iseq.pop return end end end iseq.putnil visit(node.target.collection) visit(node.target.index) visit(node.value) iseq.setn(3) iseq.send(calldata) iseq.pop when ConstPathField names = constant_names(node.target) name = names.pop if RUBY_VERSION >= "3.2" iseq.opt_getconstant_path(names) visit(node.value) iseq.swap iseq.topn(1) iseq.swap iseq.setconstant(name) else visit(node.value) iseq.dup if last_statement? iseq.opt_getconstant_path(names) iseq.setconstant(name) end when Field iseq.putnil visit(node.target) visit(node.value) iseq.setn(2) iseq.send(YARV.calldata(:"#{node.target.name.value}=", 1)) iseq.pop when TopConstField name = node.target.constant.value.to_sym if RUBY_VERSION >= "3.2" iseq.putobject(Object) visit(node.value) iseq.swap iseq.topn(1) iseq.swap iseq.setconstant(name) else visit(node.value) iseq.dup if last_statement? iseq.putobject(Object) iseq.setconstant(name) end when VarField visit(node.value) iseq.dup if last_statement? case node.target.value when Const iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE) iseq.setconstant(node.target.value.value.to_sym) when CVar iseq.setclassvariable(node.target.value.value.to_sym) when GVar iseq.setglobal(node.target.value.value.to_sym) when Ident lookup = visit(node.target) if lookup.local.is_a?(LocalTable::BlockLocal) iseq.setblockparam(lookup.index, lookup.level) else iseq.setlocal(lookup.index, lookup.level) end when IVar iseq.setinstancevariable(node.target.value.value.to_sym) end end end
# File lib/syntax_tree/yarv/compiler.rb, line 522 def visit_assoc(node) visit(node.key) visit(node.value) end
# File lib/syntax_tree/yarv/compiler.rb, line 527 def visit_assoc_splat(node) visit(node.value) end
# File lib/syntax_tree/yarv/compiler.rb, line 531 def visit_backref(node) iseq.getspecial(GetSpecial::SVAR_BACKREF, node.value[1..].to_i << 1) end
# File lib/syntax_tree/yarv/compiler.rb, line 535 def visit_bare_assoc_hash(node) if (compiled = RubyVisitor.compile(node)) iseq.duphash(compiled) else visit_all(node.assocs) end end
# File lib/syntax_tree/yarv/compiler.rb, line 543 def visit_begin(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 546 def visit_binary(node) case node.operator when :"&&" done_label = iseq.label visit(node.left) iseq.dup iseq.branchunless(done_label) iseq.pop visit(node.right) iseq.push(done_label) when :"||" visit(node.left) iseq.dup skip_right_label = iseq.label iseq.branchif(skip_right_label) iseq.pop visit(node.right) iseq.push(skip_right_label) else visit(node.left) visit(node.right) iseq.send(YARV.calldata(node.operator, 1)) end end
# File lib/syntax_tree/yarv/compiler.rb, line 575 def visit_block(node) with_child_iseq(iseq.block_child_iseq(node.location.start_line)) do iseq.event(:RUBY_EVENT_B_CALL) visit(node.block_var) visit(node.bodystmt) iseq.event(:RUBY_EVENT_B_RETURN) iseq.leave end end
# File lib/syntax_tree/yarv/compiler.rb, line 585 def visit_block_var(node) params = node.params if params.requireds.length == 1 && params.optionals.empty? && !params.rest && params.posts.empty? && params.keywords.empty? && !params.keyword_rest && !params.block iseq.argument_options[:ambiguous_param0] = true end visit(node.params) node.locals.each { |local| iseq.local_table.plain(local.value.to_sym) } end
# File lib/syntax_tree/yarv/compiler.rb, line 599 def visit_blockarg(node) iseq.argument_options[:block_start] = iseq.argument_size iseq.local_table.block(node.name.value.to_sym) iseq.argument_size += 1 end
# File lib/syntax_tree/yarv/compiler.rb, line 605 def visit_bodystmt(node) visit(node.statements) end
# File lib/syntax_tree/yarv/compiler.rb, line 609 def visit_break(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 612 def visit_call(node) if node.is_a?(CallNode) return( visit_call( CommandCall.new( receiver: node.receiver, operator: node.operator, message: node.message, arguments: node.arguments, block: nil, location: node.location ) ) ) end # Track whether or not this is a method call on a block proxy receiver. # If it is, we can potentially do tailcall optimizations on it. block_receiver = false if node.receiver if node.receiver.is_a?(VarRef) lookup = iseq.local_variable(node.receiver.value.value.to_sym) if lookup.local.is_a?(LocalTable::BlockLocal) iseq.getblockparamproxy(lookup.index, lookup.level) block_receiver = true else visit(node.receiver) end else visit(node.receiver) end else iseq.putself end after_call_label = nil if node.operator&.value == "&." iseq.dup after_call_label = iseq.label iseq.branchnil(after_call_label) end arg_parts = argument_parts(node.arguments) argc = arg_parts.length flag = 0 arg_parts.each do |arg_part| case arg_part when ArgBlock argc -= 1 flag |= CallData::CALL_ARGS_BLOCKARG visit(arg_part) when ArgStar flag |= CallData::CALL_ARGS_SPLAT visit(arg_part) when ArgsForward flag |= CallData::CALL_TAILCALL if options.tailcall_optimization? flag |= CallData::CALL_ARGS_SPLAT lookup = iseq.local_table.find(:*) iseq.getlocal(lookup.index, lookup.level) iseq.splatarray(arg_parts.length != 1) flag |= CallData::CALL_ARGS_BLOCKARG lookup = iseq.local_table.find(:&) iseq.getblockparamproxy(lookup.index, lookup.level) when BareAssocHash flag |= CallData::CALL_KW_SPLAT visit(arg_part) else visit(arg_part) end end block_iseq = visit(node.block) if node.block # If there's no block and we don't already have any special flags set, # then we can safely call this simple arguments. Note that has to be the # first flag we set after looking at the arguments to get the flags # correct. flag |= CallData::CALL_ARGS_SIMPLE if block_iseq.nil? && flag == 0 # If there's no receiver, then this is an "fcall". flag |= CallData::CALL_FCALL if node.receiver.nil? # If we're calling a method on the passed block object and we have # tailcall optimizations turned on, then we can set the tailcall flag. if block_receiver && options.tailcall_optimization? flag |= CallData::CALL_TAILCALL end iseq.send( YARV.calldata(node.message.value.to_sym, argc, flag), block_iseq ) iseq.event(after_call_label) if after_call_label end
# File lib/syntax_tree/yarv/compiler.rb, line 712 def visit_case(node) visit(node.value) if node.value clauses = [] else_clause = nil current = node.consequent while current clauses << current if (current = current.consequent).is_a?(Else) else_clause = current break end end branches = clauses.map do |clause| visit(clause.arguments) iseq.topn(1) iseq.send( YARV.calldata( :===, 1, CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE ) ) label = iseq.label iseq.branchif(label) [clause, label] end iseq.pop else_clause ? visit(else_clause) : iseq.putnil iseq.leave branches.each_with_index do |(clause, label), index| iseq.leave if index != 0 iseq.push(label) iseq.pop visit(clause) end end
# File lib/syntax_tree/yarv/compiler.rb, line 757 def visit_class(node) name = node.constant.constant.value.to_sym class_iseq = with_child_iseq( iseq.class_child_iseq(name, node.location.start_line) ) do iseq.event(:RUBY_EVENT_CLASS) visit(node.bodystmt) iseq.event(:RUBY_EVENT_END) iseq.leave end flags = DefineClass::TYPE_CLASS case node.constant when ConstPathRef flags |= DefineClass::FLAG_SCOPED visit(node.constant.parent) when ConstRef iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE) when TopConstRef flags |= DefineClass::FLAG_SCOPED iseq.putobject(Object) end if node.superclass flags |= DefineClass::FLAG_HAS_SUPERCLASS visit(node.superclass) else iseq.putnil end iseq.defineclass(name, class_iseq, flags) end
# File lib/syntax_tree/yarv/compiler.rb, line 792 def visit_command(node) visit_call( CommandCall.new( receiver: nil, operator: nil, message: node.message, arguments: node.arguments, block: node.block, location: node.location ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 805 def visit_command_call(node) visit_call( CommandCall.new( receiver: node.receiver, operator: node.operator, message: node.message, arguments: node.arguments, block: node.block, location: node.location ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 818 def visit_const_path_field(node) visit(node.parent) end
# File lib/syntax_tree/yarv/compiler.rb, line 822 def visit_const_path_ref(node) names = constant_names(node) iseq.opt_getconstant_path(names) end
# File lib/syntax_tree/yarv/compiler.rb, line 827 def visit_def(node) name = node.name.value.to_sym method_iseq = iseq.method_child_iseq(name.to_s, node.location.start_line) with_child_iseq(method_iseq) do visit(node.params) if node.params iseq.event(:RUBY_EVENT_CALL) visit(node.bodystmt) iseq.event(:RUBY_EVENT_RETURN) iseq.leave end if node.target visit(node.target) iseq.definesmethod(name, method_iseq) else iseq.definemethod(name, method_iseq) end iseq.putobject(name) end
# File lib/syntax_tree/yarv/compiler.rb, line 850 def visit_defined(node) case node.value when Assign # If we're assigning to a local variable, then we need to make sure # that we put it into the local table. if node.value.target.is_a?(VarField) && node.value.target.value.is_a?(Ident) iseq.local_table.plain(node.value.target.value.value.to_sym) end iseq.putobject("assignment") when VarRef value = node.value.value name = value.value.to_sym case value when Const iseq.putnil iseq.defined(Defined::TYPE_CONST, name, "constant") when CVar iseq.putnil iseq.defined(Defined::TYPE_CVAR, name, "class variable") when GVar iseq.putnil iseq.defined(Defined::TYPE_GVAR, name, "global-variable") when Ident iseq.putobject("local-variable") when IVar iseq.definedivar(name, iseq.inline_storage, "instance-variable") when Kw case name when :false iseq.putobject("false") when :nil iseq.putobject("nil") when :self iseq.putobject("self") when :true iseq.putobject("true") end end when VCall iseq.putself name = node.value.value.value.to_sym iseq.defined(Defined::TYPE_FUNC, name, "method") when YieldNode iseq.putnil iseq.defined(Defined::TYPE_YIELD, false, "yield") when ZSuper iseq.putnil iseq.defined(Defined::TYPE_ZSUPER, false, "super") else iseq.putobject("expression") end end
# File lib/syntax_tree/yarv/compiler.rb, line 907 def visit_dyna_symbol(node) if node.parts.length == 1 && node.parts.first.is_a?(TStringContent) iseq.putobject(node.parts.first.value.to_sym) end end
# File lib/syntax_tree/yarv/compiler.rb, line 913 def visit_else(node) visit(node.statements) iseq.pop unless last_statement? end
# File lib/syntax_tree/yarv/compiler.rb, line 918 def visit_elsif(node) visit_if( IfNode.new( predicate: node.predicate, statements: node.statements, consequent: node.consequent, location: node.location ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 929 def visit_ensure(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 932 def visit_field(node) visit(node.parent) end
# File lib/syntax_tree/yarv/compiler.rb, line 936 def visit_float(node) iseq.putobject(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 940 def visit_fndptn(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 943 def visit_for(node) visit(node.collection) name = node.index.value.value.to_sym iseq.local_table.plain(name) block_iseq = with_child_iseq( iseq.block_child_iseq(node.statements.location.start_line) ) do iseq.argument_options[:lead_num] ||= 0 iseq.argument_options[:lead_num] += 1 iseq.argument_options[:ambiguous_param0] = true iseq.argument_size += 1 iseq.local_table.plain(2) iseq.getlocal(0, 0) local_variable = iseq.local_variable(name) iseq.setlocal(local_variable.index, local_variable.level) iseq.event(:RUBY_EVENT_B_CALL) iseq.nop visit(node.statements) iseq.event(:RUBY_EVENT_B_RETURN) iseq.leave end iseq.send(YARV.calldata(:each, 0, 0), block_iseq) end
# File lib/syntax_tree/yarv/compiler.rb, line 976 def visit_hash(node) if (compiled = RubyVisitor.compile(node)) iseq.duphash(compiled) else visit_all(node.assocs) iseq.newhash(node.assocs.length * 2) end end
# File lib/syntax_tree/yarv/compiler.rb, line 988 def visit_heredoc(node) if node.beginning.value.end_with?("`") visit_xstring_literal(node) elsif node.parts.length == 1 && node.parts.first.is_a?(TStringContent) visit(node.parts.first) else length = visit_string_parts(node) iseq.concatstrings(length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 985 def visit_hshptn(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 999 def visit_if(node) if node.predicate.is_a?(RangeNode) true_label = iseq.label false_label = iseq.label end_label = iseq.label iseq.getspecial(GetSpecial::SVAR_FLIPFLOP_START, 0) iseq.branchif(true_label) visit(node.predicate.left) iseq.branchunless(end_label) iseq.putobject(true) iseq.setspecial(GetSpecial::SVAR_FLIPFLOP_START) iseq.push(true_label) visit(node.predicate.right) iseq.branchunless(false_label) iseq.putobject(false) iseq.setspecial(GetSpecial::SVAR_FLIPFLOP_START) iseq.push(false_label) visit(node.statements) iseq.leave iseq.push(end_label) iseq.putnil else consequent_label = iseq.label visit(node.predicate) iseq.branchunless(consequent_label) visit(node.statements) if last_statement? iseq.leave iseq.push(consequent_label) node.consequent ? visit(node.consequent) : iseq.putnil else iseq.pop if node.consequent done_label = iseq.label iseq.jump(done_label) iseq.push(consequent_label) visit(node.consequent) iseq.push(done_label) else iseq.push(consequent_label) end end end end
# File lib/syntax_tree/yarv/compiler.rb, line 1053 def visit_if_op(node) visit_if( IfNode.new( predicate: node.predicate, statements: Statements.new(body: [node.truthy], location: Location.default), consequent: Else.new( keyword: Kw.new(value: "else", location: Location.default), statements: Statements.new( body: [node.falsy], location: Location.default ), location: Location.default ), location: Location.default ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1074 def visit_imaginary(node) iseq.putobject(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1078 def visit_int(node) iseq.putobject(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1082 def visit_kwrest_param(node) iseq.argument_options[:kwrest] = iseq.argument_size iseq.argument_size += 1 iseq.local_table.plain(node.name.value.to_sym) end
# File lib/syntax_tree/yarv/compiler.rb, line 1088 def visit_label(node) iseq.putobject(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1092 def visit_lambda(node) lambda_iseq = with_child_iseq(iseq.block_child_iseq(node.location.start_line)) do iseq.event(:RUBY_EVENT_B_CALL) visit(node.params) visit(node.statements) iseq.event(:RUBY_EVENT_B_RETURN) iseq.leave end iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.send(YARV.calldata(:lambda, 0, CallData::CALL_FCALL), lambda_iseq) end
# File lib/syntax_tree/yarv/compiler.rb, line 1106 def visit_lambda_var(node) visit_block_var(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1110 def visit_massign(node) visit(node.value) iseq.dup visit(node.target) end
# File lib/syntax_tree/yarv/compiler.rb, line 1116 def visit_method_add_block(node) visit_call( CommandCall.new( receiver: node.call.receiver, operator: node.call.operator, message: node.call.message, arguments: node.call.arguments, block: node.block, location: node.location ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1129 def visit_mlhs(node) lookups = [] node.parts.each do |part| case part when VarField lookups << visit(part) end end iseq.expandarray(lookups.length, 0) lookups.each { |lookup| iseq.setlocal(lookup.index, lookup.level) } end
# File lib/syntax_tree/yarv/compiler.rb, line 1142 def visit_module(node) name = node.constant.constant.value.to_sym module_iseq = with_child_iseq( iseq.module_child_iseq(name, node.location.start_line) ) do iseq.event(:RUBY_EVENT_CLASS) visit(node.bodystmt) iseq.event(:RUBY_EVENT_END) iseq.leave end flags = DefineClass::TYPE_MODULE case node.constant when ConstPathRef flags |= DefineClass::FLAG_SCOPED visit(node.constant.parent) when ConstRef iseq.putspecialobject(PutSpecialObject::OBJECT_CONST_BASE) when TopConstRef flags |= DefineClass::FLAG_SCOPED iseq.putobject(Object) end iseq.putnil iseq.defineclass(name, module_iseq, flags) end
# File lib/syntax_tree/yarv/compiler.rb, line 1171 def visit_mrhs(node) if (compiled = RubyVisitor.compile(node)) iseq.duparray(compiled) else visit_all(node.parts) iseq.newarray(node.parts.length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1180 def visit_next(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1183 def visit_not(node) visit(node.statement) iseq.send(YARV.calldata(:!)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1188 def visit_opassign(node) flag = CallData::CALL_ARGS_SIMPLE if node.target.is_a?(ConstPathField) || node.target.is_a?(TopConstField) flag |= CallData::CALL_FCALL end case (operator = node.operator.value.chomp("=").to_sym) when :"&&" done_label = iseq.label with_opassign(node) do iseq.dup iseq.branchunless(done_label) iseq.pop visit(node.value) end case node.target when ARefField iseq.leave iseq.push(done_label) iseq.setn(3) iseq.adjuststack(3) when ConstPathField, TopConstField iseq.push(done_label) iseq.swap iseq.pop else iseq.push(done_label) end when :"||" if node.target.is_a?(ConstPathField) || node.target.is_a?(TopConstField) opassign_defined(node) iseq.swap iseq.pop elsif node.target.is_a?(VarField) && [Const, CVar, GVar].include?(node.target.value.class) opassign_defined(node) else skip_value_label = iseq.label with_opassign(node) do iseq.dup iseq.branchif(skip_value_label) iseq.pop visit(node.value) end if node.target.is_a?(ARefField) iseq.leave iseq.push(skip_value_label) iseq.setn(3) iseq.adjuststack(3) else iseq.push(skip_value_label) end end else with_opassign(node) do visit(node.value) iseq.send(YARV.calldata(operator, 1, flag)) end end end
# File lib/syntax_tree/yarv/compiler.rb, line 1254 def visit_params(node) if node.requireds.any? iseq.argument_options[:lead_num] = 0 node.requireds.each do |required| iseq.local_table.plain(required.value.to_sym) iseq.argument_size += 1 iseq.argument_options[:lead_num] += 1 end end node.optionals.each do |(optional, value)| index = iseq.local_table.size name = optional.value.to_sym iseq.local_table.plain(name) iseq.argument_size += 1 unless iseq.argument_options.key?(:opt) start_label = iseq.label iseq.push(start_label) iseq.argument_options[:opt] = [start_label] end visit(value) iseq.setlocal(index, 0) arg_given_label = iseq.label iseq.push(arg_given_label) iseq.argument_options[:opt] << arg_given_label end visit(node.rest) if node.rest if node.posts.any? iseq.argument_options[:post_start] = iseq.argument_size iseq.argument_options[:post_num] = 0 node.posts.each do |post| iseq.local_table.plain(post.value.to_sym) iseq.argument_size += 1 iseq.argument_options[:post_num] += 1 end end if node.keywords.any? iseq.argument_options[:kwbits] = 0 iseq.argument_options[:keyword] = [] keyword_bits_name = node.keyword_rest ? 3 : 2 iseq.argument_size += 1 keyword_bits_index = iseq.local_table.locals.size + node.keywords.size node.keywords.each_with_index do |(keyword, value), keyword_index| name = keyword.value.chomp(":").to_sym index = iseq.local_table.size iseq.local_table.plain(name) iseq.argument_size += 1 iseq.argument_options[:kwbits] += 1 if value.nil? iseq.argument_options[:keyword] << name elsif (compiled = RubyVisitor.compile(value)) iseq.argument_options[:keyword] << [name, compiled] else skip_value_label = iseq.label iseq.argument_options[:keyword] << [name] iseq.checkkeyword(keyword_bits_index, keyword_index) iseq.branchif(skip_value_label) visit(value) iseq.setlocal(index, 0) iseq.push(skip_value_label) end end iseq.local_table.plain(keyword_bits_name) end if node.keyword_rest.is_a?(ArgsForward) if RUBY_VERSION >= "3.2" iseq.local_table.plain(:*) iseq.local_table.plain(:&) iseq.local_table.plain(:"...") iseq.argument_options[:rest_start] = iseq.argument_size iseq.argument_options[:block_start] = iseq.argument_size + 1 iseq.argument_size += 2 else iseq.local_table.plain(:*) iseq.local_table.plain(:&) iseq.argument_options[:rest_start] = iseq.argument_size iseq.argument_options[:block_start] = iseq.argument_size + 1 iseq.argument_size += 2 end elsif node.keyword_rest visit(node.keyword_rest) end visit(node.block) if node.block end
# File lib/syntax_tree/yarv/compiler.rb, line 1360 def visit_paren(node) visit(node.contents) end
# File lib/syntax_tree/yarv/compiler.rb, line 1364 def visit_pinned_begin(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1367 def visit_pinned_var_ref(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1370 def visit_program(node) node.statements.body.each do |statement| break unless statement.is_a?(Comment) if statement.value == "# frozen_string_literal: true" options.frozen_string_literal! end end preexes = [] statements = [] node.statements.body.each do |statement| case statement when Comment, EmbDoc, EndContent, VoidStmt # ignore when BEGINBlock preexes << statement else statements << statement end end top_iseq = InstructionSequence.new( "<compiled>", "<compiled>", 1, :top, nil, options ) with_child_iseq(top_iseq) do visit_all(preexes) if statements.empty? iseq.putnil else *statements, last_statement = statements visit_all(statements) with_last_statement { visit(last_statement) } end iseq.leave end top_iseq.compile! top_iseq end
# File lib/syntax_tree/yarv/compiler.rb, line 1421 def visit_qsymbols(node) iseq.duparray(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1425 def visit_qwords(node) if options.frozen_string_literal? iseq.duparray(node.accept(RubyVisitor.new)) else visit_all(node.elements) iseq.newarray(node.elements.length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1434 def visit_range(node) if (compiled = RubyVisitor.compile(node)) iseq.putobject(compiled) else visit(node.left) visit(node.right) iseq.newrange(node.operator.value == ".." ? 0 : 1) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1444 def visit_rassign(node) iseq.putnil if node.operator.is_a?(Kw) match_label = iseq.label visit(node.value) iseq.dup visit_pattern(node.pattern, match_label) iseq.pop iseq.pop iseq.putobject(false) iseq.leave iseq.push(match_label) iseq.adjuststack(2) iseq.putobject(true) else no_key_label = iseq.label end_leave_label = iseq.label end_label = iseq.label iseq.putnil iseq.putobject(false) iseq.putnil iseq.putnil visit(node.value) iseq.dup visit_pattern(node.pattern, end_label) # First we're going to push the core onto the stack, then we'll check # if the value to match is truthy. If it is, we'll jump down to raise # NoMatchingPatternKeyError. Otherwise we'll raise # NoMatchingPatternError. iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.topn(4) iseq.branchif(no_key_label) # Here we're going to raise NoMatchingPatternError. iseq.putobject(NoMatchingPatternError) iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.putobject("%p: %s") iseq.topn(4) iseq.topn(7) iseq.send(YARV.calldata(:"core#sprintf", 3)) iseq.send(YARV.calldata(:"core#raise", 2)) iseq.jump(end_leave_label) # Here we're going to raise NoMatchingPatternKeyError. iseq.push(no_key_label) iseq.putobject(NoMatchingPatternKeyError) iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.putobject("%p: %s") iseq.topn(4) iseq.topn(7) iseq.send(YARV.calldata(:"core#sprintf", 3)) iseq.topn(7) iseq.topn(9) iseq.send( YARV.calldata(:new, 1, CallData::CALL_KWARG, %i[matchee key]) ) iseq.send(YARV.calldata(:"core#raise", 1)) iseq.push(end_leave_label) iseq.adjuststack(7) iseq.putnil iseq.leave iseq.push(end_label) iseq.adjuststack(6) iseq.putnil end end
# File lib/syntax_tree/yarv/compiler.rb, line 1521 def visit_rational(node) iseq.putobject(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1525 def visit_redo(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1528 def visit_regexp_literal(node) if (compiled = RubyVisitor.compile(node)) iseq.putobject(compiled) else flags = RubyVisitor.new.visit_regexp_literal_flags(node) length = visit_string_parts(node) iseq.toregexp(flags, length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1538 def visit_rescue(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1541 def visit_rescue_ex(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1544 def visit_rescue_mod(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1547 def visit_rest_param(node) iseq.local_table.plain(node.name.value.to_sym) iseq.argument_options[:rest_start] = iseq.argument_size iseq.argument_size += 1 end
# File lib/syntax_tree/yarv/compiler.rb, line 1553 def visit_retry(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1556 def visit_return(node) end
# File lib/syntax_tree/yarv/compiler.rb, line 1559 def visit_sclass(node) visit(node.target) iseq.putnil singleton_iseq = with_child_iseq( iseq.singleton_class_child_iseq(node.location.start_line) ) do iseq.event(:RUBY_EVENT_CLASS) visit(node.bodystmt) iseq.event(:RUBY_EVENT_END) iseq.leave end iseq.defineclass( :singletonclass, singleton_iseq, DefineClass::TYPE_SINGLETON_CLASS ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1580 def visit_statements(node) statements = node.body.select do |statement| case statement when Comment, EmbDoc, EndContent, VoidStmt false else true end end statements.empty? ? iseq.putnil : visit_all(statements) end
# File lib/syntax_tree/yarv/compiler.rb, line 1594 def visit_string_concat(node) value = node.left.parts.first.value + node.right.parts.first.value visit_string_literal( StringLiteral.new( parts: [TStringContent.new(value: value, location: node.location)], quote: node.left.quote, location: node.location ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1606 def visit_string_embexpr(node) visit(node.statements) end
# File lib/syntax_tree/yarv/compiler.rb, line 1610 def visit_string_literal(node) if node.parts.length == 1 && node.parts.first.is_a?(TStringContent) visit(node.parts.first) else length = visit_string_parts(node) iseq.concatstrings(length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1619 def visit_super(node) iseq.putself visit(node.arguments) iseq.invokesuper( YARV.calldata( nil, argument_parts(node.arguments).length, CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE | CallData::CALL_SUPER ), nil ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1633 def visit_symbol_literal(node) iseq.putobject(node.accept(RubyVisitor.new)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1637 def visit_symbols(node) if (compiled = RubyVisitor.compile(node)) iseq.duparray(compiled) else node.elements.each do |element| if element.parts.length == 1 && element.parts.first.is_a?(TStringContent) iseq.putobject(element.parts.first.value.to_sym) else length = visit_string_parts(element) iseq.concatstrings(length) iseq.intern end end iseq.newarray(node.elements.length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1656 def visit_top_const_ref(node) iseq.opt_getconstant_path(constant_names(node)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1660 def visit_tstring_content(node) if options.frozen_string_literal? iseq.putobject(node.accept(RubyVisitor.new)) else iseq.putstring(node.accept(RubyVisitor.new)) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1668 def visit_unary(node) method_id = case node.operator when "+", "-" "#{node.operator}@" else node.operator end visit_call( CommandCall.new( receiver: node.statement, operator: nil, message: Ident.new(value: method_id, location: Location.default), arguments: nil, block: nil, location: Location.default ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1689 def visit_undef(node) node.symbols.each_with_index do |symbol, index| iseq.pop if index != 0 iseq.putspecialobject(PutSpecialObject::OBJECT_VMCORE) iseq.putspecialobject(PutSpecialObject::OBJECT_CBASE) visit(symbol) iseq.send(YARV.calldata(:"core#undef_method", 2)) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1699 def visit_unless(node) statements_label = iseq.label visit(node.predicate) iseq.branchunless(statements_label) node.consequent ? visit(node.consequent) : iseq.putnil if last_statement? iseq.leave iseq.push(statements_label) visit(node.statements) else iseq.pop if node.consequent done_label = iseq.label iseq.jump(done_label) iseq.push(statements_label) visit(node.consequent) iseq.push(done_label) else iseq.push(statements_label) end end end
# File lib/syntax_tree/yarv/compiler.rb, line 1725 def visit_until(node) predicate_label = iseq.label statements_label = iseq.label iseq.jump(predicate_label) iseq.putnil iseq.pop iseq.jump(predicate_label) iseq.push(statements_label) visit(node.statements) iseq.pop iseq.push(predicate_label) visit(node.predicate) iseq.branchunless(statements_label) iseq.putnil if last_statement? end
# File lib/syntax_tree/yarv/compiler.rb, line 1744 def visit_var_field(node) case node.value when CVar, IVar name = node.value.value.to_sym iseq.inline_storage_for(name) when Ident name = node.value.value.to_sym if (local_variable = iseq.local_variable(name)) local_variable else iseq.local_table.plain(name) iseq.local_variable(name) end end end
# File lib/syntax_tree/yarv/compiler.rb, line 1761 def visit_var_ref(node) case node.value when Const iseq.opt_getconstant_path(constant_names(node)) when CVar name = node.value.value.to_sym iseq.getclassvariable(name) when GVar iseq.getglobal(node.value.value.to_sym) when Ident lookup = iseq.local_variable(node.value.value.to_sym) case lookup.local when LocalTable::BlockLocal iseq.getblockparam(lookup.index, lookup.level) when LocalTable::PlainLocal iseq.getlocal(lookup.index, lookup.level) end when IVar name = node.value.value.to_sym iseq.getinstancevariable(name) when Kw case node.value.value when "false" iseq.putobject(false) when "nil" iseq.putnil when "self" iseq.putself when "true" iseq.putobject(true) end end end
# File lib/syntax_tree/yarv/compiler.rb, line 1796 def visit_vcall(node) iseq.putself iseq.send( YARV.calldata( node.value.value.to_sym, 0, CallData::CALL_FCALL | CallData::CALL_VCALL | CallData::CALL_ARGS_SIMPLE ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1808 def visit_when(node) visit(node.statements) end
# File lib/syntax_tree/yarv/compiler.rb, line 1812 def visit_while(node) predicate_label = iseq.label statements_label = iseq.label iseq.jump(predicate_label) iseq.putnil iseq.pop iseq.jump(predicate_label) iseq.push(statements_label) visit(node.statements) iseq.pop iseq.push(predicate_label) visit(node.predicate) iseq.branchif(statements_label) iseq.putnil if last_statement? end
# File lib/syntax_tree/yarv/compiler.rb, line 1831 def visit_word(node) if node.parts.length == 1 && node.parts.first.is_a?(TStringContent) visit(node.parts.first) else length = visit_string_parts(node) iseq.concatstrings(length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1840 def visit_words(node) if options.frozen_string_literal? && (compiled = RubyVisitor.compile(node)) iseq.duparray(compiled) else visit_all(node.elements) iseq.newarray(node.elements.length) end end
# File lib/syntax_tree/yarv/compiler.rb, line 1850 def visit_xstring_literal(node) iseq.putself length = visit_string_parts(node) iseq.concatstrings(node.parts.length) if length > 1 iseq.send( YARV.calldata( :`, 1, CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE ) ) end
# File lib/syntax_tree/yarv/compiler.rb, line 1863 def visit_yield(node) parts = argument_parts(node.arguments) visit_all(parts) iseq.invokeblock(YARV.calldata(nil, parts.length)) end
# File lib/syntax_tree/yarv/compiler.rb, line 1869 def visit_zsuper(_node) iseq.putself iseq.invokesuper( YARV.calldata( nil, 0, CallData::CALL_FCALL | CallData::CALL_ARGS_SIMPLE | CallData::CALL_SUPER | CallData::CALL_ZSUPER ), nil ) end