Permalink
Browse files

Reimplement $~, known as last_match.

Also fixes Enumerable#grep needing to update $~ in it's caller.
  • Loading branch information...
1 parent 1024ca2 commit 458a55855d62230de08b46539c4e2aa8415b5a58 Evan Phoenix committed Feb 26, 2010
View
28 kernel/bootstrap/regexp.rb
@@ -30,4 +30,32 @@ def options
Ruby.primitive :regexp_options
raise PrimitiveFailure, "Regexp#options primitive failed"
end
+
+ def self.last_match(field=nil)
+ Ruby.primitive :regexp_last_match
+
+ return last_match(Integer(field)) if field
+ raise PrimitiveFailure, "Regexp#last_match failed"
+ end
+
+ def self.last_match=(match)
+ Ruby.primitive :regexp_set_last_match
+
+ unless match.kind_of? MatchData
+ raise TypeError, "Expected MatchData, got #{match.inspect}"
+ end
+
+ raise PrimitiveFailure, "Regexp#set_last_match failed"
+ end
+
+ def self.propagate_last_match
+ Ruby.primitive :regexp_propagate_last_match
+ raise PrimitiveFailure, "Regexp#propagate_last_match failed"
+ end
+
+ def self.set_block_last_match
+ Ruby.primitive :regexp_set_block_last_match
+ raise PrimitiveFailure, "Regexp#set_block_last_match failed"
+ end
+
end
View
61 kernel/bootstrap/variable_scope.rb
@@ -17,66 +17,5 @@ def locals
# To handle Module#private, protected
attr_accessor :method_visibility
-
- # To handle $~, $1, etc
-
- def last_match=(match)
- if parent
- scope = self
- while scope.parent
- scope = scope.parent
- end
- scope.last_match = match
- else
- @last_match = match
- end
-
- match
- end
-
- def last_match
- if parent
- scope = self
- while scope.parent
- scope = scope.parent
- end
-
- scope.last_match
- else
- @last_match
- end
- end
- ##
- # The Nth group of the last regexp match.
-
- def nth_ref(n)
- if lm = last_match()
- return lm[n]
- end
-
- return nil
- end
-
- ##
- # One of the special globals $&, $`, $' or $+.
-
- def back_ref(kind)
- if lm = last_match()
- res = case kind
- when :&
- lm[0]
- when :"`"
- lm.pre_match
- when :"'"
- lm.post_match
- when :+
- lm.captures.last
- end
-
- return res
- end
-
- return nil
- end
end
end
View
1 kernel/common/enumerable.rb
@@ -109,6 +109,7 @@ def grep(pattern)
ary = []
each do |o|
if pattern === o
+ Regexp.set_block_last_match
ary << (block_given? ? yield(o) : o)
end
end
View
33 kernel/common/regexp.rb
@@ -103,23 +103,6 @@ def initialize_copy(other)
initialize other.source, other.options, other.kcode
end
- def self.last_match(field = nil)
- match = Rubinius::VariableScope.of_sender.last_match
- if match
- return match if field.nil?
- return match[field]
- else
- return nil
- end
- end
-
- def self.last_match=(match)
- unless match.nil? || match.kind_of?(MatchData)
- raise ::TypeError, "wrong argument type #{match.class} (expected MatchData)"
- end
- Rubinius::VariableScope.of_sender.last_match = match
- end
-
def self.union(*patterns)
if patterns.size == 1
pat = patterns.first
@@ -142,7 +125,7 @@ def source
def ~
line = $_
if !line.is_a?(String)
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
return nil
end
res = self.match(line)
@@ -158,10 +141,10 @@ def =~(str)
match = match_from(str, 0)
if match
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
return match.begin(0)
else
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
return nil
end
end
@@ -183,15 +166,15 @@ def match_all(str)
def ===(other)
if !other.is_a?(String)
if !other.respond_to?(:to_str)
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
return false
end
end
if match = self.match_from(other.to_str, 0)
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
return true
else
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
return false
end
end
@@ -239,13 +222,13 @@ def kcode
# Performs normal match and returns MatchData object from $~ or nil.
def match(str)
if str.nil?
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
return nil
end
str = StringValue(str)
- Rubinius::VariableScope.of_sender.last_match = search_region(str, 0, str.size, true)
+ Regexp.last_match = search_region(str, 0, str.size, true)
end
def match_from(str, count)
View
216 kernel/common/string.rb
@@ -171,10 +171,10 @@ def =~(pattern)
case pattern
when Regexp
if m = pattern.match_from(self, 0)
- Rubinius::VariableScope.of_sender.last_match = m
+ Regexp.last_match = m
return m.begin(0)
end
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
return nil
when String
raise TypeError, "type mismatch: String given"
@@ -233,7 +233,7 @@ def [](index, other = undefined)
if index.kind_of? Regexp
match, str = subpattern(index, length)
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
return str
else
start = Type.coerce_to(index, Fixnum, :to_int)
@@ -244,7 +244,7 @@ def [](index, other = undefined)
case index
when Regexp
match, str = subpattern(index, 0)
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
return str
when String
return include?(index) ? index.dup : nil
@@ -835,8 +835,10 @@ def eql?(other)
# "hello".gsub(/[aeiou]/, '*') #=> "h*ll*"
# "hello".gsub(/([aeiou])/, '<\1>') #=> "h<e>ll<o>"
# "hello".gsub(/./) {|s| s[0].to_s + ' '} #=> "104 101 108 108 111 "
- def gsub(pattern, replacement = undefined, &prc)
- return to_enum :gsub, pattern, replacement unless block_given? || replacement != undefined
+ def gsub(pattern, replacement=undefined)
+ unless block_given? or replacement != undefined
+ return to_enum :gsub, pattern, replacement
+ end
tainted = false
@@ -858,21 +860,21 @@ def gsub(pattern, replacement = undefined, &prc)
offset = match.begin 0 if match
- while match do
- ret << (match.pre_match_from(last_end) || "")
+ while match
+ if str = match.pre_match_from(last_end)
+ ret << str
+ end
- unless replacement == undefined
- ret << replacement.to_sub_replacement(match)
- else
- # We do this so that we always manipulate $~ in the context
- # of the passed block.
- prc.block.top_scope.last_match = match
+ if replacement == undefined
+ Regexp.last_match = match
- val = yield(match[0].dup)
+ val = yield(match[0]).to_s
tainted ||= val.tainted?
- ret << val.to_s
+ ret << val
raise RuntimeError, "string modified" if self != copy
+ else
+ ret << replacement.to_sub_replacement(match)
end
tainted ||= val.tainted?
@@ -897,7 +899,7 @@ def gsub(pattern, replacement = undefined, &prc)
offset = match.begin 0
end
- Rubinius::VariableScope.of_sender.last_match = last_match
+ Regexp.last_match = last_match
str = substring(last_end, @num_bytes-last_end+1)
ret << str if str
@@ -906,18 +908,85 @@ def gsub(pattern, replacement = undefined, &prc)
return ret
end
-
# Performs the substitutions of <code>String#gsub</code> in place, returning
# <i>self</i>, or <code>nil</code> if no substitutions were performed.
- def gsub!(pattern, replacement = undefined, &block)
- str = gsub(pattern, replacement, &block)
+ def gsub!(pattern, replacement=undefined)
+ # Because of the behavior of $~, this is duplicated from gsub! because
+ # if we call gsub! from gsub, the last_match can't be updated properly.
+ unless block_given? or replacement != undefined
+ return to_enum :gsub, pattern, replacement
+ end
+
+ tainted = false
+
+ unless replacement == undefined
+ tainted = replacement.tainted?
+ replacement = StringValue(replacement)
+ tainted ||= replacement.tainted?
+ end
+
+ pattern = get_pattern(pattern, true)
+ copy = self.dup
+
+ last_end = 0
+ offset = nil
+ ret = self.class.pattern(0,0) # Empty string, or string subclass
+
+ last_match = nil
+ match = pattern.match_from self, last_end
+
+ offset = match.begin 0 if match
+
+ while match
+ if str = match.pre_match_from(last_end)
+ ret << str
+ end
+
+ if replacement == undefined
+ Regexp.last_match = match
+
+ val = yield(match[0]).to_s
+ tainted ||= val.tainted?
+ ret << val
+
+ raise RuntimeError, "string modified" if self != copy
+ else
+ ret << replacement.to_sub_replacement(match)
+ end
+
+ tainted ||= val.tainted?
+
+ last_end = match.end(0)
+
+ if match.collapsing?
+ if char = find_character(offset)
+ offset += char.size
+ else
+ offset += 1
+ end
+ else
+ offset = match.end(0)
+ end
+
+ last_match = match
- if lm = $~
- Rubinius::VariableScope.of_sender.last_match = lm
- replace(str)
+ match = pattern.match_from self, offset
+ break unless match
+
+ offset = match.begin 0
+ end
+
+ Regexp.last_match = last_match
+
+ str = substring(last_end, @num_bytes-last_end+1)
+ ret << str if str
+
+ ret.taint if tainted || self.tainted?
+
+ if last_match
+ replace(ret)
return self
else
- Rubinius::VariableScope.of_sender.last_match = nil
return nil
end
end
@@ -983,10 +1052,10 @@ def index(needle, offset = 0)
return find_string(needle, offset)
when Regexp
if match = needle.match_from(self[offset..-1], 0)
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
return (offset + match.begin(0))
else
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
end
else
raise TypeError, "type mismatch: #{needle.class} given"
@@ -1112,7 +1181,7 @@ def lstrip!
# 'hello'.match('xx') #=> nil
def match(pattern)
obj = get_pattern(pattern).match_from(self, 0)
- Rubinius::VariableScope.of_sender.last_match = obj
+ Regexp.last_match = obj
return obj
end
@@ -1222,7 +1291,7 @@ def rindex(sub, finish=undefined)
when Regexp
ret = sub.search_region(self, 0, finish, false)
- Rubinius::VariableScope.of_sender.last_match = ret
+ Regexp.last_match = ret
return ret.begin(0) if ret
else
@@ -1291,7 +1360,7 @@ def partition(pattern=nil)
def rpartition(pattern)
if pattern.kind_of? Regexp
if m = pattern.search_region(self, 0, size, false)
- Rubinius::VariableScope.of_sender.last_match = m
+ Regexp.last_match = m
[m.pre_match, m[0], m.post_match]
end
else
@@ -1413,14 +1482,14 @@ def scan(pattern)
val.taint if taint
if block_given?
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
yield(val)
else
ret << val
end
end
- Rubinius::VariableScope.of_sender.last_match = last_match
+ Regexp.last_match = last_match
return ret
end
@@ -1440,7 +1509,7 @@ def slice!(*args)
result = slice(*args)
lm = Regexp.last_match
self[*args] = '' unless result.nil?
- Rubinius::VariableScope.of_sender.last_match = lm
+ Regexp.last_match = lm
result
end
@@ -1724,7 +1793,7 @@ def strip!
# "hello".sub(/[aeiou]/, '*') #=> "h*llo"
# "hello".sub(/([aeiou])/, '<\1>') #=> "h<e>llo"
# "hello".sub(/./) {|s| s[0].to_s + ' ' } #=> "104 ello"
- def sub(pattern, replacement = undefined, &prc)
+ def sub(pattern, replacement=undefined)
if replacement.equal?(undefined) and !block_given?
raise ArgumentError, "wrong number of arguments (1 for 2)"
end
@@ -1736,28 +1805,24 @@ def sub(pattern, replacement = undefined, &prc)
if match = get_pattern(pattern, true).match_from(self, 0)
out = match.pre_match
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
- unless replacement == undefined
+ if replacement == undefined
+ replacement = yield(match[0].dup).to_s
out.taint if replacement.tainted?
- replacement = StringValue(replacement).to_sub_replacement(match)
else
- # We do this so that we always manipulate $~ in the context
- # of the passed block.
- prc.block.top_scope.last_match = match
-
- replacement = yield(match[0].dup).to_s
out.taint if replacement.tainted?
+ replacement = StringValue(replacement).to_sub_replacement(match)
end
# We have to reset it again to match the specs
- Rubinius::VariableScope.of_sender.last_match = match
+ Regexp.last_match = match
out << replacement << match.post_match
out.taint if self.tainted?
else
out = self
- Rubinius::VariableScope.of_sender.last_match = nil
+ Regexp.last_match = nil
end
# MRI behavior emulation. Sub'ing String subclasses doen't return the
@@ -1772,22 +1837,44 @@ def sub(pattern, replacement = undefined, &prc)
# Performs the substitutions of <code>String#sub</code> in place,
# returning <i>self</i>, or <code>nil</code> if no substitutions were
# performed.
- def sub!(pattern, replacement = undefined, &prc)
- if block_given?
- orig = self.dup
- str = sub(pattern, replacement, &prc)
- else
- str = sub(pattern, replacement)
+ def sub!(pattern, replacement=undefined)
+ # Copied mostly from sub to keep Regexp.last_match= working right.
+
+ if replacement.equal?(undefined) and !block_given?
+ raise ArgumentError, "wrong number of arguments (1 for 2)"
end
- if lm = Regexp.last_match
- Rubinius::VariableScope.of_sender.last_match = lm
- replace(str)
- return self
+ unless pattern
+ raise ArgumentError, "wrong number of arguments (0 for 2)"
+ end
+
+ if match = get_pattern(pattern, true).match_from(self, 0)
+ out = match.pre_match
+
+ Regexp.last_match = match
+
+ if replacement == undefined
+ replacement = yield(match[0].dup).to_s
+ out.taint if replacement.tainted?
+ else
+ out.taint if replacement.tainted?
+ replacement = StringValue(replacement).to_sub_replacement(match)
+ end
+
+ # We have to reset it again to match the specs
+ Regexp.last_match = match
+
+ out << replacement << match.post_match
+ out.taint if self.tainted?
else
- Rubinius::VariableScope.of_sender.last_match = nil
+ out = self
+ Regexp.last_match = nil
return nil
end
+
+ replace(out)
+
+ return self
end
# Returns the successor to <i>self</i>. The successor is calculated by
@@ -2415,17 +2502,18 @@ def shared!
@shared = true
end
- def get_pattern(pattern, quote = false)
- unless pattern.is_a?(String) || pattern.is_a?(Regexp)
- if pattern.respond_to?(:to_str)
- pattern = pattern.to_str
- else
- raise TypeError, "wrong argument type #{pattern.class} (expected Regexp)"
- end
+ def get_pattern(pattern, quote=false)
+ case pattern
+ when Regexp
+ return pattern
+ when String
+ # nothing
+ else
+ pattern = StringValue(pattern)
end
- pattern = Regexp.quote(pattern) if quote && pattern.is_a?(String)
- pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp)
- pattern
+
+ pattern = Regexp.quote(pattern) if quote
+ Regexp.new(pattern)
end
def full_to_i
View
26 lib/compiler/ast/variables.rb
@@ -8,12 +8,21 @@ def initialize(line, ref)
@kind = ref
end
+ Kinds = {
+ :& => 1,
+ :"`" => 2,
+ :"'" => 3,
+ :+ => 4
+ }
+
def bytecode(g)
pos(g)
- g.push_variables
- g.push_literal @kind
- g.send :back_ref, 1
+ unless mode = Kinds[@kind]
+ raise "Unknown backref: #{@kind}"
+ end
+
+ g.last_match mode, 0
end
end
@@ -25,12 +34,14 @@ def initialize(line, ref)
@which = ref
end
+ Mode = 5
+
def bytecode(g)
pos(g)
- g.push_variables
- g.push @which
- g.send :nth_ref, 1
+ # These are for $1, $2, etc. We subtract 1 because
+ # we start numbering the captures from 0.
+ g.last_match Mode, @which - 1
end
end
@@ -146,8 +157,7 @@ def bytecode(g)
if @name == :$!
g.push_exception
elsif @name == :$~
- g.push_variables
- g.send :last_match, 0
+ g.last_match 0, 0
else
g.push_const :Rubinius
g.find_const :Globals
View
11 lib/compiler/generator.rb
@@ -427,6 +427,17 @@ def cast_array
end
end
+ def invoke_primitive(name, count)
+ idx = find_literal(name)
+ add :invoke_primitive, idx, Integer(count)
+ end
+
+ def last_match(mode, which)
+ push_int Integer(mode)
+ push_int Integer(which)
+ invoke_primitive :regexp_last_match_result, 2
+ end
+
def send(meth, count, priv=false)
add :allow_private if priv
View
20 spec/compiler/back_ref_spec.rb
@@ -3,22 +3,10 @@
describe "An Back_ref node" do
relates "[$&, $`, $', $+]" do
compile do |g|
- g.push_variables
- g.push_literal :"&"
- g.send :back_ref, 1
-
- g.push_variables
- g.push_literal :"`"
- g.send :back_ref, 1
-
- g.push_variables
- g.push_literal :"'"
- g.send :back_ref, 1
-
- g.push_variables
- g.push_literal :"+"
- g.send :back_ref, 1
-
+ g.last_match 1, 0
+ g.last_match 2, 0
+ g.last_match 3, 0
+ g.last_match 4, 0
g.make_array 4
end
end
View
4 spec/compiler/nth_ref_spec.rb
@@ -3,9 +3,7 @@
describe "An Nth_ref node" do
relates "$1" do
compile do |g|
- g.push_variables
- g.push 1
- g.send :nth_ref, 1
+ g.last_match 5, 0
end
end
end
View
2 spec/custom/helpers/generator.rb
@@ -60,6 +60,8 @@ def #{name}(*args)
:push,
:push_literal_at,
:push_modifiers,
+ :invoke_primitive,
+ :last_match,
:send,
:send_super,
:send_with_block,
View
146 vm/builtin/regexp.cpp
@@ -8,10 +8,14 @@
#include "builtin/symbol.hpp"
#include "builtin/tuple.hpp"
#include "builtin/bytearray.hpp"
+#include "builtin/block_environment.hpp"
+#include "builtin/proc.hpp"
#include "vm.hpp"
#include "vm/object_utils.hpp"
#include "objectmemory.hpp"
+#include "call_frame.hpp"
+#include "arguments.hpp"
#include "gc/gc.hpp"
@@ -432,6 +436,148 @@ namespace rubinius {
return md;
}
+ String* MatchData::matched_string(STATE) {
+ Integer* beg = try_as<Integer>(full_->at(state, 0));
+ Integer* fin = try_as<Integer>(full_->at(state, 1));
+
+ if(!beg || !fin ||
+ (size_t)fin->to_native() > source_->size() ||
+ beg->to_native() < 0) {
+ return String::create(state, 0, 0);
+ }
+
+ const char* str = source_->c_str();
+ native_int sz = fin->to_native() - beg->to_native();
+
+ return String::create(state, str + beg->to_native(), sz);
+ }
+
+ String* MatchData::pre_matched(STATE) {
+ Integer* beg = try_as<Integer>(full_->at(state, 0));
+
+ if(!beg || beg->to_native() <= 0) {
+ return String::create(state, 0, 0);
+ }
+
+ const char* str = source_->c_str();
+ native_int sz = beg->to_native();
+
+ return String::create(state, str, sz);
+ }
+
+ String* MatchData::post_matched(STATE) {
+ Integer* fin = try_as<Integer>(full_->at(state, 1));
+
+ if(!fin || (size_t)fin->to_native() >= source_->size()) {
+ return String::create(state, 0, 0);
+ }
+
+ const char* str = source_->c_str();
+ native_int sz = (native_int)source_->size() - fin->to_native();
+
+ return String::create(state, str + fin->to_native(), sz);
+ }
+
+ Object* MatchData::nth_capture(STATE, size_t which) {
+ if(region_->num_fields() <= which) return Qnil;
+
+ Tuple* sub = try_as<Tuple>(region_->at(state, which));
+ if(!sub) return Qnil;
+
+ Integer* beg = try_as<Integer>(sub->at(state, 0));
+ Integer* fin = try_as<Integer>(sub->at(state, 1));
+
+ if(!beg || !fin ||
+ (size_t)fin->to_native() > source_->size() ||
+ beg->to_native() < 0) {
+ return Qnil;
+ }
+
+ const char* str = source_->c_str();
+ native_int sz = fin->to_native() - beg->to_native();
+
+ return String::create(state, str + beg->to_native(), sz);
+ }
+
+ Object* MatchData::last_capture(STATE) {
+ if(region_->num_fields() == 0) return Qnil;
+ return nth_capture(state, region_->num_fields() - 1);
+ }
+
+ Object* Regexp::last_match_result(STATE, Fixnum* mode, Fixnum* which,
+ CallFrame* call_frame)
+ {
+ Object* current_match = call_frame->scope->last_match(state);
+
+ if(MatchData* match = try_as<MatchData>(current_match)) {
+ switch(mode->to_native()) {
+ case 0:
+ return match;
+ case 1:
+ return match->matched_string(state);
+ case 2:
+ return match->pre_matched(state);
+ case 3:
+ return match->post_matched(state);
+ case 4:
+ return match->last_capture(state);
+ case 5:
+ return match->nth_capture(state, which->to_native());
+ }
+ }
+ return Qnil;
+ }
+
+ Object* Regexp::last_match(STATE, Arguments& args, CallFrame* call_frame) {
+ MatchData* match = try_as<MatchData>(call_frame->scope->last_match(state));
+ if(!match) return Qnil;
+
+ if(args.total() == 0) return match;
+ if(args.total() > 1) return Primitives::failure();
+
+ native_int which = as<Fixnum>(args.get_argument(0))->to_native();
+
+ if(which == 0) {
+ return match->matched_string(state);
+ } else {
+ return match->nth_capture(state, which - 1);
+ }
+ }
+
+ Object* Regexp::set_last_match(STATE, Object* obj, CallFrame* call_frame) {
+ if(!obj->nil_p() && !kind_of<MatchData>(obj)) {
+ return Primitives::failure();
+ }
+
+ if(CallFrame* parent = call_frame->previous) {
+ parent->scope->set_last_match(state, obj);
+ }
+
+ return obj;
+ }
+
+ Object* Regexp::propagate_last_match(STATE, CallFrame* call_frame) {
+ Object* obj = call_frame->scope->last_match(state);
+ if(RTEST(obj)) {
+ Regexp::set_last_match(state, obj, call_frame);
+ }
+ return obj;
+ }
+
+ Object* Regexp::set_block_last_match(STATE, CallFrame* call_frame) {
+ Object* blk = call_frame->scope->block();
+ MatchData* match = try_as<MatchData>(call_frame->scope->last_match(state));
+ if(!match) return Qnil;
+
+ if(BlockEnvironment* env = try_as<BlockEnvironment>(blk)) {
+ env->top_scope()->last_match(state, match);
+ } else if(Proc* proc = try_as<Proc>(blk)) {
+ proc->block()->top_scope()->last_match(state, match);
+ }
+
+ return match;
+ }
+
void Regexp::Info::mark(Object* obj, ObjectMark& mark) {
auto_mark(obj, mark);
View
21 vm/builtin/regexp.hpp
@@ -56,6 +56,21 @@ namespace rubinius {
// Ruby.primitive :regexp_allocate
static Regexp* allocate(STATE, Object* self);
+ // Ruby.primitive :regexp_last_match_result
+ static Object* last_match_result(STATE, Fixnum* mode, Fixnum* which, CallFrame* calling_environment);
+
+ // Ruby.primitive :regexp_last_match
+ static Object* last_match(STATE, Arguments& args, CallFrame* calling_environment);
+
+ // Ruby.primitive :regexp_set_last_match
+ static Object* set_last_match(STATE, Object* obj, CallFrame* calling_environment);
+
+ // Ruby.primitive :regexp_propagate_last_match
+ static Object* propagate_last_match(STATE, CallFrame* calling_environment);
+
+ // Ruby.primitive :regexp_set_block_last_match
+ static Object* set_block_last_match(STATE, CallFrame* calling_environment);
+
void make_managed(STATE);
class Info : public TypeInfo {
@@ -93,6 +108,12 @@ namespace rubinius {
attr_accessor(full, Tuple);
attr_accessor(region, Tuple);
+ String* matched_string(STATE);
+ String* pre_matched(STATE);
+ String* post_matched(STATE);
+ Object* last_capture(STATE);
+ Object* nth_capture(STATE, size_t which);
+
/* interface */
class Info : public TypeInfo {
View
4 vm/builtin/variable_scope.hpp
@@ -27,6 +27,7 @@ namespace rubinius {
Module* module_; // slot
VariableScope* parent_; // slot
Tuple* heap_locals_; // slot
+ Object* last_match_; // slot
public:
Object* self_; // slot
@@ -43,6 +44,7 @@ namespace rubinius {
attr_accessor(parent, VariableScope);
attr_accessor(self, Object);
attr_accessor(heap_locals, Tuple);
+ attr_accessor(last_match, Object);
static void init(STATE);
static void bootstrap_methods(STATE);
@@ -69,6 +71,7 @@ namespace rubinius {
* Initialize scope for methods.
*/
void prepare(Object* self, Module* mod, Object* block, CompiledMethod* method, int num) {
+ rubinius::abort();
init_header(UnspecifiedZone, InvalidType);
klass_ = 0;
ivars_ = 0;
@@ -134,7 +137,6 @@ namespace rubinius {
// Ruby.primitive :variable_scope_method_visibility
Object* method_visibility(STATE);
-
public: // Rubinius Type stuff
class Info : public TypeInfo {
public:
View
154 vm/codegen/field_extract.rb
@@ -224,6 +224,80 @@ def generate_jit_stub
str << "}\n\n"
end
+
+ def generate_invoke_stub
+ return if @raw or @pass_arguments
+
+ str = ""
+ if arg_types.empty?
+ arg_list = ""
+ else
+ list = []
+ arg_types.size.times { |i| list << "Object* ra#{i}" }
+ arg_list = ", " + list.join(", ")
+ end
+
+ str << "extern \"C\" Object* invoke_#{@name}(STATE, CallFrame* call_frame, Object** args, int arg_count) {\n"
+
+ str << " Object* ret;\n"
+
+ i = 0
+ arg_types.each do |t|
+ str << " #{t}* a#{i};\n"
+ i += 1
+ end
+
+ arity = arg_types.size + 1
+
+ if @type == "Object"
+ str << " Object* self;\n"
+ else
+ str << " #{@type}* self;\n"
+ end
+
+ str << " if(arg_count != #{arity}) goto fail;\n"
+
+ if @type == "Object"
+ str << " self = args[arg_count-1];\n"
+ else
+ str << " self = try_as<#{@type}>(args[arg_count-1]);\n"
+ str << " if(unlikely(self == NULL)) goto fail;\n"
+ end
+
+ args = []
+ i = 0
+ arg_types.each do |t|
+ emit_fail = true
+ str << " a#{i} = try_as<#{t}>(args[#{i + 1}]);\n"
+ str << " if(unlikely(a#{i} == NULL)) goto fail;\n"
+ args << "a#{i}"
+ i += 1
+ end
+
+ args.unshift "recv" if @pass_self
+ args.unshift "state" if @pass_state
+ args.push "call_frame" if @pass_call_frame
+
+ str << "\n"
+ str << " try {\n"
+ str << " ret = self->#{@cpp_name}(#{args.join(', ')});\n"
+ str << " } catch(const RubyException& exc) {\n"
+ str << " exc.exception->locations(state,\n"
+ str << " System::vm_backtrace(state, Fixnum::from(0), call_frame));\n"
+ str << " state->thread_state()->raise_exception(exc.exception);\n"
+ str << " return NULL;\n"
+ str << " }\n"
+ str << "\n"
+ str << " if(unlikely(ret == reinterpret_cast<Object*>(kPrimitiveFailed)))\n"
+ str << " goto fail;\n\n"
+ str << " return ret;\n"
+
+ str << "fail:\n"
+ str << " Exception::internal_error(state, call_frame, \"invoked primitive failed\");"
+ str << " return 0;\n"
+
+ str << "}\n\n"
+ end
end
class CPPStaticPrimitive < CPPPrimitive
@@ -253,7 +327,7 @@ def generate_glue
end
def generate_jit_stub
- return if @raw or @pass_call_frame or @pass_message
+ return if @raw or @pass_call_frame or @pass_arguments
# Default value, overriden below
@can_fail = true
@@ -327,6 +401,65 @@ def generate_jit_stub
str << "}\n\n"
end
+ def generate_invoke_stub
+ return if @raw or @pass_arguments
+
+ str = ""
+ if arg_types.empty?
+ arg_list = ""
+ else
+ list = []
+ arg_types.size.times { |i| list << "Object* ra#{i}" }
+ arg_list = ", " + list.join(", ")
+ end
+
+ str << "extern \"C\" Object* invoke_#{@name}(STATE, CallFrame* call_frame, Object** args, int arg_count) {\n"
+
+ str << " Object* ret;\n"
+
+ i = 0
+ arg_types.each do |t|
+ str << " #{t}* a#{i};\n"
+ i += 1
+ end
+
+ arity = arg_types.size
+ arity += 1 if @pass_self
+ str << " if(arg_count != #{arity}) goto fail;\n"
+
+ args = []
+ i = 0
+ arg_types.each do |t|
+ str << " a#{i} = try_as<#{t}>(args[#{i}]);\n"
+ str << " if(unlikely(a#{i} == NULL)) goto fail;\n"
+ args << "a#{i}"
+ i += 1
+ end
+
+ args.unshift "args[arg_count-1]" if @pass_self
+ args.unshift "state" if @pass_state
+ args.push "call_frame" if @pass_call_frame
+
+ str << "\n"
+ str << " try {\n"
+ str << " ret = #{@type}::#{@cpp_name}(#{args.join(', ')});\n"
+ str << " } catch(const RubyException& exc) {\n"
+ str << " exc.exception->locations(state,\n"
+ str << " System::vm_backtrace(state, Fixnum::from(0), call_frame));\n"
+ str << " state->thread_state()->raise_exception(exc.exception);\n"
+ str << " return NULL;\n"
+ str << " }\n"
+ str << "\n"
+ str << " if(unlikely(ret == reinterpret_cast<Object*>(kPrimitiveFailed)))\n"
+ str << " goto fail;\n\n"
+ str << " return ret;\n"
+
+ str << "fail:\n"
+ str << " Exception::internal_error(state, call_frame, \"invoked primitive failed\");"
+ str << " return 0;\n"
+
+ str << "}\n\n"
+ end
end
class CPPOverloadedPrimitive < BasicPrimitive
@@ -1056,6 +1189,8 @@ def write_if_new(path)
write_if_new "vm/gen/primitives_glue.gen.cpp" do |f|
names = []
jit_stubs = []
+ invoke_stubs = []
+
parser.classes.sort_by { |name,| name }.each do |n, cpp|
cpp.primitives.sort_by { |name,| name }.each do |pn, prim|
names << pn
@@ -1067,6 +1202,11 @@ def write_if_new(path)
jit_stubs << [prim, pn]
end
+ if prim.respond_to?(:generate_invoke_stub) and is = prim.generate_invoke_stub
+ f << is
+ invoke_stubs << [prim, pn]
+ end
+
end
f << cpp.generate_accessors
@@ -1119,4 +1259,16 @@ def write_if_new(path)
f.puts " return false;"
f.puts "}"
+
+ f.puts "InvokePrimitive Primitives::get_invoke_stub(STATE, Symbol* name) {"
+ invoke_stubs.each do |prim, name|
+ f.puts <<-EOF
+ if(name == state->symbol("#{name}")) {
+ return &invoke_#{name};
+ }
+ EOF
+ end
+
+ f.puts " return &invoke_unknown_primitive;"
+ f.puts "}"
end
View
2 vm/executor.hpp
@@ -14,6 +14,8 @@ namespace rubinius {
};
typedef Object* (*executor)(VM*, CallFrame*, Dispatch& msg, Arguments& args);
+
+ typedef Object* (*InvokePrimitive)(VM*, CallFrame*, Object**, int);
}
#endif
View
14 vm/instructions.def
@@ -1,4 +1,4 @@
-# vim: filetype=ruby
+# vim: filetype=instructions
# Definitions of the Rubinius VM instruction set.
#
@@ -2443,3 +2443,15 @@ instruction cast_multi_value() [ value -- value ]
}
}
end
+
+instruction invoke_primitive(literal args) [ +args -- value ]
+ flush_ip();
+ InvokePrimitive ip = reinterpret_cast<InvokePrimitive>(literal);
+
+ Object* ret = (*ip)(state, call_frame, stack_back_position(args), args);
+
+ stack_clear(args);
+
+ HANDLE_EXCEPTION(ret);
+ stack_push(ret);
+end
View
1 vm/llvm/jit_block.cpp
@@ -128,6 +128,7 @@ namespace jit {
"env.scope");
b().CreateStore(be_scope, get_field(vars, offset::vars_parent));
+ b().CreateStore(constant(Qnil, obj_type), get_field(vars, offset::vars_last_match));
nil_locals();
}
View
1 vm/llvm/jit_inline_method.cpp
@@ -134,6 +134,7 @@ namespace jit {
b().CreateStore(Constant::getNullValue(ls_->ptr_type("VariableScope")),
get_field(vars, offset::vars_parent));
+ b().CreateStore(constant(Qnil, obj_type), get_field(vars, offset::vars_last_match));
nil_locals();
}
View
2 vm/llvm/jit_method.cpp
@@ -357,6 +357,8 @@ namespace jit {
b().CreateStore(Constant::getNullValue(ls_->ptr_type("VariableScope")),
get_field(vars, offset::vars_parent));
+ b().CreateStore(constant(Qnil, obj_type), get_field(vars, offset::vars_last_match));
+
nil_locals();
}
View
24 vm/llvm/jit_visit.hpp
@@ -1221,6 +1221,30 @@ namespace rubinius {
}
}
+ void visit_invoke_primitive(opcode which, opcode args) {
+ InvokePrimitive invoker = reinterpret_cast<InvokePrimitive>(which);
+
+ Signature sig(ls_, "Object");
+ sig << "VM";
+ sig << "CallFrame";
+ sig << ObjArrayTy;
+ sig << ls_->Int32Ty;
+
+ Value* arg_ary = stack_objects(args);
+
+ Value* call_args[] = { vm_, call_frame_, arg_ary, cint(args) };
+
+ Value* ptr = b().CreateIntToPtr(
+ ConstantInt::get(ls_->IntPtrTy, reinterpret_cast<uintptr_t>(invoker)),
+ PointerType::getUnqual(sig.type()));
+
+ Value* call = b().CreateCall(ptr, call_args, call_args + 4, "invoked_prim");
+
+ stack_remove(args);
+ check_for_exception(call);
+ stack_push(call);
+ }
+
void visit_send_stack(opcode which, opcode args) {
InlineCache* cache = reinterpret_cast<InlineCache*>(which);
View
3 vm/llvm/offset.hpp
@@ -23,7 +23,8 @@ namespace offset {
const static int vars_self = 2;
const static int vars_block = 3;
const static int vars_module = 4;
- const static int vars_tuple = 5;
+ const static int vars_last_match = 5;
+ const static int vars_tuple = 6;
const static int varscope_block = 1;
const static int varscope_method = 2;
View
2 vm/llvm/types.cpp.gen
@@ -166,6 +166,7 @@ StructTy_struct_rubinius__StackVariables_fields.push_back(PointerTy_13_fwd);
StructTy_struct_rubinius__StackVariables_fields.push_back(PointerTy_1);
StructTy_struct_rubinius__StackVariables_fields.push_back(PointerTy_1);
StructTy_struct_rubinius__StackVariables_fields.push_back(PointerTy_7);
+StructTy_struct_rubinius__StackVariables_fields.push_back(PointerTy_1);
StructTy_struct_rubinius__StackVariables_fields.push_back(ArrayTy_12);
StructType* StructTy_struct_rubinius__StackVariables = StructType::get(mod->getContext(), StructTy_struct_rubinius__StackVariables_fields, /*isPacked=*/false);
mod->addTypeName("struct.rubinius::StackVariables", StructTy_struct_rubinius__StackVariables);
@@ -264,6 +265,7 @@ StructTy_struct_rubinius__VariableScope_fields.push_back(PointerTy_7);
StructTy_struct_rubinius__VariableScope_fields.push_back(PointerTy_13_fwd);
StructTy_struct_rubinius__VariableScope_fields.push_back(PointerTy_11);
StructTy_struct_rubinius__VariableScope_fields.push_back(PointerTy_1);
+StructTy_struct_rubinius__VariableScope_fields.push_back(PointerTy_1);
StructTy_struct_rubinius__VariableScope_fields.push_back(IntegerType::get(getGlobalContext(), 32));
StructTy_struct_rubinius__VariableScope_fields.push_back(IntegerType::get(getGlobalContext(), 8));
StructTy_struct_rubinius__VariableScope_fields.push_back(PointerTy_0);
View
6 vm/primitives.cpp
@@ -42,5 +42,11 @@ namespace rubinius {
#endif
}
+extern "C" Object* invoke_unknown_primitive(STATE, CallFrame* call_frame,
+ Object** args, int arg_count)
+{
+ return Primitives::failure();
+}
+
#include "gen/primitives_glue.gen.cpp"
}
View
1 vm/primitives.hpp
@@ -88,6 +88,7 @@ namespace rubinius {
static executor resolve_primitive(STATE, Symbol* name, int* index = 0);
static Object* unknown_primitive(STATE, CallFrame* call_frame, Dispatch& msg, Arguments& args);
static bool get_jit_stub(int index, JITStubResults& res);
+ static InvokePrimitive get_invoke_stub(STATE, Symbol* name);
#include "gen/primitives_declare.hpp"
View
52 vm/stack_variables.cpp
@@ -5,8 +5,8 @@
namespace rubinius {
- VariableScope* StackVariables::create_heap_alias(STATE,
- CallFrame* call_frame, bool full)
+ VariableScope* StackVariables::create_heap_alias(STATE, CallFrame* call_frame,
+ bool full)
{
if(on_heap_) return on_heap_;
@@ -24,6 +24,7 @@ namespace rubinius {
scope->module(state, module_);
scope->method(state, call_frame->cm);
scope->heap_locals(state, Tuple::create(state, vmm->number_of_locals));
+ scope->last_match(state, last_match_);
scope->number_of_locals_ = vmm->number_of_locals;
@@ -42,6 +43,53 @@ namespace rubinius {
return scope;
}
+ void StackVariables::set_last_match(STATE, Object* obj) {
+ // For closures, get back to the top of the chain and set the
+ // last_match there. This means that the last_match is shared
+ // amongst all closures in a method, but thats how it's implemented
+ // in ruby.
+ if(parent_) {
+ VariableScope* scope = parent_;
+ while(RTEST(scope->parent())) {
+ scope = scope->parent();
+ }
+
+ return scope->last_match(state, obj);
+ }
+
+ // Use a heap alias if there is one.
+ if(on_heap_) {
+ on_heap_->last_match(state, obj);
+
+ // Otherwise, use the local one. This is where a last_match usually
+ // first appears.
+ } else {
+ last_match_ = obj;
+ }
+ }
+
+ Object* StackVariables::last_match(STATE) {
+ // For closures, get back to the top of the chain and get that
+ // last_match.
+ if(parent_) {
+ VariableScope* scope = parent_;
+ while(RTEST(scope->parent())) {
+ scope = scope->parent();
+ }
+
+ return scope->last_match();
+ }
+
+ // Otherwise, if this has a heap alias, get the last_match from there.
+ if(on_heap_) {
+ return on_heap_->last_match();
+
+ // Lastly, use the local one. This is where a last_match begins life.
+ } else {
+ return last_match_;
+ }
+ }
+
void StackVariables::flush_to_heap(STATE) {
if(!on_heap_) return;
View
6 vm/stack_variables.hpp
@@ -13,6 +13,7 @@ namespace rubinius {
Object* self_;
Object* block_;
Module* module_;
+ Object* last_match_;
Object* locals_[];
public:
@@ -22,6 +23,7 @@ namespace rubinius {
self_ = self;
block_ = block;
module_ = module;
+ last_match_ = Qnil;
for(int i = 0; i < locals; i++) {
locals_[i] = Qnil;
@@ -64,6 +66,10 @@ namespace rubinius {
locals_[which] = val;
}
+ void set_last_match(STATE, Object* obj);
+
+ Object* last_match(STATE);
+
VariableScope* create_heap_alias(STATE, CallFrame* call_frame, bool full=true);
void flush_to_heap(STATE);
View
11 vm/vmmethod.cpp
@@ -224,6 +224,17 @@ namespace rubinius {
for(size_t ip = 0; ip < total;) {
opcode op = opcodes[ip];
switch(op) {
+ case InstructionSequence::insn_invoke_primitive: {
+ Symbol* name = try_as<Symbol>(original->literals()->at(opcodes[ip + 1]));
+ if(!name) {
+ name = state->symbol("__unknown__");
+ }
+
+ InvokePrimitive invoker = Primitives::get_invoke_stub(state, name);
+ opcodes[ip + 1] = reinterpret_cast<intptr_t>(invoker);
+ update_addresses(ip, 1);
+ break;
+ }
case InstructionSequence::insn_allow_private:
allow_private = true;
break;

0 comments on commit 458a558

Please sign in to comment.