2021-09-20 18:20:01 +02:00

153 lines
5.2 KiB
C#

/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics;
using dnSpy.Contracts.Debugger.DotNet.Evaluation;
using dnSpy.Debugger.DotNet.Interpreter;
using dnSpy.Debugger.DotNet.Metadata;
namespace dnSpy.Debugger.DotNet.Evaluation.Engine.Interpreter {
sealed class ArrayILValue : TypeILValueImpl {
long cachedArrayLength;
const long cachedArrayLength_uninitialized = -1;
const long cachedArrayLength_error = -2;
uint elementCount;
DbgDotNetArrayDimensionInfo[]? dimensionInfos;
public ArrayILValue(DebuggerRuntimeImpl runtime, DbgDotNetValue arrayValue)
: base(runtime, arrayValue) {
cachedArrayLength = cachedArrayLength_uninitialized;
}
void InitializeArrayInfo() {
if (dimensionInfos is not null)
return;
if (!ObjValue.GetArrayInfo(out elementCount, out dimensionInfos))
throw new InvalidOperationException();
}
public override bool Call(bool isCallvirt, DmdMethodBase method, ILValue[] arguments, out ILValue? returnValue) {
switch (method.SpecialMethodKind) {
case DmdSpecialMethodKind.Array_Get:
returnValue = LoadArrayElement(GetZeroBasedIndex(arguments, arguments.Length));
return returnValue is not null;
case DmdSpecialMethodKind.Array_Set:
StoreArrayElement(GetZeroBasedIndex(arguments, arguments.Length - 1), arguments[arguments.Length - 1]);
returnValue = null;
return true;
case DmdSpecialMethodKind.Array_Address:
uint index = GetZeroBasedIndex(arguments, arguments.Length);
var addrValue = ObjValue.GetArrayElementAddressAt(index);
if (addrValue is not null) {
runtime.RecordValue(addrValue.Value);
Debug.Assert(addrValue.Value.Value!.Type.IsByRef);
returnValue = new ByRefILValueImpl(runtime, addrValue.Value.Value);
}
else
returnValue = new ArrayElementAddress(runtime, this, index);
return true;
}
return base.Call(isCallvirt, method, arguments, out returnValue);
}
uint GetZeroBasedIndex(ILValue[] indexes, int count) {
if (dimensionInfos is null)
InitializeArrayInfo();
if (dimensionInfos!.Length != count)
throw new InvalidOperationException();
uint result = 0;
for (int i = 0; i < dimensionInfos.Length; i++) {
ref readonly var dim = ref dimensionInfos[i];
uint index = (uint)(runtime.ToInt32(indexes[i]) - dim.BaseIndex);
if (index >= dim.Length)
throw new InvalidOperationException();
result = checked(result * dim.Length + index);
}
return result;
}
internal DbgDotNetValue? ReadArrayElement(long index) {
if ((ulong)index > uint.MaxValue)
return null;
return runtime.RecordValue(ObjValue.GetArrayElementAt((uint)index));
}
void StoreArrayElement(uint index, ILValue value) => runtime.SetArrayElementAt(ObjValue, index, value);
internal void StoreArrayElement(uint index, object? value) => runtime.SetArrayElementAt(ObjValue, index, value);
ILValue LoadArrayElement(uint index) => runtime.CreateILValue(ObjValue.GetArrayElementAt(index));
public override ILValue? LoadSZArrayElement(LoadValueType loadValueType, long index, DmdType elementType) {
if (!ObjValue.Type.IsSZArray)
return null;
if ((ulong)index > uint.MaxValue)
return null;
return LoadArrayElement((uint)index);
}
public override bool StoreSZArrayElement(LoadValueType loadValueType, long index, ILValue value, DmdType elementType) {
if (!ObjValue.Type.IsSZArray)
return false;
if ((ulong)index > uint.MaxValue)
return false;
runtime.SetArrayElementAt(ObjValue, (uint)index, value);
return true;
}
public override ILValue? LoadSZArrayElementAddress(long index, DmdType elementType) {
if (!ObjValue.Type.IsSZArray)
return null;
if ((ulong)index > uint.MaxValue)
return null;
var addrValue = ObjValue.GetArrayElementAddressAt((uint)index);
if (addrValue is not null) {
runtime.RecordValue(addrValue.Value);
Debug.Assert(addrValue.Value.Value!.Type.IsByRef);
return new ByRefILValueImpl(runtime, addrValue.Value.Value);
}
return new ArrayElementAddress(runtime, this, (uint)index);
}
public override bool GetSZArrayLength(out long length) {
if (!ObjValue.Type.IsSZArray)
cachedArrayLength = cachedArrayLength_error;
if (cachedArrayLength == cachedArrayLength_uninitialized) {
if (!ObjValue.GetArrayCount(out var arrayCount))
cachedArrayLength = cachedArrayLength_error;
else
cachedArrayLength = arrayCount;
}
if (cachedArrayLength >= 0) {
length = cachedArrayLength;
return true;
}
Debug.Assert(cachedArrayLength == cachedArrayLength_error);
length = 0;
return false;
}
}
}