536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
1248 lines
36 KiB
C#
1248 lines
36 KiB
C#
|
|
//
|
|
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
//
|
|
// System.Reflection.Emit/ILGenerator.cs
|
|
//
|
|
// Author:
|
|
// Paolo Molaro (lupus@ximian.com)
|
|
//
|
|
// (C) 2001 Ximian, Inc. http://www.ximian.com
|
|
//
|
|
|
|
#if !FULL_AOT_RUNTIME
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.SymbolStore;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace System.Reflection.Emit {
|
|
|
|
internal struct ILExceptionBlock {
|
|
public const int CATCH = 0;
|
|
public const int FILTER = 1;
|
|
public const int FINALLY = 2;
|
|
public const int FAULT = 4;
|
|
public const int FILTER_START = -1;
|
|
|
|
internal Type extype;
|
|
internal int type;
|
|
internal int start;
|
|
internal int len;
|
|
internal int filter_offset;
|
|
|
|
internal void Debug () {
|
|
#if NO
|
|
System.Console.Write ("\ttype="+type.ToString()+" start="+start.ToString()+" len="+len.ToString());
|
|
if (extype != null)
|
|
System.Console.WriteLine (" extype="+extype.ToString());
|
|
else
|
|
System.Console.WriteLine (String.Empty);
|
|
#endif
|
|
}
|
|
}
|
|
internal struct ILExceptionInfo {
|
|
#pragma warning disable 169
|
|
#pragma warning disable 414
|
|
internal ILExceptionBlock[] handlers;
|
|
internal int start;
|
|
internal int len;
|
|
internal Label end;
|
|
#pragma warning restore 169
|
|
#pragma warning restore 414
|
|
|
|
internal int NumHandlers ()
|
|
{
|
|
return handlers.Length;
|
|
}
|
|
|
|
internal void AddCatch (Type extype, int offset)
|
|
{
|
|
int i;
|
|
End (offset);
|
|
add_block (offset);
|
|
i = handlers.Length - 1;
|
|
handlers [i].type = ILExceptionBlock.CATCH;
|
|
handlers [i].start = offset;
|
|
handlers [i].extype = extype;
|
|
}
|
|
|
|
internal void AddFinally (int offset)
|
|
{
|
|
int i;
|
|
End (offset);
|
|
add_block (offset);
|
|
i = handlers.Length - 1;
|
|
handlers [i].type = ILExceptionBlock.FINALLY;
|
|
handlers [i].start = offset;
|
|
handlers [i].extype = null;
|
|
}
|
|
|
|
internal void AddFault (int offset)
|
|
{
|
|
int i;
|
|
End (offset);
|
|
add_block (offset);
|
|
i = handlers.Length - 1;
|
|
handlers [i].type = ILExceptionBlock.FAULT;
|
|
handlers [i].start = offset;
|
|
handlers [i].extype = null;
|
|
}
|
|
|
|
internal void AddFilter (int offset)
|
|
{
|
|
int i;
|
|
End (offset);
|
|
add_block (offset);
|
|
i = handlers.Length - 1;
|
|
handlers [i].type = ILExceptionBlock.FILTER_START;
|
|
handlers [i].extype = null;
|
|
handlers [i].filter_offset = offset;
|
|
}
|
|
|
|
internal void End (int offset)
|
|
{
|
|
if (handlers == null)
|
|
return;
|
|
int i = handlers.Length - 1;
|
|
if (i >= 0)
|
|
handlers [i].len = offset - handlers [i].start;
|
|
}
|
|
|
|
internal int LastClauseType ()
|
|
{
|
|
if (handlers != null)
|
|
return handlers [handlers.Length-1].type;
|
|
else
|
|
return ILExceptionBlock.CATCH;
|
|
}
|
|
|
|
internal void PatchFilterClause (int start)
|
|
{
|
|
if (handlers != null && handlers.Length > 0) {
|
|
handlers [handlers.Length - 1].start = start;
|
|
handlers [handlers.Length - 1].type = ILExceptionBlock.FILTER;
|
|
}
|
|
}
|
|
|
|
internal void Debug (int b)
|
|
{
|
|
#if NO
|
|
System.Console.WriteLine ("Handler {0} at {1}, len: {2}", b, start, len);
|
|
for (int i = 0; i < handlers.Length; ++i)
|
|
handlers [i].Debug ();
|
|
#endif
|
|
}
|
|
|
|
void add_block (int offset)
|
|
{
|
|
if (handlers != null) {
|
|
int i = handlers.Length;
|
|
ILExceptionBlock[] new_b = new ILExceptionBlock [i + 1];
|
|
System.Array.Copy (handlers, new_b, i);
|
|
handlers = new_b;
|
|
handlers [i].len = offset - handlers [i].start;
|
|
} else {
|
|
handlers = new ILExceptionBlock [1];
|
|
len = offset - start;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal struct ILTokenInfo {
|
|
public MemberInfo member;
|
|
public int code_pos;
|
|
}
|
|
|
|
internal interface TokenGenerator {
|
|
int GetToken (string str);
|
|
|
|
int GetToken (MemberInfo member, bool create_open_instance);
|
|
|
|
int GetToken (MethodBase method, Type[] opt_param_types);
|
|
|
|
int GetToken (SignatureHelper helper);
|
|
}
|
|
|
|
[ComVisible (true)]
|
|
[ComDefaultInterface (typeof (_ILGenerator))]
|
|
[ClassInterface (ClassInterfaceType.None)]
|
|
[StructLayout (LayoutKind.Sequential)]
|
|
public class ILGenerator: _ILGenerator {
|
|
private struct LabelFixup {
|
|
public int offset; // The number of bytes between pos and the
|
|
// offset of the jump
|
|
public int pos; // Where offset of the label is placed
|
|
public int label_idx; // The label to jump to
|
|
};
|
|
|
|
struct LabelData {
|
|
public LabelData (int addr, int maxStack)
|
|
{
|
|
this.addr = addr;
|
|
this.maxStack = maxStack;
|
|
}
|
|
|
|
public int addr;
|
|
public int maxStack;
|
|
}
|
|
|
|
#region Sync with reflection.h
|
|
private byte[] code;
|
|
private int code_len;
|
|
private int max_stack;
|
|
private int cur_stack;
|
|
private LocalBuilder[] locals;
|
|
private ILExceptionInfo[] ex_handlers;
|
|
private int num_token_fixups;
|
|
private ILTokenInfo[] token_fixups;
|
|
#endregion
|
|
|
|
private LabelData [] labels;
|
|
private int num_labels;
|
|
private LabelFixup[] fixups;
|
|
private int num_fixups;
|
|
internal Module module;
|
|
private int cur_block;
|
|
private Stack open_blocks;
|
|
private TokenGenerator token_gen;
|
|
|
|
const int defaultFixupSize = 4;
|
|
const int defaultLabelsSize = 4;
|
|
const int defaultExceptionStackSize = 2;
|
|
|
|
ArrayList sequencePointLists;
|
|
SequencePointList currentSequence;
|
|
|
|
internal ILGenerator (Module m, TokenGenerator token_gen, int size)
|
|
{
|
|
if (size < 0)
|
|
size = 128;
|
|
code = new byte [size];
|
|
token_fixups = new ILTokenInfo [8];
|
|
module = m;
|
|
this.token_gen = token_gen;
|
|
}
|
|
|
|
private void add_token_fixup (MemberInfo mi)
|
|
{
|
|
if (num_token_fixups == token_fixups.Length) {
|
|
ILTokenInfo[] ntf = new ILTokenInfo [num_token_fixups * 2];
|
|
token_fixups.CopyTo (ntf, 0);
|
|
token_fixups = ntf;
|
|
}
|
|
token_fixups [num_token_fixups].member = mi;
|
|
token_fixups [num_token_fixups++].code_pos = code_len;
|
|
}
|
|
|
|
private void make_room (int nbytes)
|
|
{
|
|
if (code_len + nbytes < code.Length)
|
|
return;
|
|
byte[] new_code = new byte [(code_len + nbytes) * 2 + 128];
|
|
System.Array.Copy (code, 0, new_code, 0, code.Length);
|
|
code = new_code;
|
|
}
|
|
|
|
private void emit_int (int val)
|
|
{
|
|
code [code_len++] = (byte) (val & 0xFF);
|
|
code [code_len++] = (byte) ((val >> 8) & 0xFF);
|
|
code [code_len++] = (byte) ((val >> 16) & 0xFF);
|
|
code [code_len++] = (byte) ((val >> 24) & 0xFF);
|
|
}
|
|
|
|
/* change to pass by ref to avoid copy */
|
|
private void ll_emit (OpCode opcode)
|
|
{
|
|
/*
|
|
* there is already enough room allocated in code.
|
|
*/
|
|
// access op1 and op2 directly since the Value property is useless
|
|
if (opcode.Size == 2)
|
|
code [code_len++] = opcode.op1;
|
|
code [code_len++] = opcode.op2;
|
|
/*
|
|
* We should probably keep track of stack needs here.
|
|
* Or we may want to run the verifier on the code before saving it
|
|
* (this may be needed anyway when the ILGenerator is not used...).
|
|
*/
|
|
switch (opcode.StackBehaviourPush) {
|
|
case StackBehaviour.Push1:
|
|
case StackBehaviour.Pushi:
|
|
case StackBehaviour.Pushi8:
|
|
case StackBehaviour.Pushr4:
|
|
case StackBehaviour.Pushr8:
|
|
case StackBehaviour.Pushref:
|
|
case StackBehaviour.Varpush: /* again we are conservative and assume it pushes 1 */
|
|
cur_stack ++;
|
|
break;
|
|
case StackBehaviour.Push1_push1:
|
|
cur_stack += 2;
|
|
break;
|
|
}
|
|
if (max_stack < cur_stack)
|
|
max_stack = cur_stack;
|
|
|
|
/*
|
|
* Note that we adjust for the pop behaviour _after_ setting max_stack.
|
|
*/
|
|
switch (opcode.StackBehaviourPop) {
|
|
case StackBehaviour.Varpop:
|
|
break; /* we are conservative and assume it doesn't decrease the stack needs */
|
|
case StackBehaviour.Pop1:
|
|
case StackBehaviour.Popi:
|
|
case StackBehaviour.Popref:
|
|
cur_stack --;
|
|
break;
|
|
case StackBehaviour.Pop1_pop1:
|
|
case StackBehaviour.Popi_pop1:
|
|
case StackBehaviour.Popi_popi:
|
|
case StackBehaviour.Popi_popi8:
|
|
case StackBehaviour.Popi_popr4:
|
|
case StackBehaviour.Popi_popr8:
|
|
case StackBehaviour.Popref_pop1:
|
|
case StackBehaviour.Popref_popi:
|
|
cur_stack -= 2;
|
|
break;
|
|
case StackBehaviour.Popi_popi_popi:
|
|
case StackBehaviour.Popref_popi_popi:
|
|
case StackBehaviour.Popref_popi_popi8:
|
|
case StackBehaviour.Popref_popi_popr4:
|
|
case StackBehaviour.Popref_popi_popr8:
|
|
case StackBehaviour.Popref_popi_popref:
|
|
cur_stack -= 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static int target_len (OpCode opcode)
|
|
{
|
|
if (opcode.OperandType == OperandType.InlineBrTarget)
|
|
return 4;
|
|
return 1;
|
|
}
|
|
|
|
private void InternalEndClause ()
|
|
{
|
|
switch (ex_handlers [cur_block].LastClauseType ()) {
|
|
case ILExceptionBlock.CATCH:
|
|
case ILExceptionBlock.FILTER:
|
|
case ILExceptionBlock.FILTER_START:
|
|
// how could we optimize code size here?
|
|
Emit (OpCodes.Leave, ex_handlers [cur_block].end);
|
|
break;
|
|
case ILExceptionBlock.FAULT:
|
|
case ILExceptionBlock.FINALLY:
|
|
Emit (OpCodes.Endfinally);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public virtual void BeginCatchBlock (Type exceptionType)
|
|
{
|
|
if (open_blocks == null)
|
|
open_blocks = new Stack (defaultExceptionStackSize);
|
|
|
|
if (open_blocks.Count <= 0)
|
|
throw new NotSupportedException ("Not in an exception block");
|
|
if (exceptionType != null && exceptionType.IsUserType)
|
|
throw new NotSupportedException ("User defined subclasses of System.Type are not yet supported.");
|
|
if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER_START) {
|
|
if (exceptionType != null)
|
|
throw new ArgumentException ("Do not supply an exception type for filter clause");
|
|
Emit (OpCodes.Endfilter);
|
|
ex_handlers [cur_block].PatchFilterClause (code_len);
|
|
} else {
|
|
InternalEndClause ();
|
|
ex_handlers [cur_block].AddCatch (exceptionType, code_len);
|
|
}
|
|
|
|
cur_stack = 1; // the exception object is on the stack by default
|
|
if (max_stack < cur_stack)
|
|
max_stack = cur_stack;
|
|
|
|
//System.Console.WriteLine ("Begin catch Block: {0} {1}",exceptionType.ToString(), max_stack);
|
|
}
|
|
|
|
public virtual void BeginExceptFilterBlock ()
|
|
{
|
|
if (open_blocks == null)
|
|
open_blocks = new Stack (defaultExceptionStackSize);
|
|
|
|
if (open_blocks.Count <= 0)
|
|
throw new NotSupportedException ("Not in an exception block");
|
|
InternalEndClause ();
|
|
|
|
ex_handlers [cur_block].AddFilter (code_len);
|
|
}
|
|
|
|
public virtual Label BeginExceptionBlock ()
|
|
{
|
|
//System.Console.WriteLine ("Begin Block");
|
|
if (open_blocks == null)
|
|
open_blocks = new Stack (defaultExceptionStackSize);
|
|
|
|
if (ex_handlers != null) {
|
|
cur_block = ex_handlers.Length;
|
|
ILExceptionInfo[] new_ex = new ILExceptionInfo [cur_block + 1];
|
|
System.Array.Copy (ex_handlers, new_ex, cur_block);
|
|
ex_handlers = new_ex;
|
|
} else {
|
|
ex_handlers = new ILExceptionInfo [1];
|
|
cur_block = 0;
|
|
}
|
|
open_blocks.Push (cur_block);
|
|
ex_handlers [cur_block].start = code_len;
|
|
return ex_handlers [cur_block].end = DefineLabel ();
|
|
}
|
|
|
|
public virtual void BeginFaultBlock()
|
|
{
|
|
if (open_blocks == null)
|
|
open_blocks = new Stack (defaultExceptionStackSize);
|
|
|
|
if (open_blocks.Count <= 0)
|
|
throw new NotSupportedException ("Not in an exception block");
|
|
|
|
if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER_START) {
|
|
Emit (OpCodes.Leave, ex_handlers [cur_block].end);
|
|
ex_handlers [cur_block].PatchFilterClause (code_len);
|
|
}
|
|
|
|
InternalEndClause ();
|
|
//System.Console.WriteLine ("Begin fault Block");
|
|
ex_handlers [cur_block].AddFault (code_len);
|
|
}
|
|
|
|
public virtual void BeginFinallyBlock()
|
|
{
|
|
if (open_blocks == null)
|
|
open_blocks = new Stack (defaultExceptionStackSize);
|
|
|
|
if (open_blocks.Count <= 0)
|
|
throw new NotSupportedException ("Not in an exception block");
|
|
|
|
InternalEndClause ();
|
|
|
|
if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER_START) {
|
|
Emit (OpCodes.Leave, ex_handlers [cur_block].end);
|
|
ex_handlers [cur_block].PatchFilterClause (code_len);
|
|
}
|
|
|
|
//System.Console.WriteLine ("Begin finally Block");
|
|
ex_handlers [cur_block].AddFinally (code_len);
|
|
}
|
|
|
|
public virtual void BeginScope ()
|
|
{ }
|
|
|
|
public virtual LocalBuilder DeclareLocal (Type localType)
|
|
{
|
|
return DeclareLocal (localType, false);
|
|
}
|
|
|
|
|
|
public virtual LocalBuilder DeclareLocal (Type localType, bool pinned)
|
|
{
|
|
if (localType == null)
|
|
throw new ArgumentNullException ("localType");
|
|
if (localType.IsUserType)
|
|
throw new NotSupportedException ("User defined subclasses of System.Type are not yet supported.");
|
|
LocalBuilder res = new LocalBuilder (localType, this);
|
|
res.is_pinned = pinned;
|
|
|
|
if (locals != null) {
|
|
LocalBuilder[] new_l = new LocalBuilder [locals.Length + 1];
|
|
System.Array.Copy (locals, new_l, locals.Length);
|
|
new_l [locals.Length] = res;
|
|
locals = new_l;
|
|
} else {
|
|
locals = new LocalBuilder [1];
|
|
locals [0] = res;
|
|
}
|
|
res.position = (ushort)(locals.Length - 1);
|
|
return res;
|
|
}
|
|
|
|
public virtual Label DefineLabel ()
|
|
{
|
|
if (labels == null)
|
|
labels = new LabelData [defaultLabelsSize];
|
|
else if (num_labels >= labels.Length) {
|
|
LabelData [] t = new LabelData [labels.Length * 2];
|
|
Array.Copy (labels, t, labels.Length);
|
|
labels = t;
|
|
}
|
|
|
|
labels [num_labels] = new LabelData (-1, 0);
|
|
|
|
return new Label (num_labels++);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode)
|
|
{
|
|
make_room (2);
|
|
ll_emit (opcode);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, Byte arg)
|
|
{
|
|
make_room (3);
|
|
ll_emit (opcode);
|
|
code [code_len++] = arg;
|
|
}
|
|
|
|
[ComVisible (true)]
|
|
public virtual void Emit (OpCode opcode, ConstructorInfo con)
|
|
{
|
|
int token = token_gen.GetToken (con, true);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
if (con.DeclaringType.Module == module || (con is ConstructorOnTypeBuilderInst) || (con is ConstructorBuilder))
|
|
add_token_fixup (con);
|
|
emit_int (token);
|
|
|
|
if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
|
|
cur_stack -= con.GetParametersCount ();
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, double arg)
|
|
{
|
|
byte[] s = System.BitConverter.GetBytes (arg);
|
|
make_room (10);
|
|
ll_emit (opcode);
|
|
if (BitConverter.IsLittleEndian){
|
|
System.Array.Copy (s, 0, code, code_len, 8);
|
|
code_len += 8;
|
|
} else {
|
|
code [code_len++] = s [7];
|
|
code [code_len++] = s [6];
|
|
code [code_len++] = s [5];
|
|
code [code_len++] = s [4];
|
|
code [code_len++] = s [3];
|
|
code [code_len++] = s [2];
|
|
code [code_len++] = s [1];
|
|
code [code_len++] = s [0];
|
|
}
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, FieldInfo field)
|
|
{
|
|
int token = token_gen.GetToken (field, true);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
if (field.DeclaringType.Module == module || (field is FieldOnTypeBuilderInst) || (field is FieldBuilder))
|
|
add_token_fixup (field);
|
|
emit_int (token);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, Int16 arg)
|
|
{
|
|
make_room (4);
|
|
ll_emit (opcode);
|
|
code [code_len++] = (byte) (arg & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 8) & 0xFF);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, int arg)
|
|
{
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
emit_int (arg);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, long arg)
|
|
{
|
|
make_room (10);
|
|
ll_emit (opcode);
|
|
code [code_len++] = (byte) (arg & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 8) & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 16) & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 24) & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 32) & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 40) & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 48) & 0xFF);
|
|
code [code_len++] = (byte) ((arg >> 56) & 0xFF);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, Label label)
|
|
{
|
|
int tlen = target_len (opcode);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
if (cur_stack > labels [label.label].maxStack)
|
|
labels [label.label].maxStack = cur_stack;
|
|
|
|
if (fixups == null)
|
|
fixups = new LabelFixup [defaultFixupSize];
|
|
else if (num_fixups >= fixups.Length) {
|
|
LabelFixup[] newf = new LabelFixup [fixups.Length * 2];
|
|
System.Array.Copy (fixups, newf, fixups.Length);
|
|
fixups = newf;
|
|
}
|
|
fixups [num_fixups].offset = tlen;
|
|
fixups [num_fixups].pos = code_len;
|
|
fixups [num_fixups].label_idx = label.label;
|
|
num_fixups++;
|
|
code_len += tlen;
|
|
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, Label[] labels)
|
|
{
|
|
if (labels == null)
|
|
throw new ArgumentNullException ("labels");
|
|
|
|
/* opcode needs to be switch. */
|
|
int count = labels.Length;
|
|
make_room (6 + count * 4);
|
|
ll_emit (opcode);
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
if (cur_stack > this.labels [labels [i].label].maxStack)
|
|
this.labels [labels [i].label].maxStack = cur_stack;
|
|
|
|
emit_int (count);
|
|
if (fixups == null)
|
|
fixups = new LabelFixup [defaultFixupSize + count];
|
|
else if (num_fixups + count >= fixups.Length) {
|
|
LabelFixup[] newf = new LabelFixup [count + fixups.Length * 2];
|
|
System.Array.Copy (fixups, newf, fixups.Length);
|
|
fixups = newf;
|
|
}
|
|
|
|
// ECMA 335, Partition III, p94 (7-10)
|
|
//
|
|
// The switch instruction implements a jump table. The format of
|
|
// the instruction is an unsigned int32 representing the number of targets N,
|
|
// followed by N int32 values specifying jump targets: these targets are
|
|
// represented as offsets (positive or negative) from the beginning of the
|
|
// instruction following this switch instruction.
|
|
//
|
|
// We must make sure it gets an offset from the *end* of the last label
|
|
// (eg, the beginning of the instruction following this).
|
|
//
|
|
// remaining is the number of bytes from the current instruction to the
|
|
// instruction that will be emitted.
|
|
|
|
for (int i = 0, remaining = count * 4; i < count; ++i, remaining -= 4) {
|
|
fixups [num_fixups].offset = remaining;
|
|
fixups [num_fixups].pos = code_len;
|
|
fixups [num_fixups].label_idx = labels [i].label;
|
|
num_fixups++;
|
|
code_len += 4;
|
|
}
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, LocalBuilder local)
|
|
{
|
|
if (local == null)
|
|
throw new ArgumentNullException ("local");
|
|
if (local.ilgen != this)
|
|
throw new ArgumentException ("Trying to emit a local from a different ILGenerator.");
|
|
|
|
uint pos = local.position;
|
|
bool load_addr = false;
|
|
bool is_store = false;
|
|
bool is_load = false;
|
|
make_room (6);
|
|
|
|
/* inline the code from ll_emit () to optimize il code size */
|
|
if (opcode.StackBehaviourPop == StackBehaviour.Pop1) {
|
|
cur_stack --;
|
|
is_store = true;
|
|
} else if (opcode.StackBehaviourPush == StackBehaviour.Push1 || opcode.StackBehaviourPush == StackBehaviour.Pushi) {
|
|
cur_stack++;
|
|
is_load = true;
|
|
if (cur_stack > max_stack)
|
|
max_stack = cur_stack;
|
|
load_addr = opcode.StackBehaviourPush == StackBehaviour.Pushi;
|
|
}
|
|
if (load_addr) {
|
|
if (pos < 256) {
|
|
code [code_len++] = (byte)0x12;
|
|
code [code_len++] = (byte)pos;
|
|
} else {
|
|
code [code_len++] = (byte)0xfe;
|
|
code [code_len++] = (byte)0x0d;
|
|
code [code_len++] = (byte)(pos & 0xff);
|
|
code [code_len++] = (byte)((pos >> 8) & 0xff);
|
|
}
|
|
} else {
|
|
if (is_store) {
|
|
if (pos < 4) {
|
|
code [code_len++] = (byte)(0x0a + pos);
|
|
} else if (pos < 256) {
|
|
code [code_len++] = (byte)0x13;
|
|
code [code_len++] = (byte)pos;
|
|
} else {
|
|
code [code_len++] = (byte)0xfe;
|
|
code [code_len++] = (byte)0x0e;
|
|
code [code_len++] = (byte)(pos & 0xff);
|
|
code [code_len++] = (byte)((pos >> 8) & 0xff);
|
|
}
|
|
} else if (is_load) {
|
|
if (pos < 4) {
|
|
code [code_len++] = (byte)(0x06 + pos);
|
|
} else if (pos < 256) {
|
|
code [code_len++] = (byte)0x11;
|
|
code [code_len++] = (byte)pos;
|
|
} else {
|
|
code [code_len++] = (byte)0xfe;
|
|
code [code_len++] = (byte)0x0c;
|
|
code [code_len++] = (byte)(pos & 0xff);
|
|
code [code_len++] = (byte)((pos >> 8) & 0xff);
|
|
}
|
|
} else {
|
|
ll_emit (opcode);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, MethodInfo meth)
|
|
{
|
|
if (meth == null)
|
|
throw new ArgumentNullException ("meth");
|
|
|
|
// For compatibility with MS
|
|
if ((meth is DynamicMethod) && ((opcode == OpCodes.Ldftn) || (opcode == OpCodes.Ldvirtftn) || (opcode == OpCodes.Ldtoken)))
|
|
throw new ArgumentException ("Ldtoken, Ldftn and Ldvirtftn OpCodes cannot target DynamicMethods.");
|
|
|
|
int token = token_gen.GetToken (meth, true);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
Type declaringType = meth.DeclaringType;
|
|
// Might be a DynamicMethod with no declaring type
|
|
if (declaringType != null) {
|
|
if (declaringType.Module == module || meth is MethodOnTypeBuilderInst || meth is MethodBuilder)
|
|
add_token_fixup (meth);
|
|
}
|
|
emit_int (token);
|
|
if (meth.ReturnType != typeof (void))
|
|
cur_stack ++;
|
|
|
|
if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
|
|
cur_stack -= meth.GetParametersCount ();
|
|
}
|
|
|
|
private void Emit (OpCode opcode, MethodInfo method, int token)
|
|
{
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
// Might be a DynamicMethod with no declaring type
|
|
Type declaringType = method.DeclaringType;
|
|
if (declaringType != null) {
|
|
if (declaringType.Module == module || method is MethodBuilder)
|
|
add_token_fixup (method);
|
|
}
|
|
emit_int (token);
|
|
if (method.ReturnType != typeof (void))
|
|
cur_stack ++;
|
|
|
|
if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
|
|
cur_stack -= method.GetParametersCount ();
|
|
}
|
|
|
|
[CLSCompliant(false)]
|
|
public void Emit (OpCode opcode, sbyte arg)
|
|
{
|
|
make_room (3);
|
|
ll_emit (opcode);
|
|
code [code_len++] = (byte)arg;
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, SignatureHelper signature)
|
|
{
|
|
int token = token_gen.GetToken (signature);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
emit_int (token);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, float arg)
|
|
{
|
|
byte[] s = System.BitConverter.GetBytes (arg);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
if (BitConverter.IsLittleEndian){
|
|
System.Array.Copy (s, 0, code, code_len, 4);
|
|
code_len += 4;
|
|
} else {
|
|
code [code_len++] = s [3];
|
|
code [code_len++] = s [2];
|
|
code [code_len++] = s [1];
|
|
code [code_len++] = s [0];
|
|
}
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, string str)
|
|
{
|
|
int token = token_gen.GetToken (str);
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
emit_int (token);
|
|
}
|
|
|
|
public virtual void Emit (OpCode opcode, Type cls)
|
|
{
|
|
if (cls != null && cls.IsByRef)
|
|
throw new ArgumentException ("Cannot get TypeToken for a ByRef type.");
|
|
|
|
make_room (6);
|
|
ll_emit (opcode);
|
|
int token = token_gen.GetToken (cls, opcode != OpCodes.Ldtoken);
|
|
if (cls is TypeBuilderInstantiation || cls is SymbolType || cls is TypeBuilder || cls is GenericTypeParameterBuilder || cls is EnumBuilder)
|
|
add_token_fixup (cls);
|
|
emit_int (token);
|
|
}
|
|
|
|
[MonoLimitation ("vararg methods are not supported")]
|
|
public virtual void EmitCall (OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes)
|
|
{
|
|
if (methodInfo == null)
|
|
throw new ArgumentNullException ("methodInfo");
|
|
short value = opcode.Value;
|
|
if (!(value == OpCodes.Call.Value || value == OpCodes.Callvirt.Value))
|
|
throw new NotSupportedException ("Only Call and CallVirt are allowed");
|
|
if ((methodInfo.CallingConvention & CallingConventions.VarArgs) == 0)
|
|
optionalParameterTypes = null;
|
|
if (optionalParameterTypes != null){
|
|
if ((methodInfo.CallingConvention & CallingConventions.VarArgs) == 0){
|
|
throw new InvalidOperationException ("Method is not VarArgs method and optional types were passed");
|
|
}
|
|
|
|
int token = token_gen.GetToken (methodInfo, optionalParameterTypes);
|
|
Emit (opcode, methodInfo, token);
|
|
return;
|
|
}
|
|
Emit (opcode, methodInfo);
|
|
}
|
|
|
|
public virtual void EmitCalli (OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes)
|
|
{
|
|
// GetMethodSigHelper expects a ModuleBuilder or null, and module might be
|
|
// a normal module when using dynamic methods.
|
|
SignatureHelper helper = SignatureHelper.GetMethodSigHelper (module as ModuleBuilder, 0, unmanagedCallConv, returnType, parameterTypes);
|
|
Emit (opcode, helper);
|
|
}
|
|
|
|
public virtual void EmitCalli (OpCode opcode, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
|
|
{
|
|
if (optionalParameterTypes != null)
|
|
throw new NotImplementedException ();
|
|
|
|
SignatureHelper helper = SignatureHelper.GetMethodSigHelper (module as ModuleBuilder, callingConvention, 0, returnType, parameterTypes);
|
|
Emit (opcode, helper);
|
|
}
|
|
|
|
public virtual void EmitWriteLine (FieldInfo fld)
|
|
{
|
|
if (fld == null)
|
|
throw new ArgumentNullException ("fld");
|
|
|
|
// The MS implementation does not check for valuetypes here but it
|
|
// should. Also, it should check that if the field is not static,
|
|
// then it is a member of this type.
|
|
if (fld.IsStatic)
|
|
Emit (OpCodes.Ldsfld, fld);
|
|
else {
|
|
Emit (OpCodes.Ldarg_0);
|
|
Emit (OpCodes.Ldfld, fld);
|
|
}
|
|
Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type[1] { fld.FieldType }));
|
|
}
|
|
|
|
public virtual void EmitWriteLine (LocalBuilder localBuilder)
|
|
{
|
|
if (localBuilder == null)
|
|
throw new ArgumentNullException ("localBuilder");
|
|
if (localBuilder.LocalType is TypeBuilder)
|
|
throw new ArgumentException ("Output streams do not support TypeBuilders.");
|
|
// The MS implementation does not check for valuetypes here but it
|
|
// should.
|
|
Emit (OpCodes.Ldloc, localBuilder);
|
|
Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type[1] { localBuilder.LocalType }));
|
|
}
|
|
|
|
public virtual void EmitWriteLine (string value)
|
|
{
|
|
Emit (OpCodes.Ldstr, value);
|
|
Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new Type[1] { typeof(string)}));
|
|
}
|
|
|
|
public virtual void EndExceptionBlock ()
|
|
{
|
|
if (open_blocks == null)
|
|
open_blocks = new Stack (defaultExceptionStackSize);
|
|
|
|
if (open_blocks.Count <= 0)
|
|
throw new NotSupportedException ("Not in an exception block");
|
|
|
|
if (ex_handlers [cur_block].LastClauseType () == ILExceptionBlock.FILTER_START)
|
|
throw new InvalidOperationException ("Incorrect code generation for exception block.");
|
|
|
|
InternalEndClause ();
|
|
MarkLabel (ex_handlers [cur_block].end);
|
|
ex_handlers [cur_block].End (code_len);
|
|
ex_handlers [cur_block].Debug (cur_block);
|
|
//System.Console.WriteLine ("End Block {0} (handlers: {1})", cur_block, ex_handlers [cur_block].NumHandlers ());
|
|
open_blocks.Pop ();
|
|
if (open_blocks.Count > 0)
|
|
cur_block = (int)open_blocks.Peek ();
|
|
//Console.WriteLine ("curblock restored to {0}", cur_block);
|
|
//throw new NotImplementedException ();
|
|
}
|
|
|
|
public virtual void EndScope ()
|
|
{ }
|
|
|
|
public virtual void MarkLabel (Label loc)
|
|
{
|
|
if (loc.label < 0 || loc.label >= num_labels)
|
|
throw new System.ArgumentException ("The label is not valid");
|
|
if (labels [loc.label].addr >= 0)
|
|
throw new System.ArgumentException ("The label was already defined");
|
|
labels [loc.label].addr = code_len;
|
|
if (labels [loc.label].maxStack > cur_stack)
|
|
cur_stack = labels [loc.label].maxStack;
|
|
}
|
|
|
|
public virtual void MarkSequencePoint (ISymbolDocumentWriter document, int startLine,
|
|
int startColumn, int endLine, int endColumn)
|
|
{
|
|
if (currentSequence == null || currentSequence.Document != document) {
|
|
if (sequencePointLists == null)
|
|
sequencePointLists = new ArrayList ();
|
|
currentSequence = new SequencePointList (document);
|
|
sequencePointLists.Add (currentSequence);
|
|
}
|
|
|
|
currentSequence.AddSequencePoint (code_len, startLine, startColumn, endLine, endColumn);
|
|
}
|
|
|
|
internal void GenerateDebugInfo (ISymbolWriter symbolWriter)
|
|
{
|
|
if (sequencePointLists != null) {
|
|
SequencePointList first = (SequencePointList) sequencePointLists [0];
|
|
SequencePointList last = (SequencePointList) sequencePointLists [sequencePointLists.Count - 1];
|
|
symbolWriter.SetMethodSourceRange (first.Document, first.StartLine, first.StartColumn, last.Document, last.EndLine, last.EndColumn);
|
|
|
|
foreach (SequencePointList list in sequencePointLists)
|
|
symbolWriter.DefineSequencePoints (list.Document, list.GetOffsets(), list.GetLines(), list.GetColumns(), list.GetEndLines(), list.GetEndColumns());
|
|
|
|
if (locals != null) {
|
|
foreach (LocalBuilder local in locals) {
|
|
if (local.Name != null && local.Name.Length > 0) {
|
|
SignatureHelper sighelper = SignatureHelper.GetLocalVarSigHelper (module as ModuleBuilder);
|
|
sighelper.AddArgument (local.LocalType);
|
|
byte[] signature = sighelper.GetSignature ();
|
|
symbolWriter.DefineLocalVariable (local.Name, FieldAttributes.Public, signature, SymAddressKind.ILOffset, local.position, 0, 0, local.StartOffset, local.EndOffset);
|
|
}
|
|
}
|
|
}
|
|
sequencePointLists = null;
|
|
}
|
|
}
|
|
|
|
internal bool HasDebugInfo
|
|
{
|
|
get { return sequencePointLists != null; }
|
|
}
|
|
|
|
public virtual void ThrowException (Type excType)
|
|
{
|
|
if (excType == null)
|
|
throw new ArgumentNullException ("excType");
|
|
if (! ((excType == typeof (Exception)) ||
|
|
excType.IsSubclassOf (typeof (Exception))))
|
|
throw new ArgumentException ("Type should be an exception type", "excType");
|
|
ConstructorInfo ctor = excType.GetConstructor (Type.EmptyTypes);
|
|
if (ctor == null)
|
|
throw new ArgumentException ("Type should have a default constructor", "excType");
|
|
Emit (OpCodes.Newobj, ctor);
|
|
Emit (OpCodes.Throw);
|
|
}
|
|
|
|
[MonoTODO("Not implemented")]
|
|
public virtual void UsingNamespace (String usingNamespace)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
internal void label_fixup (MethodBase mb)
|
|
{
|
|
for (int i = 0; i < num_fixups; ++i) {
|
|
if (labels [fixups [i].label_idx].addr < 0)
|
|
throw new ArgumentException (string.Format ("Label #{0} is not marked in method `{1}'", fixups [i].label_idx + 1, mb.Name));
|
|
// Diff is the offset from the end of the jump instruction to the address of the label
|
|
int diff = labels [fixups [i].label_idx].addr - (fixups [i].pos + fixups [i].offset);
|
|
if (fixups [i].offset == 1) {
|
|
code [fixups [i].pos] = (byte)((sbyte) diff);
|
|
} else {
|
|
int old_cl = code_len;
|
|
code_len = fixups [i].pos;
|
|
emit_int (diff);
|
|
code_len = old_cl;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void FixupTokens (Dictionary<int, int> token_map, Dictionary<int, MemberInfo> member_map) {
|
|
for (int i = 0; i < num_token_fixups; ++i) {
|
|
int pos = token_fixups [i].code_pos;
|
|
int old_token = code [pos] | (code [pos + 1] << 8) | (code [pos + 2] << 16) | (code [pos + 3] << 24);
|
|
int new_token;
|
|
if (token_map.TryGetValue (old_token, out new_token)) {
|
|
token_fixups [i].member = member_map [old_token];
|
|
int old_cl = code_len;
|
|
code_len = pos;
|
|
emit_int (new_token);
|
|
code_len = old_cl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Used by MethodBuilder.SetMethodBody
|
|
internal void SetExceptionHandlers (ILExceptionInfo[] exHandlers) {
|
|
this.ex_handlers = exHandlers;
|
|
}
|
|
|
|
// Used by MethodBuilder.SetMethodBody
|
|
internal void SetTokenFixups (ILTokenInfo[] tokenFixups) {
|
|
this.token_fixups = tokenFixups;
|
|
}
|
|
|
|
// Used by DynamicILGenerator and MethodBuilder.SetMethodBody
|
|
internal void SetCode (byte[] code, int max_stack) {
|
|
// Make a copy to avoid possible security problems
|
|
this.code = (byte[])code.Clone ();
|
|
this.code_len = code.Length;
|
|
this.max_stack = max_stack;
|
|
this.cur_stack = 0;
|
|
}
|
|
|
|
internal unsafe void SetCode (byte *code, int code_size, int max_stack) {
|
|
// Make a copy to avoid possible security problems
|
|
this.code = new byte [code_size];
|
|
for (int i = 0; i < code_size; ++i)
|
|
this.code [i] = code [i];
|
|
this.code_len = code_size;
|
|
this.max_stack = max_stack;
|
|
this.cur_stack = 0;
|
|
}
|
|
|
|
internal void Init (byte[] il, int maxStack, byte[] localSignature,
|
|
IEnumerable<ExceptionHandler> exceptionHandlers, IEnumerable<int> tokenFixups)
|
|
{
|
|
SetCode (il, maxStack);
|
|
|
|
// FIXME: Process local signature
|
|
|
|
// Process exception handlers
|
|
if (exceptionHandlers != null) {
|
|
// Group exception handlers by try blocks
|
|
var tryBlocks = new Dictionary <Tuple<int, int>, List<ExceptionHandler>> ();
|
|
foreach (var h in exceptionHandlers) {
|
|
List<ExceptionHandler> list;
|
|
var key = new Tuple <int, int> (h.TryOffset, h.TryLength);
|
|
if (!tryBlocks.TryGetValue (key, out list)) {
|
|
list = new List<ExceptionHandler> ();
|
|
tryBlocks.Add (key, list);
|
|
}
|
|
list.Add (h);
|
|
}
|
|
|
|
// Generate ILExceptionInfo from tryBlocks
|
|
var infos = new List<ILExceptionInfo> ();
|
|
foreach (var kv in tryBlocks) {
|
|
var info = new ILExceptionInfo () {
|
|
start = kv.Key.Item1,
|
|
len = kv.Key.Item2,
|
|
handlers = new ILExceptionBlock [kv.Value.Count],
|
|
};
|
|
infos.Add (info);
|
|
var i = 0;
|
|
foreach (var b in kv.Value) {
|
|
info.handlers [i++] = new ILExceptionBlock () {
|
|
start = b.HandlerOffset,
|
|
len = b.HandlerLength,
|
|
filter_offset = b.FilterOffset,
|
|
type = (int) b.Kind,
|
|
extype = module.ResolveType (b.ExceptionTypeToken),
|
|
};
|
|
}
|
|
}
|
|
|
|
SetExceptionHandlers (infos.ToArray ());
|
|
}
|
|
|
|
// Process token fixups
|
|
if (tokenFixups != null) {
|
|
var tokenInfos = new List<ILTokenInfo> ();
|
|
foreach (var pos in tokenFixups) {
|
|
var token = (int) BitConverter.ToUInt32 (il, pos);
|
|
var tokenInfo = new ILTokenInfo () {
|
|
code_pos = pos,
|
|
member = ((ModuleBuilder) module).ResolveOrGetRegisteredToken (token, null, null)
|
|
};
|
|
tokenInfos.Add (tokenInfo);
|
|
}
|
|
|
|
SetTokenFixups (tokenInfos.ToArray ());
|
|
}
|
|
}
|
|
|
|
|
|
internal TokenGenerator TokenGenerator {
|
|
get {
|
|
return token_gen;
|
|
}
|
|
}
|
|
|
|
// Still used by symbolwriter
|
|
[Obsolete ("Use ILOffset", true)]
|
|
internal static int Mono_GetCurrentOffset (ILGenerator ig)
|
|
{
|
|
return ig.code_len;
|
|
}
|
|
|
|
public
|
|
virtual int ILOffset {
|
|
get { return code_len; }
|
|
}
|
|
|
|
void _ILGenerator.GetIDsOfNames ([In] ref Guid riid, IntPtr rgszNames, uint cNames, uint lcid, IntPtr rgDispId)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
void _ILGenerator.GetTypeInfo (uint iTInfo, uint lcid, IntPtr ppTInfo)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
void _ILGenerator.GetTypeInfoCount (out uint pcTInfo)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
void _ILGenerator.Invoke (uint dispIdMember, [In] ref Guid riid, uint lcid, short wFlags, IntPtr pDispParams, IntPtr pVarResult, IntPtr pExcepInfo, IntPtr puArgErr)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
internal class SequencePointList
|
|
{
|
|
ISymbolDocumentWriter doc;
|
|
SequencePoint[] points;
|
|
int count;
|
|
const int arrayGrow = 10;
|
|
|
|
public SequencePointList (ISymbolDocumentWriter doc)
|
|
{
|
|
this.doc = doc;
|
|
}
|
|
|
|
public ISymbolDocumentWriter Document {
|
|
get { return doc; }
|
|
}
|
|
|
|
public int[] GetOffsets()
|
|
{
|
|
int[] data = new int [count];
|
|
for (int n=0; n<count; n++) data [n] = points[n].Offset;
|
|
return data;
|
|
}
|
|
public int[] GetLines()
|
|
{
|
|
int[] data = new int [count];
|
|
for (int n=0; n<count; n++) data [n] = points[n].Line;
|
|
return data;
|
|
}
|
|
public int[] GetColumns()
|
|
{
|
|
int[] data = new int [count];
|
|
for (int n=0; n<count; n++) data [n] = points[n].Col;
|
|
return data;
|
|
}
|
|
public int[] GetEndLines()
|
|
{
|
|
int[] data = new int [count];
|
|
for (int n=0; n<count; n++) data [n] = points[n].EndLine;
|
|
return data;
|
|
}
|
|
public int[] GetEndColumns()
|
|
{
|
|
int[] data = new int [count];
|
|
for (int n=0; n<count; n++) data [n] = points[n].EndCol;
|
|
return data;
|
|
}
|
|
public int StartLine {
|
|
get { return points[0].Line; }
|
|
}
|
|
public int EndLine {
|
|
get { return points[count - 1].Line; }
|
|
}
|
|
public int StartColumn {
|
|
get { return points[0].Col; }
|
|
}
|
|
public int EndColumn {
|
|
get { return points[count - 1].Col; }
|
|
}
|
|
|
|
public void AddSequencePoint (int offset, int line, int col, int endLine, int endCol)
|
|
{
|
|
SequencePoint s = new SequencePoint ();
|
|
s.Offset = offset;
|
|
s.Line = line;
|
|
s.Col = col;
|
|
s.EndLine = endLine;
|
|
s.EndCol = endCol;
|
|
|
|
if (points == null) {
|
|
points = new SequencePoint [arrayGrow];
|
|
} else if (count >= points.Length) {
|
|
SequencePoint[] temp = new SequencePoint [count + arrayGrow];
|
|
Array.Copy (points, temp, points.Length);
|
|
points = temp;
|
|
}
|
|
|
|
points [count] = s;
|
|
count++;
|
|
}
|
|
}
|
|
|
|
struct SequencePoint {
|
|
public int Offset;
|
|
public int Line;
|
|
public int Col;
|
|
public int EndLine;
|
|
public int EndCol;
|
|
}
|
|
}
|
|
#endif
|