Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1864 lines
63 KiB
C#

using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Collections.Generic;
namespace System.Text.RegularExpressions {
//
// Compiler which generates IL bytecode to perform the matching instead of
// interpreting a program.
// For simplicity, we inherit from RxCompiler, and generate the IL code based
// on the program generated by it. This also allows us to fallback to interpretation
// if we can't handle something.
// This is net 2.0, since 1.0 doesn't support DynamicMethods
// FIXME: Add support for 1.0, and CompileToAssembly
// FIXME: Overwrite RxCompiler methods so we don't have to decode char
// matching opcodes
//
class CILCompiler : RxCompiler, ICompiler {
DynamicMethod[] eval_methods;
bool[] eval_methods_defined;
/*
* To avoid the overhead of decoding the countless opcode variants created
* by RxCompiler, we save the original, 'generic' version and its flags
* in these two tables.
*/
private Dictionary<int, int> generic_ops;
private Dictionary<int, int> op_flags;
private Dictionary<int, Label> labels;
static FieldInfo fi_str = typeof (RxInterpreter).GetField ("str", BindingFlags.Instance|BindingFlags.NonPublic);
static FieldInfo fi_string_start = typeof (RxInterpreter).GetField ("string_start", BindingFlags.Instance|BindingFlags.NonPublic);
static FieldInfo fi_string_end = typeof (RxInterpreter).GetField ("string_end", BindingFlags.Instance|BindingFlags.NonPublic);
static FieldInfo fi_program = typeof (RxInterpreter).GetField ("program", BindingFlags.Instance|BindingFlags.NonPublic);
static FieldInfo fi_marks = typeof (RxInterpreter).GetField ("marks", BindingFlags.Instance|BindingFlags.NonPublic);
static FieldInfo fi_groups = typeof (RxInterpreter).GetField ("groups", BindingFlags.Instance|BindingFlags.NonPublic);
static FieldInfo fi_deep = typeof (RxInterpreter).GetField ("deep", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
static FieldInfo fi_stack = typeof (RxInterpreter).GetField ("stack", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
static FieldInfo fi_mark_start = typeof (Mark).GetField ("Start", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
static FieldInfo fi_mark_end = typeof (Mark).GetField ("End", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
//static FieldInfo fi_mark_index = typeof (Mark).GetField ("Index", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
static MethodInfo mi_stack_get_count, mi_stack_set_count, mi_stack_push, mi_stack_pop;
static MethodInfo mi_set_start_of_match, mi_is_word_char, mi_reset_groups;
static MethodInfo mi_checkpoint, mi_backtrack, mi_open, mi_close;
static MethodInfo mi_get_last_defined, mi_mark_get_index, mi_mark_get_length;
public static readonly bool trace_compile = Environment.GetEnvironmentVariable ("MONO_TRACE_RX_COMPILE") != null;
public CILCompiler () {
generic_ops = new Dictionary <int, int> ();
op_flags = new Dictionary <int, int> ();
}
IMachineFactory ICompiler.GetMachineFactory () {
byte[] code = new byte [curpos];
Buffer.BlockCopy (program, 0, code, 0, curpos);
eval_methods = new DynamicMethod [code.Length];
eval_methods_defined = new bool [code.Length];
// The main eval method
DynamicMethod main = GetEvalMethod (code, 11);
if (main != null)
return new RxInterpreterFactory (code, (EvalDelegate)main.CreateDelegate (typeof (EvalDelegate)));
else
return new RxInterpreterFactory (code, null);
}
DynamicMethod GetEvalMethod (byte[] program, int pc) {
if (eval_methods_defined [pc])
return eval_methods [pc];
// FIXME: Recursion ?
eval_methods_defined [pc] = true;
eval_methods [pc] = CreateEvalMethod (program, pc);
return eval_methods [pc];
}
private MethodInfo GetMethod (Type t, string name, ref MethodInfo cached) {
if (cached == null) {
cached = t.GetMethod (name, BindingFlags.Static|BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
if (cached == null)
throw new Exception ("Method not found: " + name);
}
return cached;
}
private MethodInfo GetMethod (string name, ref MethodInfo cached) {
return GetMethod (typeof (RxInterpreter), name, ref cached);
}
private int ReadInt (byte[] code, int pc) {
int val = code [pc];
val |= code [pc + 1] << 8;
val |= code [pc + 2] << 16;
val |= code [pc + 3] << 24;
return val;
}
static OpFlags MakeFlags (bool negate, bool ignore, bool reverse, bool lazy) {
OpFlags flags = 0;
if (negate) flags |= OpFlags.Negate;
if (ignore) flags |= OpFlags.IgnoreCase;
if (reverse) flags |= OpFlags.RightToLeft;
if (lazy) flags |= OpFlags.Lazy;
return flags;
}
void EmitGenericOp (RxOp op, bool negate, bool ignore, bool reverse, bool lazy) {
generic_ops [curpos] = (int)op;
op_flags [curpos] = (int)MakeFlags (negate, ignore, reverse, false);
}
public override void EmitOp (RxOp op, bool negate, bool ignore, bool reverse) {
EmitGenericOp (op, negate, ignore, reverse, false);
base.EmitOp (op, negate, ignore, reverse);
}
public override void EmitOpIgnoreReverse (RxOp op, bool ignore, bool reverse) {
EmitGenericOp (op, false, ignore, reverse, false);
base.EmitOpIgnoreReverse (op, ignore, reverse);
}
public override void EmitOpNegateReverse (RxOp op, bool negate, bool reverse) {
EmitGenericOp (op, negate, false, reverse, false);
base.EmitOpNegateReverse (op, negate, reverse);
}
class Frame {
public Label label_pass, label_fail;
public Frame (ILGenerator ilgen) {
label_fail = ilgen.DefineLabel ();
label_pass = ilgen.DefineLabel ();
}
}
LocalBuilder local_textinfo;
/*
* Create a dynamic method which is equivalent to the RxInterpreter.EvalByteCode
* method specialized to the given program and a given pc. Return the newly
* created method or null if a not-supported opcode was encountered.
*/
DynamicMethod CreateEvalMethod (byte[] program, int pc) {
DynamicMethod m = new DynamicMethod ("Eval_" + pc, typeof (bool), new Type [] { typeof (RxInterpreter), typeof (int), typeof (int).MakeByRefType () }, typeof (RxInterpreter), true);
ILGenerator ilgen = m.GetILGenerator ();
/*
Args:
interp - 0
strpos - 1
strpos_result - 2
*/
/*
* Recursive calls to EvalByteCode are inlined manually by calling
* EmitEvalMethodBody with the pc of the recursive call. Frame objects hold
* the information required to link together the code generated by the recursive
* call with the rest of the code.
*/
Frame frame = new Frame (ilgen);
/* Cache the textinfo used by Char.ToLower () */
local_textinfo = ilgen.DeclareLocal (typeof (TextInfo));
ilgen.Emit (OpCodes.Call, typeof (Thread).GetMethod ("get_CurrentThread"));
ilgen.Emit (OpCodes.Call, typeof (Thread).GetMethod ("get_CurrentCulture"));
ilgen.Emit (OpCodes.Call, typeof (CultureInfo).GetMethod ("get_TextInfo"));
ilgen.Emit (OpCodes.Stloc, local_textinfo);
m = EmitEvalMethodBody (m, ilgen, frame, program, pc, program.Length, false, false, out pc);
if (m == null)
return null;
ilgen.MarkLabel (frame.label_pass);
ilgen.Emit (OpCodes.Ldarg_2);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stind_I4);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Ret);
ilgen.MarkLabel (frame.label_fail);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ret);
return m;
}
private int ReadShort (byte[] program, int pc) {
return (int)program [pc] | ((int)program [pc + 1] << 8);
}
private Label CreateLabelForPC (ILGenerator ilgen, int pc) {
if (labels == null)
labels = new Dictionary <int, Label> ();
Label l;
if (!labels.TryGetValue (pc, out l)) {
l = ilgen.DefineLabel ();
labels [pc] = l;
}
return l;
}
private int GetILOffset (ILGenerator ilgen) {
return (int)typeof (ILGenerator).GetField ("code_len", BindingFlags.Instance|BindingFlags.NonPublic).GetValue (ilgen);
}
/*
* Emit IL code for a sequence of opcodes between pc and end_pc. If there is a
* match, set strpos (Arg 1) to the position after the match, then
* branch to frame.label_pass. Otherwise branch to frame.label_fail,
* and leave strpos at an undefined position. The caller should
* generate code to save the original value of strpos if it needs it.
* If one_op is true, only generate code for one opcode and set out_pc
* to the next pc after the opcode.
* If no_bump is true, don't bump strpos in char matching opcodes.
* Keep this in synch with RxInterpreter.EvalByteCode (). It it is sync with
* the version in r111969.
* FIXME: Modify the regex tests so they are run with RegexOptions.Compiled as
* well.
*/
private DynamicMethod EmitEvalMethodBody (DynamicMethod m, ILGenerator ilgen,
Frame frame, byte[] program,
int pc, int end_pc,
bool one_op, bool no_bump,
out int out_pc)
{
int start, length, end;
out_pc = 0;
int group_count = 1 + ReadShort (program, 1);
while (pc < end_pc) {
RxOp op = (RxOp)program [pc];
// FIXME: Optimize this
if (generic_ops.ContainsKey (pc))
op = (RxOp)generic_ops [pc];
if (trace_compile) {
Console.WriteLine ("compiling {0} pc={1} end_pc={2}, il_offset=0x{3:x}", op, pc, end_pc, GetILOffset (ilgen));
}
if (labels != null) {
Label l;
if (labels.TryGetValue (pc, out l)) {
ilgen.MarkLabel (l);
labels.Remove (pc);
}
}
if (RxInterpreter.trace_rx) {
//Console.WriteLine ("evaluating: {0} at pc: {1}, strpos: {2}", op, pc, strpos);
ilgen.Emit (OpCodes.Ldstr, "evaluating: {0} at pc: {1}, strpos: {2}");
ilgen.Emit (OpCodes.Ldc_I4, (int)op);
ilgen.Emit (OpCodes.Box, typeof (RxOp));
ilgen.Emit (OpCodes.Ldc_I4, pc);
ilgen.Emit (OpCodes.Box, typeof (int));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Box, typeof (int));
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object), typeof (object), typeof (object) }));
}
switch (op) {
case RxOp.Anchor:
case RxOp.AnchorReverse: {
bool reverse = (RxOp)program [pc] == RxOp.AnchorReverse;
length = ReadShort (program, pc + 3);
pc += ReadShort (program, pc + 1);
// Optimize some common cases by inlining the code generated for the
// anchor body
RxOp anch_op = (RxOp)program [pc];
// FIXME: Do this even if the archor op is not the last in the regex
if (!reverse && group_count == 1 && anch_op == RxOp.Char && (RxOp)program [pc + 2] == RxOp.True) {
/*
* while (strpos < string_end) {
* if (str [strpos] == program [pc + 1]) {
* match_start = strpos;
* strpos_result = strpos + 1;
* marks [groups [0]].Start = strpos;
* if (groups.Length > 1)
* marks [groups [0]].End = res;
* return true;
* }
* strpos ++;
* }
* return false;
*/
// Add some locals to avoid an indirection
LocalBuilder local_string_end = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Stloc, local_string_end);
LocalBuilder local_str = ilgen.DeclareLocal (typeof (string));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Stloc, local_str);
//while (strpos < string_end) {
// -> Done at the end of the loop like mcs does
Label l1 = ilgen.DefineLabel ();
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l2);
ilgen.MarkLabel (l1);
// if (str [strpos] == program [pc + 1]) {
Label l3 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldloc, local_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Conv_I4);
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
ilgen.Emit (OpCodes.Beq, l3);
// The true case is done after the loop
// }
// strpos++;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
//}
ilgen.MarkLabel (l2);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldloc, local_string_end);
ilgen.Emit (OpCodes.Blt, l1);
//return false;
ilgen.Emit (OpCodes.Br, frame.label_fail);
// True case
ilgen.MarkLabel (l3);
// call SetStartOfMatch (strpos)
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter), "SetStartOfMatch", ref mi_set_start_of_match));
// strpos++;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
// return true;
ilgen.Emit (OpCodes.Br, frame.label_pass);
} else {
// General case
//Console.WriteLine ("Anchor op " + anch_op);
// Add some locals to avoid an indirection
LocalBuilder local_string_end = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, local_string_end);
//while (strpos < string_end + 1) {
// -> Done at the end of the loop like mcs does
Label l1 = ilgen.DefineLabel ();
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l2);
ilgen.MarkLabel (l1);
//if (groups.Length > 1) {
// ResetGroups ();
// marks [groups [0]].Start = strpos;
//}
if (group_count > 1) {
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Call, GetMethod ("ResetGroups", ref mi_reset_groups));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_marks);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_groups);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ldelem_I4);
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stfld, fi_mark_start);
}
// if (EvalByteCode (pc, strpos, ref res)) {
Frame new_frame = new Frame (ilgen);
// old_stros = strpos;
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc, end_pc, false, false, out out_pc);
if (m == null)
return null;
// Pass
ilgen.MarkLabel (new_frame.label_pass);
// marks [groups [0]].Start = old_strpos;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_marks);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_groups);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ldelem_I4);
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Stfld, fi_mark_start);
// if (groups.Length > 1)
// marks [groups [0]].End = res;
if (group_count > 1) {
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_marks);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_groups);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ldelem_I4);
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stfld, fi_mark_end);
}
// return true;
ilgen.Emit (OpCodes.Br, frame.label_pass);
// Fail
ilgen.MarkLabel (new_frame.label_fail);
// strpos = old_strpos +/- 1;
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Ldc_I4_1);
if (reverse)
ilgen.Emit (OpCodes.Sub);
else
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
//}
ilgen.MarkLabel (l2);
if (reverse) {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Bge, l1);
} else {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldloc, local_string_end);
ilgen.Emit (OpCodes.Blt, l1);
}
//return false;
ilgen.Emit (OpCodes.Br, frame.label_fail);
}
goto End;
}
case RxOp.Branch: {
//if (EvalByteCode (pc + 3, strpos, ref res)) {
int target_pc = pc + ReadShort (program, pc + 1);
// Emit the rest of the code inline instead of making a recursive call
Frame new_frame = new Frame (ilgen);
// old_strpos = strpos;
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 3, target_pc, false, false, out out_pc);
if (m == null)
return null;
// Pass
ilgen.MarkLabel (new_frame.label_pass);
// return true;
ilgen.Emit (OpCodes.Br, frame.label_pass);
// Fail
ilgen.MarkLabel (new_frame.label_fail);
// strpos = old_strpos;
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Starg, 1);
pc = target_pc;
break;
}
case RxOp.Char:
case RxOp.UnicodeChar:
case RxOp.Range:
case RxOp.UnicodeRange: {
OpFlags flags = (OpFlags)op_flags [pc];
bool negate = (flags & OpFlags.Negate) > 0;
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
bool reverse = (flags & OpFlags.RightToLeft) > 0;
//if (strpos < string_end) {
Label l1 = ilgen.DefineLabel ();
if (reverse) {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ble, l1);
} else {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bge, l1);
}
if (ignore)
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
// int c = str [strpos];
LocalBuilder local_c = ilgen.DeclareLocal (typeof (char));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
if (reverse) {
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
}
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
if (ignore)
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
if (op == RxOp.Char) {
ilgen.Emit (OpCodes.Conv_I4);
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
ilgen.Emit (negate ? OpCodes.Beq : OpCodes.Bne_Un, l1);
pc += 2;
} else if (op == RxOp.UnicodeChar) {
ilgen.Emit (OpCodes.Conv_I4);
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
ilgen.Emit (negate ? OpCodes.Beq : OpCodes.Bne_Un, l1);
pc += 3;
} else if (op == RxOp.Range) {
ilgen.Emit (OpCodes.Stloc, local_c);
// if (c >= program [pc + 1] && c <= program [pc + 2]) {
if (negate) {
Label l3 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
ilgen.Emit (OpCodes.Blt, l3);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 2]);
ilgen.Emit (OpCodes.Bgt, l3);
ilgen.Emit (OpCodes.Br, l1);
ilgen.MarkLabel (l3);
} else {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
ilgen.Emit (OpCodes.Blt, l1);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 2]);
ilgen.Emit (OpCodes.Bgt, l1);
}
pc += 3;
} else if (op == RxOp.UnicodeRange) {
ilgen.Emit (OpCodes.Stloc, local_c);
// if (c >= program [pc + 1] && c <= program [pc + 2]) {
if (negate) {
Label l3 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
ilgen.Emit (OpCodes.Blt, l3);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 3));
ilgen.Emit (OpCodes.Bgt, l3);
ilgen.Emit (OpCodes.Br, l1);
ilgen.MarkLabel (l3);
} else {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
ilgen.Emit (OpCodes.Blt, l1);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 3));
ilgen.Emit (OpCodes.Bgt, l1);
}
pc += 5;
} else {
throw new NotSupportedException ();
}
//ilgen.EmitWriteLine ("HIT:" + (char)program [pc + 1]);
if (!no_bump) {
// strpos++ / strpos--;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
if (reverse)
ilgen.Emit (OpCodes.Sub);
else
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
}
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l2);
//}
ilgen.MarkLabel (l1);
//return false;
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l2);
break;
}
case RxOp.True: {
// return true;
ilgen.Emit (OpCodes.Br, frame.label_pass);
pc++;
break;
}
case RxOp.False: {
// return false;
ilgen.Emit (OpCodes.Br, frame.label_fail);
pc++;
break;
}
case RxOp.AnyPosition: {
pc++;
break;
}
case RxOp.StartOfString: {
//if (strpos != 0)
// return false;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Bgt, frame.label_fail);
pc++;
break;
}
case RxOp.StartOfLine: {
// FIXME: windows line endings
//if (!(strpos == 0 || str [strpos - 1] == '\n'))
// return false;
Label l = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Beq, l);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
ilgen.Emit (OpCodes.Beq, l);
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l);
pc++;
break;
}
case RxOp.StartOfScan: {
//if (strpos != string_start)
// return false;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_start);
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
pc++;
break;
}
case RxOp.End: {
//if (!(strpos == string_end || (strpos == string_end - 1 && str [strpos] == '\n')))
// return false;
Label l = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Beq, l);
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Bne_Un, l2);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
ilgen.Emit (OpCodes.Bne_Un, l2);
ilgen.Emit (OpCodes.Br, l);
ilgen.MarkLabel (l2);
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l);
pc++;
break;
}
case RxOp.EndOfString: {
//if (strpos != string_end)
// return false;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
pc++;
break;
}
case RxOp.EndOfLine: {
//if (!(strpos == string_end || str [strpos] == '\n'))
// return false;
Label l_match = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Beq, l_match);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
ilgen.Emit (OpCodes.Beq, l_match);
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l_match);
pc++;
break;
}
case RxOp.WordBoundary:
case RxOp.NoWordBoundary: {
bool negate = op == RxOp.NoWordBoundary;
//if (string_end == 0)
// return false;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Beq, frame.label_fail);
Label l_match = ilgen.DefineLabel ();
//if (strpos == 0) {
Label l1 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Bne_Un, l1);
//if (!IsWordChar (str [strpos])) {
// return false;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
ilgen.Emit (negate ? OpCodes.Brtrue : OpCodes.Brfalse, frame.label_fail);
ilgen.Emit (OpCodes.Br, l_match);
//} else if (strpos == string_end) {
ilgen.MarkLabel (l1);
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bne_Un, l2);
//if (!IsWordChar (str [strpos - 1])) {
// return false;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
ilgen.Emit (negate ? OpCodes.Brtrue : OpCodes.Brfalse, frame.label_fail);
ilgen.Emit (OpCodes.Br, l_match);
//} else {
ilgen.MarkLabel (l2);
//if (IsWordChar (str [strpos]) == IsWordChar (str [strpos - 1])) {
// return false;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Call, GetMethod ("IsWordChar", ref mi_is_word_char));
ilgen.Emit (negate ? OpCodes.Bne_Un : OpCodes.Beq, frame.label_fail);
ilgen.Emit (OpCodes.Br, l_match);
ilgen.MarkLabel (l_match);
pc++;
break;
}
case RxOp.Bitmap:
case RxOp.UnicodeBitmap: {
OpFlags flags = (OpFlags)op_flags [pc];
bool negate = (flags & OpFlags.Negate) > 0;
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
bool reverse = (flags & OpFlags.RightToLeft) > 0;
bool unicode = (op == RxOp.UnicodeBitmap);
//if (strpos < string_end) {
Label l1 = ilgen.DefineLabel ();
Label l2 = ilgen.DefineLabel ();
Label l_match = ilgen.DefineLabel ();
if (reverse) {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ble, l1);
} else {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bge, l1);
}
// int c = str [strpos];
LocalBuilder local_c = ilgen.DeclareLocal (typeof (int));
if (ignore)
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
if (reverse) {
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
}
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Conv_I4);
if (ignore)
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
// c -= program [pc + 1];
if (unicode) {
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Stloc, local_c);
length = ReadShort (program, pc + 3);
pc += 5;
} else {
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Stloc, local_c);
length = program [pc + 2];
pc += 3;
}
// if (c < 0 || c >= (length << 3))
// return false;
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Blt, negate ? l_match : frame.label_fail);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, length << 3);
ilgen.Emit (OpCodes.Bge, negate ? l_match : frame.label_fail);
// Optimized version for small bitmaps
if (length <= 4) {
uint bitmap = program [pc];
if (length > 1)
bitmap |= ((uint)program [pc + 1] << 8);
if (length > 2)
bitmap |= ((uint)program [pc + 2] << 16);
if (length > 3)
bitmap |= ((uint)program [pc + 3] << 24);
//if ((bitmap >> c) & 1)
ilgen.Emit (OpCodes.Ldc_I4, bitmap);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Shr_Un);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.And);
ilgen.Emit (negate ? OpCodes.Brtrue : OpCodes.Brfalse, l1);
} else {
// if ((program [pc + (c >> 3)] & (1 << (c & 0x7))) != 0) {
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_program);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4_3);
ilgen.Emit (OpCodes.Shr);
ilgen.Emit (OpCodes.Ldc_I4, pc);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Ldelem_I1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, 7);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Shl);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (negate ? OpCodes.Bne_Un : OpCodes.Beq, l1);
}
ilgen.MarkLabel (l_match);
if (!no_bump) {
// strpos++ / strpos--;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
if (reverse)
ilgen.Emit (OpCodes.Sub);
else
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
}
// continue;
ilgen.Emit (OpCodes.Br, l2);
// }
//}
//return false;
ilgen.MarkLabel (l1);
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l2);
pc += length;
break;
}
case RxOp.String:
case RxOp.UnicodeString: {
OpFlags flags = (OpFlags)op_flags [pc];
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
bool reverse = (flags & OpFlags.RightToLeft) > 0;
bool unicode = (op == RxOp.UnicodeString);
if (unicode) {
start = pc + 3;
length = ReadShort (program, pc + 1);
} else {
start = pc + 2;
length = program [pc + 1];
}
//if (strpos + length > string_end)
// return false;
if (reverse) {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4, length);
ilgen.Emit (OpCodes.Blt, frame.label_fail);
} else {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4, length);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bgt, frame.label_fail);
}
/* Avoid unsafe code in Moonlight build */
#if false && !NET_2_1
// FIXME:
if (reverse || unicode)
throw new NotImplementedException ();
int i;
LocalBuilder local_strptr = ilgen.DeclareLocal (typeof (char).MakePointerType ());
// char *strptr = &str.start_char + strpos
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldflda, typeof (String).GetField ("start_char", BindingFlags.Instance|BindingFlags.NonPublic));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Shl);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, local_strptr);
end = start + length;
for (i = 0; i < length; ++i) {
// if (*(strptr + i) != program [start + i])
// return false;
if (ignore)
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
ilgen.Emit (OpCodes.Ldloc, local_strptr);
ilgen.Emit (OpCodes.Ldc_I4, i * 2);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Ldind_I2);
if (ignore)
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Ldc_I4, (int)program [start + i]);
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
}
// strpos += length
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4, length);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
#else
// Allocate a local for 'str' to save an indirection
LocalBuilder local_str = ilgen.DeclareLocal (typeof (string));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Stloc, local_str);
if (reverse) {
// strpos -= length;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4, length);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Starg, 1);
}
// FIXME: Emit a loop for long strings
end = start + (unicode ? length * 2 : length);
while (start < end) {
//if (str [strpos] != program [start])
// return false;
if (ignore)
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
ilgen.Emit (OpCodes.Ldloc, local_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
if (ignore)
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Ldc_I4, unicode ? ReadShort (program, start) : (int)program [start]);
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
//strpos++;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
if (unicode)
start += 2;
else
start ++;
}
if (reverse) {
// strpos -= length;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4, length);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Starg, 1);
}
#endif
pc = end;
break;
}
case RxOp.OpenGroup: {
//Open (program [pc + 1] | (program [pc + 2] << 8), strpos);
int group_id = ReadShort (program, pc + 1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldc_I4, group_id);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Call, GetMethod ("Open", ref mi_open));
pc += 3;
break;
}
case RxOp.CloseGroup: {
//Close (program [pc + 1] | (program [pc + 2] << 8), strpos);
int group_id = ReadShort (program, pc + 1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldc_I4, group_id);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Call, GetMethod ("Close", ref mi_close));
pc += 3;
break;
}
case RxOp.Jump: {
int target_pc = pc + ReadShort (program, pc + 1);
if (target_pc > end_pc)
/*
* This breaks the our code generation logic, see
* https://bugzilla.novell.com/show_bug.cgi?id=466151
* for an example.
*/
return null;
if (trace_compile)
Console.WriteLine ("\tjump target: {0}", target_pc);
if (labels == null)
labels = new Dictionary <int, Label> ();
Label l = CreateLabelForPC (ilgen, target_pc);
ilgen.Emit (OpCodes.Br, l);
pc += 3;
break;
}
case RxOp.Test: {
int target1 = pc + ReadShort (program, pc + 1);
int target2 = pc + ReadShort (program, pc + 3);
if (trace_compile)
Console.WriteLine ("\temitting <test_expr>");
// old_stros = strpos;
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
Frame new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 5, target1 < target2 ? target1 : target2, false, false, out pc);
if (m == null)
return null;
if (trace_compile) {
Console.WriteLine ("\temitted <test_expr>");
Console.WriteLine ("\ttarget1 = {0}", target1);
Console.WriteLine ("\ttarget2 = {0}", target2);
}
Label l1 = CreateLabelForPC (ilgen, target1);
Label l2 = CreateLabelForPC (ilgen, target2);
// Pass
ilgen.MarkLabel (new_frame.label_pass);
// strpos = old_strpos;
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Starg, 1);
ilgen.Emit (OpCodes.Br, l1);
// Fail
ilgen.MarkLabel (new_frame.label_fail);
// strpos = old_strpos;
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Starg, 1);
ilgen.Emit (OpCodes.Br, l2);
// Continue at pc, which should equal to target1
break;
}
case RxOp.SubExpression: {
int target = pc + ReadShort (program, pc + 1);
if (trace_compile)
Console.WriteLine ("\temitting <sub_expr>");
// old_stros = strpos;
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
Frame new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 3, target, false, false, out pc);
if (m == null)
return null;
if (trace_compile) {
Console.WriteLine ("\temitted <sub_expr>");
Console.WriteLine ("\ttarget = {0}", target);
}
Label l1 = CreateLabelForPC (ilgen, target);
// Pass
ilgen.MarkLabel (new_frame.label_pass);
ilgen.Emit (OpCodes.Br, l1);
// Fail
ilgen.MarkLabel (new_frame.label_fail);
// strpos = old_strpos;
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Starg, 1);
ilgen.Emit (OpCodes.Br, frame.label_fail);
// Continue at pc, which should equal to target
break;
}
case RxOp.TestCharGroup: {
int char_group_end = pc + ReadShort (program, pc + 1);
pc += 3;
Label label_match = ilgen.DefineLabel ();
/* Determine the negate/reverse flags by examining the first op */
OpFlags flags = (OpFlags)op_flags [pc];
/* Determine whenever this is a negated character class */
/* If it is, then the conditions are ANDed together, not ORed */
bool revert = (flags & OpFlags.Negate) > 0;
bool reverse = (flags & OpFlags.RightToLeft) > 0;
/*
* Generate code for all the matching ops in the group
*/
while (pc < char_group_end) {
Frame new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc, Int32.MaxValue, true, true, out pc);
if (m == null)
return null;
if (!revert) {
// Pass
ilgen.MarkLabel (new_frame.label_pass);
ilgen.Emit (OpCodes.Br, label_match);
// Fail
// Just fall through to the next test
ilgen.MarkLabel (new_frame.label_fail);
} else {
// Pass
// Just fall through to the next test
ilgen.MarkLabel (new_frame.label_pass);
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l2);
// Fail
// Fail completely
ilgen.MarkLabel (new_frame.label_fail);
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l2);
}
}
if (revert) {
/* Success */
ilgen.Emit (OpCodes.Br, label_match);
} else {
// If we reached here, all the matching ops have failed
ilgen.Emit (OpCodes.Br, frame.label_fail);
}
ilgen.MarkLabel (label_match);
// strpos++ / strpos--;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
if (reverse)
ilgen.Emit (OpCodes.Sub);
else
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
break;
}
case RxOp.FastRepeat:
case RxOp.FastRepeatLazy: {
/*
* A FastRepeat is a simplified version of Repeat which does
* not contain another repeat inside, so backtracking is
* easier.
* FIXME: Implement faster backtracking versions for
* simple inner exceptions like chars/strings.
*/
bool lazy = program [pc] == (byte)RxOp.FastRepeatLazy;
int tail = pc + ReadShort (program, pc + 1);
start = ReadInt (program, pc + 3);
end = ReadInt (program, pc + 7);
//Console.WriteLine ("min: {0}, max: {1} tail: {2}", start, end, tail);
// deep = null;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldnull);
ilgen.Emit (OpCodes.Stfld, fi_deep);
LocalBuilder local_length = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Stloc, local_length);
LocalBuilder local_old_strpos = ilgen.DeclareLocal (typeof (int));
// First match at least 'start' items
if (start > 0) {
//for (length = 0; length < start; ++length) {
Label l_loop_footer = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l_loop_footer);
Label l_loop_body = ilgen.DefineLabel ();
ilgen.MarkLabel (l_loop_body);
// int old_strpos = strpos;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
// if (!EvalByteCode (pc + 11, strpos, ref res))
Frame new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 11, tail, false, false, out out_pc);
if (m == null)
return null;
// Fail
// return false;
ilgen.MarkLabel (new_frame.label_fail);
ilgen.Emit (OpCodes.Br, frame.label_fail);
// Pass
ilgen.MarkLabel (new_frame.label_pass);
// length++
ilgen.Emit (OpCodes.Ldloc, local_length);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, local_length);
// Loop footer
ilgen.MarkLabel (l_loop_footer);
ilgen.Emit (OpCodes.Ldloc, local_length);
ilgen.Emit (OpCodes.Ldc_I4, start);
ilgen.Emit (OpCodes.Blt, l_loop_body);
}
if (lazy) {
Label l_loop_footer = ilgen.DefineLabel ();
//while (true) {
ilgen.Emit (OpCodes.Br, l_loop_footer);
Label l_loop_body = ilgen.DefineLabel ();
ilgen.MarkLabel (l_loop_body);
// Match the tail
// int cp = Checkpoint ();
LocalBuilder local_cp = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Call, GetMethod ("Checkpoint", ref mi_checkpoint));
ilgen.Emit (OpCodes.Stloc, local_cp);
// int old_strpos = strpos;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
// if (EvalByteCode (tail, strpos, ref res)) {
Frame new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, tail, end_pc, false, false, out out_pc);
if (m == null)
return null;
// Success:
ilgen.MarkLabel (new_frame.label_pass);
// return true;
ilgen.Emit (OpCodes.Br, frame.label_pass);
// Fail:
ilgen.MarkLabel (new_frame.label_fail);
// Backtrack (cp);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldloc, local_cp);
ilgen.Emit (OpCodes.Call, GetMethod ("Backtrack", ref mi_backtrack));
// strpos = old_strpos;
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Starg, 1);
//if (length >= end)
// return false;
ilgen.Emit (OpCodes.Ldloc, local_length);
ilgen.Emit (OpCodes.Ldc_I4, end);
ilgen.Emit (OpCodes.Bge, frame.label_fail);
// Match an item
//if (!EvalByteCode (pc + 11, strpos, ref res))
new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 11, tail, false, false, out out_pc);
if (m == null)
return null;
// Success:
ilgen.MarkLabel (new_frame.label_pass);
// length ++;
ilgen.Emit (OpCodes.Ldloc, local_length);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, local_length);
ilgen.Emit (OpCodes.Br, l_loop_body);
// Fail:
ilgen.MarkLabel (new_frame.label_fail);
// return false;
ilgen.Emit (OpCodes.Br, frame.label_fail);
// Loop footer
ilgen.MarkLabel (l_loop_footer);
ilgen.Emit (OpCodes.Br, l_loop_body);
} else {
// Then match as many items as possible, recording
// backtracking information
//int old_stack_size = stack.Count;
LocalBuilder local_old_stack_size = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "get_Count", ref mi_stack_get_count));
ilgen.Emit (OpCodes.Stloc, local_old_stack_size);
//while (length < end) {
Label l_loop_footer = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l_loop_footer);
Label l_loop_body = ilgen.DefineLabel ();
ilgen.MarkLabel (l_loop_body);
// int cp = Checkpoint ();
LocalBuilder local_cp = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Call, GetMethod ("Checkpoint", ref mi_checkpoint));
ilgen.Emit (OpCodes.Stloc, local_cp);
// int old_strpos = strpos;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Stloc, local_old_strpos);
// if (!EvalByteCode (pc + 11, strpos, ref res)) {
Frame new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, pc + 11, tail, false, false, out out_pc);
if (m == null)
return null;
// Fail:
ilgen.MarkLabel (new_frame.label_fail);
// strpos = old_strpos
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Starg, 1);
// Backtrack (cp);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldloc, local_cp);
ilgen.Emit (OpCodes.Call, GetMethod ("Backtrack", ref mi_backtrack));
// break;
Label l_after_loop = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l_after_loop);
// Success:
ilgen.MarkLabel (new_frame.label_pass);
//stack.Push (cp);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Ldloc, local_cp);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Push", ref mi_stack_push));
//stack.Push (strpos);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Ldloc, local_old_strpos);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Push", ref mi_stack_push));
// length++
ilgen.Emit (OpCodes.Ldloc, local_length);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, local_length);
// Loop footer
ilgen.MarkLabel (l_loop_footer);
ilgen.Emit (OpCodes.Ldloc, local_length);
ilgen.Emit (OpCodes.Ldc_I4, end);
ilgen.Emit (OpCodes.Blt, l_loop_body);
ilgen.MarkLabel (l_after_loop);
// Then, match the tail, backtracking as necessary.
//while (true) {
l_loop_footer = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l_loop_footer);
l_loop_body = ilgen.DefineLabel ();
ilgen.MarkLabel (l_loop_body);
if (RxInterpreter.trace_rx) {
ilgen.Emit (OpCodes.Ldstr, "matching tail at: {0}");
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Box, typeof (int));
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
}
// if (EvalByteCode (tail, strpos, ref res)) {
new_frame = new Frame (ilgen);
m = EmitEvalMethodBody (m, ilgen, new_frame, program, tail, end_pc, false, false, out out_pc);
if (m == null)
return null;
// Success:
ilgen.MarkLabel (new_frame.label_pass);
if (RxInterpreter.trace_rx) {
ilgen.Emit (OpCodes.Ldstr, "tail matched at: {0}");
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Box, typeof (int));
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
}
// stack.Count = old_stack_size;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Ldloc, local_old_stack_size);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "set_Count", ref mi_stack_set_count));
// return true;
ilgen.Emit (OpCodes.Br, frame.label_pass);
// Fail:
ilgen.MarkLabel (new_frame.label_fail);
if (RxInterpreter.trace_rx) {
ilgen.Emit (OpCodes.Ldstr, "tail failed to match at: {0}");
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Box, typeof (int));
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
}
// if (stack.Count == old_stack_size)
// return false;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "get_Count", ref mi_stack_get_count));
ilgen.Emit (OpCodes.Ldloc, local_old_stack_size);
ilgen.Emit (OpCodes.Beq, frame.label_fail);
// Backtrack
//strpos = stack.Pop ();
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Pop", ref mi_stack_pop));
ilgen.Emit (OpCodes.Starg, 1);
//Backtrack (stack.Pop ());
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldflda, fi_stack);
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter.IntStack), "Pop", ref mi_stack_pop));
ilgen.Emit (OpCodes.Call, GetMethod ("Backtrack", ref mi_backtrack));
if (RxInterpreter.trace_rx) {
//Console.WriteLine ("backtracking to: {0}", strpos);
ilgen.Emit (OpCodes.Ldstr, "backtracking to: {0}");
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Box, typeof (int));
ilgen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type [] { typeof (string), typeof (object) }));
}
// Loop footer
ilgen.MarkLabel (l_loop_footer);
ilgen.Emit (OpCodes.Br, l_loop_body);
}
// We already processed the tail
pc = out_pc;
goto End;
}
case RxOp.CategoryAny:
case RxOp.CategoryAnySingleline:
case RxOp.CategoryWord:
case RxOp.CategoryDigit:
case RxOp.CategoryWhiteSpace:
case RxOp.CategoryEcmaWord:
case RxOp.CategoryEcmaWhiteSpace:
case RxOp.CategoryUnicodeSpecials:
case RxOp.CategoryUnicode: {
OpFlags flags = (OpFlags)op_flags [pc];
bool negate = (flags & OpFlags.Negate) > 0;
bool reverse = (flags & OpFlags.RightToLeft) > 0;
//if (strpos < string_end) {
Label l_nomatch = ilgen.DefineLabel ();
if (reverse) {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Ble, l_nomatch);
} else {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bge, l_nomatch);
}
// int c = str [strpos];
LocalBuilder local_c = ilgen.DeclareLocal (typeof (char));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Ldarg_1);
if (reverse) {
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Sub);
}
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
ilgen.Emit (OpCodes.Stloc, local_c);
Label l_match = ilgen.DefineLabel ();
Label l_true, l_false;
l_true = negate ? l_nomatch : l_match;
l_false = negate ? l_match : l_nomatch;
switch (op) {
case RxOp.CategoryAny:
// if (str [strpos] != '\n') {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
ilgen.Emit (OpCodes.Bne_Un, l_true);
break;
case RxOp.CategoryAnySingleline:
ilgen.Emit (OpCodes.Br, l_true);
break;
case RxOp.CategoryWord:
// if (Char.IsLetterOrDigit (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation) {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("IsLetterOrDigit", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Brtrue, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("GetUnicodeCategory", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Ldc_I4, (int)UnicodeCategory.ConnectorPunctuation);
ilgen.Emit (OpCodes.Beq, l_true);
break;
case RxOp.CategoryDigit:
// if (Char.IsDigit (c)) {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("IsDigit", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Brtrue, l_true);
break;
case RxOp.CategoryWhiteSpace:
// if (Char.IsWhiteSpace (c)) {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("IsWhiteSpace", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Brtrue, l_true);
break;
case RxOp.CategoryEcmaWord:
// if ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c == '_') {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'a' - 1);
ilgen.Emit (OpCodes.Cgt);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'z' + 1);
ilgen.Emit (OpCodes.Clt);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Brtrue, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'A' - 1);
ilgen.Emit (OpCodes.Cgt);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'Z' + 1);
ilgen.Emit (OpCodes.Clt);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Brtrue, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'0' - 1);
ilgen.Emit (OpCodes.Cgt);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'9' + 1);
ilgen.Emit (OpCodes.Clt);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Brtrue, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'_');
ilgen.Emit (OpCodes.Beq, l_true);
break;
case RxOp.CategoryEcmaWhiteSpace:
// if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v') {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)' ');
ilgen.Emit (OpCodes.Beq, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\t');
ilgen.Emit (OpCodes.Beq, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\n');
ilgen.Emit (OpCodes.Beq, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\r');
ilgen.Emit (OpCodes.Beq, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\f');
ilgen.Emit (OpCodes.Beq, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\v');
ilgen.Emit (OpCodes.Beq, l_true);
break;
case RxOp.CategoryUnicodeSpecials:
// if ('\uFEFF' <= c && c <= '\uFEFF' || '\uFFF0' <= c && c <= '\uFFFD') {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFEFF' - 1);
ilgen.Emit (OpCodes.Cgt);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFEFF' + 1);
ilgen.Emit (OpCodes.Clt);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Brtrue, l_true);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFFF0' - 1);
ilgen.Emit (OpCodes.Cgt);
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Ldc_I4, (int)'\uFFFD' + 1);
ilgen.Emit (OpCodes.Clt);
ilgen.Emit (OpCodes.And);
ilgen.Emit (OpCodes.Brtrue, l_true);
break;
case RxOp.CategoryUnicode:
// if (Char.GetUnicodeCategory (c) == (UnicodeCategory)program [pc + 1]) {
ilgen.Emit (OpCodes.Ldloc, local_c);
ilgen.Emit (OpCodes.Call, typeof (Char).GetMethod ("GetUnicodeCategory", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Ldc_I4, (int)program [pc + 1]);
ilgen.Emit (OpCodes.Beq, l_true);
break;
}
ilgen.Emit (OpCodes.Br, l_false);
ilgen.MarkLabel (l_match);
// strpos++;
if (!no_bump) {
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
if (reverse)
ilgen.Emit (OpCodes.Sub);
else
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
}
// }
Label l2 = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l2);
//}
ilgen.MarkLabel (l_nomatch);
//return false;
ilgen.Emit (OpCodes.Br, frame.label_fail);
ilgen.MarkLabel (l2);
if (op == RxOp.CategoryUnicode)
pc += 2;
else
pc++;
break;
}
case RxOp.Reference: {
OpFlags flags = (OpFlags)op_flags [pc];
bool ignore = (flags & OpFlags.IgnoreCase) > 0;
bool reverse = (flags & OpFlags.RightToLeft) > 0;
//length = GetLastDefined (program [pc + 1] | ((int)program [pc + 2] << 8));
LocalBuilder loc_length = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldc_I4, ReadShort (program, pc + 1));
ilgen.Emit (OpCodes.Call, GetMethod (typeof (RxInterpreter), "GetLastDefined", ref mi_get_last_defined));
ilgen.Emit (OpCodes.Stloc, loc_length);
//if (length < 0)
// return false;
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Blt, frame.label_fail);
//start = marks [length].Index;
LocalBuilder loc_start = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_marks);
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
ilgen.Emit (OpCodes.Call, GetMethod (typeof (Mark), "get_Index", ref mi_mark_get_index));
ilgen.Emit (OpCodes.Stloc, loc_start);
// length = marks [length].Length;
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_marks);
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Ldelema, typeof (Mark));
ilgen.Emit (OpCodes.Call, GetMethod (typeof (Mark), "get_Length", ref mi_mark_get_length));
ilgen.Emit (OpCodes.Stloc, loc_length);
if (reverse) {
//ptr -= length;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Starg, 1);
//if (ptr < 0)
//goto Fail;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_0);
ilgen.Emit (OpCodes.Blt, frame.label_fail);
} else {
//if (strpos + length > string_end)
// return false;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_string_end);
ilgen.Emit (OpCodes.Bgt, frame.label_fail);
}
LocalBuilder local_str = ilgen.DeclareLocal (typeof (string));
ilgen.Emit (OpCodes.Ldarg_0);
ilgen.Emit (OpCodes.Ldfld, fi_str);
ilgen.Emit (OpCodes.Stloc, local_str);
// end = start + length;
LocalBuilder loc_end = ilgen.DeclareLocal (typeof (int));
ilgen.Emit (OpCodes.Ldloc, loc_start);
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, loc_end);
//for (; start < end; ++start) {
Label l_loop_footer = ilgen.DefineLabel ();
ilgen.Emit (OpCodes.Br, l_loop_footer);
Label l_loop_body = ilgen.DefineLabel ();
ilgen.MarkLabel (l_loop_body);
//if (str [strpos] != str [start])
//return false;
if (ignore)
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
ilgen.Emit (OpCodes.Ldloc, local_str);
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
if (ignore)
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
if (ignore)
ilgen.Emit (OpCodes.Ldloc, local_textinfo);
ilgen.Emit (OpCodes.Ldloc, local_str);
ilgen.Emit (OpCodes.Ldloc, loc_start);
ilgen.Emit (OpCodes.Callvirt, typeof (string).GetMethod ("get_Chars"));
if (ignore)
ilgen.Emit (OpCodes.Callvirt, typeof (TextInfo).GetMethod ("ToLower", new Type [] { typeof (char) }));
ilgen.Emit (OpCodes.Bne_Un, frame.label_fail);
// strpos++;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Starg, 1);
// start++
ilgen.Emit (OpCodes.Ldloc, loc_start);
ilgen.Emit (OpCodes.Ldc_I4_1);
ilgen.Emit (OpCodes.Add);
ilgen.Emit (OpCodes.Stloc, loc_start);
// Loop footer
ilgen.MarkLabel (l_loop_footer);
ilgen.Emit (OpCodes.Ldloc, loc_start);
ilgen.Emit (OpCodes.Ldloc, loc_end);
ilgen.Emit (OpCodes.Blt, l_loop_body);
if (reverse) {
//ptr -= length;
ilgen.Emit (OpCodes.Ldarg_1);
ilgen.Emit (OpCodes.Ldloc, loc_length);
ilgen.Emit (OpCodes.Sub);
ilgen.Emit (OpCodes.Starg, 1);
}
pc += 3;
break;
}
case RxOp.Repeat:
case RxOp.RepeatLazy:
case RxOp.IfDefined:
// FIXME:
if (RxInterpreter.trace_rx || trace_compile)
Console.WriteLine ("Opcode " + op + " not supported.");
return null;
default:
throw new NotImplementedException ("Opcode '" + op + "' not supported by the regex->IL compiler.");
}
if (one_op)
break;
}
End:
out_pc = pc;
return m;
}
}
}