Compare commits
47 Commits
Author | SHA1 | Date |
---|---|---|
yangzhi | 1df4408da0 | |
yangzhi | e511ad28a8 | |
yangzhi | 0822c41fc4 | |
yangzhi | 71f194d65a | |
yangzhi | 4d6a82f341 | |
yangzhi | e2f8f88186 | |
yangzhi | 8839cfe262 | |
yangzhi | c8f1bdc245 | |
yangzhi | dbf1476c4d | |
yangzhi | c14ceff074 | |
yangzhi | 0a17a0e6fd | |
yangzhi | b62fdf88e6 | |
yangzhi | fa62d719d5 | |
yangzhi | 0e859cfbcb | |
yangzhi | c0ef6a5b53 | |
yangzhi | e18ac2a111 | |
yangzhi | a22263b70a | |
yangzhi | 0c2c5fdcf6 | |
yangzhi | 46b21d1d3a | |
yangzhi | 589d48a969 | |
yangzhi | 44ddc4ca42 | |
Jesse Plamondon-Willard | 9e521091fe | |
Jesse Plamondon-Willard | 86a1049395 | |
Jesse Plamondon-Willard | a246862b5d | |
Jesse Plamondon-Willard | a8f2667fdd | |
Jesse Plamondon-Willard | 0bc1d090f8 | |
Jesse Plamondon-Willard | a2a5d591f2 | |
Jesse Plamondon-Willard | c3a9b69daa | |
Jesse Plamondon-Willard | e6e15bef1c | |
Jesse Plamondon-Willard | 9127b37a9b | |
Jesse Plamondon-Willard | e586c5bb60 | |
Jesse Plamondon-Willard | 07f3f1772f | |
Jesse Plamondon-Willard | c158c7fe29 | |
archification | e2126aae96 | |
Jesse Plamondon-Willard | a0fc06c395 | |
Jesse Plamondon-Willard | 16b37e0004 | |
Jesse Plamondon-Willard | 37a4754998 | |
Jesse Plamondon-Willard | f238b9f541 | |
Jesse Plamondon-Willard | 99e64eeeb2 | |
Jesse Plamondon-Willard | 448cf30e1b | |
Jesse Plamondon-Willard | 87afd4fbf8 | |
Jesse Plamondon-Willard | 5dddb0cf1f | |
Jesse Plamondon-Willard | 9c7537ce0b | |
Jesse Plamondon-Willard | afd1daef4c | |
Jesse Plamondon-Willard | 40891a176f | |
Jesse Plamondon-Willard | d50b983c0c | |
Jesse Plamondon-Willard | b47755d1eb |
|
@ -18,6 +18,9 @@ _ReSharper*/
|
|||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# LocalHistory
|
||||
.localhistory/
|
||||
|
||||
# NuGet packages
|
||||
*.nupkg
|
||||
**/packages/*
|
||||
|
@ -26,3 +29,6 @@ _ReSharper*/
|
|||
|
||||
# sensitive files
|
||||
appsettings.Development.json
|
||||
|
||||
# assemblies
|
||||
assemblies/
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.572
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DllRewrite", "DllRewrite\DllRewrite.csproj", "{F7213CDC-B3C1-4585-B6B8-FD358D199ECD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F7213CDC-B3C1-4585-B6B8-FD358D199ECD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F7213CDC-B3C1-4585-B6B8-FD358D199ECD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F7213CDC-B3C1-4585-B6B8-FD358D199ECD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F7213CDC-B3C1-4585-B6B8-FD358D199ECD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E0E6D6E4-81C7-4FE5-80EB-396DCEA89F8E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{F7213CDC-B3C1-4585-B6B8-FD358D199ECD}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>DllRewrite</RootNamespace>
|
||||
<AssemblyName>DllRewrite</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony">
|
||||
<HintPath>lib\0Harmony.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil">
|
||||
<HintPath>lib\Mono.Cecil.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Mdb">
|
||||
<HintPath>lib\Mono.Cecil.Mdb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Pdb">
|
||||
<HintPath>lib\Mono.Cecil.Pdb.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Mono.Cecil.Rocks">
|
||||
<HintPath>lib\Mono.Cecil.Rocks.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MethodPatcher.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace DllRewrite
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
MethodPatcher mp = new MethodPatcher();
|
||||
AssemblyDefinition StardewValley = mp.InsertModHooks();
|
||||
StardewValley.Write("./StardewValley.dll");
|
||||
//AssemblyDefinition MonoFramework = mp.InsertMonoHooks();
|
||||
//MonoFramework.Write("./MonoGame.Framework.dll");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 有关程序集的一般信息由以下
|
||||
// 控制。更改这些特性值可修改
|
||||
// 与程序集关联的信息。
|
||||
[assembly: AssemblyTitle("DllRewrite")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DllRewrite")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||
//请将此类型的 ComVisible 特性设置为 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||
[assembly: Guid("f7213cdc-b3c1-4585-b6b8-fd358d199ecd")]
|
||||
|
||||
// 程序集的版本信息由下列四个值组成:
|
||||
//
|
||||
// 主版本
|
||||
// 次版本
|
||||
// 生成号
|
||||
// 修订号
|
||||
//
|
||||
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
// 方法是按如下所示使用“*”: :
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,6 @@
|
|||
1.Patch the StardewValley.dll£¬see PatchStep.txt
|
||||
2.Put the folder SMDroid into /sdcard/
|
||||
3.Put the patched StardewValley.dll into /sdcard/SMDroid/
|
||||
4.Replace StardewValley.dll and StardewModdingAPI.dll into game's base.apk(copy to user folder to operate if no root acesss), compress level must be storage.Resign apk if you have no root access.
|
||||
5.Reinstall the patched base.apk
|
||||
Enjoy it.
|
|
@ -0,0 +1,61 @@
|
|||
1. Inject assembly reference, namespace: StardewModdingAPI
|
||||
|
||||
2.Modify class StardewValley.Game1, modify constructor method£¬insert Instructions at beginning:
|
||||
newobj System.Void SMDroid.ModEntry::.ctor()
|
||||
stsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
|
||||
3.Modify class StardewValley.ModHooks, inject method:
|
||||
public virtual void OnGame1_Update(GameTime time);
|
||||
public virtual void OnGame1_Draw(GameTime time, RenderTarget2D toBuffer);
|
||||
public virtual LocalizedContentManager OnGame1_CreateContentManager(IServiceProvider serviceProvider, string rootDirectory) => null;
|
||||
|
||||
4.Modify class StardewValley.Game1, modify method Update(GameTime gameTime), insert Instructions at beginning:
|
||||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.1
|
||||
callvirt System.Void StardewValley.ModHooks::OnGame1_Update(Microsoft.Xna.Framework.GameTime)
|
||||
|
||||
5.Modify class StardewValley.Game1, modify method CreateContentManager(GameTime gameTime), replace Instructions to:
|
||||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.1
|
||||
ldarg.2
|
||||
callvirt StardewValley.LocalizedContentManager StardewValley.ModHooks::OnGame1_CreateContentManager(System.IServiceProvider,System.String)
|
||||
ret
|
||||
|
||||
6.Modify class StardewValley.Game1, modify method Draw(GameTime gameTime, RenderTarget2D toBuffer), modify Instructions at beginning:
|
||||
ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
ldarg.1
|
||||
ldnull
|
||||
callvirt System.Void StardewValley.ModHooks::OnGame1_Draw(Microsoft.Xna.Framework.GameTime,Microsoft.Xna.Framework.Graphics.RenderTarget2D)
|
||||
|
||||
|
||||
|
||||
Optional
|
||||
|
||||
GamePad Input
|
||||
Modify class StardewValley.Game1, modify method updateAndroidMenus(), modify Instructions at beginning:
|
||||
ldsfld StardewValley.InputState StardewValley.Game1::input
|
||||
callvirt Microsoft.Xna.Framework.Input.GamePadState StardewValley.InputState::GetGamePadState()
|
||||
|
||||
|
||||
Json Asset£º
|
||||
checkForAction Prefix
|
||||
|
||||
0 ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
5 ldarg.0
|
||||
6 callvirt System.Boolean StardewValley.ModHooks::OnObject_checkForAction(StardewValley.Object)
|
||||
11 brtrue.s -> (6) ldarg.2
|
||||
13 ldc.i4.0
|
||||
14 ret
|
||||
|
||||
isIndexOkForBasicShippedCategory Postfix
|
||||
0 ldarg.0
|
||||
1 ldc.i4 434
|
||||
6 bne.un.s -> (5) ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
8 ldc.i4.0
|
||||
9 ret
|
||||
10 ldsfld StardewValley.ModHooks StardewValley.Game1::hooks
|
||||
15 ldarg.0
|
||||
16 ldloca.s -> (0) (System.Boolean)
|
||||
18 callvirt System.Void StardewValley.ModHooks::OnObject_isIndexOkForBasicShippedCategory(System.Int32,System.Boolean&)
|
||||
23 ldloc.0
|
||||
24 ret
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
|
||||
|
||||
|
||||
This file contains advanced configuration for SMAPI. You generally shouldn't change this file.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The console color theme to use. The possible values are:
|
||||
* - AutoDetect: SMAPI will assume a light background on Mac, and detect the background color automatically on Linux or Windows.
|
||||
* - LightBackground: use darker text colors that look better on a white or light background.
|
||||
* - DarkBackground: use lighter text colors that look better on a black or dark background.
|
||||
*/
|
||||
"ColorScheme": "AutoDetect",
|
||||
|
||||
/**
|
||||
* Whether SMAPI should check for newer versions of SMAPI and mods when you load the game. If new
|
||||
* versions are available, an alert will be shown in the console. This doesn't affect the load
|
||||
* time even if your connection is offline or slow, because it happens in the background.
|
||||
*/
|
||||
"CheckForUpdates": true,
|
||||
|
||||
/**
|
||||
* Whether to enable features intended for mod developers. Currently this only makes TRACE-level
|
||||
* messages appear in the console.
|
||||
*/
|
||||
"DeveloperMode": false,
|
||||
|
||||
/**
|
||||
* Whether to add a section to the 'mod issues' list for mods which directly use potentially
|
||||
* sensitive .NET APIs like file or shell access. Note that many mods do this legitimately as
|
||||
* part of their normal functionality, so these warnings are meaningless without further
|
||||
* investigation. When this is commented out, it'll be true for local debug builds and false
|
||||
* otherwise.
|
||||
*/
|
||||
//"ParanoidWarnings": true,
|
||||
|
||||
/**
|
||||
* Whether SMAPI should show newer beta versions as an available update. When this is commented
|
||||
* out, it'll be true if the current SMAPI version is beta, and false otherwise.
|
||||
*/
|
||||
//"UseBetaChannel": true,
|
||||
|
||||
/**
|
||||
* SMAPI's GitHub project name, used to perform update checks.
|
||||
*/
|
||||
"GitHubProjectName": "Pathoschild/SMAPI",
|
||||
|
||||
/**
|
||||
* The base URL for SMAPI's web API, used to perform update checks.
|
||||
* Note: the protocol will be changed to http:// on Linux/Mac due to OpenSSL issues with the
|
||||
* game's bundled Mono.
|
||||
*/
|
||||
"WebApiBaseUrl": "https://api.smapi.io",
|
||||
|
||||
/**
|
||||
* Whether SMAPI should log more information about the game context.
|
||||
*/
|
||||
"VerboseLogging": false,
|
||||
|
||||
/**
|
||||
* Whether to generate a 'SMAPI-latest.metadata-dump.json' file in the logs folder with the full mod
|
||||
* metadata for detected mods. This is only needed when troubleshooting some cases.
|
||||
*/
|
||||
"DumpMetadata": false,
|
||||
|
||||
/**
|
||||
* The mod IDs SMAPI should ignore when performing update checks or validating update keys.
|
||||
*/
|
||||
"SuppressUpdateChecks": [
|
||||
"SMAPI.ConsoleCommands",
|
||||
"SMAPI.SaveBackup"
|
||||
]
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,48 @@
|
|||
Additions allow you to add arbitrary C# to the generated classes
|
||||
before they are compiled. This can be helpful for providing convenience
|
||||
methods or adding pure C# classes.
|
||||
|
||||
== Adding Methods to Generated Classes ==
|
||||
|
||||
Let's say the library being bound has a Rectangle class with a constructor
|
||||
that takes an x and y position, and a width and length size. It will look like
|
||||
this:
|
||||
|
||||
public partial class Rectangle
|
||||
{
|
||||
public Rectangle (int x, int y, int width, int height)
|
||||
{
|
||||
// JNI bindings
|
||||
}
|
||||
}
|
||||
|
||||
Imagine we want to add a constructor to this class that takes a Point and
|
||||
Size structure instead of 4 ints. We can add a new file called Rectangle.cs
|
||||
with a partial class containing our new method:
|
||||
|
||||
public partial class Rectangle
|
||||
{
|
||||
public Rectangle (Point location, Size size) :
|
||||
this (location.X, location.Y, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
At compile time, the additions class will be added to the generated class
|
||||
and the final assembly will a Rectangle class with both constructors.
|
||||
|
||||
|
||||
== Adding C# Classes ==
|
||||
|
||||
Another thing that can be done is adding fully C# managed classes to the
|
||||
generated library. In the above example, let's assume that there isn't a
|
||||
Point class available in Java or our library. The one we create doesn't need
|
||||
to interact with Java, so we'll create it like a normal class in C#.
|
||||
|
||||
By adding a Point.cs file with this class, it will end up in the binding library:
|
||||
|
||||
public class Point
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{A8FBE3C3-BDE1-42C5-A4B9-F34CE5A68F46}</ProjectGuid>
|
||||
<ProjectTypeGuids>{10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TemplateGuid>{77efb91c-a7e9-4b0e-a7c5-31eeec3c6d46}</TemplateGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>JarBinding</RootNamespace>
|
||||
<AssemblyName>JarBinding</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
|
||||
<AndroidClassParser>class-parse</AndroidClassParser>
|
||||
<AndroidCodegenTarget>XAJavaInterop1</AndroidCodegenTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Jars\AboutJars.txt" />
|
||||
<None Include="Additions\AboutAdditions.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<TransformFile Include="Transforms\Metadata.xml" />
|
||||
<TransformFile Include="Transforms\EnumFields.xml" />
|
||||
<TransformFile Include="Transforms\EnumMethods.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedJar Include="Jars\pgyer_sdk_3.0.4.jar" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,24 @@
|
|||
This directory is for Android .jars.
|
||||
|
||||
There are 2 types of jars that are supported:
|
||||
|
||||
== Input Jar ==
|
||||
|
||||
This is the jar that bindings should be generated for.
|
||||
|
||||
For example, if you were binding the Google Maps library, this would
|
||||
be Google's "maps.jar".
|
||||
|
||||
Set the build action for these jars in the properties page to "InputJar".
|
||||
|
||||
|
||||
== Reference Jars ==
|
||||
|
||||
These are jars that are referenced by the input jar. C# bindings will
|
||||
not be created for these jars. These jars will be used to resolve
|
||||
types used by the input jar.
|
||||
|
||||
NOTE: Do not add "android.jar" as a reference jar. It will be added automatically
|
||||
based on the Target Framework selected.
|
||||
|
||||
Set the build action for these jars in the properties page to "ReferenceJar".
|
Binary file not shown.
|
@ -0,0 +1,30 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("JarBinding")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("JarBinding")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,14 @@
|
|||
<enum-field-mappings>
|
||||
<!--
|
||||
This example converts the constants Fragment_id, Fragment_name,
|
||||
and Fragment_tag from android.support.v4.app.FragmentActivity.FragmentTag
|
||||
to an enum called Android.Support.V4.App.FragmentTagType with values
|
||||
Id, Name, and Tag.
|
||||
|
||||
<mapping jni-class="android/support/v4/app/FragmentActivity$FragmentTag" clr-enum-type="Android.Support.V4.App.FragmentTagType">
|
||||
<field jni-name="Fragment_name" clr-name="Name" value="0" />
|
||||
<field jni-name="Fragment_id" clr-name="Id" value="1" />
|
||||
<field jni-name="Fragment_tag" clr-name="Tag" value="2" />
|
||||
</mapping>
|
||||
-->
|
||||
</enum-field-mappings>
|
|
@ -0,0 +1,13 @@
|
|||
<enum-method-mappings>
|
||||
<!--
|
||||
This example changes the Java method:
|
||||
android.support.v4.app.Fragment.SavedState.writeToParcel (int flags)
|
||||
to be:
|
||||
android.support.v4.app.Fragment.SavedState.writeToParcel (Android.OS.ParcelableWriteFlags flags)
|
||||
when bound in C#.
|
||||
|
||||
<mapping jni-class="android/support/v4/app/Fragment.SavedState">
|
||||
<method jni-name="writeToParcel" parameter="flags" clr-enum-type="Android.OS.ParcelableWriteFlags" />
|
||||
</mapping>
|
||||
-->
|
||||
</enum-method-mappings>
|
|
@ -0,0 +1,9 @@
|
|||
<metadata>
|
||||
<!--
|
||||
This sample removes the class: android.support.v4.content.AsyncTaskLoader.LoadTask:
|
||||
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='AsyncTaskLoader.LoadTask']" />
|
||||
|
||||
This sample removes the method: android.support.v4.content.CursorLoader.loadInBackground:
|
||||
<remove-node path="/api/package[@name='android.support.v4.content']/class[@name='CursorLoader']/method[@name='loadInBackground']" />
|
||||
-->
|
||||
</metadata>
|
|
@ -0,0 +1,608 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Support.V4.App;
|
||||
using Android.Support.V4.Content;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Com.Pgyersdk.Update;
|
||||
using DllRewrite;
|
||||
using Java.Lang;
|
||||
using Microsoft.AppCenter;
|
||||
using Microsoft.AppCenter.Analytics;
|
||||
using Microsoft.AppCenter.Crashes;
|
||||
using Microsoft.Xna.Framework;
|
||||
using ModLoader.Common;
|
||||
using ModLoader.Helper;
|
||||
using Mono.Cecil;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Framework.ModLoading;
|
||||
using StardewModdingAPI.Toolkit;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModData;
|
||||
using StardewModdingAPI.Toolkit.Serialisation;
|
||||
using static ModLoader.Common.Utils;
|
||||
using Exception = System.Exception;
|
||||
using File = Java.IO.File;
|
||||
using Object = System.Object;
|
||||
using Thread = System.Threading.Thread;
|
||||
using Uri = Android.Net.Uri;
|
||||
|
||||
namespace ModLoader
|
||||
{
|
||||
[Activity(Label = "@string/ApplicationName"
|
||||
, MainLauncher = true
|
||||
, Icon = "@drawable/icon"
|
||||
, Theme = "@style/Theme.Splash"
|
||||
, AlwaysRetainTaskState = true
|
||||
, LaunchMode = LaunchMode.SingleInstance
|
||||
, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize | ConfigChanges.ScreenLayout)]
|
||||
[SuppressMessage("ReSharper", "ArrangeThisQualifier")]
|
||||
public class Activity1 : AndroidGameActivity
|
||||
{
|
||||
private string[] requiredPermissions => new[] { "android.permission.ACCESS_NETWORK_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.INTERNET", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.VIBRATE", "android.permission.WAKE_LOCK", "android.permission.WRITE_EXTERNAL_STORAGE", "com.android.vending.CHECK_LICENSE" };
|
||||
private string[] DeniedPermissionsArray
|
||||
{
|
||||
get
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
foreach (string permission in this.requiredPermissions)
|
||||
{
|
||||
if (this.PackageManager.CheckPermission(permission, this.PackageName) != Permission.Granted)
|
||||
{
|
||||
list.Add(permission);
|
||||
}
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
public bool HasPermissions => this.PackageManager.CheckPermission("android.permission.ACCESS_NETWORK_STATE", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("android.permission.ACCESS_WIFI_STATE", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("android.permission.INTERNET", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("android.permission.READ_EXTERNAL_STORAGE", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("android.permission.VIBRATE", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("android.permission.WAKE_LOCK", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("android.permission.WRITE_EXTERNAL_STORAGE", this.PackageName) == Permission.Granted && this.PackageManager.CheckPermission("com.android.vending.CHECK_LICENSE", this.PackageName) == Permission.Granted;
|
||||
|
||||
public void PromptForPermissions()
|
||||
{
|
||||
ActivityCompat.RequestPermissions(this, this.DeniedPermissionsArray, 0);
|
||||
}
|
||||
|
||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
|
||||
{
|
||||
if (permissions.Length == 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
string languageCode = Java.Util.Locale.Default.Language.Substring(0, 2);
|
||||
int num = 0;
|
||||
if (requestCode == 0)
|
||||
{
|
||||
for (int index = 0; index < grantResults.Length; ++index)
|
||||
{
|
||||
if (grantResults[index] == Android.Content.PM.Permission.Granted)
|
||||
++num;
|
||||
else if (grantResults[index] == Android.Content.PM.Permission.Denied)
|
||||
{
|
||||
try
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder((Context)this);
|
||||
if (ActivityCompat.ShouldShowRequestPermissionRationale((Activity)this, permissions[index]))
|
||||
{
|
||||
builder.SetMessage(this.PermissionMessageA(languageCode));
|
||||
builder.SetPositiveButton(this.GetOKString(languageCode), (EventHandler<DialogClickEventArgs>)((senderAlert, args) =>
|
||||
{
|
||||
this.PromptForPermissions();
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.SetMessage(this.PermissionMessageB(languageCode));
|
||||
builder.SetPositiveButton(this.GetOKString(languageCode), (EventHandler<DialogClickEventArgs>)((senderAlert, args) => OpenAppSettingsOnPhone(this)));
|
||||
}
|
||||
Dialog dialog = (Dialog)builder.Create();
|
||||
if (this.IsFinishing)
|
||||
return;
|
||||
dialog.Show();
|
||||
return;
|
||||
}
|
||||
catch (IllegalArgumentException ex)
|
||||
{
|
||||
// ISSUE: variable of the null type
|
||||
Microsoft.AppCenter.Crashes.Crashes.TrackError((System.Exception)ex, null);
|
||||
OpenInPlayStore();
|
||||
this.Finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num != permissions.Length)
|
||||
return;
|
||||
this.OnCreatePartTwo();
|
||||
}
|
||||
}
|
||||
private readonly Mutex _working = new Mutex(false);
|
||||
|
||||
public static Activity1 Instance { get; private set; }
|
||||
|
||||
private readonly HttpClient _httpClient = new HttpClient();
|
||||
|
||||
private static readonly Dictionary<int, Action> MessageHandler = new Dictionary<int, Action>();
|
||||
private readonly Handler _handler = new Handler(message =>
|
||||
{
|
||||
if (MessageHandler.ContainsKey(message.What))
|
||||
{
|
||||
MessageHandler[message.What]();
|
||||
}
|
||||
});
|
||||
|
||||
public void InvokeActivityThread(int what, Action action)
|
||||
{
|
||||
Message msg = new Message();
|
||||
msg.What = what;
|
||||
MessageHandler[what] = action;
|
||||
this._handler.SendMessage(msg);
|
||||
}
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
Instance = this;
|
||||
base.OnCreate(bundle);
|
||||
this.RequestWindowFeature(WindowFeatures.NoTitle);
|
||||
if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.N)
|
||||
{
|
||||
StrictMode.SetVmPolicy(new StrictMode.VmPolicy.Builder().Build());
|
||||
}
|
||||
try {
|
||||
File errorLog = this.FilesDir.ListFiles().FirstOrDefault(f => f.IsDirectory && f.Name == "error")?.ListFiles().FirstOrDefault(f => f.Name.EndsWith(".dat"));
|
||||
if (errorLog != null)
|
||||
{
|
||||
string errorLogPath = Path.Combine(this.ExternalCacheDir.AbsolutePath, "error.dat");
|
||||
StreamToFile(new FileStream(errorLog.AbsolutePath, FileMode.Open), errorLogPath);
|
||||
ShowConfirmDialog(this, Resource.String.Error, Resource.String.CrashReportMessage, Resource.String.View, Resource.String.Dismiss, () => { OpenTextFile(this, errorLogPath); });
|
||||
}
|
||||
Type[] services = { typeof(Analytics), typeof(Crashes) };
|
||||
AppCenter.Start("b8eaba94-d276-4c97-9953-0c91e7357e21", services);
|
||||
IEnumerable<File> errorLogs = this.FilesDir.ListFiles().FirstOrDefault(f => f.IsDirectory && f.Name == "error")?.ListFiles().ToList().FindAll(f => f.Name.EndsWith(".dat") || f.Name.EndsWith(".throwable"));
|
||||
if (errorLogs != null) foreach (var file in errorLogs) file.Delete();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.P)
|
||||
{
|
||||
this.Window.Attributes.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges;
|
||||
}
|
||||
this.Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);
|
||||
this.Window.SetFlags(WindowManagerFlags.KeepScreenOn, WindowManagerFlags.KeepScreenOn);
|
||||
this.SetContentView(Resource.Layout.layout_main);
|
||||
if (!this.HasPermissions)
|
||||
this.PromptForPermissions();
|
||||
else
|
||||
this.OnCreatePartTwo();
|
||||
}
|
||||
|
||||
private void OnCreatePartTwo()
|
||||
{
|
||||
if (GetConfig(this, "compatCheck", "true") == "false")
|
||||
Constants.CompatCheck = false;
|
||||
if (GetConfig(this, "upgradeCheck", "true") == "false")
|
||||
Constants.UpgradeCheck = false;
|
||||
else
|
||||
new PgyUpdateManager.Builder().SetForced(false).SetUserCanRetry(true).SetDeleteHistroyApk(true).Register();
|
||||
this.InitEnvironment();
|
||||
this.FindViewById<Button>(Resource.Id.buttonSetting).Click += (sender, args) =>
|
||||
{
|
||||
this.StartActivity(typeof(ActivitySetting));
|
||||
};
|
||||
|
||||
this.FindViewById<Button>(Resource.Id.buttonExtract).Click += (sender, args) =>
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
if (!this._working.WaitOne(10))
|
||||
return;
|
||||
try
|
||||
{
|
||||
PackageInfo packageInfo = this.PackageManager.GetInstalledPackages(PackageInfoFlags.MatchAll)
|
||||
.FirstOrDefault(package => package.PackageName == Constants.GamePackageName);
|
||||
if (packageInfo == null)
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.NotInstalledMessage), ToastLength.Short);
|
||||
return;
|
||||
}
|
||||
if (!new File(Constants.GamePath).Exists())
|
||||
{
|
||||
Directory.CreateDirectory(Constants.GamePath);
|
||||
}
|
||||
StatFs sf = new StatFs(Constants.GamePath);
|
||||
if (sf.AvailableBytes + GetDirectoryLength(Path.Combine(Constants.GamePath, "Game" + Path.DirectorySeparatorChar)) -
|
||||
160 * 1024 * 1024 < 0)
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.StorageIsFullMessage), ToastLength.Short);
|
||||
return;
|
||||
}
|
||||
AlertDialog dialog = null;
|
||||
ShowProgressDialog(this, Resource.String.Extract, this.Resources.GetText(Resource.String.ExtractingMessage), dlg => { dialog = dlg; });
|
||||
while (dialog == null)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
string sourceDir = packageInfo.ApplicationInfo.SourceDir;
|
||||
ZipHelper.UnZip(sourceDir, Path.Combine(Constants.GamePath, "Game" + Path.DirectorySeparatorChar));
|
||||
dialog.Dismiss();
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.ExtractedMessage),
|
||||
ToastLength.Long);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._working.ReleaseMutex();
|
||||
}
|
||||
}).Start();
|
||||
};
|
||||
this.FindViewById<Button>(Resource.Id.buttonGenerate).Click += (sender, args) =>
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
if (!this._working.WaitOne(10))
|
||||
return;
|
||||
try
|
||||
{
|
||||
if (!new File(Constants.AssemblyPath).Exists())
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.NotExtractedMessage), ToastLength.Short);
|
||||
return;
|
||||
}
|
||||
AlertDialog dialog = null;
|
||||
ShowProgressDialog(this, Resource.String.Generate, this.Resources.GetText(Resource.String.GeneratingMessage), dlg => { dialog = dlg; });
|
||||
while (dialog == null)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
MethodPatcher mp = new MethodPatcher();
|
||||
AssemblyDefinition stardewValley = mp.InsertModHooks();
|
||||
FileStream stream = new FileStream(Path.Combine(Constants.GamePath, "StardewValley.dll"), FileMode.Create,
|
||||
FileAccess.Write, FileShare.Read);
|
||||
stardewValley.Write(stream);
|
||||
stream.Close();
|
||||
AssemblyDefinition monoFramework = mp.InsertMonoHooks();
|
||||
stream = new FileStream(Path.Combine(Constants.GamePath, "MonoGame.Framework.dll"), FileMode.Create,
|
||||
FileAccess.Write, FileShare.Read);
|
||||
monoFramework.Write(stream);
|
||||
stream.Close();
|
||||
Stream stream2 = this.Resources.OpenRawResource(Resource.Raw.SMDroidFiles);
|
||||
ZipHelper.UnZip(stream2, Constants.GamePath);
|
||||
dialog.Dismiss();
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.GeneratedMessage),
|
||||
ToastLength.Long);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._working.ReleaseMutex();
|
||||
}
|
||||
}).Start();
|
||||
};
|
||||
this.FindViewById<Button>(Resource.Id.buttonLaunch).Click += (sender, args) =>
|
||||
{
|
||||
if (!this._working.WaitOne(10))
|
||||
return;
|
||||
this._working.ReleaseMutex();
|
||||
if (!new File(Constants.GamePath).Exists())
|
||||
{
|
||||
Directory.CreateDirectory(Constants.GamePath);
|
||||
}
|
||||
if (!new File(Path.Combine(Constants.ContentPath, "XACT/FarmerSounds.xgs".Replace('/', Path.DirectorySeparatorChar))).Exists())
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.NotExtractedMessage), ToastLength.Short);
|
||||
return;
|
||||
}
|
||||
if (!new File(Path.Combine(Constants.GamePath, "StardewValley.dll")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GamePath, "MonoGame.Framework.dll")).Exists())
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.NotGeneratedMessage), ToastLength.Short);
|
||||
return;
|
||||
}
|
||||
if (!new File(Path.Combine(Constants.GameInternalPath, "StardewModdingAPI.config.json")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GameInternalPath, "StardewModdingAPI.metadata.json")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GamePath, "StardewModdingAPI.dll")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GamePath, "System.Xml.Linq.dll")).Exists())
|
||||
{
|
||||
Stream stream = this.Resources.OpenRawResource(Resource.Raw.SMDroidFiles);
|
||||
ZipHelper.UnZip(stream, Constants.GamePath);
|
||||
}
|
||||
this.StartActivityForResult(typeof(SMainActivity), 1);
|
||||
};
|
||||
|
||||
this.FindViewById<Button>(Resource.Id.buttonWiki).Click += (sender, args) =>
|
||||
{
|
||||
Uri uri = Uri.Parse("http://smd.zaneyork.cn");
|
||||
Intent intent = new Intent(Intent.ActionView, uri);
|
||||
this.StartActivity(intent);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void InitEnvironment()
|
||||
{
|
||||
this._working.WaitOne();
|
||||
try
|
||||
{
|
||||
if (!new File(Constants.GamePath).Exists())
|
||||
{
|
||||
Directory.CreateDirectory(Constants.GamePath);
|
||||
}
|
||||
if (!new File(Path.Combine(Constants.GameInternalPath, "StardewModdingAPI.config.json")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GameInternalPath, "StardewModdingAPI.metadata.json")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GamePath, "StardewModdingAPI.dll")).Exists() ||
|
||||
!new File(Path.Combine(Constants.GamePath, "System.Xml.Linq.dll")).Exists())
|
||||
{
|
||||
Stream stream = this.Resources.OpenRawResource(Resource.Raw.SMDroidFiles);
|
||||
ZipHelper.UnZip(stream, Constants.GamePath);
|
||||
}
|
||||
string modListFileName = Path.Combine(Constants.GameInternalPath, "ModList.json");
|
||||
if (!new File(modListFileName).Exists())
|
||||
{
|
||||
Stream stream = this.Resources.OpenRawResource(Resource.Raw.ModList);
|
||||
StreamToFile(stream, modListFileName);
|
||||
}
|
||||
this.PrepareModList();
|
||||
new Thread(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpResponseMessage responseMessage = this._httpClient
|
||||
.GetAsync("https://github.com/ZaneYork/SMAPI/raw/android/ModLoader/Resources/Raw/ModList.json")
|
||||
.Result.EnsureSuccessStatusCode();
|
||||
string modList = await responseMessage.Content.ReadAsStringAsync();
|
||||
string originJson = System.IO.File.ReadAllText(modListFileName);
|
||||
if (originJson != modList)
|
||||
{
|
||||
new JsonHelper().Deserialise<ModInfo[]>(modList);
|
||||
System.IO.File.WriteAllText(modListFileName, modList);
|
||||
if (!this.IsFinishing)
|
||||
{
|
||||
this.InvokeActivityThread(0, this.PrepareModList);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._working.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
|
||||
internal void InstallMod(ModInfo mod)
|
||||
{
|
||||
new Thread(async () =>
|
||||
{
|
||||
if (!this._working.WaitOne(10))
|
||||
return;
|
||||
try
|
||||
{
|
||||
AlertDialog dialog = null;
|
||||
ShowProgressDialog(this, Resource.String.ModInstall,
|
||||
this.Resources.GetText(Resource.String.ModDownloadingMessage), dlg => { dialog = dlg; });
|
||||
while (dialog == null)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
try
|
||||
{
|
||||
HttpResponseMessage responseMessage = this._httpClient.GetAsync(mod.DownloadUrl).Result.EnsureSuccessStatusCode();
|
||||
byte[] bytes = await responseMessage.Content.ReadAsByteArrayAsync();
|
||||
if (bytes[0] == 80 && bytes[1] == 75)
|
||||
{
|
||||
ZipHelper.UnZip(new MemoryStream(bytes), Constants.ModPath + Path.DirectorySeparatorChar);
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.ModInstalledMessage),
|
||||
ToastLength.Long);
|
||||
this.InvokeActivityThread(0, this.PrepareModList);
|
||||
}
|
||||
else
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.NetworkErrorMessage),
|
||||
ToastLength.Long);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.NetworkErrorMessage),
|
||||
ToastLength.Long);
|
||||
}
|
||||
finally
|
||||
{
|
||||
dialog.Dismiss();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this._working.ReleaseMutex();
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
internal void ConfigMod(string configPath)
|
||||
{
|
||||
OpenTextFile(this, configPath);
|
||||
}
|
||||
|
||||
internal void RemoveMod(ModInfo mod)
|
||||
{
|
||||
if (mod.Metadata?.DirectoryPath != null)
|
||||
{
|
||||
File file = new File(mod.Metadata.DirectoryPath);
|
||||
if (file.Exists() && file.IsDirectory)
|
||||
{
|
||||
ShowConfirmDialog(this, Resource.String.Confirm, Resource.String.RemoveConfirmMessage, Resource.String.Confirm, Resource.String.Cancel,
|
||||
() =>
|
||||
{
|
||||
Directory.Delete(mod.Metadata.DirectoryPath, true);
|
||||
MakeToast(this, this.Resources.GetText(Resource.String.ModRemovedMessage),
|
||||
ToastLength.Long);
|
||||
this.InvokeActivityThread(0, this.PrepareModList);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PrepareModList()
|
||||
{
|
||||
if (!new File(Constants.ModPath).Exists())
|
||||
{
|
||||
Directory.CreateDirectory(Constants.ModPath);
|
||||
}
|
||||
string modListFileName = Path.Combine(Constants.GameInternalPath, "ModList.json");
|
||||
new JsonHelper().ReadJsonFileIfExists(modListFileName, out ModInfo[] modInfos);
|
||||
Dictionary<string, ModInfo> modInfoDictionary = modInfos.ToDictionary(info => info.UniqueID, info => info);
|
||||
ListView listView = this.FindViewById<ListView>(Resource.Id.listView1);
|
||||
ModToolkit toolkit = new ModToolkit();
|
||||
ModDatabase modDatabase = toolkit.GetModDatabase(StardewModdingAPI.Constants.ApiMetadataPath);
|
||||
ModResolver resolver = new ModResolver();
|
||||
IModMetadata[] mods = resolver.ReadManifests(toolkit, Constants.ModPath, modDatabase).ToArray();
|
||||
Array.Sort(mods, (a, b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.CurrentCulture));
|
||||
List<ModInfo> modList = new List<ModInfo>();
|
||||
HashSet<string> installedModList = new HashSet<string>();
|
||||
foreach (IModMetadata metadata in mods)
|
||||
{
|
||||
if (!metadata.HasManifest())
|
||||
{
|
||||
modList.Add(new ModInfo(metadata));
|
||||
}
|
||||
else if (modInfoDictionary.ContainsKey(metadata.Manifest.UniqueID))
|
||||
{
|
||||
modInfoDictionary[metadata.Manifest.UniqueID].Metadata = metadata;
|
||||
modList.Add(modInfoDictionary[metadata.Manifest.UniqueID]);
|
||||
installedModList.Add(metadata.Manifest.UniqueID);
|
||||
}
|
||||
else
|
||||
{
|
||||
modList.Add(new ModInfo(metadata));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ModInfo modInfo in modInfos)
|
||||
{
|
||||
if (!installedModList.Contains(modInfo.UniqueID))
|
||||
{
|
||||
modList.Add(modInfo);
|
||||
}
|
||||
}
|
||||
listView.ScrollStateChanged -= this.ListView_ScrollStateChanged;
|
||||
listView.ScrollStateChanged += this.ListView_ScrollStateChanged;
|
||||
listView.Adapter = new ModListAdapter(this, Resource.Layout.layout_mod_list, modList);
|
||||
if (this._position != -1)
|
||||
{
|
||||
listView.SetSelection(this._position);
|
||||
}
|
||||
}
|
||||
|
||||
private int _position = -1;
|
||||
private void ListView_ScrollStateChanged(object sender, AbsListView.ScrollStateChangedEventArgs e)
|
||||
{
|
||||
if (e.ScrollState == ScrollState.Idle)
|
||||
{
|
||||
this._position = e.View.FirstVisiblePosition;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
|
||||
{
|
||||
if (requestCode == 1)
|
||||
{
|
||||
this.Finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
PgyUpdateManager.UnRegister();
|
||||
}
|
||||
|
||||
private string PermissionMessageA(string languageCode)
|
||||
{
|
||||
if (languageCode == "de")
|
||||
return "Du musst die Erlaubnis zum Lesen/Schreiben auf dem externen Speicher geben, um das Spiel zu speichern und Speicherstände auf andere Plattformen übertragen zu können. Bitte gib diese Genehmigung, um spielen zu können.";
|
||||
if (languageCode == "es")
|
||||
return "Para guardar la partida y transferir partidas guardadas a y desde otras plataformas, se necesita permiso para leer/escribir en almacenamiento externo. Concede este permiso para poder jugar.";
|
||||
if (languageCode == "ja")
|
||||
return "外部機器への読み込み/書き出しの許可が、ゲームのセーブデータの保存や他プラットフォームとの双方向のデータ移行実行に必要です。プレイを続けるには許可をしてください。";
|
||||
if (languageCode == "pt")
|
||||
return "Para salvar o jogo e transferir jogos salvos entre plataformas é necessário permissão para ler/gravar em armazenamento externo. Forneça essa permissão para jogar.";
|
||||
if (languageCode == "ru")
|
||||
return "Для сохранения игры и переноса сохранений с/на другие платформы нужно разрешение на чтение-запись на внешнюю память. Дайте разрешение, чтобы начать играть.";
|
||||
if (languageCode == "ko")
|
||||
return "게임을 저장하려면 외부 저장공간에 대한 읽기/쓰기 권한이 필요합니다. 또한 저장 데이터 이전 기능을 허용해 다른 플랫폼에서 게임 진행상황을 가져올 때에도 권한이 필요합니다. 게임을 플레이하려면 권한을 허용해 주십시오.";
|
||||
if (languageCode == "tr")
|
||||
return "Oyunu kaydetmek ve kayıtları platformlardan platformlara taşımak için harici depolamada okuma/yazma izni gereklidir. Lütfen oynayabilmek için izin verin.";
|
||||
if (languageCode == "fr")
|
||||
return "Une autorisation de lecture / écriture sur un stockage externe est requise pour sauvegarder le jeu et vous permettre de transférer des sauvegardes vers et depuis d'autres plateformes. Veuillez donner l'autorisation afin de jouer.";
|
||||
if (languageCode == "hu")
|
||||
return "A játék mentéséhez, és ahhoz, hogy a különböző platformok között hordozhasd a játékmentést, engedélyezned kell a külső tárhely olvasását/írását, Kérjük, a játékhoz engedélyezd ezeket.";
|
||||
if (languageCode == "it")
|
||||
return "È necessaria l'autorizzazione a leggere/scrivere su un dispositivo di memorizzazione esterno per salvare la partita e per consentire di trasferire i salvataggi da e su altre piattaforme. Concedi l'autorizzazione per giocare.";
|
||||
return languageCode == "zh" ? "《星露谷物语》请求获得授权用来保存游戏数据以及访问线上功能。" : "Read/write to external storage permission is required to save the game, and to allow to you transfer saves to and from other platforms. Please give permission in order to play.";
|
||||
}
|
||||
|
||||
private string PermissionMessageB(string languageCode)
|
||||
{
|
||||
if (languageCode == "de")
|
||||
return "Bitte geh in die Handy-Einstellungen > Apps > Stardew Valley > Berechtigungen und aktiviere den Speicher, um das Spiel zu spielen.";
|
||||
if (languageCode == "es")
|
||||
return "En el teléfono, ve a Ajustes > Aplicaciones > Stardew Valley > Permisos y activa Almacenamiento para jugar al juego.";
|
||||
if (languageCode == "ja")
|
||||
return "設定 > アプリ > スターデューバレー > 許可の順に開いていき、ストレージを有効にしてからゲームをプレイしましょう。";
|
||||
if (languageCode == "pt")
|
||||
return "Acesse Configurar > Aplicativos > Stardew Valley > Permissões e ative Armazenamento para jogar.";
|
||||
if (languageCode == "ru")
|
||||
return "Перейдите в меню Настройки > Приложения > Stardew Valley > Разрешения и дайте доступ к памяти, чтобы начать играть.";
|
||||
if (languageCode == "ko")
|
||||
return "휴대전화의 설정 > 어플리케이션 > 스타듀 밸리 > 권한 에서 저장공간을 활성화한 뒤 게임을 플레이해 주십시오.";
|
||||
if (languageCode == "tr")
|
||||
return "Lütfen oyunu oynayabilmek için telefonda Ayarlar > Uygulamalar > Stardew Valley > İzinler ve Depolamayı etkinleştir yapın.";
|
||||
if (languageCode == "fr")
|
||||
return "Veuillez aller dans les Paramètres du téléphone> Applications> Stardew Valley> Autorisations, puis activez Stockage pour jouer.";
|
||||
if (languageCode == "hu")
|
||||
return "Lépje be a telefonodon a Beállítások > Alkalmazások > Stardew Valley > Engedélyek menübe, majd engedélyezd a Tárhelyet a játékhoz.";
|
||||
if (languageCode == "it")
|
||||
return "Nel telefono, vai su Impostazioni > Applicazioni > Stardew Valley > Autorizzazioni e attiva Memoria archiviazione per giocare.";
|
||||
return languageCode == "zh" ? "可在“设置-权限隐私-按应用管理权限-星露谷物语”进行设置,并打开“电话”、“读取位置信息”、“存储”权限。" : "Please go into phone Settings > Apps > Stardew Valley > Permissions, and enable Storage to play the game.";
|
||||
}
|
||||
private string GetOKString(string languageCode)
|
||||
{
|
||||
if (languageCode == "de")
|
||||
return "OK";
|
||||
if (languageCode == "es")
|
||||
return "DE ACUERDO";
|
||||
if (languageCode == "ja")
|
||||
return "OK";
|
||||
if (languageCode == "pt")
|
||||
return "Está bem";
|
||||
if (languageCode == "ru")
|
||||
return "Хорошо";
|
||||
if (languageCode == "ko")
|
||||
return "승인";
|
||||
if (languageCode == "tr")
|
||||
return "tamam";
|
||||
if (languageCode == "fr")
|
||||
return "D'accord";
|
||||
if (languageCode == "hu")
|
||||
return "rendben";
|
||||
if (languageCode == "it")
|
||||
return "ok";
|
||||
return languageCode == "zh" ? "好" : "OK";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using ModLoader.Common;
|
||||
using static ModLoader.Common.Utils;
|
||||
|
||||
namespace ModLoader
|
||||
{
|
||||
[Activity(Label = "ActivitySetting")]
|
||||
public class ActivitySetting : Activity
|
||||
{
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
this.SetContentView(Resource.Layout.layout_setting);
|
||||
CheckBox checkBoxCompat = this.FindViewById<CheckBox>(Resource.Id.checkBoxCompat);
|
||||
this.WireConfig(checkBoxCompat, "compatCheck", b => Constants.CompatCheck = b);
|
||||
CheckBox checkBoxUpgrade = this.FindViewById<CheckBox>(Resource.Id.checkBoxUpgrade);
|
||||
this.WireConfig(checkBoxUpgrade, "upgradeCheck", b => Constants.UpgradeCheck = b);
|
||||
}
|
||||
|
||||
private void WireConfig(CheckBox checkBox, string configSection, Action<bool> onChecked)
|
||||
{
|
||||
if (GetConfig(this, configSection, "true") == "false")
|
||||
{
|
||||
onChecked(false);
|
||||
checkBox.Checked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
onChecked(true);
|
||||
checkBox.Checked = true;
|
||||
}
|
||||
|
||||
checkBox.Click += (sender, args) =>
|
||||
{
|
||||
onChecked(checkBox.Checked);
|
||||
SetConfig(this, configSection, checkBox.Checked ? "true" : "false");
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
Any raw assets you want to be deployed with your application can be placed in
|
||||
this directory (and child directories) and given a Build Action of "AndroidAsset".
|
||||
|
||||
These files will be deployed with you package and will be accessible using Android's
|
||||
AssetManager, like this:
|
||||
|
||||
public class ReadAsset : Activity
|
||||
{
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
base.OnCreate (bundle);
|
||||
|
||||
InputStream input = Assets.Open ("my_asset.txt");
|
||||
}
|
||||
}
|
||||
|
||||
Additionally, some Android functions will automatically load asset files:
|
||||
|
||||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
|
Binary file not shown.
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace ModLoader.Common
|
||||
{
|
||||
class Constants
|
||||
{
|
||||
public static string GamePackageName { get; } = "com.chucklefish.stardewvalley";
|
||||
public static string GamePath { get; } = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.Path, "SMDroid" + Path.DirectorySeparatorChar);
|
||||
public static string AssemblyPath { get; } = Path.Combine(GamePath, "Game/assemblies/".Replace('/', Path.DirectorySeparatorChar));
|
||||
public static string ModPath { get; } = Path.Combine(GamePath, "Mods");
|
||||
public static string ContentPath { get; } = Path.Combine(Constants.GamePath, "Game/assets/Content".Replace('/', Path.DirectorySeparatorChar));
|
||||
public static string GameInternalPath { get; } = Path.Combine(Constants.GamePath, "smapi-internal");
|
||||
public static bool CompatCheck { get; set; } = true;
|
||||
public static bool UpgradeCheck { get; set; } = true;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,34 @@
|
|||
using Newtonsoft.Json;
|
||||
using StardewModdingAPI;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Framework.ModLoading;
|
||||
using StardewModdingAPI.Toolkit.Framework.ModData;
|
||||
using StardewModdingAPI.Toolkit.Serialisation.Converters;
|
||||
using StardewModdingAPI.Toolkit.Serialisation.Models;
|
||||
|
||||
namespace ModLoader.Common
|
||||
{
|
||||
public class ModInfo
|
||||
{
|
||||
public ModInfo() { }
|
||||
internal ModInfo(IModMetadata metadata)
|
||||
{
|
||||
this.Metadata = metadata;
|
||||
if (metadata != null)
|
||||
{
|
||||
this.Name = metadata?.DisplayName;
|
||||
this.Description = metadata?.Manifest?.Description;
|
||||
}
|
||||
}
|
||||
|
||||
public string UniqueID { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public ISemanticVersion Version { get; set; }
|
||||
[JsonConverter(typeof(ManifestDependencyArrayConverter))]
|
||||
public IManifestDependency[] Dependencies { get; set; }
|
||||
internal IModMetadata Metadata { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using ModLoader.Helper;
|
||||
using StardewModdingAPI.Framework;
|
||||
|
||||
namespace ModLoader.Common
|
||||
{
|
||||
class ModListAdapter : ArrayAdapter<ModInfo>
|
||||
{
|
||||
private int textViewResourceId;
|
||||
public ModListAdapter(Context context, int textViewResourceId, List<ModInfo> mods) : base(context, textViewResourceId, mods)
|
||||
{
|
||||
this.textViewResourceId = textViewResourceId;
|
||||
}
|
||||
|
||||
public override View GetView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
ModInfo mod = this.GetItem(position);
|
||||
View view = LayoutInflater.From(this.Context).Inflate(this.textViewResourceId, parent, false);
|
||||
TextView headText = view.FindViewById<TextView>(Resource.Id.textModName);
|
||||
TextView descriptionText = view.FindViewById<TextView>(Resource.Id.textDescription);
|
||||
Button buttonAddOrRemove = view.FindViewById<Button>(Resource.Id.buttonAddOrRemove);
|
||||
Button buttonConfig = view.FindViewById<Button>(Resource.Id.buttonConfig);
|
||||
headText.Text = mod.Name;
|
||||
descriptionText.Text = mod.Description;
|
||||
if (mod.Metadata == null)
|
||||
{
|
||||
buttonAddOrRemove.Text = this.Context.Resources.GetText(Resource.String.ModInstall);
|
||||
headText.SetTextColor(Color.Gray);
|
||||
}
|
||||
else if (mod.Metadata.HasManifest() && mod.Metadata.Manifest.Version != null && mod.Version != null && mod.Metadata.Manifest.Version.IsOlderThan(mod.Version))
|
||||
{
|
||||
buttonAddOrRemove.Text = this.Context.Resources.GetText(Resource.String.Update);
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonAddOrRemove.Text = this.Context.Resources.GetText(Resource.String.ModRemove);
|
||||
}
|
||||
|
||||
if (mod.Metadata == null || !File.Exists(System.IO.Path.Combine(mod.Metadata.DirectoryPath, "config.json")))
|
||||
{
|
||||
buttonConfig.Visibility = ViewStates.Invisible;
|
||||
if (headText.LayoutParameters is RelativeLayout.LayoutParams layoutParams1)
|
||||
{
|
||||
layoutParams1.RemoveRule(LayoutRules.LeftOf);
|
||||
layoutParams1.AddRule(LayoutRules.LeftOf, Resource.Id.buttonAddOrRemove);
|
||||
}
|
||||
if (descriptionText.LayoutParameters is RelativeLayout.LayoutParams layoutParams2)
|
||||
{
|
||||
layoutParams2.RemoveRule(LayoutRules.LeftOf);
|
||||
layoutParams2.AddRule(LayoutRules.LeftOf, Resource.Id.buttonAddOrRemove);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonConfig.Click += (sender, args) =>
|
||||
{
|
||||
Activity1.Instance.ConfigMod(System.IO.Path.Combine(mod.Metadata.DirectoryPath, "config.json"));
|
||||
};
|
||||
}
|
||||
buttonAddOrRemove.Click += (sender, args) =>
|
||||
{
|
||||
if (mod.Metadata == null)
|
||||
{
|
||||
Activity1.Instance.InstallMod(mod);
|
||||
}
|
||||
else if (mod.Metadata.HasManifest() && mod.Metadata.Manifest.Version != null && mod.Version != null && mod.Metadata.Manifest.Version.IsOlderThan(mod.Version))
|
||||
{
|
||||
Activity1.Instance.InstallMod(mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
Activity1.Instance.RemoveMod(mod);
|
||||
}
|
||||
};
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using Java.IO;
|
||||
|
||||
namespace ModLoader.Common
|
||||
{
|
||||
class Utils
|
||||
{
|
||||
public static byte[] FileToMemory(string filename)
|
||||
{
|
||||
byte[] bytes = new byte[2048];
|
||||
FileInputStream fs = new FileInputStream(filename);
|
||||
MemoryStream outStream = new MemoryStream();
|
||||
int len;
|
||||
while ((len = fs.Read(bytes, 0, bytes.Length)) > 0)
|
||||
{
|
||||
outStream.Write(bytes, 0, len);
|
||||
}
|
||||
|
||||
fs.Close();
|
||||
return outStream.ToArray();
|
||||
}
|
||||
|
||||
public static void StreamToFile(Stream stream, string fileName)
|
||||
{
|
||||
byte[] bytes = new byte[2048];
|
||||
FileStream fs = new FileStream(fileName, FileMode.Create);
|
||||
BinaryWriter bw = new BinaryWriter(fs);
|
||||
int len;
|
||||
while ((len = stream.Read(bytes, 0, bytes.Length)) > 0)
|
||||
{
|
||||
bw.Write(bytes, 0, len);
|
||||
}
|
||||
|
||||
bw.Close();
|
||||
fs.Close();
|
||||
}
|
||||
|
||||
public static long GetDirectoryLength(string dirPath)
|
||||
{
|
||||
//判断给定的路径是否存在,如果不存在则退出
|
||||
if (!Directory.Exists(dirPath))
|
||||
return 0;
|
||||
long len = 0;
|
||||
//定义一个DirectoryInfo对象
|
||||
DirectoryInfo di = new DirectoryInfo(dirPath);
|
||||
//通过GetFiles方法,获取di目录中的所有文件的大小
|
||||
foreach (FileInfo fi in di.GetFiles())
|
||||
{
|
||||
len += fi.Length;
|
||||
}
|
||||
//获取di中所有的文件夹,并存到一个新的对象数组中,以进行递归
|
||||
DirectoryInfo[] dis = di.GetDirectories();
|
||||
if (dis.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < dis.Length; i++)
|
||||
{
|
||||
|
||||
len += GetDirectoryLength(dis[i].FullName);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
public static void InvokeLooperThread(Action action)
|
||||
{
|
||||
new Thread(() =>
|
||||
{
|
||||
|
||||
Looper.Prepare();
|
||||
new Handler().Post(action);
|
||||
Looper.Loop();
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public static void MakeToast(Context context, string message, ToastLength toastLength)
|
||||
{
|
||||
InvokeLooperThread(() => Toast.MakeText(context, message, toastLength).Show());
|
||||
}
|
||||
|
||||
public static void ShowProgressDialog(Context context, int titleId, string message, Action<AlertDialog> returnCallback)
|
||||
{
|
||||
InvokeLooperThread(() =>
|
||||
{
|
||||
ProgressDialog dialog = new ProgressDialog(context);
|
||||
dialog.SetTitle(titleId);
|
||||
dialog.SetMessage(message);
|
||||
dialog.SetCancelable(false);
|
||||
dialog.SetProgressStyle(ProgressDialogStyle.Spinner);
|
||||
dialog.Show();
|
||||
returnCallback(dialog);
|
||||
});
|
||||
}
|
||||
|
||||
public static void ShowConfirmDialog(Context context, int titleId, int messageId, int confirmId, int cancelId, Action onConfirm = null,
|
||||
Action onCancel = null)
|
||||
{
|
||||
InvokeLooperThread(() =>
|
||||
{
|
||||
new AlertDialog.Builder(context).SetTitle(titleId).SetMessage(messageId).SetCancelable(true)
|
||||
.SetPositiveButton(confirmId, (sender, args) => onConfirm?.Invoke())
|
||||
.SetNegativeButton(cancelId, (sender, args) => onCancel?.Invoke())
|
||||
.Show();
|
||||
});
|
||||
}
|
||||
|
||||
public static void ShowAlertDialog(Context context, int titleId, string message, int confirmId, Action onConfirm = null)
|
||||
{
|
||||
InvokeLooperThread(() =>
|
||||
{
|
||||
new AlertDialog.Builder(context).SetTitle(titleId).SetMessage(message).SetCancelable(true)
|
||||
.SetPositiveButton(confirmId, (sender, args) => onConfirm?.Invoke())
|
||||
.Show();
|
||||
});
|
||||
}
|
||||
public static void OpenAppSettingsOnPhone(Context context)
|
||||
{
|
||||
Intent intent = new Intent();
|
||||
intent.SetAction("android.settings.APPLICATION_DETAILS_SETTINGS");
|
||||
Android.Net.Uri data = Android.Net.Uri.FromParts("package", context.PackageName, (string)null);
|
||||
intent.SetData(data);
|
||||
context.StartActivity(intent);
|
||||
}
|
||||
public static void OpenInPlayStore()
|
||||
{
|
||||
try
|
||||
{
|
||||
Intent intent = new Intent("android.intent.action.VIEW", Android.Net.Uri.Parse("market://details?id=" + Constants.GamePackageName));
|
||||
intent.AddFlags(ActivityFlags.NewTask);
|
||||
Application.Context.StartActivity(intent);
|
||||
}
|
||||
catch (ActivityNotFoundException)
|
||||
{
|
||||
Intent intent = new Intent("android.intent.action.VIEW", Android.Net.Uri.Parse("https://play.google.com/store/apps/details?id=" + Constants.GamePackageName));
|
||||
intent.AddFlags(ActivityFlags.NewTask);
|
||||
Application.Context.StartActivity(intent);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Microsoft.AppCenter.Crashes.Crashes.TrackError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenTextFile(Context context, string filename)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ActionView);
|
||||
intent.AddCategory(Intent.CategoryDefault);
|
||||
Java.IO.File configFile = new Java.IO.File(filename);
|
||||
intent.SetDataAndType(Android.Net.Uri.FromFile(configFile), "text/plain");
|
||||
intent.AddFlags(ActivityFlags.NewTask);
|
||||
try
|
||||
{
|
||||
context.StartActivity(intent);
|
||||
}
|
||||
catch (ActivityNotFoundException) { }
|
||||
}
|
||||
|
||||
public static string GetConfig(Context context, string key, string defValue)
|
||||
{
|
||||
ISharedPreferences sp = context.GetSharedPreferences("main_prefs", FileCreationMode.Private);
|
||||
return sp.GetString(key, defValue);
|
||||
}
|
||||
public static void SetConfig(Context context, string key, string value)
|
||||
{
|
||||
ISharedPreferences sp = context.GetSharedPreferences("main_prefs", FileCreationMode.Private);
|
||||
ISharedPreferencesEditor editor = sp.Edit();
|
||||
editor.PutString(key, value);
|
||||
editor.Apply();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Java.Util.Zip;
|
||||
|
||||
namespace ModLoader.Helper
|
||||
{
|
||||
|
||||
public class ZipHelper
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 功能:解压zip格式的文件。
|
||||
/// </summary>
|
||||
/// <param name="stream">压缩文件流</param>
|
||||
/// <param name="unZipDir">解压文件存放路径,为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹</param>
|
||||
/// <returns>解压是否成功</returns>
|
||||
public static void UnZip(Stream stream, string unZipDir)
|
||||
{
|
||||
using (var s = new ZipInputStream(stream))
|
||||
{
|
||||
|
||||
ZipEntry theEntry;
|
||||
while ((theEntry = s.NextEntry) != null)
|
||||
{
|
||||
string directoryName = Path.GetDirectoryName(theEntry.Name);
|
||||
string fileName = Path.GetFileName(theEntry.Name);
|
||||
if (!string.IsNullOrEmpty(directoryName))
|
||||
{
|
||||
Directory.CreateDirectory(unZipDir + directoryName);
|
||||
}
|
||||
if (directoryName != null && !directoryName.EndsWith("/"))
|
||||
{
|
||||
}
|
||||
if (fileName != string.Empty)
|
||||
{
|
||||
FileStream streamWriter = null;
|
||||
try
|
||||
{
|
||||
|
||||
using (streamWriter = File.Create(unZipDir + theEntry.Name))
|
||||
{
|
||||
|
||||
int size;
|
||||
byte[] data = new byte[2048];
|
||||
while (true)
|
||||
{
|
||||
size = s.Read(data, 0, data.Length);
|
||||
if (size > 0)
|
||||
{
|
||||
streamWriter.Write(data, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
finally
|
||||
{
|
||||
streamWriter?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void UnZip(string zipFilePath, string unZipDir)
|
||||
{
|
||||
if (zipFilePath == string.Empty)
|
||||
{
|
||||
throw new Exception("压缩文件不能为空!");
|
||||
}
|
||||
if (!File.Exists(zipFilePath))
|
||||
{
|
||||
throw new FileNotFoundException("压缩文件不存在!");
|
||||
}
|
||||
//解压文件夹为空时默认与压缩文件同一级目录下,跟压缩文件同名的文件夹
|
||||
if (unZipDir == string.Empty)
|
||||
unZipDir = zipFilePath.Replace(Path.GetFileName(zipFilePath), Path.GetFileNameWithoutExtension(zipFilePath));
|
||||
if (!unZipDir.EndsWith("/"))
|
||||
unZipDir += "/";
|
||||
if (!Directory.Exists(unZipDir))
|
||||
Directory.CreateDirectory(unZipDir);
|
||||
UnZip(File.OpenRead(zipFilePath), unZipDir);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
#----------------------------- Global Properties ----------------------------#
|
||||
|
||||
/outputDir:bin/$(Platform)
|
||||
/intermediateDir:obj/$(Platform)
|
||||
/platform:Android
|
||||
/config:
|
||||
/profile:Reach
|
||||
/compress:False
|
||||
|
||||
#-------------------------------- References --------------------------------#
|
||||
|
||||
|
||||
#---------------------------------- Content ---------------------------------#
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Internal.ConsoleWriting;
|
||||
using StardewValley;
|
||||
using StardewValley.Menus;
|
||||
|
||||
namespace StardewModdingAPI
|
||||
{
|
||||
class GameConsole : IClickableMenu
|
||||
{
|
||||
public static GameConsole Instance;
|
||||
public bool IsVisible;
|
||||
|
||||
private readonly LinkedList<KeyValuePair<ConsoleLogLevel, string>> _consoleMessageQueue = new LinkedList<KeyValuePair<ConsoleLogLevel, string>>();
|
||||
private readonly TextBox Textbox;
|
||||
private Rectangle TextboxBounds;
|
||||
|
||||
private SpriteFont _smallFont;
|
||||
|
||||
internal GameConsole()
|
||||
{
|
||||
Instance = this;
|
||||
this.IsVisible = true;
|
||||
this.Textbox = new TextBox(null, null, Game1.dialogueFont, Game1.textColor)
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Width = 1280,
|
||||
Height = 320
|
||||
};
|
||||
this.TextboxBounds = new Rectangle(this.Textbox.X, this.Textbox.Y, this.Textbox.Width, this.Textbox.Height);
|
||||
}
|
||||
|
||||
internal void InitContent(LocalizedContentManager content)
|
||||
{
|
||||
this._smallFont = content.Load<SpriteFont>(@"Fonts\SmallFont");
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
Game1.activeClickableMenu = this;
|
||||
this.IsVisible = true;
|
||||
}
|
||||
|
||||
public override void receiveLeftClick(int x, int y, bool playSound = true)
|
||||
{
|
||||
if (this.TextboxBounds.Contains(x, y))
|
||||
{
|
||||
this.Textbox.OnEnterPressed += sender => { SGame.instance.CommandQueue.Enqueue(sender.Text); this.Textbox.Text = ""; };
|
||||
Game1.keyboardDispatcher.Subscriber = this.Textbox;
|
||||
typeof(TextBox).GetMethod("ShowAndroidKeyboard", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(this.Textbox, new object[] { });
|
||||
}
|
||||
else
|
||||
{
|
||||
Game1.activeClickableMenu = null;
|
||||
this.IsVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteLine(string consoleMessage, ConsoleLogLevel level)
|
||||
{
|
||||
lock (this._consoleMessageQueue)
|
||||
{
|
||||
this._consoleMessageQueue.AddFirst(new KeyValuePair<ConsoleLogLevel, string>(level, consoleMessage));
|
||||
if (this._consoleMessageQueue.Count > 2000)
|
||||
{
|
||||
this._consoleMessageQueue.RemoveLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
Vector2 size = this._smallFont.MeasureString("aA");
|
||||
float y = Game1.game1.screen.Height - size.Y * 2;
|
||||
lock (this._consoleMessageQueue)
|
||||
{
|
||||
foreach (var log in this._consoleMessageQueue)
|
||||
{
|
||||
string text = log.Value;
|
||||
switch (log.Key)
|
||||
{
|
||||
case ConsoleLogLevel.Critical:
|
||||
case ConsoleLogLevel.Error:
|
||||
spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.Red);
|
||||
break;
|
||||
case ConsoleLogLevel.Alert:
|
||||
case ConsoleLogLevel.Warn:
|
||||
spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.Orange);
|
||||
break;
|
||||
case ConsoleLogLevel.Info:
|
||||
case ConsoleLogLevel.Success:
|
||||
spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.AntiqueWhite);
|
||||
break;
|
||||
case ConsoleLogLevel.Debug:
|
||||
case ConsoleLogLevel.Trace:
|
||||
spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.LightGray);
|
||||
break;
|
||||
default:
|
||||
spriteBatch.DrawString(this._smallFont, text, new Vector2(16, y), Color.LightGray);
|
||||
break;
|
||||
}
|
||||
|
||||
size = this._smallFont.MeasureString(text);
|
||||
if (y < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
y -= size.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,325 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public enum MethodType
|
||||
{
|
||||
Normal,
|
||||
Getter,
|
||||
Setter,
|
||||
Constructor,
|
||||
StaticConstructor
|
||||
}
|
||||
|
||||
[Obsolete("This enum will be removed in the next major version. To define special methods, use MethodType")]
|
||||
public enum PropertyMethod
|
||||
{
|
||||
Getter,
|
||||
Setter
|
||||
}
|
||||
|
||||
public enum ArgumentType
|
||||
{
|
||||
Normal,
|
||||
Ref,
|
||||
Out,
|
||||
Pointer
|
||||
}
|
||||
|
||||
public enum HarmonyPatchType
|
||||
{
|
||||
All,
|
||||
Prefix,
|
||||
Postfix,
|
||||
Transpiler
|
||||
}
|
||||
|
||||
public class HarmonyAttribute : Attribute
|
||||
{
|
||||
public HarmonyMethod info = new HarmonyMethod();
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class HarmonyPatch : HarmonyAttribute
|
||||
{
|
||||
// no argument (for use with TargetMethod)
|
||||
|
||||
public HarmonyPatch()
|
||||
{
|
||||
}
|
||||
|
||||
// starting with 'Type'
|
||||
|
||||
public HarmonyPatch(Type declaringType)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, Type[] argumentTypes)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, string methodName)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodName = methodName;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, string methodName, params Type[] argumentTypes)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodName = methodName;
|
||||
info.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodName = methodName;
|
||||
ParseSpecialArguments(argumentTypes, argumentVariations);
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, MethodType methodType)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodType = methodType;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, MethodType methodType, params Type[] argumentTypes)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodType = methodType;
|
||||
info.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodType = methodType;
|
||||
ParseSpecialArguments(argumentTypes, argumentVariations);
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type declaringType, string propertyName, MethodType methodType)
|
||||
{
|
||||
info.declaringType = declaringType;
|
||||
info.methodName = propertyName;
|
||||
info.methodType = methodType;
|
||||
}
|
||||
|
||||
// starting with 'string'
|
||||
|
||||
public HarmonyPatch(string methodName)
|
||||
{
|
||||
info.methodName = methodName;
|
||||
}
|
||||
|
||||
public HarmonyPatch(string methodName, params Type[] argumentTypes)
|
||||
{
|
||||
info.methodName = methodName;
|
||||
info.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public HarmonyPatch(string methodName, Type[] argumentTypes, ArgumentType[] argumentVariations)
|
||||
{
|
||||
info.methodName = methodName;
|
||||
ParseSpecialArguments(argumentTypes, argumentVariations);
|
||||
}
|
||||
|
||||
public HarmonyPatch(string propertyName, MethodType methodType)
|
||||
{
|
||||
info.methodName = propertyName;
|
||||
info.methodType = methodType;
|
||||
}
|
||||
|
||||
// starting with 'MethodType'
|
||||
|
||||
public HarmonyPatch(MethodType methodType)
|
||||
{
|
||||
info.methodType = methodType;
|
||||
}
|
||||
|
||||
public HarmonyPatch(MethodType methodType, params Type[] argumentTypes)
|
||||
{
|
||||
info.methodType = methodType;
|
||||
info.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public HarmonyPatch(MethodType methodType, Type[] argumentTypes, ArgumentType[] argumentVariations)
|
||||
{
|
||||
info.methodType = methodType;
|
||||
ParseSpecialArguments(argumentTypes, argumentVariations);
|
||||
}
|
||||
|
||||
// starting with 'Type[]'
|
||||
|
||||
public HarmonyPatch(Type[] argumentTypes)
|
||||
{
|
||||
info.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public HarmonyPatch(Type[] argumentTypes, ArgumentType[] argumentVariations)
|
||||
{
|
||||
ParseSpecialArguments(argumentTypes, argumentVariations);
|
||||
}
|
||||
|
||||
// Obsolete attributes
|
||||
|
||||
[Obsolete("This attribute will be removed in the next major version. Use HarmonyPatch together with MethodType.Getter or MethodType.Setter instead")]
|
||||
public HarmonyPatch(string propertyName, PropertyMethod type)
|
||||
{
|
||||
info.methodName = propertyName;
|
||||
info.methodType = type == PropertyMethod.Getter ? MethodType.Getter : MethodType.Setter;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
private void ParseSpecialArguments(Type[] argumentTypes, ArgumentType[] argumentVariations)
|
||||
{
|
||||
if (argumentVariations == null || argumentVariations.Length == 0)
|
||||
{
|
||||
info.argumentTypes = argumentTypes;
|
||||
return;
|
||||
}
|
||||
|
||||
if (argumentTypes.Length < argumentVariations.Length)
|
||||
throw new ArgumentException("argumentVariations contains more elements than argumentTypes", nameof(argumentVariations));
|
||||
|
||||
var types = new List<Type>();
|
||||
for (var i = 0; i < argumentTypes.Length; i++)
|
||||
{
|
||||
var type = argumentTypes[i];
|
||||
switch (argumentVariations[i])
|
||||
{
|
||||
case ArgumentType.Ref:
|
||||
case ArgumentType.Out:
|
||||
type = type.MakeByRefType();
|
||||
break;
|
||||
case ArgumentType.Pointer:
|
||||
type = type.MakePointerType();
|
||||
break;
|
||||
}
|
||||
types.Add(type);
|
||||
}
|
||||
info.argumentTypes = types.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class HarmonyPatchAll : HarmonyAttribute
|
||||
{
|
||||
public HarmonyPatchAll()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class HarmonyPriority : HarmonyAttribute
|
||||
{
|
||||
public HarmonyPriority(int prioritiy)
|
||||
{
|
||||
info.prioritiy = prioritiy;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class HarmonyBefore : HarmonyAttribute
|
||||
{
|
||||
public HarmonyBefore(params string[] before)
|
||||
{
|
||||
info.before = before;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class HarmonyAfter : HarmonyAttribute
|
||||
{
|
||||
public HarmonyAfter(params string[] after)
|
||||
{
|
||||
info.after = after;
|
||||
}
|
||||
}
|
||||
|
||||
// If you don't want to use the special method names you can annotate
|
||||
// using the following attributes:
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyPrepare : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyCleanup : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyTargetMethod : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyTargetMethods : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyPrefix : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyPostfix : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class HarmonyTranspiler : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
|
||||
public class HarmonyArgument : Attribute
|
||||
{
|
||||
public string OriginalName { get; private set; }
|
||||
public int Index { get; private set; }
|
||||
public string NewName { get; private set; }
|
||||
|
||||
public HarmonyArgument(string originalName) : this(originalName, null)
|
||||
{
|
||||
}
|
||||
|
||||
public HarmonyArgument(int index) : this(index, null)
|
||||
{
|
||||
}
|
||||
|
||||
public HarmonyArgument(string originalName, string newName)
|
||||
{
|
||||
OriginalName = originalName;
|
||||
Index = -1;
|
||||
NewName = newName;
|
||||
}
|
||||
|
||||
public HarmonyArgument(int index, string name)
|
||||
{
|
||||
OriginalName = null;
|
||||
Index = index;
|
||||
NewName = name;
|
||||
}
|
||||
}
|
||||
|
||||
// This attribute is for Harmony patching itself to the latest
|
||||
//
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)]
|
||||
internal class UpgradeToLatestVersion : Attribute
|
||||
{
|
||||
public int version;
|
||||
|
||||
public UpgradeToLatestVersion(int version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using Harmony.ILCopying;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class CodeInstruction
|
||||
{
|
||||
public OpCode opcode;
|
||||
public object operand;
|
||||
public List<Label> labels = new List<Label>();
|
||||
public List<ExceptionBlock> blocks = new List<ExceptionBlock>();
|
||||
|
||||
public CodeInstruction(OpCode opcode, object operand = null)
|
||||
{
|
||||
this.opcode = opcode;
|
||||
this.operand = operand;
|
||||
}
|
||||
|
||||
public CodeInstruction(CodeInstruction instruction)
|
||||
{
|
||||
opcode = instruction.opcode;
|
||||
operand = instruction.operand;
|
||||
labels = instruction.labels.ToArray().ToList();
|
||||
}
|
||||
|
||||
public CodeInstruction Clone()
|
||||
{
|
||||
return new CodeInstruction(this) { labels = new List<Label>() };
|
||||
}
|
||||
|
||||
public CodeInstruction Clone(OpCode opcode)
|
||||
{
|
||||
var instruction = new CodeInstruction(this) { labels = new List<Label>() };
|
||||
instruction.opcode = opcode;
|
||||
return instruction;
|
||||
}
|
||||
|
||||
public CodeInstruction Clone(OpCode opcode, object operand)
|
||||
{
|
||||
var instruction = new CodeInstruction(this) { labels = new List<Label>() };
|
||||
instruction.opcode = opcode;
|
||||
instruction.operand = operand;
|
||||
return instruction;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var list = new List<string>();
|
||||
foreach (var label in labels)
|
||||
list.Add("Label" + label.GetHashCode());
|
||||
foreach (var block in blocks)
|
||||
list.Add("EX_" + block.blockType.ToString().Replace("Block", ""));
|
||||
|
||||
var extras = list.Count > 0 ? " [" + string.Join(", ", list.ToArray()) + "]" : "";
|
||||
var operandStr = Emitter.FormatArgument(operand);
|
||||
if (operandStr != "") operandStr = " " + operandStr;
|
||||
return opcode + operandStr + extras;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
using System.Collections.Generic;
|
||||
using Harmony.ILCopying;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class CodeTranspiler
|
||||
{
|
||||
private IEnumerable<CodeInstruction> codeInstructions;
|
||||
private List<MethodInfo> transpilers = new List<MethodInfo>();
|
||||
|
||||
public CodeTranspiler(List<ILInstruction> ilInstructions)
|
||||
{
|
||||
codeInstructions = ilInstructions
|
||||
.Select(ilInstruction => ilInstruction.GetCodeInstruction())
|
||||
.ToList().AsEnumerable();
|
||||
}
|
||||
|
||||
public void Add(MethodInfo transpiler)
|
||||
{
|
||||
transpilers.Add(transpiler);
|
||||
}
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
public static object ConvertInstruction(Type type, object op, out Dictionary<string, object> unassigned)
|
||||
{
|
||||
var nonExisting = new Dictionary<string, object>();
|
||||
var elementTo = AccessTools.MakeDeepCopy(op, type, (namePath, trvSrc, trvDest) =>
|
||||
{
|
||||
var value = trvSrc.GetValue();
|
||||
|
||||
if (trvDest.FieldExists() == false)
|
||||
{
|
||||
nonExisting[namePath] = value;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (namePath == nameof(CodeInstruction.opcode))
|
||||
return ReplaceShortJumps((OpCode)value);
|
||||
|
||||
return value;
|
||||
});
|
||||
unassigned = nonExisting;
|
||||
return elementTo;
|
||||
}
|
||||
|
||||
// ShouldAddExceptionInfo is used to determine if CodeInstructions from an older Harmony version were duplicating
|
||||
// exception information as well as to preserve the exception information from being dropped when piping through
|
||||
// multiple transpilers with mixed Harmony versions.
|
||||
//
|
||||
public static bool ShouldAddExceptionInfo(object op, int opIndex, List<object> originalInstructions, List<object> newInstructions, Dictionary<object, Dictionary<string, object>> unassignedValues)
|
||||
{
|
||||
var originalIndex = originalInstructions.IndexOf(op);
|
||||
if (originalIndex == -1)
|
||||
return false; // no need, new instruction
|
||||
|
||||
Dictionary<string, object> unassigned = null;
|
||||
if (unassignedValues.TryGetValue(op, out unassigned) == false)
|
||||
return false; // no need, no unassigned info
|
||||
|
||||
if (unassigned.TryGetValue(nameof(CodeInstruction.blocks), out var blocksObject) == false)
|
||||
return false; // no need, no try-catch info
|
||||
var blocks = blocksObject as List<ExceptionBlock>;
|
||||
|
||||
var dupCount = newInstructions.Count(instr => instr == op);
|
||||
if (dupCount <= 1)
|
||||
return true; // ok, no duplicate found
|
||||
|
||||
var isStartBlock = blocks.FirstOrDefault(block => block.blockType != ExceptionBlockType.EndExceptionBlock);
|
||||
var isEndBlock = blocks.FirstOrDefault(block => block.blockType == ExceptionBlockType.EndExceptionBlock);
|
||||
|
||||
if (isStartBlock != null && isEndBlock == null)
|
||||
{
|
||||
var pairInstruction = originalInstructions.Skip(originalIndex + 1).FirstOrDefault(instr =>
|
||||
{
|
||||
if (unassignedValues.TryGetValue(instr, out unassigned) == false)
|
||||
return false;
|
||||
if (unassigned.TryGetValue(nameof(CodeInstruction.blocks), out blocksObject) == false)
|
||||
return false;
|
||||
blocks = blocksObject as List<ExceptionBlock>;
|
||||
return blocks.Count() > 0;
|
||||
});
|
||||
if (pairInstruction != null)
|
||||
{
|
||||
var pairStart = originalIndex + 1;
|
||||
var pairEnd = pairStart + originalInstructions.Skip(pairStart).ToList().IndexOf(pairInstruction) - 1;
|
||||
var originalBetweenInstructions = originalInstructions
|
||||
.GetRange(pairStart, pairEnd - pairStart)
|
||||
.Intersect(newInstructions);
|
||||
|
||||
pairInstruction = newInstructions.Skip(opIndex + 1).FirstOrDefault(instr =>
|
||||
{
|
||||
if (unassignedValues.TryGetValue(instr, out unassigned) == false)
|
||||
return false;
|
||||
if (unassigned.TryGetValue(nameof(CodeInstruction.blocks), out blocksObject) == false)
|
||||
return false;
|
||||
blocks = blocksObject as List<ExceptionBlock>;
|
||||
return blocks.Count() > 0;
|
||||
});
|
||||
if (pairInstruction != null)
|
||||
{
|
||||
pairStart = opIndex + 1;
|
||||
pairEnd = pairStart + newInstructions.Skip(opIndex + 1).ToList().IndexOf(pairInstruction) - 1;
|
||||
var newBetweenInstructions = newInstructions.GetRange(pairStart, pairEnd - pairStart);
|
||||
var remaining = originalBetweenInstructions.Except(newBetweenInstructions).ToList();
|
||||
return remaining.Count() == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isStartBlock == null && isEndBlock != null)
|
||||
{
|
||||
var pairInstruction = originalInstructions.GetRange(0, originalIndex).LastOrDefault(instr =>
|
||||
{
|
||||
if (unassignedValues.TryGetValue(instr, out unassigned) == false)
|
||||
return false;
|
||||
if (unassigned.TryGetValue(nameof(CodeInstruction.blocks), out blocksObject) == false)
|
||||
return false;
|
||||
blocks = blocksObject as List<ExceptionBlock>;
|
||||
return blocks.Count() > 0;
|
||||
});
|
||||
if (pairInstruction != null)
|
||||
{
|
||||
var pairStart = originalInstructions.GetRange(0, originalIndex).LastIndexOf(pairInstruction);
|
||||
var pairEnd = originalIndex;
|
||||
var originalBetweenInstructions = originalInstructions
|
||||
.GetRange(pairStart, pairEnd - pairStart)
|
||||
.Intersect(newInstructions);
|
||||
|
||||
pairInstruction = newInstructions.GetRange(0, opIndex).LastOrDefault(instr =>
|
||||
{
|
||||
if (unassignedValues.TryGetValue(instr, out unassigned) == false)
|
||||
return false;
|
||||
if (unassigned.TryGetValue(nameof(CodeInstruction.blocks), out blocksObject) == false)
|
||||
return false;
|
||||
blocks = blocksObject as List<ExceptionBlock>;
|
||||
return blocks.Count() > 0;
|
||||
});
|
||||
if (pairInstruction != null)
|
||||
{
|
||||
pairStart = newInstructions.GetRange(0, opIndex).LastIndexOf(pairInstruction);
|
||||
pairEnd = opIndex;
|
||||
var newBetweenInstructions = newInstructions.GetRange(pairStart, pairEnd - pairStart);
|
||||
var remaining = originalBetweenInstructions.Except(newBetweenInstructions);
|
||||
return remaining.Count() == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unclear or unexpected case, ok by default
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable ConvertInstructionsAndUnassignedValues(Type type, IEnumerable enumerable, out Dictionary<object, Dictionary<string, object>> unassignedValues)
|
||||
{
|
||||
var enumerableAssembly = type.GetGenericTypeDefinition().Assembly;
|
||||
var genericListType = enumerableAssembly.GetType(typeof(List<>).FullName);
|
||||
var elementType = type.GetGenericArguments()[0];
|
||||
var listType = enumerableAssembly.GetType(genericListType.MakeGenericType(new Type[] { elementType }).FullName);
|
||||
var list = Activator.CreateInstance(listType);
|
||||
var listAdd = list.GetType().GetMethod("Add");
|
||||
unassignedValues = new Dictionary<object, Dictionary<string, object>>();
|
||||
foreach (var op in enumerable)
|
||||
{
|
||||
var elementTo = ConvertInstruction(elementType, op, out var unassigned);
|
||||
unassignedValues.Add(elementTo, unassigned);
|
||||
listAdd.Invoke(list, new object[] { elementTo });
|
||||
// cannot yield return 'elementTo' here because we have an out parameter in the method
|
||||
}
|
||||
return list as IEnumerable;
|
||||
}
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
public static IEnumerable ConvertToOurInstructions(IEnumerable instructions, List<object> originalInstructions, Dictionary<object, Dictionary<string, object>> unassignedValues)
|
||||
{
|
||||
// Since we are patching this method, we cannot use typeof(CodeInstruction)
|
||||
// because that would use the CodeInstruction of the Harmony lib that patches
|
||||
// us and we get a type assignment error.
|
||||
// Instead, we know that our caller returns List<X> where X is the type we need
|
||||
//
|
||||
var codeInstructionType = new StackTrace().GetFrames()
|
||||
.Select(frame => frame.GetMethod())
|
||||
.OfType<MethodInfo>()
|
||||
.Select(method =>
|
||||
{
|
||||
var returnType = method.ReturnType;
|
||||
if (returnType.IsGenericType == false) return null;
|
||||
var listTypes = returnType.GetGenericArguments();
|
||||
if (listTypes.Length != 1) return null;
|
||||
var type = listTypes[0];
|
||||
return type.FullName == typeof(CodeInstruction).FullName ? type : null;
|
||||
})
|
||||
.Where(type => type != null)
|
||||
.First();
|
||||
|
||||
var newInstructions = instructions.Cast<object>().ToList();
|
||||
|
||||
var index = -1;
|
||||
foreach (var op in newInstructions)
|
||||
{
|
||||
index++;
|
||||
var elementTo = AccessTools.MakeDeepCopy(op, codeInstructionType);
|
||||
if (unassignedValues.TryGetValue(op, out var fields))
|
||||
{
|
||||
var addExceptionInfo = ShouldAddExceptionInfo(op, index, originalInstructions, newInstructions, unassignedValues);
|
||||
|
||||
var trv = Traverse.Create(elementTo);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (addExceptionInfo || field.Key != nameof(CodeInstruction.blocks))
|
||||
trv.Field(field.Key).SetValue(field.Value);
|
||||
}
|
||||
}
|
||||
yield return elementTo;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable ConvertToGeneralInstructions(MethodInfo transpiler, IEnumerable enumerable, out Dictionary<object, Dictionary<string, object>> unassignedValues)
|
||||
{
|
||||
var type = transpiler.GetParameters()
|
||||
.Select(p => p.ParameterType)
|
||||
.FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition().Name.StartsWith("IEnumerable"));
|
||||
return ConvertInstructionsAndUnassignedValues(type, enumerable, out unassignedValues);
|
||||
}
|
||||
|
||||
public static List<object> GetTranspilerCallParameters(ILGenerator generator, MethodInfo transpiler, MethodBase method, IEnumerable instructions)
|
||||
{
|
||||
var parameter = new List<object>();
|
||||
transpiler.GetParameters().Select(param => param.ParameterType).Do(type =>
|
||||
{
|
||||
if (type.IsAssignableFrom(typeof(ILGenerator)))
|
||||
parameter.Add(generator);
|
||||
else if (type.IsAssignableFrom(typeof(MethodBase)))
|
||||
parameter.Add(method);
|
||||
else
|
||||
parameter.Add(instructions);
|
||||
});
|
||||
return parameter;
|
||||
}
|
||||
|
||||
public List<CodeInstruction> GetResult(ILGenerator generator, MethodBase method)
|
||||
{
|
||||
IEnumerable instructions = codeInstructions;
|
||||
transpilers.ForEach(transpiler =>
|
||||
{
|
||||
// before calling some transpiler, convert the input to 'their' CodeInstruction type
|
||||
// also remember any unassignable values that otherwise would be lost
|
||||
instructions = ConvertToGeneralInstructions(transpiler, instructions, out var unassignedValues);
|
||||
|
||||
// remember the order of the original input (for detection of dupped code instructions)
|
||||
var originalInstructions = new List<object>();
|
||||
originalInstructions.AddRange(instructions.Cast<object>());
|
||||
|
||||
// call the transpiler
|
||||
var parameter = GetTranspilerCallParameters(generator, transpiler, method, instructions);
|
||||
instructions = transpiler.Invoke(null, parameter.ToArray()) as IEnumerable;
|
||||
|
||||
// convert result back to 'our' CodeInstruction and re-assign otherwise lost fields
|
||||
instructions = ConvertToOurInstructions(instructions, originalInstructions, unassignedValues);
|
||||
});
|
||||
return instructions.Cast<CodeInstruction>().ToList();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
static readonly Dictionary<OpCode, OpCode> allJumpCodes = new Dictionary<OpCode, OpCode>
|
||||
{
|
||||
{ OpCodes.Beq_S, OpCodes.Beq },
|
||||
{ OpCodes.Bge_S, OpCodes.Bge },
|
||||
{ OpCodes.Bge_Un_S, OpCodes.Bge_Un },
|
||||
{ OpCodes.Bgt_S, OpCodes.Bgt },
|
||||
{ OpCodes.Bgt_Un_S, OpCodes.Bgt_Un },
|
||||
{ OpCodes.Ble_S, OpCodes.Ble },
|
||||
{ OpCodes.Ble_Un_S, OpCodes.Ble_Un },
|
||||
{ OpCodes.Blt_S, OpCodes.Blt },
|
||||
{ OpCodes.Blt_Un_S, OpCodes.Blt_Un },
|
||||
{ OpCodes.Bne_Un_S, OpCodes.Bne_Un },
|
||||
{ OpCodes.Brfalse_S, OpCodes.Brfalse },
|
||||
{ OpCodes.Brtrue_S, OpCodes.Brtrue },
|
||||
{ OpCodes.Br_S, OpCodes.Br },
|
||||
{ OpCodes.Leave_S, OpCodes.Leave }
|
||||
};
|
||||
static OpCode ReplaceShortJumps(OpCode opcode)
|
||||
{
|
||||
foreach (var pair in allJumpCodes)
|
||||
if (opcode == pair.Key)
|
||||
return pair.Value;
|
||||
return opcode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class DelegateTypeFactory
|
||||
{
|
||||
readonly ModuleBuilder module;
|
||||
|
||||
static int counter;
|
||||
public DelegateTypeFactory()
|
||||
{
|
||||
counter++;
|
||||
var name = new AssemblyName("HarmonyDTFAssembly" + counter);
|
||||
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
|
||||
module = assembly.DefineDynamicModule("HarmonyDTFModule" + counter);
|
||||
}
|
||||
|
||||
public Type CreateDelegateType(MethodInfo method)
|
||||
{
|
||||
var attr = TypeAttributes.Sealed | TypeAttributes.Public;
|
||||
var typeBuilder = module.DefineType("HarmonyDTFType" + counter, attr, typeof(MulticastDelegate));
|
||||
|
||||
var constructor = typeBuilder.DefineConstructor(
|
||||
MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
|
||||
CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
|
||||
constructor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
var invokeMethod = typeBuilder.DefineMethod(
|
||||
"Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public,
|
||||
method.ReturnType, parameters.Types());
|
||||
invokeMethod.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
invokeMethod.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
|
||||
|
||||
return typeBuilder.CreateType();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
// Based on https://www.codeproject.com/Articles/14973/Dynamic-Code-Generation-vs-Reflection
|
||||
|
||||
public delegate object GetterHandler(object source);
|
||||
public delegate void SetterHandler(object source, object value);
|
||||
public delegate object InstantiationHandler();
|
||||
|
||||
public class FastAccess
|
||||
{
|
||||
public static InstantiationHandler CreateInstantiationHandler(Type type)
|
||||
{
|
||||
var constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);
|
||||
if (constructorInfo == null)
|
||||
{
|
||||
throw new ApplicationException(string.Format("The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).", type));
|
||||
}
|
||||
|
||||
var dynamicMethod = new DynamicMethod("InstantiateObject_" + type.Name, MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), null, type, true);
|
||||
var generator = dynamicMethod.GetILGenerator();
|
||||
generator.Emit(OpCodes.Newobj, constructorInfo);
|
||||
generator.Emit(OpCodes.Ret);
|
||||
return (InstantiationHandler)dynamicMethod.CreateDelegate(typeof(InstantiationHandler));
|
||||
}
|
||||
|
||||
public static GetterHandler CreateGetterHandler(PropertyInfo propertyInfo)
|
||||
{
|
||||
var getMethodInfo = propertyInfo.GetGetMethod(true);
|
||||
var dynamicGet = CreateGetDynamicMethod(propertyInfo.DeclaringType);
|
||||
var getGenerator = dynamicGet.GetILGenerator();
|
||||
|
||||
getGenerator.Emit(OpCodes.Ldarg_0);
|
||||
getGenerator.Emit(OpCodes.Call, getMethodInfo);
|
||||
BoxIfNeeded(getMethodInfo.ReturnType, getGenerator);
|
||||
getGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
return (GetterHandler)dynamicGet.CreateDelegate(typeof(GetterHandler));
|
||||
}
|
||||
|
||||
public static GetterHandler CreateGetterHandler(FieldInfo fieldInfo)
|
||||
{
|
||||
var dynamicGet = CreateGetDynamicMethod(fieldInfo.DeclaringType);
|
||||
var getGenerator = dynamicGet.GetILGenerator();
|
||||
|
||||
getGenerator.Emit(OpCodes.Ldarg_0);
|
||||
getGenerator.Emit(OpCodes.Ldfld, fieldInfo);
|
||||
BoxIfNeeded(fieldInfo.FieldType, getGenerator);
|
||||
getGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
return (GetterHandler)dynamicGet.CreateDelegate(typeof(GetterHandler));
|
||||
}
|
||||
|
||||
public static GetterHandler CreateFieldGetter(Type type, params string[] names)
|
||||
{
|
||||
foreach (var name in names)
|
||||
{
|
||||
if (AccessTools.Field(typeof(ILGenerator), name) != null)
|
||||
return CreateGetterHandler(AccessTools.Field(type, name));
|
||||
|
||||
if (AccessTools.Property(typeof(ILGenerator), name) != null)
|
||||
return CreateGetterHandler(AccessTools.Property(type, name));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SetterHandler CreateSetterHandler(PropertyInfo propertyInfo)
|
||||
{
|
||||
var setMethodInfo = propertyInfo.GetSetMethod(true);
|
||||
var dynamicSet = CreateSetDynamicMethod(propertyInfo.DeclaringType);
|
||||
var setGenerator = dynamicSet.GetILGenerator();
|
||||
|
||||
setGenerator.Emit(OpCodes.Ldarg_0);
|
||||
setGenerator.Emit(OpCodes.Ldarg_1);
|
||||
UnboxIfNeeded(setMethodInfo.GetParameters()[0].ParameterType, setGenerator);
|
||||
setGenerator.Emit(OpCodes.Call, setMethodInfo);
|
||||
setGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
return (SetterHandler)dynamicSet.CreateDelegate(typeof(SetterHandler));
|
||||
}
|
||||
|
||||
public static SetterHandler CreateSetterHandler(FieldInfo fieldInfo)
|
||||
{
|
||||
var dynamicSet = CreateSetDynamicMethod(fieldInfo.DeclaringType);
|
||||
var setGenerator = dynamicSet.GetILGenerator();
|
||||
|
||||
setGenerator.Emit(OpCodes.Ldarg_0);
|
||||
setGenerator.Emit(OpCodes.Ldarg_1);
|
||||
UnboxIfNeeded(fieldInfo.FieldType, setGenerator);
|
||||
setGenerator.Emit(OpCodes.Stfld, fieldInfo);
|
||||
setGenerator.Emit(OpCodes.Ret);
|
||||
|
||||
return (SetterHandler)dynamicSet.CreateDelegate(typeof(SetterHandler));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
static DynamicMethod CreateGetDynamicMethod(Type type)
|
||||
{
|
||||
return new DynamicMethod("DynamicGet_" + type.Name, typeof(object), new Type[] { typeof(object) }, type, true);
|
||||
}
|
||||
|
||||
static DynamicMethod CreateSetDynamicMethod(Type type)
|
||||
{
|
||||
return new DynamicMethod("DynamicSet_" + type.Name, typeof(void), new Type[] { typeof(object), typeof(object) }, type, true);
|
||||
}
|
||||
|
||||
static void BoxIfNeeded(Type type, ILGenerator generator)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
generator.Emit(OpCodes.Box, type);
|
||||
}
|
||||
|
||||
static void UnboxIfNeeded(Type type, ILGenerator generator)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
generator.Emit(OpCodes.Unbox_Any, type);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
// Based on https://www.codeproject.com/Articles/14593/A-General-Fast-Method-Invoker
|
||||
|
||||
public delegate object FastInvokeHandler(object target, object[] paramters);
|
||||
|
||||
public class MethodInvoker
|
||||
{
|
||||
public static FastInvokeHandler GetHandler(DynamicMethod methodInfo, Module module)
|
||||
{
|
||||
return Handler(methodInfo, module);
|
||||
}
|
||||
|
||||
public static FastInvokeHandler GetHandler(MethodInfo methodInfo)
|
||||
{
|
||||
return Handler(methodInfo, methodInfo.DeclaringType.Module);
|
||||
}
|
||||
|
||||
static FastInvokeHandler Handler(MethodInfo methodInfo, Module module, bool directBoxValueAccess = false)
|
||||
{
|
||||
var dynamicMethod = new DynamicMethod("FastInvoke_" + methodInfo.Name + "_" + (directBoxValueAccess ? "direct" : "indirect"), typeof(object), new Type[] { typeof(object), typeof(object[]) }, module, true);
|
||||
var il = dynamicMethod.GetILGenerator();
|
||||
|
||||
if (!methodInfo.IsStatic)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
EmitUnboxIfNeeded(il, methodInfo.DeclaringType);
|
||||
}
|
||||
|
||||
var generateLocalBoxValuePtr = true;
|
||||
var ps = methodInfo.GetParameters();
|
||||
for (var i = 0; i < ps.Length; i++)
|
||||
{
|
||||
var argType = ps[i].ParameterType;
|
||||
var argIsByRef = argType.IsByRef;
|
||||
if (argIsByRef)
|
||||
argType = argType.GetElementType();
|
||||
var argIsValueType = argType.IsValueType;
|
||||
|
||||
if (argIsByRef && argIsValueType && !directBoxValueAccess)
|
||||
{
|
||||
// used later when storing back the reference to the new box in the array.
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
EmitFastInt(il, i);
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
EmitFastInt(il, i);
|
||||
|
||||
if (argIsByRef && !argIsValueType)
|
||||
{
|
||||
il.Emit(OpCodes.Ldelema, typeof(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldelem_Ref);
|
||||
if (argIsValueType)
|
||||
{
|
||||
if (!argIsByRef || !directBoxValueAccess)
|
||||
{
|
||||
// if !directBoxValueAccess, create a new box if required
|
||||
il.Emit(OpCodes.Unbox_Any, argType);
|
||||
if (argIsByRef)
|
||||
{
|
||||
// box back
|
||||
il.Emit(OpCodes.Box, argType);
|
||||
|
||||
// store new box value address to local 0
|
||||
il.Emit(OpCodes.Dup);
|
||||
il.Emit(OpCodes.Unbox, argType);
|
||||
if (generateLocalBoxValuePtr)
|
||||
{
|
||||
generateLocalBoxValuePtr = false;
|
||||
// Yes, you're seeing this right - a local of type void* to store the box value address!
|
||||
il.DeclareLocal(typeof(void*), true);
|
||||
}
|
||||
il.Emit(OpCodes.Stloc_0);
|
||||
|
||||
// arr and index set up already
|
||||
il.Emit(OpCodes.Stelem_Ref);
|
||||
|
||||
// load address back to stack
|
||||
il.Emit(OpCodes.Ldloc_0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if directBoxValueAccess, emit unbox (get value address)
|
||||
il.Emit(OpCodes.Unbox, argType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable XS0001
|
||||
if (methodInfo.IsStatic)
|
||||
il.EmitCall(OpCodes.Call, methodInfo, null);
|
||||
else
|
||||
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
|
||||
#pragma warning restore XS0001
|
||||
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
else
|
||||
EmitBoxIfNeeded(il, methodInfo.ReturnType);
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
|
||||
var invoder = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler));
|
||||
return invoder;
|
||||
}
|
||||
|
||||
static void EmitCastToReference(ILGenerator il, Type type)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
il.Emit(OpCodes.Unbox_Any, type);
|
||||
else
|
||||
il.Emit(OpCodes.Castclass, type);
|
||||
}
|
||||
|
||||
static void EmitUnboxIfNeeded(ILGenerator il, Type type)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
il.Emit(OpCodes.Unbox_Any, type);
|
||||
}
|
||||
|
||||
static void EmitBoxIfNeeded(ILGenerator il, Type type)
|
||||
{
|
||||
if (type.IsValueType)
|
||||
il.Emit(OpCodes.Box, type);
|
||||
}
|
||||
|
||||
static void EmitFastInt(ILGenerator il, int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case -1:
|
||||
il.Emit(OpCodes.Ldc_I4_M1);
|
||||
return;
|
||||
case 0:
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
return;
|
||||
case 1:
|
||||
il.Emit(OpCodes.Ldc_I4_1);
|
||||
return;
|
||||
case 2:
|
||||
il.Emit(OpCodes.Ldc_I4_2);
|
||||
return;
|
||||
case 3:
|
||||
il.Emit(OpCodes.Ldc_I4_3);
|
||||
return;
|
||||
case 4:
|
||||
il.Emit(OpCodes.Ldc_I4_4);
|
||||
return;
|
||||
case 5:
|
||||
il.Emit(OpCodes.Ldc_I4_5);
|
||||
return;
|
||||
case 6:
|
||||
il.Emit(OpCodes.Ldc_I4_6);
|
||||
return;
|
||||
case 7:
|
||||
il.Emit(OpCodes.Ldc_I4_7);
|
||||
return;
|
||||
case 8:
|
||||
il.Emit(OpCodes.Ldc_I4_8);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value > -129 && value < 128)
|
||||
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
|
||||
else
|
||||
il.Emit(OpCodes.Ldc_I4, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "Harmony.FileLog.#LogBytes(System.Int64,System.Int32)")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "Harmony.DynamicTools.#PrepareDynamicMethod(System.Reflection.Emit.DynamicMethod)")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Scope = "member", Target = "Harmony.HarmonySharedState.#GetState()")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Scope = "member", Target = "Harmony.ILCopying.Memory.#GetMethodStart(System.Reflection.MethodBase)")]
|
|
@ -0,0 +1,190 @@
|
|||
using Harmony.Tools;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class Patches
|
||||
{
|
||||
public readonly ReadOnlyCollection<Patch> Prefixes;
|
||||
public readonly ReadOnlyCollection<Patch> Postfixes;
|
||||
public readonly ReadOnlyCollection<Patch> Transpilers;
|
||||
|
||||
public ReadOnlyCollection<string> Owners
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = new HashSet<string>();
|
||||
result.UnionWith(Prefixes.Select(p => p.owner));
|
||||
result.UnionWith(Postfixes.Select(p => p.owner));
|
||||
result.UnionWith(Transpilers.Select(p => p.owner));
|
||||
return result.ToList().AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public Patches(Patch[] prefixes, Patch[] postfixes, Patch[] transpilers)
|
||||
{
|
||||
if (prefixes == null) prefixes = new Patch[0];
|
||||
if (postfixes == null) postfixes = new Patch[0];
|
||||
if (transpilers == null) transpilers = new Patch[0];
|
||||
|
||||
Prefixes = prefixes.ToList().AsReadOnly();
|
||||
Postfixes = postfixes.ToList().AsReadOnly();
|
||||
Transpilers = transpilers.ToList().AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public class HarmonyInstance
|
||||
{
|
||||
readonly string id;
|
||||
public string Id => id;
|
||||
public static bool DEBUG = false;
|
||||
|
||||
private static bool selfPatchingDone = false;
|
||||
|
||||
HarmonyInstance(string id)
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
var assembly = typeof(HarmonyInstance).Assembly;
|
||||
var version = assembly.GetName().Version;
|
||||
var location = assembly.Location;
|
||||
if (location == null || location == "") location = new Uri(assembly.CodeBase).LocalPath;
|
||||
FileLog.Log("### Harmony id=" + id + ", version=" + version + ", location=" + location);
|
||||
var callingMethod = GetOutsideCaller();
|
||||
var callingAssembly = callingMethod.DeclaringType.Assembly;
|
||||
location = callingAssembly.Location;
|
||||
if (location == null || location == "") location = new Uri(callingAssembly.CodeBase).LocalPath;
|
||||
FileLog.Log("### Started from " + callingMethod.FullDescription() + ", location " + location);
|
||||
FileLog.Log("### At " + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss"));
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
|
||||
if (!selfPatchingDone)
|
||||
{
|
||||
selfPatchingDone = true;
|
||||
SelfPatching.PatchOldHarmonyMethods();
|
||||
}
|
||||
}
|
||||
|
||||
public static HarmonyInstance Create(string id)
|
||||
{
|
||||
if (id == null) throw new Exception("id cannot be null");
|
||||
return new HarmonyInstance(id);
|
||||
}
|
||||
|
||||
private MethodBase GetOutsideCaller()
|
||||
{
|
||||
var trace = new StackTrace(true);
|
||||
foreach (var frame in trace.GetFrames())
|
||||
{
|
||||
var method = frame.GetMethod();
|
||||
if (method.DeclaringType.Namespace != typeof(HarmonyInstance).Namespace)
|
||||
return method;
|
||||
}
|
||||
throw new Exception("Unexpected end of stack trace");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public void PatchAll()
|
||||
{
|
||||
var method = new StackTrace().GetFrame(1).GetMethod();
|
||||
var assembly = method.ReflectedType.Assembly;
|
||||
PatchAll(assembly);
|
||||
}
|
||||
|
||||
public void PatchAll(Assembly assembly)
|
||||
{
|
||||
assembly.GetTypes().Do(type =>
|
||||
{
|
||||
var parentMethodInfos = type.GetHarmonyMethods();
|
||||
if (parentMethodInfos != null && parentMethodInfos.Count() > 0)
|
||||
{
|
||||
var info = HarmonyMethod.Merge(parentMethodInfos);
|
||||
var processor = new PatchProcessor(this, type, info);
|
||||
processor.Patch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public DynamicMethod Patch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
|
||||
{
|
||||
var processor = new PatchProcessor(this, new List<MethodBase> { original }, prefix, postfix, transpiler);
|
||||
return processor.Patch().FirstOrDefault();
|
||||
}
|
||||
|
||||
public void UnpatchAll(string harmonyID = null)
|
||||
{
|
||||
bool IDCheck(Patch patchInfo) => harmonyID == null || patchInfo.owner == harmonyID;
|
||||
|
||||
var originals = GetPatchedMethods().ToList();
|
||||
foreach (var original in originals)
|
||||
{
|
||||
var info = GetPatchInfo(original);
|
||||
info.Prefixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
|
||||
info.Postfixes.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
|
||||
info.Transpilers.DoIf(IDCheck, patchInfo => Unpatch(original, patchInfo.patch));
|
||||
}
|
||||
}
|
||||
|
||||
public void Unpatch(MethodBase original, HarmonyPatchType type, string harmonyID = null)
|
||||
{
|
||||
var processor = new PatchProcessor(this, new List<MethodBase> { original });
|
||||
processor.Unpatch(type, harmonyID);
|
||||
}
|
||||
|
||||
public void Unpatch(MethodBase original, MethodInfo patch)
|
||||
{
|
||||
var processor = new PatchProcessor(this, new List<MethodBase> { original });
|
||||
processor.Unpatch(patch);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public bool HasAnyPatches(string harmonyID)
|
||||
{
|
||||
return GetPatchedMethods()
|
||||
.Select(original => GetPatchInfo(original))
|
||||
.Any(info => info.Owners.Contains(harmonyID));
|
||||
}
|
||||
|
||||
public Patches GetPatchInfo(MethodBase method)
|
||||
{
|
||||
return PatchProcessor.GetPatchInfo(method);
|
||||
}
|
||||
|
||||
public IEnumerable<MethodBase> GetPatchedMethods()
|
||||
{
|
||||
return HarmonySharedState.GetPatchedMethods();
|
||||
}
|
||||
|
||||
public Dictionary<string, Version> VersionInfo(out Version currentVersion)
|
||||
{
|
||||
currentVersion = typeof(HarmonyInstance).Assembly.GetName().Version;
|
||||
var assemblies = new Dictionary<string, Assembly>();
|
||||
GetPatchedMethods().Do(method =>
|
||||
{
|
||||
var info = HarmonySharedState.GetPatchInfo(method);
|
||||
info.prefixes.Do(fix => assemblies[fix.owner] = fix.patch.DeclaringType.Assembly);
|
||||
info.postfixes.Do(fix => assemblies[fix.owner] = fix.patch.DeclaringType.Assembly);
|
||||
info.transpilers.Do(fix => assemblies[fix.owner] = fix.patch.DeclaringType.Assembly);
|
||||
});
|
||||
|
||||
var result = new Dictionary<string, Version>();
|
||||
assemblies.Do(info =>
|
||||
{
|
||||
var assemblyName = info.Value.GetReferencedAssemblies().FirstOrDefault(a => a.FullName.StartsWith("0Harmony, Version"));
|
||||
if (assemblyName != null)
|
||||
result[info.Key] = assemblyName.Version;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class HarmonyMethod
|
||||
{
|
||||
public MethodInfo method; // need to be called 'method'
|
||||
|
||||
public Type declaringType;
|
||||
public string methodName;
|
||||
public MethodType? methodType;
|
||||
public Type[] argumentTypes;
|
||||
public int prioritiy = -1;
|
||||
public string[] before;
|
||||
public string[] after;
|
||||
|
||||
public HarmonyMethod()
|
||||
{
|
||||
}
|
||||
|
||||
void ImportMethod(MethodInfo theMethod)
|
||||
{
|
||||
method = theMethod;
|
||||
if (method != null)
|
||||
{
|
||||
var infos = method.GetHarmonyMethods();
|
||||
if (infos != null)
|
||||
Merge(infos).CopyTo(this);
|
||||
}
|
||||
}
|
||||
|
||||
public HarmonyMethod(MethodInfo method)
|
||||
{
|
||||
ImportMethod(method);
|
||||
}
|
||||
|
||||
public HarmonyMethod(Type type, string name, Type[] parameters = null)
|
||||
{
|
||||
var method = AccessTools.Method(type, name, parameters);
|
||||
ImportMethod(method);
|
||||
}
|
||||
|
||||
public static List<string> HarmonyFields()
|
||||
{
|
||||
return AccessTools
|
||||
.GetFieldNames(typeof(HarmonyMethod))
|
||||
.Where(s => s != "method")
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static HarmonyMethod Merge(List<HarmonyMethod> attributes)
|
||||
{
|
||||
var result = new HarmonyMethod();
|
||||
if (attributes == null) return result;
|
||||
var resultTrv = Traverse.Create(result);
|
||||
attributes.ForEach(attribute =>
|
||||
{
|
||||
var trv = Traverse.Create(attribute);
|
||||
HarmonyFields().ForEach(f =>
|
||||
{
|
||||
var val = trv.Field(f).GetValue();
|
||||
if (val != null)
|
||||
resultTrv.Field(f).SetValue(val);
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var result = "HarmonyMethod[";
|
||||
var trv = Traverse.Create(this);
|
||||
HarmonyFields().ForEach(f =>
|
||||
{
|
||||
result += f + '=' + trv.Field(f).GetValue();
|
||||
});
|
||||
return result + "]";
|
||||
}
|
||||
}
|
||||
|
||||
public static class HarmonyMethodExtensions
|
||||
{
|
||||
public static void CopyTo(this HarmonyMethod from, HarmonyMethod to)
|
||||
{
|
||||
if (to == null) return;
|
||||
var fromTrv = Traverse.Create(from);
|
||||
var toTrv = Traverse.Create(to);
|
||||
HarmonyMethod.HarmonyFields().ForEach(f =>
|
||||
{
|
||||
var val = fromTrv.Field(f).GetValue();
|
||||
if (val != null) toTrv.Field(f).SetValue(val);
|
||||
});
|
||||
}
|
||||
|
||||
public static HarmonyMethod Clone(this HarmonyMethod original)
|
||||
{
|
||||
var result = new HarmonyMethod();
|
||||
original.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static HarmonyMethod Merge(this HarmonyMethod master, HarmonyMethod detail)
|
||||
{
|
||||
if (detail == null) return master;
|
||||
var result = new HarmonyMethod();
|
||||
var resultTrv = Traverse.Create(result);
|
||||
var masterTrv = Traverse.Create(master);
|
||||
var detailTrv = Traverse.Create(detail);
|
||||
HarmonyMethod.HarmonyFields().ForEach(f =>
|
||||
{
|
||||
var baseValue = masterTrv.Field(f).GetValue();
|
||||
var detailValue = detailTrv.Field(f).GetValue();
|
||||
resultTrv.Field(f).SetValue(detailValue ?? baseValue);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<HarmonyMethod> GetHarmonyMethods(this Type type)
|
||||
{
|
||||
return type.GetCustomAttributes(true)
|
||||
.Where(attr => attr is HarmonyAttribute)
|
||||
.Cast<HarmonyAttribute>()
|
||||
.Select(attr => attr.info)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static List<HarmonyMethod> GetHarmonyMethods(this MethodBase method)
|
||||
{
|
||||
if (method is DynamicMethod) return new List<HarmonyMethod>();
|
||||
return method.GetCustomAttributes(true)
|
||||
.Where(attr => attr is HarmonyAttribute)
|
||||
.Cast<HarmonyAttribute>()
|
||||
.Select(attr => attr.info)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class HarmonySharedState
|
||||
{
|
||||
static readonly string name = "HarmonySharedState";
|
||||
internal static readonly int internalVersion = 100;
|
||||
internal static int actualVersion = -1;
|
||||
|
||||
static Dictionary<MethodBase, byte[]> GetState()
|
||||
{
|
||||
lock (name)
|
||||
{
|
||||
var assembly = SharedStateAssembly();
|
||||
if (assembly == null)
|
||||
{
|
||||
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(name), AssemblyBuilderAccess.Run);
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule(name);
|
||||
var typeAttributes = TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Abstract;
|
||||
var typeBuilder = moduleBuilder.DefineType(name, typeAttributes);
|
||||
typeBuilder.DefineField("state", typeof(Dictionary<MethodBase, byte[]>), FieldAttributes.Static | FieldAttributes.Public);
|
||||
typeBuilder.DefineField("version", typeof(int), FieldAttributes.Static | FieldAttributes.Public).SetConstant(internalVersion);
|
||||
typeBuilder.CreateType();
|
||||
|
||||
assembly = SharedStateAssembly();
|
||||
if (assembly == null) throw new Exception("Cannot find or create harmony shared state");
|
||||
}
|
||||
|
||||
var versionField = assembly.GetType(name).GetField("version");
|
||||
if (versionField == null) throw new Exception("Cannot find harmony state version field");
|
||||
actualVersion = (int)versionField.GetValue(null);
|
||||
|
||||
var stateField = assembly.GetType(name).GetField("state");
|
||||
if (stateField == null) throw new Exception("Cannot find harmony state field");
|
||||
if (stateField.GetValue(null) == null) stateField.SetValue(null, new Dictionary<MethodBase, byte[]>());
|
||||
return (Dictionary<MethodBase, byte[]>)stateField.GetValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
static Assembly SharedStateAssembly()
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies()
|
||||
.FirstOrDefault(a => a.GetName().Name.Contains(name));
|
||||
}
|
||||
|
||||
internal static PatchInfo GetPatchInfo(MethodBase method)
|
||||
{
|
||||
var bytes = GetState().GetValueSafe(method);
|
||||
if (bytes == null) return null;
|
||||
return PatchInfoSerialization.Deserialize(bytes);
|
||||
}
|
||||
|
||||
internal static IEnumerable<MethodBase> GetPatchedMethods()
|
||||
{
|
||||
return GetState().Keys.AsEnumerable();
|
||||
}
|
||||
|
||||
internal static void UpdatePatchInfo(MethodBase method, PatchInfo patchInfo)
|
||||
{
|
||||
GetState()[method] = patchInfo.Serialize();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
using System;
|
||||
|
||||
namespace Harmony.ILCopying
|
||||
{
|
||||
public class ByteBuffer
|
||||
{
|
||||
public byte[] buffer;
|
||||
public int position;
|
||||
|
||||
public ByteBuffer(byte[] buffer)
|
||||
{
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
CheckCanRead(1);
|
||||
return buffer[position++];
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int length)
|
||||
{
|
||||
CheckCanRead(length);
|
||||
var value = new byte[length];
|
||||
Buffer.BlockCopy(buffer, position, value, 0, length);
|
||||
position += length;
|
||||
return value;
|
||||
}
|
||||
|
||||
public short ReadInt16()
|
||||
{
|
||||
CheckCanRead(2);
|
||||
var value = (short)(buffer[position]
|
||||
| (buffer[position + 1] << 8));
|
||||
position += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
CheckCanRead(4);
|
||||
var value = buffer[position]
|
||||
| (buffer[position + 1] << 8)
|
||||
| (buffer[position + 2] << 16)
|
||||
| (buffer[position + 3] << 24);
|
||||
position += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
public long ReadInt64()
|
||||
{
|
||||
CheckCanRead(8);
|
||||
var low = (uint)(buffer[position]
|
||||
| (buffer[position + 1] << 8)
|
||||
| (buffer[position + 2] << 16)
|
||||
| (buffer[position + 3] << 24));
|
||||
|
||||
var high = (uint)(buffer[position + 4]
|
||||
| (buffer[position + 5] << 8)
|
||||
| (buffer[position + 6] << 16)
|
||||
| (buffer[position + 7] << 24));
|
||||
|
||||
var value = (((long)high) << 32) | low;
|
||||
position += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
public float ReadSingle()
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
var bytes = ReadBytes(4);
|
||||
Array.Reverse(bytes);
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
CheckCanRead(4);
|
||||
var value = BitConverter.ToSingle(buffer, position);
|
||||
position += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
public double ReadDouble()
|
||||
{
|
||||
if (!BitConverter.IsLittleEndian)
|
||||
{
|
||||
var bytes = ReadBytes(8);
|
||||
Array.Reverse(bytes);
|
||||
return BitConverter.ToDouble(bytes, 0);
|
||||
}
|
||||
|
||||
CheckCanRead(8);
|
||||
var value = BitConverter.ToDouble(buffer, position);
|
||||
position += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
void CheckCanRead(int count)
|
||||
{
|
||||
if (position + count > buffer.Length)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Harmony.ILCopying
|
||||
{
|
||||
public class LeaveTry
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return "(autogenerated)";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Emitter
|
||||
{
|
||||
static readonly GetterHandler codeLenGetter = FastAccess.CreateFieldGetter(typeof(ILGenerator), "code_len", "m_length");
|
||||
static readonly GetterHandler localsGetter = FastAccess.CreateFieldGetter(typeof(ILGenerator), "locals");
|
||||
static readonly GetterHandler localCountGetter = FastAccess.CreateFieldGetter(typeof(ILGenerator), "m_localCount");
|
||||
|
||||
public static string CodePos(ILGenerator il)
|
||||
{
|
||||
var offset = (int)codeLenGetter(il);
|
||||
return string.Format("L_{0:x4}: ", offset);
|
||||
}
|
||||
|
||||
public static void LogIL(ILGenerator il, OpCode opCode, object argument)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
var argStr = FormatArgument(argument);
|
||||
var space = argStr.Length > 0 ? " " : "";
|
||||
FileLog.LogBuffered(string.Format("{0}{1}{2}{3}", CodePos(il), opCode, space, argStr));
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogLocalVariable(ILGenerator il, LocalBuilder variable)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
var localCount = -1;
|
||||
var localsArray = localsGetter != null ? (LocalBuilder[])localsGetter(il) : null;
|
||||
if (localsArray != null && localsArray.Length > 0)
|
||||
localCount = localsArray.Length;
|
||||
else
|
||||
localCount = (int)localCountGetter(il);
|
||||
|
||||
var str = string.Format("{0}Local var {1}: {2}{3}", CodePos(il), localCount - 1, variable.LocalType.FullName, variable.IsPinned ? "(pinned)" : "");
|
||||
FileLog.LogBuffered(str);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatArgument(object argument)
|
||||
{
|
||||
if (argument == null) return "NULL";
|
||||
var type = argument.GetType();
|
||||
|
||||
if (type == typeof(string))
|
||||
return "\"" + argument + "\"";
|
||||
if (type == typeof(Label))
|
||||
return "Label" + ((Label)argument).GetHashCode();
|
||||
if (type == typeof(Label[]))
|
||||
return "Labels" + string.Join(",", ((Label[])argument).Select(l => l.GetHashCode().ToString()).ToArray());
|
||||
if (type == typeof(LocalBuilder))
|
||||
return ((LocalBuilder)argument).LocalIndex + " (" + ((LocalBuilder)argument).LocalType + ")";
|
||||
|
||||
return argument.ToString().Trim();
|
||||
}
|
||||
|
||||
public static void MarkLabel(ILGenerator il, Label label)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered(CodePos(il) + FormatArgument(label));
|
||||
il.MarkLabel(label);
|
||||
}
|
||||
|
||||
public static void MarkBlockBefore(ILGenerator il, ExceptionBlock block, out Label? label)
|
||||
{
|
||||
label = null;
|
||||
switch (block.blockType)
|
||||
{
|
||||
case ExceptionBlockType.BeginExceptionBlock:
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
FileLog.LogBuffered(".try");
|
||||
FileLog.LogBuffered("{");
|
||||
FileLog.ChangeIndent(1);
|
||||
}
|
||||
label = il.BeginExceptionBlock();
|
||||
return;
|
||||
|
||||
case ExceptionBlockType.BeginCatchBlock:
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
// fake log a LEAVE code since BeginCatchBlock() does add it
|
||||
LogIL(il, OpCodes.Leave, new LeaveTry());
|
||||
|
||||
FileLog.ChangeIndent(-1);
|
||||
FileLog.LogBuffered("} // end try");
|
||||
|
||||
FileLog.LogBuffered(".catch " + block.catchType);
|
||||
FileLog.LogBuffered("{");
|
||||
FileLog.ChangeIndent(1);
|
||||
}
|
||||
il.BeginCatchBlock(block.catchType);
|
||||
return;
|
||||
|
||||
case ExceptionBlockType.BeginExceptFilterBlock:
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
// fake log a LEAVE code since BeginCatchBlock() does add it
|
||||
LogIL(il, OpCodes.Leave, new LeaveTry());
|
||||
|
||||
FileLog.ChangeIndent(-1);
|
||||
FileLog.LogBuffered("} // end try");
|
||||
|
||||
FileLog.LogBuffered(".filter");
|
||||
FileLog.LogBuffered("{");
|
||||
FileLog.ChangeIndent(1);
|
||||
}
|
||||
il.BeginExceptFilterBlock();
|
||||
return;
|
||||
|
||||
case ExceptionBlockType.BeginFaultBlock:
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
// fake log a LEAVE code since BeginCatchBlock() does add it
|
||||
LogIL(il, OpCodes.Leave, new LeaveTry());
|
||||
|
||||
FileLog.ChangeIndent(-1);
|
||||
FileLog.LogBuffered("} // end try");
|
||||
|
||||
FileLog.LogBuffered(".fault");
|
||||
FileLog.LogBuffered("{");
|
||||
FileLog.ChangeIndent(1);
|
||||
}
|
||||
il.BeginFaultBlock();
|
||||
return;
|
||||
|
||||
case ExceptionBlockType.BeginFinallyBlock:
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
// fake log a LEAVE code since BeginCatchBlock() does add it
|
||||
LogIL(il, OpCodes.Leave, new LeaveTry());
|
||||
|
||||
FileLog.ChangeIndent(-1);
|
||||
FileLog.LogBuffered("} // end try");
|
||||
|
||||
FileLog.LogBuffered(".finally");
|
||||
FileLog.LogBuffered("{");
|
||||
FileLog.ChangeIndent(1);
|
||||
}
|
||||
il.BeginFinallyBlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void MarkBlockAfter(ILGenerator il, ExceptionBlock block)
|
||||
{
|
||||
if (block.blockType == ExceptionBlockType.EndExceptionBlock)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
// fake log a LEAVE code since BeginCatchBlock() does add it
|
||||
LogIL(il, OpCodes.Leave, new LeaveTry());
|
||||
|
||||
FileLog.ChangeIndent(-1);
|
||||
FileLog.LogBuffered("} // end handler");
|
||||
}
|
||||
il.EndExceptionBlock();
|
||||
}
|
||||
}
|
||||
|
||||
// MethodCopier calls when Operand type is InlineNone
|
||||
public static void Emit(ILGenerator il, OpCode opcode)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered(CodePos(il) + opcode);
|
||||
il.Emit(opcode);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, LocalBuilder local)
|
||||
{
|
||||
LogIL(il, opcode, local);
|
||||
il.Emit(opcode, local);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, FieldInfo field)
|
||||
{
|
||||
LogIL(il, opcode, field);
|
||||
il.Emit(opcode, field);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, Label[] labels)
|
||||
{
|
||||
LogIL(il, opcode, labels);
|
||||
il.Emit(opcode, labels);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, Label label)
|
||||
{
|
||||
LogIL(il, opcode, label);
|
||||
il.Emit(opcode, label);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, string str)
|
||||
{
|
||||
LogIL(il, opcode, str);
|
||||
il.Emit(opcode, str);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, float arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, byte arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, sbyte arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, double arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, int arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, MethodInfo meth)
|
||||
{
|
||||
LogIL(il, opcode, meth);
|
||||
il.Emit(opcode, meth);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, short arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, SignatureHelper signature)
|
||||
{
|
||||
LogIL(il, opcode, signature);
|
||||
il.Emit(opcode, signature);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, ConstructorInfo con)
|
||||
{
|
||||
LogIL(il, opcode, con);
|
||||
il.Emit(opcode, con);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, Type cls)
|
||||
{
|
||||
LogIL(il, opcode, cls);
|
||||
il.Emit(opcode, cls);
|
||||
}
|
||||
|
||||
// MethodCopier calls by 3rd argument type
|
||||
public static void Emit(ILGenerator il, OpCode opcode, long arg)
|
||||
{
|
||||
LogIL(il, opcode, arg);
|
||||
il.Emit(opcode, arg);
|
||||
}
|
||||
|
||||
// called from MethodInvoker (calls from MethodCopier use the corresponding Emit() call above)
|
||||
public static void EmitCall(ILGenerator il, OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered(string.Format("{0}Call {1} {2} {3}", CodePos(il), opcode, methodInfo, optionalParameterTypes));
|
||||
il.EmitCall(opcode, methodInfo, optionalParameterTypes);
|
||||
}
|
||||
|
||||
// not called yet
|
||||
public static void EmitCalli(ILGenerator il, OpCode opcode, CallingConvention unmanagedCallConv, Type returnType, Type[] parameterTypes)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered(string.Format("{0}Calli {1} {2} {3} {4}", CodePos(il), opcode, unmanagedCallConv, returnType, parameterTypes));
|
||||
il.EmitCalli(opcode, unmanagedCallConv, returnType, parameterTypes);
|
||||
}
|
||||
|
||||
// not called yet
|
||||
public static void EmitCalli(ILGenerator il, OpCode opcode, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered(string.Format("{0}Calli {1} {2} {3} {4} {5}", CodePos(il), opcode, callingConvention, returnType, parameterTypes, optionalParameterTypes));
|
||||
il.EmitCalli(opcode, callingConvention, returnType, parameterTypes, optionalParameterTypes);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony.ILCopying
|
||||
{
|
||||
public enum ExceptionBlockType
|
||||
{
|
||||
BeginExceptionBlock,
|
||||
BeginCatchBlock,
|
||||
BeginExceptFilterBlock,
|
||||
BeginFaultBlock,
|
||||
BeginFinallyBlock,
|
||||
EndExceptionBlock
|
||||
}
|
||||
|
||||
public class ExceptionBlock
|
||||
{
|
||||
public ExceptionBlockType blockType;
|
||||
public Type catchType;
|
||||
|
||||
public ExceptionBlock(ExceptionBlockType blockType, Type catchType)
|
||||
{
|
||||
this.blockType = blockType;
|
||||
this.catchType = catchType;
|
||||
}
|
||||
}
|
||||
|
||||
public class ILInstruction
|
||||
{
|
||||
public int offset;
|
||||
public OpCode opcode;
|
||||
public object operand;
|
||||
public object argument;
|
||||
|
||||
public List<Label> labels = new List<Label>();
|
||||
public List<ExceptionBlock> blocks = new List<ExceptionBlock>();
|
||||
|
||||
public ILInstruction(OpCode opcode, object operand = null)
|
||||
{
|
||||
this.opcode = opcode;
|
||||
this.operand = operand;
|
||||
argument = operand;
|
||||
}
|
||||
|
||||
public CodeInstruction GetCodeInstruction()
|
||||
{
|
||||
var instr = new CodeInstruction(opcode, argument);
|
||||
if (opcode.OperandType == OperandType.InlineNone)
|
||||
instr.operand = null;
|
||||
instr.labels = labels;
|
||||
instr.blocks = blocks;
|
||||
return instr;
|
||||
}
|
||||
|
||||
public int GetSize()
|
||||
{
|
||||
var size = opcode.Size;
|
||||
|
||||
switch (opcode.OperandType)
|
||||
{
|
||||
case OperandType.InlineSwitch:
|
||||
size += (1 + ((Array)operand).Length) * 4;
|
||||
break;
|
||||
|
||||
case OperandType.InlineI8:
|
||||
case OperandType.InlineR:
|
||||
size += 8;
|
||||
break;
|
||||
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.InlineField:
|
||||
case OperandType.InlineI:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineSig:
|
||||
case OperandType.InlineString:
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.ShortInlineR:
|
||||
size += 4;
|
||||
break;
|
||||
|
||||
case OperandType.InlineVar:
|
||||
size += 2;
|
||||
break;
|
||||
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.ShortInlineI:
|
||||
case OperandType.ShortInlineVar:
|
||||
size += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var instruction = "";
|
||||
|
||||
AppendLabel(ref instruction, this);
|
||||
instruction = instruction + ": " + opcode.Name;
|
||||
|
||||
if (operand == null)
|
||||
return instruction;
|
||||
|
||||
instruction = instruction + " ";
|
||||
|
||||
switch (opcode.OperandType)
|
||||
{
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
AppendLabel(ref instruction, operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var switchLabels = (ILInstruction[])operand;
|
||||
for (var i = 0; i < switchLabels.Length; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
instruction = instruction + ",";
|
||||
|
||||
AppendLabel(ref instruction, switchLabels[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case OperandType.InlineString:
|
||||
instruction = instruction + "\"" + operand + "\"";
|
||||
break;
|
||||
|
||||
default:
|
||||
instruction = instruction + operand;
|
||||
break;
|
||||
}
|
||||
|
||||
return instruction;
|
||||
}
|
||||
|
||||
static void AppendLabel(ref string str, object argument)
|
||||
{
|
||||
var instruction = argument as ILInstruction;
|
||||
if (instruction != null)
|
||||
str = str + "IL_" + instruction.offset.ToString("X4");
|
||||
else
|
||||
str = str + "IL_" + argument;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Harmony.ILCopying
|
||||
{
|
||||
[Flags]
|
||||
public enum Protection
|
||||
{
|
||||
PAGE_NOACCESS = 0x01,
|
||||
PAGE_READONLY = 0x02,
|
||||
PAGE_READWRITE = 0x04,
|
||||
PAGE_WRITECOPY = 0x08,
|
||||
PAGE_EXECUTE = 0x10,
|
||||
PAGE_EXECUTE_READ = 0x20,
|
||||
PAGE_EXECUTE_READWRITE = 0x40,
|
||||
PAGE_EXECUTE_WRITECOPY = 0x80,
|
||||
PAGE_GUARD = 0x100,
|
||||
PAGE_NOCACHE = 0x200,
|
||||
PAGE_WRITECOMBINE = 0x400
|
||||
}
|
||||
|
||||
public static class Memory
|
||||
{
|
||||
private static readonly HashSet<PlatformID> WindowsPlatformIDSet = new HashSet<PlatformID>
|
||||
{
|
||||
PlatformID.Win32NT, PlatformID.Win32S, PlatformID.Win32Windows, PlatformID.WinCE
|
||||
};
|
||||
|
||||
public static bool IsWindows => WindowsPlatformIDSet.Contains(Environment.OSVersion.Platform);
|
||||
|
||||
// Safe to use windows reference since this will only ever be called on windows
|
||||
//
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, Protection flNewProtect, out Protection lpflOldProtect);
|
||||
|
||||
public static void UnprotectMemoryPage(long memory)
|
||||
{
|
||||
if (IsWindows)
|
||||
{
|
||||
var success = VirtualProtect(new IntPtr(memory), new UIntPtr(1), Protection.PAGE_EXECUTE_READWRITE, out var _ignored);
|
||||
if (success == false)
|
||||
throw new System.ComponentModel.Win32Exception();
|
||||
}
|
||||
}
|
||||
|
||||
public static string DetourMethod(MethodBase original, MethodBase replacement)
|
||||
{
|
||||
var originalCodeStart = GetMethodStart(original, out var exception);
|
||||
if (originalCodeStart == 0)
|
||||
return exception.Message;
|
||||
var patchCodeStart = GetMethodStart(replacement, out exception);
|
||||
if (patchCodeStart == 0)
|
||||
return exception.Message;
|
||||
|
||||
return WriteJump(originalCodeStart, patchCodeStart);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is still a rough part in Harmony. So much information and no easy way
|
||||
* to determine when and what is valid. Especially with different environments
|
||||
* and .NET versions. More information might be found here:
|
||||
*
|
||||
* https://stackoverflow.com/questions/38782934/how-to-replace-the-pointer-to-the-overridden-virtual-method-in-the-pointer-of/
|
||||
* https://stackoverflow.com/questions/39034018/how-to-replace-a-pointer-to-a-pointer-to-a-method-in-a-class-of-my-method-inheri
|
||||
*
|
||||
*/
|
||||
public static string WriteJump(long memory, long destination)
|
||||
{
|
||||
UnprotectMemoryPage(memory);
|
||||
|
||||
if (IntPtr.Size == sizeof(long))
|
||||
{
|
||||
if (CompareBytes(memory, new byte[] { 0xe9 }))
|
||||
{
|
||||
var offset = ReadInt(memory + 1);
|
||||
memory += 5 + offset;
|
||||
}
|
||||
|
||||
memory = WriteBytes(memory, new byte[] { 0x48, 0xB8 });
|
||||
memory = WriteLong(memory, destination);
|
||||
memory = WriteBytes(memory, new byte[] { 0xFF, 0xE0 });
|
||||
}
|
||||
else
|
||||
{
|
||||
memory = WriteByte(memory, 0x68);
|
||||
memory = WriteInt(memory, (int)destination);
|
||||
memory = WriteByte(memory, 0xc3);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static RuntimeMethodHandle GetRuntimeMethodHandle(MethodBase method)
|
||||
{
|
||||
if (method is DynamicMethod)
|
||||
{
|
||||
var nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
|
||||
// DynamicMethod actually generates its m_methodHandle on-the-fly and therefore
|
||||
// we should call GetMethodDescriptor to force it to be created
|
||||
//
|
||||
var m_GetMethodDescriptor = typeof(DynamicMethod).GetMethod("GetMethodDescriptor", nonPublicInstance);
|
||||
if (m_GetMethodDescriptor != null)
|
||||
return (RuntimeMethodHandle)m_GetMethodDescriptor.Invoke(method, new object[0]);
|
||||
|
||||
// .Net Core
|
||||
var f_m_method = typeof(DynamicMethod).GetField("m_method", nonPublicInstance);
|
||||
if (f_m_method != null)
|
||||
return (RuntimeMethodHandle)f_m_method.GetValue(method);
|
||||
|
||||
// Mono
|
||||
var f_mhandle = typeof(DynamicMethod).GetField("mhandle", nonPublicInstance);
|
||||
return (RuntimeMethodHandle)f_mhandle.GetValue(method);
|
||||
}
|
||||
|
||||
return method.MethodHandle;
|
||||
}
|
||||
|
||||
public static long GetMethodStart(MethodBase method, out Exception exception)
|
||||
{
|
||||
// required in .NET Core so that the method is JITed and the method start does not change
|
||||
//
|
||||
var handle = GetRuntimeMethodHandle(method);
|
||||
try
|
||||
{
|
||||
RuntimeHelpers.PrepareMethod(handle);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
exception = null;
|
||||
return handle.GetFunctionPointer().ToInt64();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe bool CompareBytes(long memory, byte[] values)
|
||||
{
|
||||
var p = (byte*)memory;
|
||||
foreach (var value in values)
|
||||
{
|
||||
if (value != *p) return false;
|
||||
p++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe byte ReadByte(long memory)
|
||||
{
|
||||
var p = (byte*)memory;
|
||||
return *p;
|
||||
}
|
||||
|
||||
public static unsafe int ReadInt(long memory)
|
||||
{
|
||||
var p = (int*)memory;
|
||||
return *p;
|
||||
}
|
||||
|
||||
public static unsafe long ReadLong(long memory)
|
||||
{
|
||||
var p = (long*)memory;
|
||||
return *p;
|
||||
}
|
||||
|
||||
public static unsafe long WriteByte(long memory, byte value)
|
||||
{
|
||||
var p = (byte*)memory;
|
||||
*p = value;
|
||||
return memory + sizeof(byte);
|
||||
}
|
||||
|
||||
public static unsafe long WriteBytes(long memory, byte[] values)
|
||||
{
|
||||
foreach (var value in values)
|
||||
memory = WriteByte(memory, value);
|
||||
return memory;
|
||||
}
|
||||
|
||||
public static unsafe long WriteInt(long memory, int value)
|
||||
{
|
||||
var p = (int*)memory;
|
||||
*p = value;
|
||||
return memory + sizeof(int);
|
||||
}
|
||||
|
||||
public static unsafe long WriteLong(long memory, long value)
|
||||
{
|
||||
var p = (long*)memory;
|
||||
*p = value;
|
||||
return memory + sizeof(long);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,730 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Harmony.ILCopying
|
||||
{
|
||||
public class MethodCopier
|
||||
{
|
||||
readonly MethodBodyReader reader;
|
||||
readonly List<MethodInfo> transpilers = new List<MethodInfo>();
|
||||
|
||||
public MethodCopier(MethodBase fromMethod, ILGenerator toILGenerator, LocalBuilder[] existingVariables = null)
|
||||
{
|
||||
if (fromMethod == null) throw new ArgumentNullException("Method cannot be null");
|
||||
reader = new MethodBodyReader(fromMethod, toILGenerator);
|
||||
reader.DeclareVariables(existingVariables);
|
||||
reader.ReadInstructions();
|
||||
}
|
||||
|
||||
public void AddTranspiler(MethodInfo transpiler)
|
||||
{
|
||||
transpilers.Add(transpiler);
|
||||
}
|
||||
|
||||
public void Finalize(List<Label> endLabels, List<ExceptionBlock> endBlocks)
|
||||
{
|
||||
reader.FinalizeILCodes(transpilers, endLabels, endBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
public class MethodBodyReader
|
||||
{
|
||||
readonly ILGenerator generator;
|
||||
|
||||
readonly MethodBase method;
|
||||
readonly Module module;
|
||||
readonly Type[] typeArguments;
|
||||
readonly Type[] methodArguments;
|
||||
readonly ByteBuffer ilBytes;
|
||||
readonly ParameterInfo this_parameter;
|
||||
readonly ParameterInfo[] parameters;
|
||||
readonly IList<LocalVariableInfo> locals;
|
||||
readonly IList<ExceptionHandlingClause> exceptions;
|
||||
List<ILInstruction> ilInstructions;
|
||||
|
||||
LocalBuilder[] variables;
|
||||
|
||||
// NOTE: you cannot simply "copy" ILInstructions from a method. They contain references to
|
||||
// local variables which must be CREATED on an ILGenerator or else they are invalid when you
|
||||
// want to use the ILInstruction. If you are really clever, you can supply a dummy generator
|
||||
// and edit out all labels during the processing but that might be more trickier than you think
|
||||
//
|
||||
// In order to copy together a bunch of method parts within a transpiler, you have to pass in
|
||||
// your current generator that builds your new method
|
||||
//
|
||||
// You will end up with the sum of all declared local variables of all methods you run
|
||||
// GetInstructions on or use a dummy generator but edit out the invalid labels from the codes
|
||||
// you copy
|
||||
//
|
||||
public static List<ILInstruction> GetInstructions(ILGenerator generator, MethodBase method)
|
||||
{
|
||||
if (method == null) throw new ArgumentNullException("Method cannot be null");
|
||||
var reader = new MethodBodyReader(method, generator);
|
||||
reader.DeclareVariables(null);
|
||||
reader.ReadInstructions();
|
||||
return reader.ilInstructions;
|
||||
}
|
||||
|
||||
// constructor
|
||||
//
|
||||
public MethodBodyReader(MethodBase method, ILGenerator generator)
|
||||
{
|
||||
this.generator = generator;
|
||||
this.method = method;
|
||||
module = method.Module;
|
||||
|
||||
var body = method.GetMethodBody();
|
||||
if (body == null)
|
||||
throw new ArgumentException("Method " + method.FullDescription() + " has no body");
|
||||
|
||||
var bytes = body.GetILAsByteArray();
|
||||
if (bytes == null)
|
||||
throw new ArgumentException("Can not get IL bytes of method " + method.FullDescription());
|
||||
ilBytes = new ByteBuffer(bytes);
|
||||
ilInstructions = new List<ILInstruction>((bytes.Length + 1) / 2);
|
||||
|
||||
var type = method.DeclaringType;
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
try { typeArguments = type.GetGenericArguments(); }
|
||||
catch { typeArguments = null; }
|
||||
}
|
||||
|
||||
if (method.IsGenericMethod)
|
||||
{
|
||||
try { methodArguments = method.GetGenericArguments(); }
|
||||
catch { methodArguments = null; }
|
||||
}
|
||||
|
||||
if (!method.IsStatic)
|
||||
this_parameter = new ThisParameter(method);
|
||||
parameters = method.GetParameters();
|
||||
|
||||
locals = body.LocalVariables;
|
||||
exceptions = body.ExceptionHandlingClauses;
|
||||
}
|
||||
|
||||
// read and parse IL codes
|
||||
//
|
||||
public void ReadInstructions()
|
||||
{
|
||||
while (ilBytes.position < ilBytes.buffer.Length)
|
||||
{
|
||||
var loc = ilBytes.position; // get location first (ReadOpCode will advance it)
|
||||
var instruction = new ILInstruction(ReadOpCode()) { offset = loc };
|
||||
ReadOperand(instruction);
|
||||
ilInstructions.Add(instruction);
|
||||
}
|
||||
|
||||
ResolveBranches();
|
||||
ParseExceptions();
|
||||
}
|
||||
|
||||
// declare local variables
|
||||
//
|
||||
public void DeclareVariables(LocalBuilder[] existingVariables)
|
||||
{
|
||||
if (generator == null) return;
|
||||
if (existingVariables != null)
|
||||
variables = existingVariables;
|
||||
else
|
||||
variables = locals.Select(
|
||||
lvi => generator.DeclareLocal(lvi.LocalType, lvi.IsPinned)
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
// process all jumps
|
||||
//
|
||||
void ResolveBranches()
|
||||
{
|
||||
foreach (var ilInstruction in ilInstructions)
|
||||
{
|
||||
switch (ilInstruction.opcode.OperandType)
|
||||
{
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
ilInstruction.operand = GetInstruction((int)ilInstruction.operand, false);
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var offsets = (int[])ilInstruction.operand;
|
||||
var branches = new ILInstruction[offsets.Length];
|
||||
for (var j = 0; j < offsets.Length; j++)
|
||||
branches[j] = GetInstruction(offsets[j], false);
|
||||
|
||||
ilInstruction.operand = branches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process all exception blocks
|
||||
//
|
||||
void ParseExceptions()
|
||||
{
|
||||
foreach (var exception in exceptions)
|
||||
{
|
||||
var try_start = exception.TryOffset;
|
||||
var try_end = exception.TryOffset + exception.TryLength - 1;
|
||||
|
||||
var handler_start = exception.HandlerOffset;
|
||||
var handler_end = exception.HandlerOffset + exception.HandlerLength - 1;
|
||||
|
||||
var instr1 = GetInstruction(try_start, false);
|
||||
instr1.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginExceptionBlock, null));
|
||||
|
||||
var instr2 = GetInstruction(handler_end, true);
|
||||
instr2.blocks.Add(new ExceptionBlock(ExceptionBlockType.EndExceptionBlock, null));
|
||||
|
||||
// The FilterOffset property is meaningful only for Filter clauses.
|
||||
// The CatchType property is not meaningful for Filter or Finally clauses.
|
||||
//
|
||||
switch (exception.Flags)
|
||||
{
|
||||
case ExceptionHandlingClauseOptions.Filter:
|
||||
var instr3 = GetInstruction(exception.FilterOffset, false);
|
||||
instr3.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginExceptFilterBlock, null));
|
||||
break;
|
||||
|
||||
case ExceptionHandlingClauseOptions.Finally:
|
||||
var instr4 = GetInstruction(handler_start, false);
|
||||
instr4.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginFinallyBlock, null));
|
||||
break;
|
||||
|
||||
case ExceptionHandlingClauseOptions.Clause:
|
||||
var instr5 = GetInstruction(handler_start, false);
|
||||
instr5.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginCatchBlock, exception.CatchType));
|
||||
break;
|
||||
|
||||
case ExceptionHandlingClauseOptions.Fault:
|
||||
var instr6 = GetInstruction(handler_start, false);
|
||||
instr6.blocks.Add(new ExceptionBlock(ExceptionBlockType.BeginFaultBlock, null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// used in FinalizeILCodes to convert short jumps to long ones
|
||||
static Dictionary<OpCode, OpCode> shortJumps = new Dictionary<OpCode, OpCode>()
|
||||
{
|
||||
{ OpCodes.Leave_S, OpCodes.Leave },
|
||||
{ OpCodes.Brfalse_S, OpCodes.Brfalse },
|
||||
{ OpCodes.Brtrue_S, OpCodes.Brtrue },
|
||||
{ OpCodes.Beq_S, OpCodes.Beq },
|
||||
{ OpCodes.Bge_S, OpCodes.Bge },
|
||||
{ OpCodes.Bgt_S, OpCodes.Bgt },
|
||||
{ OpCodes.Ble_S, OpCodes.Ble },
|
||||
{ OpCodes.Blt_S, OpCodes.Blt },
|
||||
{ OpCodes.Bne_Un_S, OpCodes.Bne_Un },
|
||||
{ OpCodes.Bge_Un_S, OpCodes.Bge_Un },
|
||||
{ OpCodes.Bgt_Un_S, OpCodes.Bgt_Un },
|
||||
{ OpCodes.Ble_Un_S, OpCodes.Ble_Un },
|
||||
{ OpCodes.Br_S, OpCodes.Br },
|
||||
{ OpCodes.Blt_Un_S, OpCodes.Blt_Un }
|
||||
};
|
||||
|
||||
// use parsed IL codes and emit them to a generator
|
||||
//
|
||||
public void FinalizeILCodes(List<MethodInfo> transpilers, List<Label> endLabels, List<ExceptionBlock> endBlocks)
|
||||
{
|
||||
if (generator == null) return;
|
||||
|
||||
// pass1 - define labels and add them to instructions that are target of a jump
|
||||
//
|
||||
foreach (var ilInstruction in ilInstructions)
|
||||
{
|
||||
switch (ilInstruction.opcode.OperandType)
|
||||
{
|
||||
case OperandType.InlineSwitch:
|
||||
{
|
||||
var targets = ilInstruction.operand as ILInstruction[];
|
||||
if (targets != null)
|
||||
{
|
||||
var labels = new List<Label>();
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var label = generator.DefineLabel();
|
||||
target.labels.Add(label);
|
||||
labels.Add(label);
|
||||
}
|
||||
ilInstruction.argument = labels.ToArray();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
{
|
||||
var target = ilInstruction.operand as ILInstruction;
|
||||
if (target != null)
|
||||
{
|
||||
var label = generator.DefineLabel();
|
||||
target.labels.Add(label);
|
||||
ilInstruction.argument = label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pass2 - filter through all processors
|
||||
//
|
||||
var codeTranspiler = new CodeTranspiler(ilInstructions);
|
||||
transpilers.Do(transpiler => codeTranspiler.Add(transpiler));
|
||||
var codeInstructions = codeTranspiler.GetResult(generator, method);
|
||||
|
||||
// pass3 - remove RET if it appears at the end
|
||||
while (true)
|
||||
{
|
||||
var lastInstruction = codeInstructions.LastOrDefault();
|
||||
if (lastInstruction == null || lastInstruction.opcode != OpCodes.Ret) break;
|
||||
|
||||
// remember any existing labels
|
||||
endLabels.AddRange(lastInstruction.labels);
|
||||
|
||||
codeInstructions.RemoveAt(codeInstructions.Count - 1);
|
||||
}
|
||||
|
||||
// pass4 - mark labels and exceptions and emit codes
|
||||
//
|
||||
var idx = 0;
|
||||
codeInstructions.Do(codeInstruction =>
|
||||
{
|
||||
// mark all labels
|
||||
codeInstruction.labels.Do(label => Emitter.MarkLabel(generator, label));
|
||||
|
||||
// start all exception blocks
|
||||
// TODO: we ignore the resulting label because we have no way to use it
|
||||
//
|
||||
codeInstruction.blocks.Do(block => { Label? label; Emitter.MarkBlockBefore(generator, block, out label); });
|
||||
|
||||
var code = codeInstruction.opcode;
|
||||
var operand = codeInstruction.operand;
|
||||
|
||||
// replace RET with a jump to the end (outside this code)
|
||||
if (code == OpCodes.Ret)
|
||||
{
|
||||
var endLabel = generator.DefineLabel();
|
||||
code = OpCodes.Br;
|
||||
operand = endLabel;
|
||||
endLabels.Add(endLabel);
|
||||
}
|
||||
|
||||
// replace short jumps with long ones (can be optimized but requires byte counting, not instruction counting)
|
||||
if (shortJumps.TryGetValue(code, out var longJump))
|
||||
code = longJump;
|
||||
|
||||
var emitCode = true;
|
||||
|
||||
//if (code == OpCodes.Leave || code == OpCodes.Leave_S)
|
||||
//{
|
||||
// // skip LEAVE on EndExceptionBlock
|
||||
// if (codeInstruction.blocks.Any(block => block.blockType == ExceptionBlockType.EndExceptionBlock))
|
||||
// emitCode = false;
|
||||
|
||||
// // skip LEAVE on next instruction starts a new exception handler and we are already in
|
||||
// if (idx < instructions.Length - 1)
|
||||
// if (instructions[idx + 1].blocks.Any(block => block.blockType != ExceptionBlockType.EndExceptionBlock))
|
||||
// emitCode = false;
|
||||
//}
|
||||
|
||||
if (emitCode)
|
||||
{
|
||||
switch (code.OperandType)
|
||||
{
|
||||
case OperandType.InlineNone:
|
||||
Emitter.Emit(generator, code);
|
||||
break;
|
||||
|
||||
case OperandType.InlineSig:
|
||||
|
||||
// TODO the following will fail because we do not convert the token (operand)
|
||||
// All the decompilers can show the arguments correctly, we just need to find out how
|
||||
//
|
||||
if (operand == null) throw new Exception("Wrong null argument: " + codeInstruction);
|
||||
if ((operand is int) == false) throw new Exception("Wrong Emit argument type " + operand.GetType() + " in " + codeInstruction);
|
||||
Emitter.Emit(generator, code, (int)operand);
|
||||
|
||||
/*
|
||||
// the following will only work if we can convert the original signature token to the required arguments
|
||||
//
|
||||
var callingConvention = System.Runtime.InteropServices.CallingConvention.ThisCall;
|
||||
var returnType = typeof(object);
|
||||
var parameterTypes = new[] { typeof(object) };
|
||||
Emitter.EmitCalli(generator, code, callingConvention, returnType, parameterTypes);
|
||||
|
||||
var callingConventions = System.Reflection.CallingConventions.Standard;
|
||||
var optionalParameterTypes = new[] { typeof(object) };
|
||||
Emitter.EmitCalli(generator, code, callingConventions, returnType, parameterTypes, optionalParameterTypes);
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
if (operand == null) throw new Exception("Wrong null argument: " + codeInstruction);
|
||||
var emitMethod = EmitMethodForType(operand.GetType());
|
||||
if (emitMethod == null) throw new Exception("Unknown Emit argument type " + operand.GetType() + " in " + codeInstruction);
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered(Emitter.CodePos(generator) + code + " " + Emitter.FormatArgument(operand));
|
||||
emitMethod.Invoke(generator, new object[] { code, operand });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
codeInstruction.blocks.Do(block => Emitter.MarkBlockAfter(generator, block));
|
||||
|
||||
idx++;
|
||||
});
|
||||
}
|
||||
|
||||
// interpret member info value
|
||||
//
|
||||
static void GetMemberInfoValue(MemberInfo info, out object result)
|
||||
{
|
||||
result = null;
|
||||
switch (info.MemberType)
|
||||
{
|
||||
case MemberTypes.Constructor:
|
||||
result = (ConstructorInfo)info;
|
||||
break;
|
||||
|
||||
case MemberTypes.Event:
|
||||
result = (EventInfo)info;
|
||||
break;
|
||||
|
||||
case MemberTypes.Field:
|
||||
result = (FieldInfo)info;
|
||||
break;
|
||||
|
||||
case MemberTypes.Method:
|
||||
result = (MethodInfo)info;
|
||||
break;
|
||||
|
||||
case MemberTypes.TypeInfo:
|
||||
case MemberTypes.NestedType:
|
||||
result = (Type)info;
|
||||
break;
|
||||
|
||||
case MemberTypes.Property:
|
||||
result = (PropertyInfo)info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// interpret instruction operand
|
||||
//
|
||||
void ReadOperand(ILInstruction instruction)
|
||||
{
|
||||
switch (instruction.opcode.OperandType)
|
||||
{
|
||||
case OperandType.InlineNone:
|
||||
{
|
||||
instruction.argument = null;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
{
|
||||
var length = ilBytes.ReadInt32();
|
||||
var base_offset = ilBytes.position + (4 * length);
|
||||
var branches = new int[length];
|
||||
for (var i = 0; i < length; i++)
|
||||
branches[i] = ilBytes.ReadInt32() + base_offset;
|
||||
instruction.operand = branches;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
{
|
||||
var val = (sbyte)ilBytes.ReadByte();
|
||||
instruction.operand = val + ilBytes.position;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineBrTarget:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = val + ilBytes.position;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.ShortInlineI:
|
||||
{
|
||||
if (instruction.opcode == OpCodes.Ldc_I4_S)
|
||||
{
|
||||
var sb = (sbyte)ilBytes.ReadByte();
|
||||
instruction.operand = sb;
|
||||
instruction.argument = (sbyte)instruction.operand;
|
||||
}
|
||||
else
|
||||
{
|
||||
var b = ilBytes.ReadByte();
|
||||
instruction.operand = b;
|
||||
instruction.argument = (byte)instruction.operand;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineI:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = val;
|
||||
instruction.argument = (int)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.ShortInlineR:
|
||||
{
|
||||
var val = ilBytes.ReadSingle();
|
||||
instruction.operand = val;
|
||||
instruction.argument = (float)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineR:
|
||||
{
|
||||
var val = ilBytes.ReadDouble();
|
||||
instruction.operand = val;
|
||||
instruction.argument = (double)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineI8:
|
||||
{
|
||||
var val = ilBytes.ReadInt64();
|
||||
instruction.operand = val;
|
||||
instruction.argument = (long)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineSig:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
var bytes = module.ResolveSignature(val);
|
||||
instruction.operand = bytes;
|
||||
instruction.argument = bytes;
|
||||
Debugger.Log(0, "TEST", "METHOD " + method.FullDescription() + "\n");
|
||||
Debugger.Log(0, "TEST", "Signature = " + bytes.Select(b => string.Format("0x{0:x02}", b)).Aggregate((a, b) => a + " " + b) + "\n");
|
||||
Debugger.Break();
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineString:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = module.ResolveString(val);
|
||||
instruction.argument = (string)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineTok:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = module.ResolveMember(val, typeArguments, methodArguments);
|
||||
GetMemberInfoValue((MemberInfo)instruction.operand, out instruction.argument);
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineType:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = module.ResolveType(val, typeArguments, methodArguments);
|
||||
instruction.argument = (Type)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineMethod:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = module.ResolveMethod(val, typeArguments, methodArguments);
|
||||
if (instruction.operand is ConstructorInfo)
|
||||
instruction.argument = (ConstructorInfo)instruction.operand;
|
||||
else
|
||||
instruction.argument = (MethodInfo)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineField:
|
||||
{
|
||||
var val = ilBytes.ReadInt32();
|
||||
instruction.operand = module.ResolveField(val, typeArguments, methodArguments);
|
||||
instruction.argument = (FieldInfo)instruction.operand;
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.ShortInlineVar:
|
||||
{
|
||||
var idx = ilBytes.ReadByte();
|
||||
if (TargetsLocalVariable(instruction.opcode))
|
||||
{
|
||||
var lvi = GetLocalVariable(idx);
|
||||
if (lvi == null)
|
||||
instruction.argument = idx;
|
||||
else
|
||||
{
|
||||
instruction.operand = lvi;
|
||||
instruction.argument = variables[lvi.LocalIndex];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction.operand = GetParameter(idx);
|
||||
instruction.argument = idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OperandType.InlineVar:
|
||||
{
|
||||
var idx = ilBytes.ReadInt16();
|
||||
if (TargetsLocalVariable(instruction.opcode))
|
||||
{
|
||||
var lvi = GetLocalVariable(idx);
|
||||
if (lvi == null)
|
||||
instruction.argument = idx;
|
||||
else
|
||||
{
|
||||
instruction.operand = lvi;
|
||||
instruction.argument = variables[lvi.LocalIndex];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instruction.operand = GetParameter(idx);
|
||||
instruction.argument = idx;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
ILInstruction GetInstruction(int offset, bool isEndOfInstruction)
|
||||
{
|
||||
var lastInstructionIndex = ilInstructions.Count - 1;
|
||||
if (offset < 0 || offset > ilInstructions[lastInstructionIndex].offset)
|
||||
throw new Exception("Instruction offset " + offset + " is outside valid range 0 - " + ilInstructions[lastInstructionIndex].offset);
|
||||
|
||||
var min = 0;
|
||||
var max = lastInstructionIndex;
|
||||
while (min <= max)
|
||||
{
|
||||
var mid = min + ((max - min) / 2);
|
||||
var instruction = ilInstructions[mid];
|
||||
|
||||
if (isEndOfInstruction)
|
||||
{
|
||||
if (offset == instruction.offset + instruction.GetSize() - 1)
|
||||
return instruction;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (offset == instruction.offset)
|
||||
return instruction;
|
||||
}
|
||||
|
||||
if (offset < instruction.offset)
|
||||
max = mid - 1;
|
||||
else
|
||||
min = mid + 1;
|
||||
}
|
||||
|
||||
throw new Exception("Cannot find instruction for " + offset.ToString("X4"));
|
||||
}
|
||||
|
||||
static bool TargetsLocalVariable(OpCode opcode)
|
||||
{
|
||||
return opcode.Name.Contains("loc");
|
||||
}
|
||||
|
||||
LocalVariableInfo GetLocalVariable(int index)
|
||||
{
|
||||
return locals?[index];
|
||||
}
|
||||
|
||||
ParameterInfo GetParameter(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return this_parameter;
|
||||
|
||||
return parameters[index - 1];
|
||||
}
|
||||
|
||||
OpCode ReadOpCode()
|
||||
{
|
||||
var op = ilBytes.ReadByte();
|
||||
return op != 0xfe
|
||||
? one_byte_opcodes[op]
|
||||
: two_bytes_opcodes[ilBytes.ReadByte()];
|
||||
}
|
||||
|
||||
MethodInfo EmitMethodForType(Type type)
|
||||
{
|
||||
foreach (var entry in emitMethods)
|
||||
if (entry.Key == type) return entry.Value;
|
||||
foreach (var entry in emitMethods)
|
||||
if (entry.Key.IsAssignableFrom(type)) return entry.Value;
|
||||
return null;
|
||||
}
|
||||
|
||||
// static initializer to prep opcodes
|
||||
|
||||
static readonly OpCode[] one_byte_opcodes;
|
||||
static readonly OpCode[] two_bytes_opcodes;
|
||||
|
||||
static readonly Dictionary<Type, MethodInfo> emitMethods;
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
static MethodBodyReader()
|
||||
{
|
||||
one_byte_opcodes = new OpCode[0xe1];
|
||||
two_bytes_opcodes = new OpCode[0x1f];
|
||||
|
||||
var fields = typeof(OpCodes).GetFields(
|
||||
BindingFlags.Public | BindingFlags.Static);
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
var opcode = (OpCode)field.GetValue(null);
|
||||
if (opcode.OpCodeType == OpCodeType.Nternal)
|
||||
continue;
|
||||
|
||||
if (opcode.Size == 1)
|
||||
one_byte_opcodes[opcode.Value] = opcode;
|
||||
else
|
||||
two_bytes_opcodes[opcode.Value & 0xff] = opcode;
|
||||
}
|
||||
|
||||
emitMethods = new Dictionary<Type, MethodInfo>();
|
||||
typeof(ILGenerator).GetMethods().ToList()
|
||||
.Do(method =>
|
||||
{
|
||||
if (method.Name != "Emit") return;
|
||||
var pinfos = method.GetParameters();
|
||||
if (pinfos.Length != 2) return;
|
||||
var types = pinfos.Select(p => p.ParameterType).ToArray();
|
||||
if (types[0] != typeof(OpCode)) return;
|
||||
emitMethods[types[1]] = method;
|
||||
});
|
||||
}
|
||||
|
||||
// a custom this parameter
|
||||
|
||||
class ThisParameter : ParameterInfo
|
||||
{
|
||||
public ThisParameter(MethodBase method)
|
||||
{
|
||||
MemberImpl = method;
|
||||
ClassImpl = method.DeclaringType;
|
||||
NameImpl = "this";
|
||||
PositionImpl = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.Remoting.Contexts;
|
||||
using System.Linq;
|
||||
|
||||
namespace Harmony.ILCopying
|
||||
{
|
||||
/*
|
||||
* TODO - this needs to be worked on. The purpose is to decode the signature into
|
||||
* a high level reflection based calling signature that is valid in the
|
||||
* current assembly
|
||||
*
|
||||
* See also where OperandType.InlineSig is handled in MethodCopier.cs
|
||||
*
|
||||
public static class Signature
|
||||
{
|
||||
internal const byte DEFAULT = 0x00;
|
||||
internal const byte VARARG = 0x05;
|
||||
internal const byte GENERIC = 0x10;
|
||||
internal const byte HASTHIS = 0x20;
|
||||
internal const byte EXPLICITTHIS = 0x40;
|
||||
internal const byte FIELD = 0x06;
|
||||
internal const byte LOCAL_SIG = 0x07;
|
||||
internal const byte PROPERTY = 0x08;
|
||||
internal const byte GENERICINST = 0x0A;
|
||||
internal const byte SENTINEL = 0x41;
|
||||
internal const byte ELEMENT_TYPE_VOID = 0x01;
|
||||
internal const byte ELEMENT_TYPE_BOOLEAN = 0x02;
|
||||
internal const byte ELEMENT_TYPE_CHAR = 0x03;
|
||||
internal const byte ELEMENT_TYPE_I1 = 0x04;
|
||||
internal const byte ELEMENT_TYPE_U1 = 0x05;
|
||||
internal const byte ELEMENT_TYPE_I2 = 0x06;
|
||||
internal const byte ELEMENT_TYPE_U2 = 0x07;
|
||||
internal const byte ELEMENT_TYPE_I4 = 0x08;
|
||||
internal const byte ELEMENT_TYPE_U4 = 0x09;
|
||||
internal const byte ELEMENT_TYPE_I8 = 0x0a;
|
||||
internal const byte ELEMENT_TYPE_U8 = 0x0b;
|
||||
internal const byte ELEMENT_TYPE_R4 = 0x0c;
|
||||
internal const byte ELEMENT_TYPE_R8 = 0x0d;
|
||||
internal const byte ELEMENT_TYPE_STRING = 0x0e;
|
||||
internal const byte ELEMENT_TYPE_PTR = 0x0f;
|
||||
internal const byte ELEMENT_TYPE_BYREF = 0x10;
|
||||
internal const byte ELEMENT_TYPE_VALUETYPE = 0x11;
|
||||
internal const byte ELEMENT_TYPE_CLASS = 0x12;
|
||||
internal const byte ELEMENT_TYPE_VAR = 0x13;
|
||||
internal const byte ELEMENT_TYPE_ARRAY = 0x14;
|
||||
internal const byte ELEMENT_TYPE_GENERICINST = 0x15;
|
||||
internal const byte ELEMENT_TYPE_TYPEDBYREF = 0x16;
|
||||
internal const byte ELEMENT_TYPE_I = 0x18;
|
||||
internal const byte ELEMENT_TYPE_U = 0x19;
|
||||
internal const byte ELEMENT_TYPE_FNPTR = 0x1b;
|
||||
internal const byte ELEMENT_TYPE_OBJECT = 0x1c;
|
||||
internal const byte ELEMENT_TYPE_SZARRAY = 0x1d;
|
||||
internal const byte ELEMENT_TYPE_MVAR = 0x1e;
|
||||
internal const byte ELEMENT_TYPE_CMOD_REQD = 0x1f;
|
||||
internal const byte ELEMENT_TYPE_CMOD_OPT = 0x20;
|
||||
internal const byte ELEMENT_TYPE_PINNED = 0x45;
|
||||
|
||||
static int ReadCompressedUInt(byte[] bytes, ref int n)
|
||||
{
|
||||
var b1 = bytes[n++];
|
||||
if (b1 <= 0x7F)
|
||||
{
|
||||
return b1;
|
||||
}
|
||||
else if ((b1 & 0xC0) == 0x80)
|
||||
{
|
||||
var b2 = bytes[n++];
|
||||
return ((b1 & 0x3F) << 8) | b2;
|
||||
}
|
||||
else
|
||||
{
|
||||
var b2 = bytes[n++];
|
||||
var b3 = bytes[n++];
|
||||
var b4 = bytes[n++];
|
||||
return ((b1 & 0x3F) << 24) + (b2 << 16) + (b3 << 8) + b4;
|
||||
}
|
||||
}
|
||||
|
||||
static Type ReadTypeOrVoid(Module module, byte[] bytes, ref int n, Context context)
|
||||
{
|
||||
if (bytes[n] == ELEMENT_TYPE_VOID)
|
||||
{
|
||||
n++;
|
||||
return module.GetType("System.Void");
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReadType(module, br, context);
|
||||
}
|
||||
}
|
||||
|
||||
static Type ReadFunctionPointer(Module module, byte[] bytes, ref int n, Context context)
|
||||
{
|
||||
var len = bytes.Length - n;
|
||||
var newBytes = new byte[len];
|
||||
for (var i = 0; i < len; i++)
|
||||
newBytes[i] = bytes[n + i];
|
||||
|
||||
var sig = ReadStandaloneSignature(module, newBytes, context);
|
||||
if (module.universe.EnableFunctionPointers)
|
||||
{
|
||||
return FunctionPointerType.Make(module.universe, sig);
|
||||
}
|
||||
else
|
||||
{
|
||||
// by default, like .NET we return System.IntPtr here
|
||||
return module.universe.System_IntPtr;
|
||||
}
|
||||
}
|
||||
|
||||
static Type ReadType(Module module, byte[] bytes, ref int n, Context context)
|
||||
{
|
||||
CustomModifiers mods;
|
||||
switch (bytes[n++])
|
||||
{
|
||||
case ELEMENT_TYPE_CLASS:
|
||||
return ReadTypeDefOrRefEncoded(module, br, context).MarkNotValueType();
|
||||
case ELEMENT_TYPE_VALUETYPE:
|
||||
return ReadTypeDefOrRefEncoded(module, br, context).MarkValueType();
|
||||
case ELEMENT_TYPE_BOOLEAN:
|
||||
return module.GetType("System.Boolean");
|
||||
case ELEMENT_TYPE_CHAR:
|
||||
return module.GetType("System.Char");
|
||||
case ELEMENT_TYPE_I1:
|
||||
return module.GetType("System.SByte");
|
||||
case ELEMENT_TYPE_U1:
|
||||
return module.GetType("System.Byte");
|
||||
case ELEMENT_TYPE_I2:
|
||||
return module.GetType("System.Int16");
|
||||
case ELEMENT_TYPE_U2:
|
||||
return module.GetType("System.UInt16");
|
||||
case ELEMENT_TYPE_I4:
|
||||
return module.GetType("System.Int32");
|
||||
case ELEMENT_TYPE_U4:
|
||||
return module.GetType("System.UInt32");
|
||||
case ELEMENT_TYPE_I8:
|
||||
return module.GetType("System.Int64");
|
||||
case ELEMENT_TYPE_U8:
|
||||
return module.GetType("System.UInt64");
|
||||
case ELEMENT_TYPE_R4:
|
||||
return module.GetType("System.Single");
|
||||
case ELEMENT_TYPE_R8:
|
||||
return module.GetType("System.Double");
|
||||
case ELEMENT_TYPE_I:
|
||||
return module.GetType("System.IntPtr");
|
||||
case ELEMENT_TYPE_U:
|
||||
return module.GetType("System.UIntPtr");
|
||||
case ELEMENT_TYPE_STRING:
|
||||
return module.GetType("System.String");
|
||||
case ELEMENT_TYPE_OBJECT:
|
||||
return module.GetType("System.Object");
|
||||
case ELEMENT_TYPE_VAR:
|
||||
return context.GetGenericTypeArgument(br.ReadCompressedUInt());
|
||||
case ELEMENT_TYPE_MVAR:
|
||||
return context.GetGenericMethodArgument(br.ReadCompressedUInt());
|
||||
case ELEMENT_TYPE_GENERICINST:
|
||||
return ReadGenericInst(module, br, context);
|
||||
case ELEMENT_TYPE_SZARRAY:
|
||||
mods = CustomModifiers.Read(module, br, context);
|
||||
return ReadType(module, br, context).__MakeArrayType(mods);
|
||||
case ELEMENT_TYPE_ARRAY:
|
||||
mods = CustomModifiers.Read(module, br, context);
|
||||
return ReadType(module, br, context).__MakeArrayType(br.ReadCompressedUInt(), ReadArraySizes(br), ReadArrayBounds(br), mods);
|
||||
case ELEMENT_TYPE_PTR:
|
||||
mods = CustomModifiers.Read(module, br, context);
|
||||
return ReadTypeOrVoid(module, br, context).__MakePointerType(mods);
|
||||
case ELEMENT_TYPE_FNPTR:
|
||||
return ReadFunctionPointer(module, br, context);
|
||||
default:
|
||||
throw new BadImageFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
static Type ReadTypeOrByRef(Module module, byte[] bytes, ref int n, Context context)
|
||||
{
|
||||
if (bytes[n] == ELEMENT_TYPE_BYREF)
|
||||
{
|
||||
n++;
|
||||
// LAMESPEC it is allowed (by C++/CLI, ilasm and peverify) to have custom modifiers after the BYREF
|
||||
// (which makes sense, as it is analogous to pointers)
|
||||
CustomModifiers mods = CustomModifiers.Read(module, br, context);
|
||||
// C++/CLI generates void& local variables, so we need to use ReadTypeOrVoid here
|
||||
return ReadTypeOrVoid(module, br, context).__MakeByRefType(mods);
|
||||
}
|
||||
return ReadType(module, br, context);
|
||||
}
|
||||
|
||||
static Type ReadRetType(Module module, byte[] bytes, ref int n, Context context)
|
||||
{
|
||||
switch (bytes[n])
|
||||
{
|
||||
case ELEMENT_TYPE_VOID:
|
||||
n++;
|
||||
return module.GetType("System.Void");
|
||||
case ELEMENT_TYPE_TYPEDBYREF:
|
||||
n++;
|
||||
return module.GetType("System.TypedReference");
|
||||
default:
|
||||
return ReadTypeOrByRef(module, br, context);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReadStandaloneSignature(Module module, byte[] bytes, Context context)
|
||||
{
|
||||
CallingConvention unmanagedCallingConvention;
|
||||
CallingConventions callingConvention;
|
||||
|
||||
var n = 0;
|
||||
unmanagedCallingConvention = 0;
|
||||
callingConvention = 0;
|
||||
|
||||
var flags = bytes[n++];
|
||||
bool unmanaged;
|
||||
switch (flags & 7)
|
||||
{
|
||||
case DEFAULT:
|
||||
callingConvention = CallingConventions.Standard;
|
||||
unmanaged = false;
|
||||
break;
|
||||
case 0x01: // C
|
||||
unmanagedCallingConvention = CallingConvention.Cdecl;
|
||||
unmanaged = true;
|
||||
break;
|
||||
case 0x02: // STDCALL
|
||||
unmanagedCallingConvention = CallingConvention.StdCall;
|
||||
unmanaged = true;
|
||||
break;
|
||||
case 0x03: // THISCALL
|
||||
unmanagedCallingConvention = CallingConvention.ThisCall;
|
||||
unmanaged = true;
|
||||
break;
|
||||
case 0x04: // FASTCALL
|
||||
unmanagedCallingConvention = CallingConvention.FastCall;
|
||||
unmanaged = true;
|
||||
break;
|
||||
case VARARG:
|
||||
callingConvention = CallingConventions.VarArgs;
|
||||
unmanaged = false;
|
||||
break;
|
||||
default:
|
||||
throw new BadImageFormatException();
|
||||
}
|
||||
if ((flags & HASTHIS) != 0) callingConvention |= CallingConventions.HasThis;
|
||||
if ((flags & EXPLICITTHIS) != 0) callingConvention |= CallingConventions.ExplicitThis;
|
||||
if ((flags & GENERIC) != 0) throw new BadImageFormatException();
|
||||
var paramCount = ReadCompressedUInt(bytes, ref n);
|
||||
CustomModifiers[] customModifiers = null;
|
||||
PackedCustomModifiers.Pack(ref customModifiers, 0, CustomModifiers.Read(module, br, context), paramCount + 1);
|
||||
Type returnType = ReadRetType(module, br, context);
|
||||
List<Type> parameterTypes = new List<Type>();
|
||||
List<Type> optionalParameterTypes = new List<Type>();
|
||||
List<Type> curr = parameterTypes;
|
||||
for (int i = 0; i < paramCount; i++)
|
||||
{
|
||||
if (br.PeekByte() == SENTINEL)
|
||||
{
|
||||
br.ReadByte();
|
||||
curr = optionalParameterTypes;
|
||||
}
|
||||
PackedCustomModifiers.Pack(ref customModifiers, i + 1, CustomModifiers.Read(module, br, context), paramCount + 1);
|
||||
curr.Add(ReadParam(module, br, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
using Harmony.ILCopying;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class MethodPatcher
|
||||
{
|
||||
// special parameter names that can be used in prefix and postfix methods
|
||||
//
|
||||
public static string INSTANCE_PARAM = "__instance";
|
||||
public static string ORIGINAL_METHOD_PARAM = "__originalMethod";
|
||||
public static string RESULT_VAR = "__result";
|
||||
public static string STATE_VAR = "__state";
|
||||
public static string PARAM_INDEX_PREFIX = "__";
|
||||
public static string INSTANCE_FIELD_PREFIX = "___";
|
||||
|
||||
// in case of trouble, set to true to write dynamic method to desktop as a dll
|
||||
// won't work for all methods because of the inability to extend a type compared
|
||||
// to the way DynamicTools.CreateDynamicMethod works
|
||||
//
|
||||
static readonly bool DEBUG_METHOD_GENERATION_BY_DLL_CREATION = false;
|
||||
|
||||
// for fixing old harmony bugs
|
||||
[UpgradeToLatestVersion(1)]
|
||||
public static DynamicMethod CreatePatchedMethod(MethodBase original, List<MethodInfo> prefixes, List<MethodInfo> postfixes, List<MethodInfo> transpilers)
|
||||
{
|
||||
return CreatePatchedMethod(original, "HARMONY_PATCH_1.1.1", prefixes, postfixes, transpilers);
|
||||
}
|
||||
|
||||
public static DynamicMethod CreatePatchedMethod(MethodBase original, string harmonyInstanceID, List<MethodInfo> prefixes, List<MethodInfo> postfixes, List<MethodInfo> transpilers)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (HarmonyInstance.DEBUG) FileLog.LogBuffered("### Patch " + original.DeclaringType + ", " + original);
|
||||
|
||||
var idx = prefixes.Count() + postfixes.Count();
|
||||
var patch = DynamicTools.CreateDynamicMethod(original, "_Patch" + idx);
|
||||
if (patch == null)
|
||||
return null;
|
||||
|
||||
var il = patch.GetILGenerator();
|
||||
|
||||
// for debugging
|
||||
AssemblyBuilder assemblyBuilder = null;
|
||||
TypeBuilder typeBuilder = null;
|
||||
if (DEBUG_METHOD_GENERATION_BY_DLL_CREATION)
|
||||
il = DynamicTools.CreateSaveableMethod(original, "_Patch" + idx, out assemblyBuilder, out typeBuilder);
|
||||
|
||||
var originalVariables = DynamicTools.DeclareLocalVariables(original, il);
|
||||
var privateVars = new Dictionary<string, LocalBuilder>();
|
||||
|
||||
LocalBuilder resultVariable = null;
|
||||
if (idx > 0)
|
||||
{
|
||||
resultVariable = DynamicTools.DeclareLocalVariable(il, AccessTools.GetReturnedType(original));
|
||||
privateVars[RESULT_VAR] = resultVariable;
|
||||
}
|
||||
|
||||
prefixes.ForEach(prefix =>
|
||||
{
|
||||
prefix.GetParameters()
|
||||
.Where(patchParam => patchParam.Name == STATE_VAR)
|
||||
.Do(patchParam =>
|
||||
{
|
||||
var privateStateVariable = DynamicTools.DeclareLocalVariable(il, patchParam.ParameterType);
|
||||
privateVars[prefix.DeclaringType.FullName] = privateStateVariable;
|
||||
});
|
||||
});
|
||||
|
||||
var skipOriginalLabel = il.DefineLabel();
|
||||
var canHaveJump = AddPrefixes(il, original, prefixes, privateVars, skipOriginalLabel);
|
||||
|
||||
var copier = new MethodCopier(original, il, originalVariables);
|
||||
foreach (var transpiler in transpilers)
|
||||
copier.AddTranspiler(transpiler);
|
||||
|
||||
var endLabels = new List<Label>();
|
||||
var endBlocks = new List<ExceptionBlock>();
|
||||
copier.Finalize(endLabels, endBlocks);
|
||||
|
||||
foreach (var label in endLabels)
|
||||
Emitter.MarkLabel(il, label);
|
||||
foreach (var block in endBlocks)
|
||||
Emitter.MarkBlockAfter(il, block);
|
||||
if (resultVariable != null)
|
||||
Emitter.Emit(il, OpCodes.Stloc, resultVariable);
|
||||
if (canHaveJump)
|
||||
Emitter.MarkLabel(il, skipOriginalLabel);
|
||||
|
||||
AddPostfixes(il, original, postfixes, privateVars, false);
|
||||
|
||||
if (resultVariable != null)
|
||||
Emitter.Emit(il, OpCodes.Ldloc, resultVariable);
|
||||
|
||||
AddPostfixes(il, original, postfixes, privateVars, true);
|
||||
|
||||
Emitter.Emit(il, OpCodes.Ret);
|
||||
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
FileLog.LogBuffered("DONE");
|
||||
FileLog.LogBuffered("");
|
||||
FileLog.FlushBuffer();
|
||||
}
|
||||
|
||||
// for debugging
|
||||
if (DEBUG_METHOD_GENERATION_BY_DLL_CREATION)
|
||||
{
|
||||
DynamicTools.SaveMethod(assemblyBuilder, typeBuilder);
|
||||
return null;
|
||||
}
|
||||
|
||||
DynamicTools.PrepareDynamicMethod(patch);
|
||||
return patch;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Exception from HarmonyInstance \"" + harmonyInstanceID + "\"", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (HarmonyInstance.DEBUG)
|
||||
FileLog.FlushBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
static OpCode LoadIndOpCodeFor(Type type)
|
||||
{
|
||||
if (type.IsEnum) return OpCodes.Ldind_I4;
|
||||
|
||||
if (type == typeof(float)) return OpCodes.Ldind_R4;
|
||||
if (type == typeof(double)) return OpCodes.Ldind_R8;
|
||||
|
||||
if (type == typeof(byte)) return OpCodes.Ldind_U1;
|
||||
if (type == typeof(ushort)) return OpCodes.Ldind_U2;
|
||||
if (type == typeof(uint)) return OpCodes.Ldind_U4;
|
||||
if (type == typeof(ulong)) return OpCodes.Ldind_I8;
|
||||
|
||||
if (type == typeof(sbyte)) return OpCodes.Ldind_I1;
|
||||
if (type == typeof(short)) return OpCodes.Ldind_I2;
|
||||
if (type == typeof(int)) return OpCodes.Ldind_I4;
|
||||
if (type == typeof(long)) return OpCodes.Ldind_I8;
|
||||
|
||||
return OpCodes.Ldind_Ref;
|
||||
}
|
||||
|
||||
static HarmonyArgument GetArgumentAttribute(this ParameterInfo parameter)
|
||||
{
|
||||
return parameter.GetCustomAttributes(false).FirstOrDefault(attr => attr is HarmonyArgument) as HarmonyArgument;
|
||||
}
|
||||
|
||||
static HarmonyArgument[] GetArgumentAttributes(this MethodInfo method)
|
||||
{
|
||||
return method.GetCustomAttributes(false).Where(attr => attr is HarmonyArgument).Cast<HarmonyArgument>().ToArray();
|
||||
}
|
||||
|
||||
static HarmonyArgument[] GetArgumentAttributes(this Type type)
|
||||
{
|
||||
return type.GetCustomAttributes(false).Where(attr => attr is HarmonyArgument).Cast<HarmonyArgument>().ToArray();
|
||||
}
|
||||
|
||||
static string GetOriginalArgumentName(this ParameterInfo parameter, string[] originalParameterNames)
|
||||
{
|
||||
var attribute = parameter.GetArgumentAttribute();
|
||||
if (attribute == null)
|
||||
return null;
|
||||
|
||||
if (string.IsNullOrEmpty(attribute.OriginalName) == false)
|
||||
return attribute.OriginalName;
|
||||
|
||||
if (attribute.Index >= 0 && attribute.Index < originalParameterNames.Length)
|
||||
return originalParameterNames[attribute.Index];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static string GetOriginalArgumentName(HarmonyArgument[] attributes, string name, string[] originalParameterNames)
|
||||
{
|
||||
if (attributes.Length <= 0)
|
||||
return null;
|
||||
|
||||
var attribute = attributes.SingleOrDefault(p => p.NewName == name);
|
||||
if (attribute == null)
|
||||
return null;
|
||||
|
||||
if (string.IsNullOrEmpty(attribute.OriginalName) == false)
|
||||
return attribute.OriginalName;
|
||||
|
||||
if (attribute.Index >= 0 && attribute.Index < originalParameterNames.Length)
|
||||
return originalParameterNames[attribute.Index];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static string GetOriginalArgumentName(this MethodInfo method, string[] originalParameterNames, string name)
|
||||
{
|
||||
string argumentName;
|
||||
|
||||
argumentName = GetOriginalArgumentName(method.GetArgumentAttributes(), name, originalParameterNames);
|
||||
if (argumentName != null)
|
||||
return argumentName;
|
||||
|
||||
argumentName = GetOriginalArgumentName(method.DeclaringType.GetArgumentAttributes(), name, originalParameterNames);
|
||||
if (argumentName != null)
|
||||
return argumentName;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private static int GetArgumentIndex(MethodInfo patch, string[] originalParameterNames, ParameterInfo patchParam)
|
||||
{
|
||||
var originalName = patchParam.GetOriginalArgumentName(originalParameterNames);
|
||||
if (originalName != null)
|
||||
return Array.IndexOf(originalParameterNames, originalName);
|
||||
|
||||
var patchParamName = patchParam.Name;
|
||||
originalName = patch.GetOriginalArgumentName(originalParameterNames, patchParamName);
|
||||
if (originalName != null)
|
||||
return Array.IndexOf(originalParameterNames, originalName);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static MethodInfo getMethodMethod = typeof(MethodBase).GetMethod("GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle) });
|
||||
|
||||
static void EmitCallParameter(ILGenerator il, MethodBase original, MethodInfo patch, Dictionary<string, LocalBuilder> variables, bool allowFirsParamPassthrough)
|
||||
{
|
||||
var isInstance = original.IsStatic == false;
|
||||
var originalParameters = original.GetParameters();
|
||||
var originalParameterNames = originalParameters.Select(p => p.Name).ToArray();
|
||||
|
||||
// check for passthrough using first parameter (which must have same type as return type)
|
||||
var parameters = patch.GetParameters().ToList();
|
||||
if (allowFirsParamPassthrough && patch.ReturnType != typeof(void) && parameters.Count > 0 && parameters[0].ParameterType == patch.ReturnType)
|
||||
parameters.RemoveRange(0, 1);
|
||||
|
||||
foreach (var patchParam in parameters)
|
||||
{
|
||||
if (patchParam.Name == ORIGINAL_METHOD_PARAM)
|
||||
{
|
||||
var constructorInfo = original as ConstructorInfo;
|
||||
if (constructorInfo != null)
|
||||
{
|
||||
Emitter.Emit(il, OpCodes.Ldtoken, constructorInfo);
|
||||
Emitter.Emit(il, OpCodes.Call, getMethodMethod);
|
||||
continue;
|
||||
}
|
||||
var methodInfo = original as MethodInfo;
|
||||
if (methodInfo != null)
|
||||
{
|
||||
Emitter.Emit(il, OpCodes.Ldtoken, methodInfo);
|
||||
Emitter.Emit(il, OpCodes.Call, getMethodMethod);
|
||||
continue;
|
||||
}
|
||||
Emitter.Emit(il, OpCodes.Ldnull);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patchParam.Name == INSTANCE_PARAM)
|
||||
{
|
||||
if (original.IsStatic)
|
||||
Emitter.Emit(il, OpCodes.Ldnull);
|
||||
else if (patchParam.ParameterType.IsByRef)
|
||||
Emitter.Emit(il, OpCodes.Ldarga, 0); // probably won't work or will be useless
|
||||
else
|
||||
Emitter.Emit(il, OpCodes.Ldarg_0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patchParam.Name.StartsWith(INSTANCE_FIELD_PREFIX))
|
||||
{
|
||||
var fieldName = patchParam.Name.Substring(INSTANCE_FIELD_PREFIX.Length);
|
||||
FieldInfo fieldInfo;
|
||||
if (fieldName.All(char.IsDigit))
|
||||
{
|
||||
fieldInfo = AccessTools.Field(original.DeclaringType, int.Parse(fieldName));
|
||||
if (fieldInfo == null)
|
||||
throw new ArgumentException("No field found at given index in class " + original.DeclaringType.FullName, fieldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldInfo = AccessTools.Field(original.DeclaringType, fieldName);
|
||||
if (fieldInfo == null)
|
||||
throw new ArgumentException("No such field defined in class " + original.DeclaringType.FullName, fieldName);
|
||||
}
|
||||
|
||||
if (fieldInfo.IsStatic)
|
||||
{
|
||||
if (patchParam.ParameterType.IsByRef)
|
||||
Emitter.Emit(il, OpCodes.Ldsflda, fieldInfo);
|
||||
else
|
||||
Emitter.Emit(il, OpCodes.Ldsfld, fieldInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patchParam.ParameterType.IsByRef)
|
||||
{
|
||||
Emitter.Emit(il, OpCodes.Ldarg_0);
|
||||
Emitter.Emit(il, OpCodes.Ldflda, fieldInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Emitter.Emit(il, OpCodes.Ldarg_0);
|
||||
Emitter.Emit(il, OpCodes.Ldfld, fieldInfo);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patchParam.Name == STATE_VAR)
|
||||
{
|
||||
var ldlocCode = patchParam.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
|
||||
Emitter.Emit(il, ldlocCode, variables[patch.DeclaringType.FullName]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (patchParam.Name == RESULT_VAR)
|
||||
{
|
||||
if (AccessTools.GetReturnedType(original) == typeof(void))
|
||||
throw new Exception("Cannot get result from void method " + original.FullDescription());
|
||||
var ldlocCode = patchParam.ParameterType.IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc;
|
||||
Emitter.Emit(il, ldlocCode, variables[RESULT_VAR]);
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx;
|
||||
if (patchParam.Name.StartsWith(PARAM_INDEX_PREFIX))
|
||||
{
|
||||
var val = patchParam.Name.Substring(PARAM_INDEX_PREFIX.Length);
|
||||
if (!int.TryParse(val, out idx))
|
||||
throw new Exception("Parameter " + patchParam.Name + " does not contain a valid index");
|
||||
if (idx < 0 || idx >= originalParameters.Length)
|
||||
throw new Exception("No parameter found at index " + idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = GetArgumentIndex(patch, originalParameterNames, patchParam);
|
||||
if (idx == -1) throw new Exception("Parameter \"" + patchParam.Name + "\" not found in method " + original.FullDescription());
|
||||
}
|
||||
|
||||
// original -> patch opcode
|
||||
// --------------------------------------
|
||||
// 1 normal -> normal : LDARG
|
||||
// 2 normal -> ref/out : LDARGA
|
||||
// 3 ref/out -> normal : LDARG, LDIND_x
|
||||
// 4 ref/out -> ref/out : LDARG
|
||||
//
|
||||
var originalIsNormal = originalParameters[idx].IsOut == false && originalParameters[idx].ParameterType.IsByRef == false;
|
||||
var patchIsNormal = patchParam.IsOut == false && patchParam.ParameterType.IsByRef == false;
|
||||
var patchArgIndex = idx + (isInstance ? 1 : 0);
|
||||
|
||||
// Case 1 + 4
|
||||
if (originalIsNormal == patchIsNormal)
|
||||
{
|
||||
Emitter.Emit(il, OpCodes.Ldarg, patchArgIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Case 2
|
||||
if (originalIsNormal && patchIsNormal == false)
|
||||
{
|
||||
Emitter.Emit(il, OpCodes.Ldarga, patchArgIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Case 3
|
||||
Emitter.Emit(il, OpCodes.Ldarg, patchArgIndex);
|
||||
Emitter.Emit(il, LoadIndOpCodeFor(originalParameters[idx].ParameterType));
|
||||
}
|
||||
}
|
||||
|
||||
static bool AddPrefixes(ILGenerator il, MethodBase original, List<MethodInfo> prefixes, Dictionary<string, LocalBuilder> variables, Label label)
|
||||
{
|
||||
var canHaveJump = false;
|
||||
prefixes.ForEach(fix =>
|
||||
{
|
||||
EmitCallParameter(il, original, fix, variables, false);
|
||||
Emitter.Emit(il, OpCodes.Call, fix);
|
||||
|
||||
if (fix.ReturnType != typeof(void))
|
||||
{
|
||||
if (fix.ReturnType != typeof(bool))
|
||||
throw new Exception("Prefix patch " + fix + " has not \"bool\" or \"void\" return type: " + fix.ReturnType);
|
||||
Emitter.Emit(il, OpCodes.Brfalse, label);
|
||||
canHaveJump = true;
|
||||
}
|
||||
});
|
||||
return canHaveJump;
|
||||
}
|
||||
|
||||
static void AddPostfixes(ILGenerator il, MethodBase original, List<MethodInfo> postfixes, Dictionary<string, LocalBuilder> variables, bool passthroughPatches)
|
||||
{
|
||||
postfixes
|
||||
.Where(fix => passthroughPatches == (fix.ReturnType != typeof(void)))
|
||||
.Do(fix =>
|
||||
{
|
||||
EmitCallParameter(il, original, fix, variables, true);
|
||||
Emitter.Emit(il, OpCodes.Call, fix);
|
||||
|
||||
if (fix.ReturnType != typeof(void))
|
||||
{
|
||||
var firstFixParam = fix.GetParameters().FirstOrDefault();
|
||||
var hasPassThroughResultParam = firstFixParam != null && fix.ReturnType == firstFixParam.ParameterType;
|
||||
if (!hasPassThroughResultParam)
|
||||
{
|
||||
if (firstFixParam != null)
|
||||
throw new Exception("Return type of postfix patch " + fix + " does match type of its first parameter");
|
||||
|
||||
throw new Exception("Postfix patch " + fix + " must have a \"void\" return type");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class PatchInfoSerialization
|
||||
{
|
||||
class Binder : SerializationBinder
|
||||
{
|
||||
public override Type BindToType(string assemblyName, string typeName)
|
||||
{
|
||||
var types = new Type[] {
|
||||
typeof(PatchInfo),
|
||||
typeof(Patch[]),
|
||||
typeof(Patch)
|
||||
};
|
||||
foreach (var type in types)
|
||||
if (typeName == type.FullName)
|
||||
return type;
|
||||
var typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
|
||||
return typeToDeserialize;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Serialize(this PatchInfo patchInfo)
|
||||
{
|
||||
#pragma warning disable XS0001
|
||||
using (var streamMemory = new MemoryStream())
|
||||
{
|
||||
var formatter = new BinaryFormatter();
|
||||
formatter.Serialize(streamMemory, patchInfo);
|
||||
return streamMemory.GetBuffer();
|
||||
}
|
||||
#pragma warning restore XS0001
|
||||
}
|
||||
|
||||
public static PatchInfo Deserialize(byte[] bytes)
|
||||
{
|
||||
var formatter = new BinaryFormatter { Binder = new Binder() };
|
||||
#pragma warning disable XS0001
|
||||
var streamMemory = new MemoryStream(bytes);
|
||||
#pragma warning restore XS0001
|
||||
return (PatchInfo)formatter.Deserialize(streamMemory);
|
||||
}
|
||||
|
||||
// general sorting by (in that order): before, after, priority and index
|
||||
public static int PriorityComparer(object obj, int index, int priority, string[] before, string[] after)
|
||||
{
|
||||
var trv = Traverse.Create(obj);
|
||||
var theirOwner = trv.Field("owner").GetValue<string>();
|
||||
var theirPriority = trv.Field("priority").GetValue<int>();
|
||||
var theirIndex = trv.Field("index").GetValue<int>();
|
||||
|
||||
if (before != null && Array.IndexOf(before, theirOwner) > -1)
|
||||
return -1;
|
||||
if (after != null && Array.IndexOf(after, theirOwner) > -1)
|
||||
return 1;
|
||||
|
||||
if (priority != theirPriority)
|
||||
return -(priority.CompareTo(theirPriority));
|
||||
|
||||
return index.CompareTo(theirIndex);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PatchInfo
|
||||
{
|
||||
public Patch[] prefixes;
|
||||
public Patch[] postfixes;
|
||||
public Patch[] transpilers;
|
||||
|
||||
public PatchInfo()
|
||||
{
|
||||
prefixes = new Patch[0];
|
||||
postfixes = new Patch[0];
|
||||
transpilers = new Patch[0];
|
||||
}
|
||||
|
||||
public void AddPrefix(MethodInfo patch, string owner, int priority, string[] before, string[] after)
|
||||
{
|
||||
var l = prefixes.ToList();
|
||||
l.Add(new Patch(patch, prefixes.Count() + 1, owner, priority, before, after));
|
||||
prefixes = l.ToArray();
|
||||
}
|
||||
|
||||
public void RemovePrefix(string owner)
|
||||
{
|
||||
if (owner == "*")
|
||||
{
|
||||
prefixes = new Patch[0];
|
||||
return;
|
||||
}
|
||||
prefixes = prefixes.Where(patch => patch.owner != owner).ToArray();
|
||||
}
|
||||
|
||||
public void AddPostfix(MethodInfo patch, string owner, int priority, string[] before, string[] after)
|
||||
{
|
||||
var l = postfixes.ToList();
|
||||
l.Add(new Patch(patch, postfixes.Count() + 1, owner, priority, before, after));
|
||||
postfixes = l.ToArray();
|
||||
}
|
||||
|
||||
public void RemovePostfix(string owner)
|
||||
{
|
||||
if (owner == "*")
|
||||
{
|
||||
postfixes = new Patch[0];
|
||||
return;
|
||||
}
|
||||
postfixes = postfixes.Where(patch => patch.owner != owner).ToArray();
|
||||
}
|
||||
|
||||
public void AddTranspiler(MethodInfo patch, string owner, int priority, string[] before, string[] after)
|
||||
{
|
||||
var l = transpilers.ToList();
|
||||
l.Add(new Patch(patch, transpilers.Count() + 1, owner, priority, before, after));
|
||||
transpilers = l.ToArray();
|
||||
}
|
||||
|
||||
public void RemoveTranspiler(string owner)
|
||||
{
|
||||
if (owner == "*")
|
||||
{
|
||||
transpilers = new Patch[0];
|
||||
return;
|
||||
}
|
||||
transpilers = transpilers.Where(patch => patch.owner != owner).ToArray();
|
||||
}
|
||||
|
||||
public void RemovePatch(MethodInfo patch)
|
||||
{
|
||||
prefixes = prefixes.Where(p => p.patch != patch).ToArray();
|
||||
postfixes = postfixes.Where(p => p.patch != patch).ToArray();
|
||||
transpilers = transpilers.Where(p => p.patch != patch).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Patch : IComparable
|
||||
{
|
||||
readonly public int index;
|
||||
readonly public string owner;
|
||||
readonly public int priority;
|
||||
readonly public string[] before;
|
||||
readonly public string[] after;
|
||||
|
||||
readonly public MethodInfo patch;
|
||||
|
||||
public Patch(MethodInfo patch, int index, string owner, int priority, string[] before, string[] after)
|
||||
{
|
||||
if (patch is DynamicMethod) throw new Exception("Cannot directly reference dynamic method \"" + patch.FullDescription() + "\" in Harmony. Use a factory method instead that will return the dynamic method.");
|
||||
|
||||
this.index = index;
|
||||
this.owner = owner;
|
||||
this.priority = priority;
|
||||
this.before = before;
|
||||
this.after = after;
|
||||
this.patch = patch;
|
||||
}
|
||||
|
||||
public MethodInfo GetMethod(MethodBase original)
|
||||
{
|
||||
if (patch.ReturnType != typeof(DynamicMethod)) return patch;
|
||||
if (patch.IsStatic == false) return patch;
|
||||
var parameters = patch.GetParameters();
|
||||
if (parameters.Count() != 1) return patch;
|
||||
if (parameters[0].ParameterType != typeof(MethodBase)) return patch;
|
||||
|
||||
// we have a DynamicMethod factory, let's use it
|
||||
return patch.Invoke(null, new object[] { original }) as DynamicMethod;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return ((obj != null) && (obj is Patch) && (patch == ((Patch)obj).patch));
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return PatchInfoSerialization.PriorityComparer(obj, index, priority, before, after);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return patch.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
using Harmony.ILCopying;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class PatchFunctions
|
||||
{
|
||||
public static void AddPrefix(PatchInfo patchInfo, string owner, HarmonyMethod info)
|
||||
{
|
||||
if (info == null || info.method == null) return;
|
||||
|
||||
var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy;
|
||||
var before = info.before ?? new string[0];
|
||||
var after = info.after ?? new string[0];
|
||||
|
||||
patchInfo.AddPrefix(info.method, owner, priority, before, after);
|
||||
}
|
||||
|
||||
public static void RemovePrefix(PatchInfo patchInfo, string owner)
|
||||
{
|
||||
patchInfo.RemovePrefix(owner);
|
||||
}
|
||||
|
||||
public static void AddPostfix(PatchInfo patchInfo, string owner, HarmonyMethod info)
|
||||
{
|
||||
if (info == null || info.method == null) return;
|
||||
|
||||
var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy;
|
||||
var before = info.before ?? new string[0];
|
||||
var after = info.after ?? new string[0];
|
||||
|
||||
patchInfo.AddPostfix(info.method, owner, priority, before, after);
|
||||
}
|
||||
|
||||
public static void RemovePostfix(PatchInfo patchInfo, string owner)
|
||||
{
|
||||
patchInfo.RemovePostfix(owner);
|
||||
}
|
||||
|
||||
public static void AddTranspiler(PatchInfo patchInfo, string owner, HarmonyMethod info)
|
||||
{
|
||||
if (info == null || info.method == null) return;
|
||||
|
||||
var priority = info.prioritiy == -1 ? Priority.Normal : info.prioritiy;
|
||||
var before = info.before ?? new string[0];
|
||||
var after = info.after ?? new string[0];
|
||||
|
||||
patchInfo.AddTranspiler(info.method, owner, priority, before, after);
|
||||
}
|
||||
|
||||
public static void RemoveTranspiler(PatchInfo patchInfo, string owner)
|
||||
{
|
||||
patchInfo.RemoveTranspiler(owner);
|
||||
}
|
||||
|
||||
public static void RemovePatch(PatchInfo patchInfo, MethodInfo patch)
|
||||
{
|
||||
patchInfo.RemovePatch(patch);
|
||||
}
|
||||
|
||||
// pass in a generator that will create local variables for the returned instructions
|
||||
//
|
||||
public static List<ILInstruction> GetInstructions(ILGenerator generator, MethodBase method)
|
||||
{
|
||||
return MethodBodyReader.GetInstructions(generator, method);
|
||||
}
|
||||
|
||||
public static List<MethodInfo> GetSortedPatchMethods(MethodBase original, Patch[] patches)
|
||||
{
|
||||
return patches
|
||||
.Where(p => p.patch != null)
|
||||
.OrderBy(p => p)
|
||||
.Select(p => p.GetMethod(original))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static DynamicMethod UpdateWrapper(MethodBase original, PatchInfo patchInfo, string instanceID)
|
||||
{
|
||||
var sortedPrefixes = GetSortedPatchMethods(original, patchInfo.prefixes);
|
||||
var sortedPostfixes = GetSortedPatchMethods(original, patchInfo.postfixes);
|
||||
var sortedTranspilers = GetSortedPatchMethods(original, patchInfo.transpilers);
|
||||
|
||||
var replacement = MethodPatcher.CreatePatchedMethod(original, instanceID, sortedPrefixes, sortedPostfixes, sortedTranspilers);
|
||||
if (replacement == null) throw new MissingMethodException("Cannot create dynamic replacement for " + original.FullDescription());
|
||||
|
||||
var errorString = Memory.DetourMethod(original, replacement);
|
||||
if (errorString != null)
|
||||
throw new FormatException("Method " + original.FullDescription() + " cannot be patched. Reason: " + errorString);
|
||||
|
||||
PatchTools.RememberObject(original, replacement); // no gc for new value + release old value to gc
|
||||
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,308 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using StardewModdingAPI.Framework;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class PatchProcessor
|
||||
{
|
||||
static object locker = new object();
|
||||
|
||||
readonly HarmonyInstance instance;
|
||||
|
||||
readonly Type container;
|
||||
readonly HarmonyMethod containerAttributes;
|
||||
|
||||
List<MethodBase> originals = new List<MethodBase>();
|
||||
HarmonyMethod prefix;
|
||||
HarmonyMethod postfix;
|
||||
HarmonyMethod transpiler;
|
||||
|
||||
public PatchProcessor(HarmonyInstance instance, Type type, HarmonyMethod attributes)
|
||||
{
|
||||
this.instance = instance;
|
||||
container = type;
|
||||
containerAttributes = attributes ?? new HarmonyMethod(null);
|
||||
prefix = containerAttributes.Clone();
|
||||
postfix = containerAttributes.Clone();
|
||||
transpiler = containerAttributes.Clone();
|
||||
PrepareType();
|
||||
}
|
||||
|
||||
public PatchProcessor(HarmonyInstance instance, List<MethodBase> originals, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
|
||||
{
|
||||
this.instance = instance;
|
||||
this.originals = originals;
|
||||
this.prefix = prefix ?? new HarmonyMethod(null);
|
||||
this.postfix = postfix ?? new HarmonyMethod(null);
|
||||
this.transpiler = transpiler ?? new HarmonyMethod(null);
|
||||
}
|
||||
|
||||
public static Patches GetPatchInfo(MethodBase method)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
var patchInfo = HarmonySharedState.GetPatchInfo(method);
|
||||
if (patchInfo == null) return null;
|
||||
return new Patches(patchInfo.prefixes, patchInfo.postfixes, patchInfo.transpilers);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodBase> AllPatchedMethods()
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
return HarmonySharedState.GetPatchedMethods();
|
||||
}
|
||||
}
|
||||
|
||||
public List<DynamicMethod> Patch()
|
||||
{
|
||||
if(SCore.Instance.HarmonyDetourBridgeFailed)
|
||||
return new List<DynamicMethod>();
|
||||
lock (locker)
|
||||
{
|
||||
var dynamicMethods = new List<DynamicMethod>();
|
||||
foreach (var original in originals)
|
||||
{
|
||||
if (original == null)
|
||||
throw new NullReferenceException("original");
|
||||
|
||||
var individualPrepareResult = RunMethod<HarmonyPrepare, bool>(true, original);
|
||||
if (individualPrepareResult)
|
||||
{
|
||||
var patchInfo = HarmonySharedState.GetPatchInfo(original);
|
||||
if (patchInfo == null) patchInfo = new PatchInfo();
|
||||
|
||||
PatchFunctions.AddPrefix(patchInfo, instance.Id, prefix);
|
||||
PatchFunctions.AddPostfix(patchInfo, instance.Id, postfix);
|
||||
PatchFunctions.AddTranspiler(patchInfo, instance.Id, transpiler);
|
||||
dynamicMethods.Add(PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id));
|
||||
|
||||
HarmonySharedState.UpdatePatchInfo(original, patchInfo);
|
||||
|
||||
RunMethod<HarmonyCleanup>(original);
|
||||
}
|
||||
}
|
||||
return dynamicMethods;
|
||||
}
|
||||
}
|
||||
|
||||
public void Unpatch(HarmonyPatchType type, string harmonyID)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
foreach (var original in originals)
|
||||
{
|
||||
var patchInfo = HarmonySharedState.GetPatchInfo(original);
|
||||
if (patchInfo == null) patchInfo = new PatchInfo();
|
||||
|
||||
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Prefix)
|
||||
PatchFunctions.RemovePrefix(patchInfo, harmonyID);
|
||||
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Postfix)
|
||||
PatchFunctions.RemovePostfix(patchInfo, harmonyID);
|
||||
if (type == HarmonyPatchType.All || type == HarmonyPatchType.Transpiler)
|
||||
PatchFunctions.RemoveTranspiler(patchInfo, harmonyID);
|
||||
PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id);
|
||||
|
||||
HarmonySharedState.UpdatePatchInfo(original, patchInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Unpatch(MethodInfo patch)
|
||||
{
|
||||
lock (locker)
|
||||
{
|
||||
foreach (var original in originals)
|
||||
{
|
||||
var patchInfo = HarmonySharedState.GetPatchInfo(original);
|
||||
if (patchInfo == null) patchInfo = new PatchInfo();
|
||||
|
||||
PatchFunctions.RemovePatch(patchInfo, patch);
|
||||
PatchFunctions.UpdateWrapper(original, patchInfo, instance.Id);
|
||||
|
||||
HarmonySharedState.UpdatePatchInfo(original, patchInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareType()
|
||||
{
|
||||
var mainPrepareResult = RunMethod<HarmonyPrepare, bool>(true);
|
||||
if (mainPrepareResult == false)
|
||||
return;
|
||||
|
||||
var customOriginals = RunMethod<HarmonyTargetMethods, IEnumerable<MethodBase>>(null);
|
||||
if (customOriginals != null)
|
||||
{
|
||||
originals = customOriginals.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var originalMethodType = containerAttributes.methodType;
|
||||
|
||||
// MethodType default is Normal
|
||||
if (containerAttributes.methodType == null)
|
||||
containerAttributes.methodType = MethodType.Normal;
|
||||
|
||||
var isPatchAll = Attribute.GetCustomAttribute(container, typeof(HarmonyPatchAll)) != null;
|
||||
if (isPatchAll)
|
||||
{
|
||||
var type = containerAttributes.declaringType;
|
||||
originals.AddRange(AccessTools.GetDeclaredConstructors(type).Cast<MethodBase>());
|
||||
originals.AddRange(AccessTools.GetDeclaredMethods(type).Cast<MethodBase>());
|
||||
}
|
||||
else
|
||||
{
|
||||
var original = RunMethod<HarmonyTargetMethod, MethodBase>(null);
|
||||
|
||||
if (original == null)
|
||||
original = GetOriginalMethod();
|
||||
|
||||
if (original == null)
|
||||
{
|
||||
var info = "(";
|
||||
info += "declaringType=" + containerAttributes.declaringType + ", ";
|
||||
info += "methodName =" + containerAttributes.methodName + ", ";
|
||||
info += "methodType=" + originalMethodType + ", ";
|
||||
info += "argumentTypes=" + containerAttributes.argumentTypes.Description();
|
||||
info += ")";
|
||||
throw new ArgumentException("No target method specified for class " + container.FullName + " " + info);
|
||||
}
|
||||
|
||||
originals.Add(original);
|
||||
}
|
||||
}
|
||||
|
||||
PatchTools.GetPatches(container, out prefix.method, out postfix.method, out transpiler.method);
|
||||
|
||||
if (prefix.method != null)
|
||||
{
|
||||
if (prefix.method.IsStatic == false)
|
||||
throw new ArgumentException("Patch method " + prefix.method.FullDescription() + " must be static");
|
||||
|
||||
var prefixAttributes = prefix.method.GetHarmonyMethods();
|
||||
containerAttributes.Merge(HarmonyMethod.Merge(prefixAttributes)).CopyTo(prefix);
|
||||
}
|
||||
|
||||
if (postfix.method != null)
|
||||
{
|
||||
if (postfix.method.IsStatic == false)
|
||||
throw new ArgumentException("Patch method " + postfix.method.FullDescription() + " must be static");
|
||||
|
||||
var postfixAttributes = postfix.method.GetHarmonyMethods();
|
||||
containerAttributes.Merge(HarmonyMethod.Merge(postfixAttributes)).CopyTo(postfix);
|
||||
}
|
||||
|
||||
if (transpiler.method != null)
|
||||
{
|
||||
if (transpiler.method.IsStatic == false)
|
||||
throw new ArgumentException("Patch method " + transpiler.method.FullDescription() + " must be static");
|
||||
|
||||
var infixAttributes = transpiler.method.GetHarmonyMethods();
|
||||
containerAttributes.Merge(HarmonyMethod.Merge(infixAttributes)).CopyTo(transpiler);
|
||||
}
|
||||
}
|
||||
|
||||
MethodBase GetOriginalMethod()
|
||||
{
|
||||
var attr = containerAttributes;
|
||||
if (attr.declaringType == null) return null;
|
||||
|
||||
switch (attr.methodType)
|
||||
{
|
||||
case MethodType.Normal:
|
||||
if (attr.methodName == null)
|
||||
return null;
|
||||
return AccessTools.DeclaredMethod(attr.declaringType, attr.methodName, attr.argumentTypes);
|
||||
|
||||
case MethodType.Getter:
|
||||
if (attr.methodName == null)
|
||||
return null;
|
||||
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetGetMethod(true);
|
||||
|
||||
case MethodType.Setter:
|
||||
if (attr.methodName == null)
|
||||
return null;
|
||||
return AccessTools.DeclaredProperty(attr.declaringType, attr.methodName).GetSetMethod(true);
|
||||
|
||||
case MethodType.Constructor:
|
||||
return AccessTools.DeclaredConstructor(attr.declaringType, attr.argumentTypes);
|
||||
|
||||
case MethodType.StaticConstructor:
|
||||
return AccessTools.GetDeclaredConstructors(attr.declaringType)
|
||||
.Where(c => c.IsStatic)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
T RunMethod<S, T>(T defaultIfNotExisting, params object[] parameters)
|
||||
{
|
||||
if (container == null)
|
||||
return defaultIfNotExisting;
|
||||
|
||||
var methodName = typeof(S).Name.Replace("Harmony", "");
|
||||
|
||||
var paramList = new List<object> { instance };
|
||||
paramList.AddRange(parameters);
|
||||
var paramTypes = AccessTools.GetTypes(paramList.ToArray());
|
||||
var method = PatchTools.GetPatchMethod<S>(container, methodName, paramTypes);
|
||||
if (method != null && typeof(T).IsAssignableFrom(method.ReturnType))
|
||||
return (T)method.Invoke(null, paramList.ToArray());
|
||||
|
||||
method = PatchTools.GetPatchMethod<S>(container, methodName, new Type[] { typeof(HarmonyInstance) });
|
||||
if (method != null && typeof(T).IsAssignableFrom(method.ReturnType))
|
||||
return (T)method.Invoke(null, new object[] { instance });
|
||||
|
||||
method = PatchTools.GetPatchMethod<S>(container, methodName, Type.EmptyTypes);
|
||||
if (method != null)
|
||||
{
|
||||
if (typeof(T).IsAssignableFrom(method.ReturnType))
|
||||
return (T)method.Invoke(null, Type.EmptyTypes);
|
||||
|
||||
method.Invoke(null, Type.EmptyTypes);
|
||||
return defaultIfNotExisting;
|
||||
}
|
||||
|
||||
return defaultIfNotExisting;
|
||||
}
|
||||
|
||||
void RunMethod<S>(params object[] parameters)
|
||||
{
|
||||
if (container == null)
|
||||
return;
|
||||
|
||||
var methodName = typeof(S).Name.Replace("Harmony", "");
|
||||
|
||||
var paramList = new List<object> { instance };
|
||||
paramList.AddRange(parameters);
|
||||
var paramTypes = AccessTools.GetTypes(paramList.ToArray());
|
||||
var method = PatchTools.GetPatchMethod<S>(container, methodName, paramTypes);
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, paramList.ToArray());
|
||||
return;
|
||||
}
|
||||
|
||||
method = PatchTools.GetPatchMethod<S>(container, methodName, new Type[] { typeof(HarmonyInstance) });
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, new object[] { instance });
|
||||
return;
|
||||
}
|
||||
|
||||
method = PatchTools.GetPatchMethod<S>(container, methodName, Type.EmptyTypes);
|
||||
if (method != null)
|
||||
{
|
||||
method.Invoke(null, Type.EmptyTypes);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
namespace Harmony
|
||||
{
|
||||
public static class Priority
|
||||
{
|
||||
public const int Last = 0;
|
||||
public const int VeryLow = 100;
|
||||
public const int Low = 200;
|
||||
public const int LowerThanNormal = 300;
|
||||
public const int Normal = 400;
|
||||
public const int HigherThanNormal = 500;
|
||||
public const int High = 600;
|
||||
public const int VeryHigh = 700;
|
||||
public const int First = 800;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class AccessCache
|
||||
{
|
||||
Dictionary<Type, Dictionary<string, FieldInfo>> fields = new Dictionary<Type, Dictionary<string, FieldInfo>>();
|
||||
Dictionary<Type, Dictionary<string, PropertyInfo>> properties = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
|
||||
readonly Dictionary<Type, Dictionary<string, Dictionary<int, MethodBase>>> methods = new Dictionary<Type, Dictionary<string, Dictionary<int, MethodBase>>>();
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
public FieldInfo GetFieldInfo(Type type, string name)
|
||||
{
|
||||
Dictionary<string, FieldInfo> fieldsByType = null;
|
||||
if (fields.TryGetValue(type, out fieldsByType) == false)
|
||||
{
|
||||
fieldsByType = new Dictionary<string, FieldInfo>();
|
||||
fields.Add(type, fieldsByType);
|
||||
}
|
||||
|
||||
FieldInfo field = null;
|
||||
if (fieldsByType.TryGetValue(name, out field) == false)
|
||||
{
|
||||
field = AccessTools.Field(type, name);
|
||||
fieldsByType.Add(name, field);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public PropertyInfo GetPropertyInfo(Type type, string name)
|
||||
{
|
||||
Dictionary<string, PropertyInfo> propertiesByType = null;
|
||||
if (properties.TryGetValue(type, out propertiesByType) == false)
|
||||
{
|
||||
propertiesByType = new Dictionary<string, PropertyInfo>();
|
||||
properties.Add(type, propertiesByType);
|
||||
}
|
||||
|
||||
PropertyInfo property = null;
|
||||
if (propertiesByType.TryGetValue(name, out property) == false)
|
||||
{
|
||||
property = AccessTools.Property(type, name);
|
||||
propertiesByType.Add(name, property);
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
static int CombinedHashCode(IEnumerable<object> objects)
|
||||
{
|
||||
var hash1 = (5381 << 16) + 5381;
|
||||
var hash2 = hash1;
|
||||
var i = 0;
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ obj.GetHashCode();
|
||||
else
|
||||
hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ obj.GetHashCode();
|
||||
++i;
|
||||
}
|
||||
return hash1 + (hash2 * 1566083941);
|
||||
}
|
||||
|
||||
public MethodBase GetMethodInfo(Type type, string name, Type[] arguments)
|
||||
{
|
||||
Dictionary<string, Dictionary<int, MethodBase>> methodsByName = null;
|
||||
methods.TryGetValue(type, out methodsByName);
|
||||
if (methodsByName == null)
|
||||
{
|
||||
methodsByName = new Dictionary<string, Dictionary<int, MethodBase>>();
|
||||
methods.Add(type, methodsByName);
|
||||
}
|
||||
|
||||
Dictionary<int, MethodBase> methodsByArguments = null;
|
||||
methodsByName.TryGetValue(name, out methodsByArguments);
|
||||
if (methodsByArguments == null)
|
||||
{
|
||||
methodsByArguments = new Dictionary<int, MethodBase>();
|
||||
methodsByName.Add(name, methodsByArguments);
|
||||
}
|
||||
|
||||
MethodBase method = null;
|
||||
var argumentsHash = CombinedHashCode(arguments);
|
||||
methodsByArguments.TryGetValue(argumentsHash, out method);
|
||||
if (method == null)
|
||||
{
|
||||
method = AccessTools.Method(type, name, arguments);
|
||||
methodsByArguments.Add(argumentsHash, method);
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class AccessTools
|
||||
{
|
||||
public static BindingFlags all = BindingFlags.Public
|
||||
| BindingFlags.NonPublic
|
||||
| BindingFlags.Instance
|
||||
| BindingFlags.Static
|
||||
| BindingFlags.GetField
|
||||
| BindingFlags.SetField
|
||||
| BindingFlags.GetProperty
|
||||
| BindingFlags.SetProperty;
|
||||
|
||||
public static Type TypeByName(string name)
|
||||
{
|
||||
var type = Type.GetType(name, false);
|
||||
if (type == null)
|
||||
type = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(x => x.GetTypes())
|
||||
.FirstOrDefault(x => x.FullName == name);
|
||||
if (type == null)
|
||||
type = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(x => x.GetTypes())
|
||||
.FirstOrDefault(x => x.Name == name);
|
||||
return type;
|
||||
}
|
||||
|
||||
public static T FindIncludingBaseTypes<T>(Type type, Func<Type, T> action)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var result = action(type);
|
||||
if (result != null) return result;
|
||||
if (type == typeof(object)) return default(T);
|
||||
type = type.BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
public static T FindIncludingInnerTypes<T>(Type type, Func<Type, T> action)
|
||||
{
|
||||
var result = action(type);
|
||||
if (result != null) return result;
|
||||
foreach (var subType in type.GetNestedTypes(all))
|
||||
{
|
||||
result = FindIncludingInnerTypes(subType, action);
|
||||
if (result != null)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static FieldInfo Field(Type type, string name)
|
||||
{
|
||||
if (type == null || name == null) return null;
|
||||
return FindIncludingBaseTypes(type, t => t.GetField(name, all));
|
||||
}
|
||||
|
||||
public static FieldInfo Field(Type type, int idx)
|
||||
{
|
||||
return GetDeclaredFields(type).ElementAtOrDefault(idx);
|
||||
}
|
||||
|
||||
public static PropertyInfo DeclaredProperty(Type type, string name)
|
||||
{
|
||||
if (type == null || name == null) return null;
|
||||
return type.GetProperty(name, all);
|
||||
}
|
||||
|
||||
public static PropertyInfo Property(Type type, string name)
|
||||
{
|
||||
if (type == null || name == null) return null;
|
||||
return FindIncludingBaseTypes(type, t => t.GetProperty(name, all));
|
||||
}
|
||||
|
||||
public static MethodInfo DeclaredMethod(Type type, string name, Type[] parameters = null, Type[] generics = null)
|
||||
{
|
||||
if (type == null || name == null) return null;
|
||||
MethodInfo result;
|
||||
var modifiers = new ParameterModifier[] { };
|
||||
|
||||
if (parameters == null)
|
||||
result = type.GetMethod(name, all);
|
||||
else
|
||||
result = type.GetMethod(name, all, null, parameters, modifiers);
|
||||
|
||||
if (result == null) return null;
|
||||
if (generics != null) result = result.MakeGenericMethod(generics);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MethodInfo Method(Type type, string name, Type[] parameters = null, Type[] generics = null)
|
||||
{
|
||||
if (type == null || name == null) return null;
|
||||
MethodInfo result;
|
||||
var modifiers = new ParameterModifier[] { };
|
||||
if (parameters == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = FindIncludingBaseTypes(type, t => t.GetMethod(name, all));
|
||||
}
|
||||
catch (AmbiguousMatchException)
|
||||
{
|
||||
result = FindIncludingBaseTypes(type, t => t.GetMethod(name, all, null, new Type[0], modifiers));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = FindIncludingBaseTypes(type, t => t.GetMethod(name, all, null, parameters, modifiers));
|
||||
}
|
||||
if (result == null) return null;
|
||||
if (generics != null) result = result.MakeGenericMethod(generics);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MethodInfo Method(string typeColonMethodname, Type[] parameters = null, Type[] generics = null)
|
||||
{
|
||||
if (typeColonMethodname == null) return null;
|
||||
var parts = typeColonMethodname.Split(':');
|
||||
if (parts.Length != 2)
|
||||
throw new ArgumentException("Method must be specified as 'Namespace.Type1.Type2:MethodName", nameof(typeColonMethodname));
|
||||
|
||||
var type = TypeByName(parts[0]);
|
||||
return Method(type, parts[1], parameters, generics);
|
||||
}
|
||||
|
||||
public static List<string> GetMethodNames(Type type)
|
||||
{
|
||||
if (type == null) return new List<string>();
|
||||
return type.GetMethods(all).Select(m => m.Name).ToList();
|
||||
}
|
||||
|
||||
public static List<string> GetMethodNames(object instance)
|
||||
{
|
||||
if (instance == null) return new List<string>();
|
||||
return GetMethodNames(instance.GetType());
|
||||
}
|
||||
|
||||
public static ConstructorInfo DeclaredConstructor(Type type, Type[] parameters = null)
|
||||
{
|
||||
if (type == null) return null;
|
||||
if (parameters == null) parameters = new Type[0];
|
||||
return type.GetConstructor(all, null, parameters, new ParameterModifier[] { });
|
||||
}
|
||||
|
||||
public static ConstructorInfo Constructor(Type type, Type[] parameters = null)
|
||||
{
|
||||
if (type == null) return null;
|
||||
if (parameters == null) parameters = new Type[0];
|
||||
return FindIncludingBaseTypes(type, t => t.GetConstructor(all, null, parameters, new ParameterModifier[] { }));
|
||||
}
|
||||
|
||||
public static List<ConstructorInfo> GetDeclaredConstructors(Type type)
|
||||
{
|
||||
return type.GetConstructors(all).Where(method => method.DeclaringType == type).ToList();
|
||||
}
|
||||
|
||||
public static List<MethodInfo> GetDeclaredMethods(Type type)
|
||||
{
|
||||
return type.GetMethods(all).Where(method => method.DeclaringType == type).ToList();
|
||||
}
|
||||
|
||||
public static List<PropertyInfo> GetDeclaredProperties(Type type)
|
||||
{
|
||||
return type.GetProperties(all).Where(property => property.DeclaringType == type).ToList();
|
||||
}
|
||||
|
||||
public static List<FieldInfo> GetDeclaredFields(Type type)
|
||||
{
|
||||
return type.GetFields(all).Where(field => field.DeclaringType == type).ToList();
|
||||
}
|
||||
|
||||
public static Type GetReturnedType(MethodBase method)
|
||||
{
|
||||
var constructor = method as ConstructorInfo;
|
||||
if (constructor != null) return typeof(void);
|
||||
return ((MethodInfo)method).ReturnType;
|
||||
}
|
||||
|
||||
public static Type Inner(Type type, string name)
|
||||
{
|
||||
if (type == null || name == null) return null;
|
||||
return FindIncludingBaseTypes(type, t => t.GetNestedType(name, all));
|
||||
}
|
||||
|
||||
public static Type FirstInner(Type type, Func<Type, bool> predicate)
|
||||
{
|
||||
if (type == null || predicate == null) return null;
|
||||
return type.GetNestedTypes(all).FirstOrDefault(subType => predicate(subType));
|
||||
}
|
||||
|
||||
public static MethodInfo FirstMethod(Type type, Func<MethodInfo, bool> predicate)
|
||||
{
|
||||
if (type == null || predicate == null) return null;
|
||||
return type.GetMethods(all).FirstOrDefault(method => predicate(method));
|
||||
}
|
||||
|
||||
public static ConstructorInfo FirstConstructor(Type type, Func<ConstructorInfo, bool> predicate)
|
||||
{
|
||||
if (type == null || predicate == null) return null;
|
||||
return type.GetConstructors(all).FirstOrDefault(constructor => predicate(constructor));
|
||||
}
|
||||
|
||||
public static PropertyInfo FirstProperty(Type type, Func<PropertyInfo, bool> predicate)
|
||||
{
|
||||
if (type == null || predicate == null) return null;
|
||||
return type.GetProperties(all).FirstOrDefault(property => predicate(property));
|
||||
}
|
||||
|
||||
public static Type[] GetTypes(object[] parameters)
|
||||
{
|
||||
if (parameters == null) return new Type[0];
|
||||
return parameters.Select(p => p == null ? typeof(object) : p.GetType()).ToArray();
|
||||
}
|
||||
|
||||
public static List<string> GetFieldNames(Type type)
|
||||
{
|
||||
if (type == null) return new List<string>();
|
||||
return type.GetFields(all).Select(f => f.Name).ToList();
|
||||
}
|
||||
|
||||
public static List<string> GetFieldNames(object instance)
|
||||
{
|
||||
if (instance == null) return new List<string>();
|
||||
return GetFieldNames(instance.GetType());
|
||||
}
|
||||
|
||||
public static List<string> GetPropertyNames(Type type)
|
||||
{
|
||||
if (type == null) return new List<string>();
|
||||
return type.GetProperties(all).Select(f => f.Name).ToList();
|
||||
}
|
||||
|
||||
public static List<string> GetPropertyNames(object instance)
|
||||
{
|
||||
if (instance == null) return new List<string>();
|
||||
return GetPropertyNames(instance.GetType());
|
||||
}
|
||||
|
||||
public delegate ref U FieldRef<T, U>(T obj);
|
||||
public static FieldRef<T, U> FieldRefAccess<T, U>(string fieldName)
|
||||
{
|
||||
const BindingFlags bf = BindingFlags.NonPublic |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.DeclaredOnly;
|
||||
|
||||
var fi = typeof(T).GetField(fieldName, bf);
|
||||
if (fi == null)
|
||||
throw new MissingFieldException(typeof(T).Name, fieldName);
|
||||
|
||||
var s_name = "__refget_" + typeof(T).Name + "_fi_" + fi.Name;
|
||||
|
||||
// workaround for using ref-return with DynamicMethod:
|
||||
// a.) initialize with dummy return value
|
||||
var dm = new DynamicMethod(s_name, typeof(U), new[] { typeof(T) }, typeof(T), true);
|
||||
|
||||
// b.) replace with desired 'ByRef' return value
|
||||
var trv = Traverse.Create(dm);
|
||||
trv.Field("returnType").SetValue(typeof(U).MakeByRefType());
|
||||
trv.Field("m_returnType").SetValue(typeof(U).MakeByRefType());
|
||||
|
||||
var il = dm.GetILGenerator();
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldflda, fi);
|
||||
il.Emit(OpCodes.Ret);
|
||||
return (FieldRef<T, U>)dm.CreateDelegate(typeof(FieldRef<T, U>));
|
||||
}
|
||||
|
||||
public static ref U FieldRefAccess<T, U>(T instance, string fieldName)
|
||||
{
|
||||
return ref FieldRefAccess<T, U>(fieldName)(instance);
|
||||
}
|
||||
|
||||
public static void ThrowMissingMemberException(Type type, params string[] names)
|
||||
{
|
||||
var fields = string.Join(",", GetFieldNames(type).ToArray());
|
||||
var properties = string.Join(",", GetPropertyNames(type).ToArray());
|
||||
throw new MissingMemberException(string.Join(",", names) + "; available fields: " + fields + "; available properties: " + properties);
|
||||
}
|
||||
|
||||
public static object GetDefaultValue(Type type)
|
||||
{
|
||||
if (type == null) return null;
|
||||
if (type == typeof(void)) return null;
|
||||
if (type.IsValueType)
|
||||
return Activator.CreateInstance(type);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static object CreateInstance(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new NullReferenceException("Cannot create instance for NULL type");
|
||||
var ctor = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.Any, new Type[0], null);
|
||||
if (ctor != null)
|
||||
return Activator.CreateInstance(type);
|
||||
return FormatterServices.GetUninitializedObject(type);
|
||||
}
|
||||
|
||||
public static object MakeDeepCopy(object source, Type resultType, Func<string, Traverse, Traverse, object> processor = null, string pathRoot = "")
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
var type = source.GetType();
|
||||
|
||||
if (type.IsPrimitive)
|
||||
return source;
|
||||
|
||||
if (type.IsEnum)
|
||||
return Enum.ToObject(resultType, (int)source);
|
||||
|
||||
if (type.IsGenericType && resultType.IsGenericType)
|
||||
{
|
||||
var addOperation = FirstMethod(resultType, m => m.Name == "Add" && m.GetParameters().Count() == 1);
|
||||
if (addOperation != null)
|
||||
{
|
||||
var addableResult = Activator.CreateInstance(resultType);
|
||||
var addInvoker = MethodInvoker.GetHandler(addOperation);
|
||||
var newElementType = resultType.GetGenericArguments()[0];
|
||||
var i = 0;
|
||||
foreach (var element in source as IEnumerable)
|
||||
{
|
||||
var iStr = (i++).ToString();
|
||||
var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr;
|
||||
var newElement = MakeDeepCopy(element, newElementType, processor, path);
|
||||
addInvoker(addableResult, new object[] { newElement });
|
||||
}
|
||||
return addableResult;
|
||||
}
|
||||
|
||||
// TODO: add dictionaries support
|
||||
// maybe use methods in Dictionary<KeyValuePair<TKey,TVal>>
|
||||
}
|
||||
|
||||
if (type.IsArray && resultType.IsArray)
|
||||
{
|
||||
var elementType = resultType.GetElementType();
|
||||
var length = ((Array)source).Length;
|
||||
var arrayResult = Activator.CreateInstance(resultType, new object[] { length }) as object[];
|
||||
var originalArray = source as object[];
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var iStr = i.ToString();
|
||||
var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr;
|
||||
arrayResult[i] = MakeDeepCopy(originalArray[i], elementType, processor, path);
|
||||
}
|
||||
return arrayResult;
|
||||
}
|
||||
|
||||
var ns = type.Namespace;
|
||||
if (ns == "System" || (ns?.StartsWith("System.") ?? false))
|
||||
return source;
|
||||
|
||||
var result = CreateInstance(resultType);
|
||||
Traverse.IterateFields(source, result, (name, src, dst) =>
|
||||
{
|
||||
var path = pathRoot.Length > 0 ? pathRoot + "." + name : name;
|
||||
var value = processor != null ? processor(path, src, dst) : src.GetValue();
|
||||
dst.SetValue(MakeDeepCopy(value, dst.GetValueType(), processor, path));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void MakeDeepCopy<T>(object source, out T result, Func<string, Traverse, Traverse, object> processor = null, string pathRoot = "")
|
||||
{
|
||||
result = (T)MakeDeepCopy(source, typeof(T), processor, pathRoot);
|
||||
}
|
||||
|
||||
public static bool IsStruct(Type type)
|
||||
{
|
||||
return type.IsValueType && !IsValue(type) && !IsVoid(type);
|
||||
}
|
||||
|
||||
public static bool IsClass(Type type)
|
||||
{
|
||||
return !type.IsValueType;
|
||||
}
|
||||
|
||||
public static bool IsValue(Type type)
|
||||
{
|
||||
return type.IsPrimitive || type.IsEnum;
|
||||
}
|
||||
|
||||
public static bool IsVoid(Type type)
|
||||
{
|
||||
return type == typeof(void);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
using Harmony.ILCopying;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class DynamicTools
|
||||
{
|
||||
/* TODO add support for functions that return structs larger than 8 bytes
|
||||
*
|
||||
* https://github.com/dotnet/coreclr/issues/12503
|
||||
* https://stackoverflow.com/questions/44641195/what-could-cause-p-invoke-arguments-to-be-out-of-order-when-passed
|
||||
*
|
||||
* ERROR
|
||||
* Managed Debugging Assistant 'FatalExecutionEngineError'
|
||||
* The runtime has encountered a fatal error. The address of the error was at 0x72747d0e, on thread 0x9c38. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
|
||||
*
|
||||
* Calling signatures change:
|
||||
* .NET <4.5 jits to void Func(ref LargeReturnStruct, object this, params)
|
||||
* .NET 4.5+ jits to Func(object this, ref LargeReturnStruct, params)
|
||||
*
|
||||
* // Test case
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||
public struct Struct1
|
||||
{
|
||||
public double foo;
|
||||
public double bar;
|
||||
}
|
||||
|
||||
public class StructTest1
|
||||
{
|
||||
public Struct1 PatchMe()
|
||||
{
|
||||
return default(Struct1);
|
||||
}
|
||||
}
|
||||
*
|
||||
*/
|
||||
public static DynamicMethod CreateDynamicMethod(MethodBase original, string suffix)
|
||||
{
|
||||
if (original == null) throw new ArgumentNullException("original cannot be null");
|
||||
var patchName = original.Name + suffix;
|
||||
patchName = patchName.Replace("<>", "");
|
||||
|
||||
var parameters = original.GetParameters();
|
||||
var result = parameters.Types().ToList();
|
||||
if (original.IsStatic == false)
|
||||
result.Insert(0, typeof(object));
|
||||
var paramTypes = result.ToArray();
|
||||
var returnType = AccessTools.GetReturnedType(original);
|
||||
|
||||
// DynamicMethod does not support byref return types
|
||||
if (returnType == null || returnType.IsByRef)
|
||||
return null;
|
||||
|
||||
DynamicMethod method;
|
||||
try
|
||||
{
|
||||
method = new DynamicMethod(
|
||||
patchName,
|
||||
MethodAttributes.Public | MethodAttributes.Static,
|
||||
CallingConventions.Standard,
|
||||
returnType,
|
||||
paramTypes,
|
||||
original.DeclaringType,
|
||||
true
|
||||
);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
method.DefineParameter(i + 1, parameters[i].Attributes, parameters[i].Name);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
public static ILGenerator CreateSaveableMethod(MethodBase original, string suffix, out AssemblyBuilder assemblyBuilder, out TypeBuilder typeBuilder)
|
||||
{
|
||||
var assemblyName = new AssemblyName("DebugAssembly");
|
||||
var path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave, path);
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
|
||||
typeBuilder = moduleBuilder.DefineType("Debug" + original.DeclaringType.Name, TypeAttributes.Public);
|
||||
|
||||
if (original == null) throw new ArgumentNullException("original cannot be null");
|
||||
var patchName = original.Name + suffix;
|
||||
patchName = patchName.Replace("<>", "");
|
||||
|
||||
var parameters = original.GetParameters();
|
||||
var result = parameters.Types().ToList();
|
||||
if (original.IsStatic == false)
|
||||
result.Insert(0, typeof(object));
|
||||
var paramTypes = result.ToArray();
|
||||
|
||||
var methodBuilder = typeBuilder.DefineMethod(
|
||||
patchName,
|
||||
MethodAttributes.Public | MethodAttributes.Static,
|
||||
CallingConventions.Standard,
|
||||
AccessTools.GetReturnedType(original),
|
||||
paramTypes
|
||||
);
|
||||
|
||||
return methodBuilder.GetILGenerator();
|
||||
}
|
||||
|
||||
public static void SaveMethod(AssemblyBuilder assemblyBuilder, TypeBuilder typeBuilder)
|
||||
{
|
||||
var t = typeBuilder.CreateType();
|
||||
assemblyBuilder.Save("HarmonyDebugAssembly.dll");
|
||||
}
|
||||
|
||||
public static LocalBuilder[] DeclareLocalVariables(MethodBase original, ILGenerator il, bool logOutput = true)
|
||||
{
|
||||
var vars = original.GetMethodBody()?.LocalVariables;
|
||||
if (vars == null)
|
||||
return new LocalBuilder[0];
|
||||
return vars.Select(lvi =>
|
||||
{
|
||||
var localBuilder = il.DeclareLocal(lvi.LocalType, lvi.IsPinned);
|
||||
if (logOutput)
|
||||
Emitter.LogLocalVariable(il, localBuilder);
|
||||
return localBuilder;
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public static LocalBuilder DeclareLocalVariable(ILGenerator il, Type type)
|
||||
{
|
||||
if (type.IsByRef) type = type.GetElementType();
|
||||
|
||||
if (AccessTools.IsClass(type))
|
||||
{
|
||||
var v = il.DeclareLocal(type);
|
||||
Emitter.LogLocalVariable(il, v);
|
||||
Emitter.Emit(il, OpCodes.Ldnull);
|
||||
Emitter.Emit(il, OpCodes.Stloc, v);
|
||||
return v;
|
||||
}
|
||||
if (AccessTools.IsStruct(type))
|
||||
{
|
||||
var v = il.DeclareLocal(type);
|
||||
Emitter.LogLocalVariable(il, v);
|
||||
Emitter.Emit(il, OpCodes.Ldloca, v);
|
||||
Emitter.Emit(il, OpCodes.Initobj, type);
|
||||
return v;
|
||||
}
|
||||
if (AccessTools.IsValue(type))
|
||||
{
|
||||
var v = il.DeclareLocal(type);
|
||||
Emitter.LogLocalVariable(il, v);
|
||||
if (type == typeof(float))
|
||||
Emitter.Emit(il, OpCodes.Ldc_R4, (float)0);
|
||||
else if (type == typeof(double))
|
||||
Emitter.Emit(il, OpCodes.Ldc_R8, (double)0);
|
||||
else if (type == typeof(long))
|
||||
Emitter.Emit(il, OpCodes.Ldc_I8, (long)0);
|
||||
else
|
||||
Emitter.Emit(il, OpCodes.Ldc_I4, 0);
|
||||
Emitter.Emit(il, OpCodes.Stloc, v);
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void PrepareDynamicMethod(DynamicMethod method)
|
||||
{
|
||||
var nonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
var nonPublicStatic = BindingFlags.NonPublic | BindingFlags.Static;
|
||||
|
||||
// on mono, just call 'CreateDynMethod'
|
||||
//
|
||||
var m_CreateDynMethod = typeof(DynamicMethod).GetMethod("CreateDynMethod", nonPublicInstance);
|
||||
if (m_CreateDynMethod != null)
|
||||
{
|
||||
m_CreateDynMethod.Invoke(method, new object[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// on all .NET Core versions, call 'RuntimeHelpers._CompileMethod' but with a different parameter:
|
||||
//
|
||||
var m__CompileMethod = typeof(RuntimeHelpers).GetMethod("_CompileMethod", nonPublicStatic);
|
||||
|
||||
var m_GetMethodDescriptor = typeof(DynamicMethod).GetMethod("GetMethodDescriptor", nonPublicInstance);
|
||||
var handle = (RuntimeMethodHandle)m_GetMethodDescriptor.Invoke(method, new object[0]);
|
||||
|
||||
// 1) RuntimeHelpers._CompileMethod(handle.GetMethodInfo())
|
||||
//
|
||||
var m_GetMethodInfo = typeof(RuntimeMethodHandle).GetMethod("GetMethodInfo", nonPublicInstance);
|
||||
if (m_GetMethodInfo != null)
|
||||
{
|
||||
var runtimeMethodInfo = m_GetMethodInfo.Invoke(handle, new object[0]);
|
||||
try
|
||||
{
|
||||
// this can throw BadImageFormatException "An attempt was made to load a program with an incorrect format"
|
||||
m__CompileMethod.Invoke(null, new object[] { runtimeMethodInfo });
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// 2) RuntimeHelpers._CompileMethod(handle.Value)
|
||||
//
|
||||
if (m__CompileMethod.GetParameters()[0].ParameterType.IsAssignableFrom(handle.Value.GetType()))
|
||||
{
|
||||
m__CompileMethod.Invoke(null, new object[] { handle.Value });
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) RuntimeHelpers._CompileMethod(handle)
|
||||
//
|
||||
if (m__CompileMethod.GetParameters()[0].ParameterType.IsAssignableFrom(handle.GetType()))
|
||||
{
|
||||
m__CompileMethod.Invoke(null, new object[] { handle });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class GeneralExtensions
|
||||
{
|
||||
public static string Join<T>(this IEnumerable<T> enumeration, Func<T, string> converter = null, string delimiter = ", ")
|
||||
{
|
||||
if (converter == null) converter = t => t.ToString();
|
||||
return enumeration.Aggregate("", (prev, curr) => prev + (prev != "" ? delimiter : "") + converter(curr));
|
||||
}
|
||||
|
||||
public static string Description(this Type[] parameters)
|
||||
{
|
||||
if (parameters == null) return "NULL";
|
||||
var pattern = @", \w+, Version=[0-9.]+, Culture=neutral, PublicKeyToken=[0-9a-f]+";
|
||||
return "(" + parameters.Join(p => p?.FullName == null ? "null" : Regex.Replace(p.FullName, pattern, "")) + ")";
|
||||
}
|
||||
|
||||
public static string FullDescription(this MethodBase method)
|
||||
{
|
||||
var parameters = method.GetParameters().Select(p => p.ParameterType).ToArray();
|
||||
return method.DeclaringType.FullName + "." + method.Name + parameters.Description();
|
||||
}
|
||||
|
||||
public static Type[] Types(this ParameterInfo[] pinfo)
|
||||
{
|
||||
return pinfo.Select(pi => pi.ParameterType).ToArray();
|
||||
}
|
||||
|
||||
public static T GetValueSafe<S, T>(this Dictionary<S, T> dictionary, S key)
|
||||
{
|
||||
T result;
|
||||
if (dictionary.TryGetValue(key, out result))
|
||||
return result;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static T GetTypedValue<T>(this Dictionary<string, object> dictionary, string key)
|
||||
{
|
||||
object result;
|
||||
if (dictionary.TryGetValue(key, out result))
|
||||
if (result is T)
|
||||
return (T)result;
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
public static void Do<T>(this IEnumerable<T> sequence, Action<T> action)
|
||||
{
|
||||
if (sequence == null) return;
|
||||
var enumerator = sequence.GetEnumerator();
|
||||
while (enumerator.MoveNext()) action(enumerator.Current);
|
||||
}
|
||||
|
||||
public static void DoIf<T>(this IEnumerable<T> sequence, Func<T, bool> condition, Action<T> action)
|
||||
{
|
||||
sequence.Where(condition).Do(action);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Add<T>(this IEnumerable<T> sequence, T item)
|
||||
{
|
||||
return (sequence ?? Enumerable.Empty<T>()).Concat(new[] { item });
|
||||
}
|
||||
|
||||
public static T[] AddRangeToArray<T>(this T[] sequence, T[] items)
|
||||
{
|
||||
return (sequence ?? Enumerable.Empty<T>()).Concat(items).ToArray();
|
||||
}
|
||||
|
||||
public static T[] AddToArray<T>(this T[] sequence, T item)
|
||||
{
|
||||
return Add(sequence, item).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class FileLog
|
||||
{
|
||||
public static string logPath;
|
||||
public static char indentChar = '\t';
|
||||
public static int indentLevel = 0;
|
||||
static List<string> buffer = new List<string>();
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
static FileLog()
|
||||
{
|
||||
logPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + Path.DirectorySeparatorChar + "harmony.log.txt";
|
||||
}
|
||||
|
||||
static string IndentString()
|
||||
{
|
||||
return new string(indentChar, indentLevel);
|
||||
}
|
||||
|
||||
public static void ChangeIndent(int delta)
|
||||
{
|
||||
indentLevel = Math.Max(0, indentLevel + delta);
|
||||
}
|
||||
|
||||
// use this method only if you are sure that FlushBuffer will be called
|
||||
// or else logging information is incomplete in case of a crash
|
||||
//
|
||||
public static void LogBuffered(string str)
|
||||
{
|
||||
lock (logPath)
|
||||
{
|
||||
buffer.Add(IndentString() + str);
|
||||
}
|
||||
}
|
||||
|
||||
public static void FlushBuffer()
|
||||
{
|
||||
lock (logPath)
|
||||
{
|
||||
if (buffer.Count > 0)
|
||||
{
|
||||
using (var writer = File.AppendText(logPath))
|
||||
{
|
||||
foreach (var str in buffer)
|
||||
writer.WriteLine(str);
|
||||
}
|
||||
buffer.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is the slower method that flushes changes directly to the file
|
||||
// to prevent missing information in case of a cache
|
||||
//
|
||||
public static void Log(string str)
|
||||
{
|
||||
lock (logPath)
|
||||
{
|
||||
using (var writer = File.AppendText(logPath))
|
||||
{
|
||||
writer.WriteLine(IndentString() + str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
lock (logPath)
|
||||
{
|
||||
var path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + Path.DirectorySeparatorChar + "harmony.log.txt";
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void LogBytes(long ptr, int len)
|
||||
{
|
||||
lock (logPath)
|
||||
{
|
||||
var p = (byte*)ptr;
|
||||
var s = "";
|
||||
for (var i = 1; i <= len; i++)
|
||||
{
|
||||
if (s == "") s = "# ";
|
||||
s = s + (*p).ToString("X2") + " ";
|
||||
if (i > 1 || len == 1)
|
||||
{
|
||||
if (i % 8 == 0 || i == len)
|
||||
{
|
||||
Log(s);
|
||||
s = "";
|
||||
}
|
||||
else if (i % 4 == 0)
|
||||
s = s + " ";
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
var arr = new byte[len];
|
||||
Marshal.Copy((IntPtr)ptr, arr, 0, len);
|
||||
var md5Hash = MD5.Create();
|
||||
var hash = md5Hash.ComputeHash(arr);
|
||||
#pragma warning disable XS0001
|
||||
var sBuilder = new StringBuilder();
|
||||
#pragma warning restore XS0001
|
||||
for (var i = 0; i < hash.Length; i++)
|
||||
sBuilder.Append(hash[i].ToString("X2"));
|
||||
Log("HASH: " + sBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class PatchTools
|
||||
{
|
||||
// this holds all the objects we want to keep alive so they don't get garbage-collected
|
||||
static Dictionary<object, object> objectReferences = new Dictionary<object, object>();
|
||||
public static void RememberObject(object key, object value)
|
||||
{
|
||||
objectReferences[key] = value;
|
||||
}
|
||||
|
||||
public static MethodInfo GetPatchMethod<T>(Type patchType, string name, Type[] parameters = null)
|
||||
{
|
||||
var method = patchType.GetMethods(AccessTools.all)
|
||||
.FirstOrDefault(m => m.GetCustomAttributes(typeof(T), true).Any());
|
||||
if (method == null)
|
||||
method = AccessTools.Method(patchType, name, parameters);
|
||||
return method;
|
||||
}
|
||||
|
||||
public static void GetPatches(Type patchType, out MethodInfo prefix, out MethodInfo postfix, out MethodInfo transpiler)
|
||||
{
|
||||
prefix = GetPatchMethod<HarmonyPrefix>(patchType, "Prefix");
|
||||
postfix = GetPatchMethod<HarmonyPostfix>(patchType, "Postfix");
|
||||
transpiler = GetPatchMethod<HarmonyTranspiler>(patchType, "Transpiler");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
using Harmony.ILCopying;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Harmony.Tools
|
||||
{
|
||||
internal class SelfPatching
|
||||
{
|
||||
static readonly int upgradeToLatestVersionFullNameHash = typeof(UpgradeToLatestVersion).FullName.GetHashCode();
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
static int GetVersion(MethodBase method)
|
||||
{
|
||||
var attribute = method.GetCustomAttributes(false)
|
||||
.Where(attr => attr.GetType().FullName.GetHashCode() == upgradeToLatestVersionFullNameHash)
|
||||
.FirstOrDefault();
|
||||
if (attribute == null)
|
||||
return -1;
|
||||
return Traverse.Create(attribute).Field("version").GetValue<int>();
|
||||
}
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
static string MethodKey(MethodBase method)
|
||||
{
|
||||
return method.FullDescription();
|
||||
}
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
static bool IsHarmonyAssembly(Assembly assembly)
|
||||
{
|
||||
try
|
||||
{
|
||||
return assembly.ReflectionOnly == false && assembly.GetType(typeof(HarmonyInstance).FullName) != null;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<MethodBase> GetAllMethods(Assembly assembly)
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
return types
|
||||
.SelectMany(type => type.GetMethods(AccessTools.all).Cast<MethodBase>())
|
||||
.Concat(types.SelectMany(type => type.GetConstructors(AccessTools.all)).Cast<MethodBase>())
|
||||
.Concat(types.SelectMany(type => type.GetProperties(AccessTools.all)).Select(prop => prop.GetGetMethod()).Cast<MethodBase>())
|
||||
.Concat(types.SelectMany(type => type.GetProperties(AccessTools.all)).Select(prop => prop.GetSetMethod()).Cast<MethodBase>())
|
||||
.Where(method => method != null && method.DeclaringType.Assembly == assembly)
|
||||
.OrderBy(method => method.FullDescription())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static string AssemblyInfo(Assembly assembly)
|
||||
{
|
||||
var version = assembly.GetName().Version;
|
||||
var location = assembly.Location;
|
||||
if (location == null || location == "") location = new Uri(assembly.CodeBase).LocalPath;
|
||||
return location + "(v" + version + (assembly.GlobalAssemblyCache ? ", cached" : "") + ")";
|
||||
}
|
||||
|
||||
[UpgradeToLatestVersion(1)]
|
||||
public static void PatchOldHarmonyMethods()
|
||||
{
|
||||
var watch = new Stopwatch();
|
||||
watch.Start();
|
||||
|
||||
var ourAssembly = new StackTrace(true).GetFrame(1).GetMethod().DeclaringType.Assembly;
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
var originalVersion = ourAssembly.GetName().Version;
|
||||
var runningVersion = typeof(SelfPatching).Assembly.GetName().Version;
|
||||
if (runningVersion > originalVersion)
|
||||
{
|
||||
// log start because FileLog has not done it
|
||||
FileLog.Log("### Harmony v" + originalVersion + " started");
|
||||
FileLog.Log("### Self-patching unnecessary because we are already patched by v" + runningVersion);
|
||||
FileLog.Log("### At " + DateTime.Now.ToString("yyyy-MM-dd hh.mm.ss"));
|
||||
return;
|
||||
}
|
||||
FileLog.Log("Self-patching started (v" + originalVersion + ")");
|
||||
}
|
||||
|
||||
var potentialMethodsToUpgrade = new Dictionary<string, MethodBase>();
|
||||
GetAllMethods(ourAssembly)
|
||||
.Where(method => method != null && method.GetCustomAttributes(false).Any(attr => attr is UpgradeToLatestVersion))
|
||||
.Do(method => potentialMethodsToUpgrade.Add(MethodKey(method), method));
|
||||
|
||||
var otherHarmonyAssemblies = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(assembly => IsHarmonyAssembly(assembly) && assembly != ourAssembly)
|
||||
.ToList();
|
||||
|
||||
if (HarmonyInstance.DEBUG)
|
||||
{
|
||||
otherHarmonyAssemblies.Do(assembly => FileLog.Log("Found Harmony " + AssemblyInfo(assembly)));
|
||||
|
||||
FileLog.Log("Potential methods to upgrade:");
|
||||
potentialMethodsToUpgrade.Values.OrderBy(method => method.FullDescription()).Do(method => FileLog.Log("- " + method.FullDescription()));
|
||||
}
|
||||
|
||||
var totalCounter = 0;
|
||||
var potentialCounter = 0;
|
||||
var patchedCounter = 0;
|
||||
foreach (var assembly in otherHarmonyAssemblies)
|
||||
{
|
||||
foreach (var oldMethod in GetAllMethods(assembly))
|
||||
{
|
||||
totalCounter++;
|
||||
|
||||
if (potentialMethodsToUpgrade.TryGetValue(MethodKey(oldMethod), out var newMethod))
|
||||
{
|
||||
var newVersion = GetVersion(newMethod);
|
||||
potentialCounter++;
|
||||
|
||||
var oldVersion = GetVersion(oldMethod);
|
||||
if (oldVersion < newVersion)
|
||||
{
|
||||
if (HarmonyInstance.DEBUG)
|
||||
FileLog.Log("Self-patching " + oldMethod.FullDescription() + " in " + AssemblyInfo(assembly));
|
||||
patchedCounter++;
|
||||
Memory.DetourMethod(oldMethod, newMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HarmonyInstance.DEBUG)
|
||||
FileLog.Log("Self-patched " + patchedCounter + " out of " + totalCounter + " methods (" + (potentialCounter - patchedCounter) + " skipped) in " + watch.ElapsedMilliseconds + "ms");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class SymbolExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Given a lambda expression that calls a method, returns the method info.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="expression">The expression.</param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethodInfo(Expression<Action> expression)
|
||||
{
|
||||
return GetMethodInfo((LambdaExpression)expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a lambda expression that calls a method, returns the method info.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="expression">The expression.</param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
|
||||
{
|
||||
return GetMethodInfo((LambdaExpression)expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a lambda expression that calls a method, returns the method info.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="expression">The expression.</param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression)
|
||||
{
|
||||
return GetMethodInfo((LambdaExpression)expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a lambda expression that calls a method, returns the method info.
|
||||
/// </summary>
|
||||
/// <param name="expression">The expression.</param>
|
||||
/// <returns></returns>
|
||||
public static MethodInfo GetMethodInfo(LambdaExpression expression)
|
||||
{
|
||||
var outermostExpression = expression.Body as MethodCallExpression;
|
||||
|
||||
if (outermostExpression == null)
|
||||
throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
|
||||
|
||||
var method = outermostExpression.Method;
|
||||
if (method == null)
|
||||
throw new Exception("Cannot find method for expression " + expression);
|
||||
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,298 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public class Traverse<T>
|
||||
{
|
||||
private Traverse traverse;
|
||||
|
||||
Traverse()
|
||||
{
|
||||
}
|
||||
|
||||
public Traverse(Traverse traverse)
|
||||
{
|
||||
this.traverse = traverse;
|
||||
}
|
||||
|
||||
public T Value
|
||||
{
|
||||
get => traverse.GetValue<T>();
|
||||
set => traverse.SetValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Traverse
|
||||
{
|
||||
static AccessCache Cache;
|
||||
|
||||
Type _type;
|
||||
object _root;
|
||||
MemberInfo _info;
|
||||
MethodBase _method;
|
||||
object[] _params;
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
static Traverse()
|
||||
{
|
||||
if (Cache == null)
|
||||
Cache = new AccessCache();
|
||||
}
|
||||
|
||||
public static Traverse Create(Type type)
|
||||
{
|
||||
return new Traverse(type);
|
||||
}
|
||||
|
||||
public static Traverse Create<T>()
|
||||
{
|
||||
return Create(typeof(T));
|
||||
}
|
||||
|
||||
public static Traverse Create(object root)
|
||||
{
|
||||
return new Traverse(root);
|
||||
}
|
||||
|
||||
public static Traverse CreateWithType(string name)
|
||||
{
|
||||
return new Traverse(AccessTools.TypeByName(name));
|
||||
}
|
||||
|
||||
Traverse()
|
||||
{
|
||||
}
|
||||
|
||||
public Traverse(Type type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public Traverse(object root)
|
||||
{
|
||||
_root = root;
|
||||
_type = root?.GetType();
|
||||
}
|
||||
|
||||
Traverse(object root, MemberInfo info, object[] index)
|
||||
{
|
||||
_root = root;
|
||||
_type = root?.GetType();
|
||||
_info = info;
|
||||
_params = index;
|
||||
}
|
||||
|
||||
Traverse(object root, MethodInfo method, object[] parameter)
|
||||
{
|
||||
_root = root;
|
||||
_type = method.ReturnType;
|
||||
_method = method;
|
||||
_params = parameter;
|
||||
}
|
||||
|
||||
public object GetValue()
|
||||
{
|
||||
if (_info is FieldInfo)
|
||||
return ((FieldInfo)_info).GetValue(_root);
|
||||
if (_info is PropertyInfo)
|
||||
return ((PropertyInfo)_info).GetValue(_root, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
|
||||
if (_method != null)
|
||||
return _method.Invoke(_root, _params);
|
||||
if (_root == null && _type != null) return _type;
|
||||
return _root;
|
||||
}
|
||||
|
||||
public T GetValue<T>()
|
||||
{
|
||||
var value = GetValue();
|
||||
if (value == null) return default(T);
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
public object GetValue(params object[] arguments)
|
||||
{
|
||||
if (_method == null)
|
||||
throw new Exception("cannot get method value without method");
|
||||
return _method.Invoke(_root, arguments);
|
||||
}
|
||||
|
||||
public T GetValue<T>(params object[] arguments)
|
||||
{
|
||||
if (_method == null)
|
||||
throw new Exception("cannot get method value without method");
|
||||
return (T)_method.Invoke(_root, arguments);
|
||||
}
|
||||
|
||||
public Traverse SetValue(object value)
|
||||
{
|
||||
if (_info is FieldInfo)
|
||||
((FieldInfo)_info).SetValue(_root, value, AccessTools.all, null, CultureInfo.CurrentCulture);
|
||||
if (_info is PropertyInfo)
|
||||
((PropertyInfo)_info).SetValue(_root, value, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
|
||||
if (_method != null)
|
||||
throw new Exception("cannot set value of method " + _method.FullDescription());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Type GetValueType()
|
||||
{
|
||||
if (_info is FieldInfo)
|
||||
return ((FieldInfo)_info).FieldType;
|
||||
if (_info is PropertyInfo)
|
||||
return ((PropertyInfo)_info).PropertyType;
|
||||
return null;
|
||||
}
|
||||
|
||||
Traverse Resolve()
|
||||
{
|
||||
if (_root == null && _type != null) return this;
|
||||
return new Traverse(GetValue());
|
||||
}
|
||||
|
||||
public Traverse Type(string name)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null");
|
||||
if (_type == null) return new Traverse();
|
||||
var type = AccessTools.Inner(_type, name);
|
||||
if (type == null) return new Traverse();
|
||||
return new Traverse(type);
|
||||
}
|
||||
|
||||
public Traverse Field(string name)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null");
|
||||
var resolved = Resolve();
|
||||
if (resolved._type == null) return new Traverse();
|
||||
var info = Cache.GetFieldInfo(resolved._type, name);
|
||||
if (info == null) return new Traverse();
|
||||
if (info.IsStatic == false && resolved._root == null) return new Traverse();
|
||||
return new Traverse(resolved._root, info, null);
|
||||
}
|
||||
|
||||
public Traverse<T> Field<T>(string name)
|
||||
{
|
||||
return new Traverse<T>(Field(name));
|
||||
}
|
||||
|
||||
public List<string> Fields()
|
||||
{
|
||||
var resolved = Resolve();
|
||||
return AccessTools.GetFieldNames(resolved._type);
|
||||
}
|
||||
|
||||
public Traverse Property(string name, object[] index = null)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null");
|
||||
var resolved = Resolve();
|
||||
if (resolved._root == null || resolved._type == null) return new Traverse();
|
||||
var info = Cache.GetPropertyInfo(resolved._type, name);
|
||||
if (info == null) return new Traverse();
|
||||
return new Traverse(resolved._root, info, index);
|
||||
}
|
||||
|
||||
public Traverse<T> Property<T>(string name, object[] index = null)
|
||||
{
|
||||
return new Traverse<T>(Property(name, index));
|
||||
}
|
||||
|
||||
public List<string> Properties()
|
||||
{
|
||||
var resolved = Resolve();
|
||||
return AccessTools.GetPropertyNames(resolved._type);
|
||||
}
|
||||
|
||||
public Traverse Method(string name, params object[] arguments)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null");
|
||||
var resolved = Resolve();
|
||||
if (resolved._type == null) return new Traverse();
|
||||
var types = AccessTools.GetTypes(arguments);
|
||||
var method = Cache.GetMethodInfo(resolved._type, name, types);
|
||||
if (method == null) return new Traverse();
|
||||
return new Traverse(resolved._root, (MethodInfo)method, arguments);
|
||||
}
|
||||
|
||||
public Traverse Method(string name, Type[] paramTypes, object[] arguments = null)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException("name cannot be null");
|
||||
var resolved = Resolve();
|
||||
if (resolved._type == null) return new Traverse();
|
||||
var method = Cache.GetMethodInfo(resolved._type, name, paramTypes);
|
||||
if (method == null) return new Traverse();
|
||||
return new Traverse(resolved._root, (MethodInfo)method, arguments);
|
||||
}
|
||||
|
||||
public List<string> Methods()
|
||||
{
|
||||
var resolved = Resolve();
|
||||
return AccessTools.GetMethodNames(resolved._type);
|
||||
}
|
||||
|
||||
public bool FieldExists()
|
||||
{
|
||||
return _info != null;
|
||||
}
|
||||
|
||||
public bool MethodExists()
|
||||
{
|
||||
return _method != null;
|
||||
}
|
||||
|
||||
public bool TypeExists()
|
||||
{
|
||||
return _type != null;
|
||||
}
|
||||
|
||||
public static void IterateFields(object source, Action<Traverse> action)
|
||||
{
|
||||
var sourceTrv = Create(source);
|
||||
AccessTools.GetFieldNames(source).ForEach(f => action(sourceTrv.Field(f)));
|
||||
}
|
||||
|
||||
public static void IterateFields(object source, object target, Action<Traverse, Traverse> action)
|
||||
{
|
||||
var sourceTrv = Create(source);
|
||||
var targetTrv = Create(target);
|
||||
AccessTools.GetFieldNames(source).ForEach(f => action(sourceTrv.Field(f), targetTrv.Field(f)));
|
||||
}
|
||||
|
||||
public static void IterateFields(object source, object target, Action<string, Traverse, Traverse> action)
|
||||
{
|
||||
var sourceTrv = Create(source);
|
||||
var targetTrv = Create(target);
|
||||
AccessTools.GetFieldNames(source).ForEach(f => action(f, sourceTrv.Field(f), targetTrv.Field(f)));
|
||||
}
|
||||
|
||||
public static void IterateProperties(object source, Action<Traverse> action)
|
||||
{
|
||||
var sourceTrv = Create(source);
|
||||
AccessTools.GetPropertyNames(source).ForEach(f => action(sourceTrv.Property(f)));
|
||||
}
|
||||
|
||||
public static void IterateProperties(object source, object target, Action<Traverse, Traverse> action)
|
||||
{
|
||||
var sourceTrv = Create(source);
|
||||
var targetTrv = Create(target);
|
||||
AccessTools.GetPropertyNames(source).ForEach(f => action(sourceTrv.Property(f), targetTrv.Property(f)));
|
||||
}
|
||||
|
||||
public static void IterateProperties(object source, object target, Action<string, Traverse, Traverse> action)
|
||||
{
|
||||
var sourceTrv = Create(source);
|
||||
var targetTrv = Create(target);
|
||||
AccessTools.GetPropertyNames(source).ForEach(f => action(f, sourceTrv.Property(f), targetTrv.Property(f)));
|
||||
}
|
||||
|
||||
public static Action<Traverse, Traverse> CopyFields = (from, to) => { to.SetValue(from.GetValue()); };
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var value = _method ?? GetValue();
|
||||
return value?.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
|
||||
namespace Harmony
|
||||
{
|
||||
public static class Transpilers
|
||||
{
|
||||
public static IEnumerable<CodeInstruction> MethodReplacer(this IEnumerable<CodeInstruction> instructions, MethodBase from, MethodBase to)
|
||||
{
|
||||
if (from == null)
|
||||
throw new ArgumentException("Unexpected null argument", nameof(from));
|
||||
if (to == null)
|
||||
throw new ArgumentException("Unexpected null argument", nameof(to));
|
||||
|
||||
foreach (var instruction in instructions)
|
||||
{
|
||||
var method = instruction.operand as MethodBase;
|
||||
if (method == from)
|
||||
{
|
||||
instruction.opcode = to.IsConstructor ? OpCodes.Newobj : OpCodes.Call;
|
||||
instruction.operand = to;
|
||||
}
|
||||
yield return instruction;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CodeInstruction> DebugLogger(this IEnumerable<CodeInstruction> instructions, string text)
|
||||
{
|
||||
yield return new CodeInstruction(OpCodes.Ldstr, text);
|
||||
yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(FileLog), nameof(FileLog.Log)));
|
||||
foreach (var instruction in instructions) yield return instruction;
|
||||
}
|
||||
|
||||
// more added soon
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Com.Pgyersdk;
|
||||
using Com.Pgyersdk.Crash;
|
||||
|
||||
namespace ModLoader
|
||||
{
|
||||
[Application]
|
||||
public class MainApplication : Application
|
||||
{
|
||||
public MainApplication(IntPtr handle, JniHandleOwnership ownerShip) : base(handle, ownerShip)
|
||||
{
|
||||
}
|
||||
public override void OnCreate()
|
||||
{
|
||||
base.OnCreate();
|
||||
Pgyer.SetAppId("8bb59a7cd53b363d849b099428ae2a8a");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using StardewValley;
|
||||
using StardewModdingAPI.Framework;
|
||||
using StardewModdingAPI.Patches;
|
||||
using StardewValley.Menus;
|
||||
|
||||
namespace SMDroid
|
||||
{
|
||||
public class ModEntry : ModHooks
|
||||
{
|
||||
private SCore core;
|
||||
/// <summary>SMAPI's content manager.</summary>
|
||||
private ContentCoordinator ContentCore { get; set; }
|
||||
|
||||
public static bool ContextInitialize = true;
|
||||
|
||||
public static ModEntry Instance;
|
||||
|
||||
public ModEntry()
|
||||
{
|
||||
this.core = SCore.Instance;
|
||||
Instance = this;
|
||||
}
|
||||
public override bool OnCommonHook_Prefix(string hookName, object __instance, ref object param1, ref object param2, ref object param3, ref object param4, ref object __result)
|
||||
{
|
||||
switch (hookName)
|
||||
{
|
||||
case "StardewValley.Object.getDescription":
|
||||
if (SCore.Instance.HarmonyDetourBridgeFailed && !ObjectErrorPatch.Object_GetDescription_Prefix(__instance as StardewValley.Object, ref __result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return this.core.GameInstance.OnCommonHook_Prefix(hookName, __instance, ref param1, ref param2, ref param3, ref param4, ref __result);
|
||||
default:
|
||||
if ((hookName == "StardewValley.Dialogue..ctor") && SCore.Instance.HarmonyDetourBridgeFailed && !DialogueErrorPatch.Prefix(__instance as Dialogue, (string)param1, param2 as NPC))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return this.core.GameInstance.OnCommonHook_Prefix(hookName, __instance, ref param1, ref param2, ref param3, ref param4, ref __result);
|
||||
}
|
||||
}
|
||||
public override void OnCommonHook_Postfix(string hookName, object __instance, ref object param1, ref object param2, ref object param3, ref object param4, ref bool __state, ref object __result)
|
||||
{
|
||||
switch (hookName)
|
||||
{
|
||||
default:
|
||||
this.core.GameInstance.OnCommonHook_Postfix(hookName, __instance, ref param1, ref param2, ref param3, ref param4, ref __state, ref __result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
public override bool OnCommonStaticHook_Prefix(string hookName, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref object __result)
|
||||
{
|
||||
return this.core.GameInstance.OnCommonStaticHook_Prefix(hookName, ref param1, ref param2, ref param3, ref param4, ref param5, ref __result);
|
||||
}
|
||||
public override void OnCommonStaticHook_Postfix(string hookName, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref bool __state, ref object __result)
|
||||
{
|
||||
this.core.GameInstance.OnCommonStaticHook_Postfix(hookName, ref param1, ref param2, ref param3, ref param4, ref param5, ref __state, ref __result);
|
||||
}
|
||||
public override void OnCommonHook10_Postfix(string hookName, object __instance, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref object param6, ref object param7, ref object param8, ref object param9, ref bool __state, ref object __result)
|
||||
{
|
||||
this.core.GameInstance.OnCommonHook10_Postfix(hookName, __instance, ref param1, ref param2, ref param3, ref param4, ref param5, ref param6, ref param7, ref param8, ref param9, ref __state, ref __result);
|
||||
}
|
||||
public override bool OnCommonHook10_Prefix(string hookName, object __instance, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref object param6, ref object param7, ref object param8, ref object param9, ref object __result)
|
||||
{
|
||||
if (SCore.Instance.HarmonyDetourBridgeFailed && (hookName == "StardewValley.Menus.IClickableMenu.drawToolTip") && !ObjectErrorPatch.IClickableMenu_DrawTooltip_Prefix(__instance as IClickableMenu, param4 as Item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return this.core.GameInstance.OnCommonHook10_Prefix(hookName, __instance, ref param1, ref param2, ref param3, ref param4, ref param5, ref param6, ref param7, ref param8, ref param9, ref __result);
|
||||
}
|
||||
public override void OnCommonStaticHook10_Postfix(string hookName, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref object param6, ref object param7, ref object param8, ref object param9, ref object param10, ref bool __state, ref object __result)
|
||||
{
|
||||
this.core.GameInstance.OnCommonStaticHook10_Postfix(hookName, ref param1, ref param2, ref param3, ref param4, ref param5, ref param6, ref param7, ref param8, ref param9, ref param10, ref __state, ref __result);
|
||||
}
|
||||
|
||||
public override bool OnCommonStaticHook10_Prefix(string hookName, ref object param1, ref object param2, ref object param3, ref object param4, ref object param5, ref object param6, ref object param7, ref object param8, ref object param9, ref object param10, ref object __result)
|
||||
{
|
||||
return this.core.GameInstance.OnCommonStaticHook10_Prefix(hookName, ref param1, ref param2, ref param3, ref param4, ref param5, ref param6, ref param7, ref param8, ref param9, ref param10, ref __result);
|
||||
}
|
||||
|
||||
public override void OnGame1_NewDayAfterFade(Action action)
|
||||
{
|
||||
this.core.GameInstance.OnNewDayAfterFade();
|
||||
base.OnGame1_NewDayAfterFade(action);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using Mono.Collections.Generic;
|
||||
using MD = Mono.Cecil.Metadata;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public struct ArrayDimension {
|
||||
|
||||
int? lower_bound;
|
||||
int? upper_bound;
|
||||
|
||||
public int? LowerBound {
|
||||
get { return lower_bound; }
|
||||
set { lower_bound = value; }
|
||||
}
|
||||
|
||||
public int? UpperBound {
|
||||
get { return upper_bound; }
|
||||
set { upper_bound = value; }
|
||||
}
|
||||
|
||||
public bool IsSized {
|
||||
get { return lower_bound.HasValue || upper_bound.HasValue; }
|
||||
}
|
||||
|
||||
public ArrayDimension (int? lowerBound, int? upperBound)
|
||||
{
|
||||
this.lower_bound = lowerBound;
|
||||
this.upper_bound = upperBound;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return !IsSized
|
||||
? string.Empty
|
||||
: lower_bound + "..." + upper_bound;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ArrayType : TypeSpecification {
|
||||
|
||||
Collection<ArrayDimension> dimensions;
|
||||
|
||||
public Collection<ArrayDimension> Dimensions {
|
||||
get {
|
||||
if (dimensions != null)
|
||||
return dimensions;
|
||||
|
||||
dimensions = new Collection<ArrayDimension> ();
|
||||
dimensions.Add (new ArrayDimension ());
|
||||
return dimensions;
|
||||
}
|
||||
}
|
||||
|
||||
public int Rank {
|
||||
get { return dimensions == null ? 1 : dimensions.Count; }
|
||||
}
|
||||
|
||||
public bool IsVector {
|
||||
get {
|
||||
if (dimensions == null)
|
||||
return true;
|
||||
|
||||
if (dimensions.Count > 1)
|
||||
return false;
|
||||
|
||||
var dimension = dimensions [0];
|
||||
|
||||
return !dimension.IsSized;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsValueType {
|
||||
get { return false; }
|
||||
set { throw new InvalidOperationException (); }
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return base.Name + Suffix; }
|
||||
}
|
||||
|
||||
public override string FullName {
|
||||
get { return base.FullName + Suffix; }
|
||||
}
|
||||
|
||||
string Suffix {
|
||||
get {
|
||||
if (IsVector)
|
||||
return "[]";
|
||||
|
||||
var suffix = new StringBuilder ();
|
||||
suffix.Append ("[");
|
||||
for (int i = 0; i < dimensions.Count; i++) {
|
||||
if (i > 0)
|
||||
suffix.Append (",");
|
||||
|
||||
suffix.Append (dimensions [i].ToString ());
|
||||
}
|
||||
suffix.Append ("]");
|
||||
|
||||
return suffix.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsArray {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public ArrayType (TypeReference type)
|
||||
: base (type)
|
||||
{
|
||||
Mixin.CheckType (type);
|
||||
this.etype = MD.ElementType.Array;
|
||||
}
|
||||
|
||||
public ArrayType (TypeReference type, int rank)
|
||||
: this (type)
|
||||
{
|
||||
Mixin.CheckType (type);
|
||||
|
||||
if (rank == 1)
|
||||
return;
|
||||
|
||||
dimensions = new Collection<ArrayDimension> (rank);
|
||||
for (int i = 0; i < rank; i++)
|
||||
dimensions.Add (new ArrayDimension ());
|
||||
this.etype = MD.ElementType.Array;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public sealed class AssemblyDefinition : ICustomAttributeProvider, ISecurityDeclarationProvider, IDisposable {
|
||||
|
||||
AssemblyNameDefinition name;
|
||||
|
||||
internal ModuleDefinition main_module;
|
||||
Collection<ModuleDefinition> modules;
|
||||
Collection<CustomAttribute> custom_attributes;
|
||||
Collection<SecurityDeclaration> security_declarations;
|
||||
|
||||
public AssemblyNameDefinition Name {
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
|
||||
public string FullName {
|
||||
get { return name != null ? name.FullName : string.Empty; }
|
||||
}
|
||||
|
||||
public MetadataToken MetadataToken {
|
||||
get { return new MetadataToken (TokenType.Assembly, 1); }
|
||||
set { }
|
||||
}
|
||||
|
||||
public Collection<ModuleDefinition> Modules {
|
||||
get {
|
||||
if (modules != null)
|
||||
return modules;
|
||||
|
||||
if (main_module.HasImage)
|
||||
return main_module.Read (ref modules, this, (_, reader) => reader.ReadModules ());
|
||||
|
||||
return modules = new Collection<ModuleDefinition> (1) { main_module };
|
||||
}
|
||||
}
|
||||
|
||||
public ModuleDefinition MainModule {
|
||||
get { return main_module; }
|
||||
}
|
||||
|
||||
public MethodDefinition EntryPoint {
|
||||
get { return main_module.EntryPoint; }
|
||||
set { main_module.EntryPoint = value; }
|
||||
}
|
||||
|
||||
public bool HasCustomAttributes {
|
||||
get {
|
||||
if (custom_attributes != null)
|
||||
return custom_attributes.Count > 0;
|
||||
|
||||
return this.GetHasCustomAttributes (main_module);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<CustomAttribute> CustomAttributes {
|
||||
get { return custom_attributes ?? (this.GetCustomAttributes (ref custom_attributes, main_module)); }
|
||||
}
|
||||
|
||||
public bool HasSecurityDeclarations {
|
||||
get {
|
||||
if (security_declarations != null)
|
||||
return security_declarations.Count > 0;
|
||||
|
||||
return this.GetHasSecurityDeclarations (main_module);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<SecurityDeclaration> SecurityDeclarations {
|
||||
get { return security_declarations ?? (this.GetSecurityDeclarations (ref security_declarations, main_module)); }
|
||||
}
|
||||
|
||||
internal AssemblyDefinition ()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (this.modules == null) {
|
||||
main_module.Dispose ();
|
||||
return;
|
||||
}
|
||||
|
||||
var modules = this.Modules;
|
||||
for (int i = 0; i < modules.Count; i++)
|
||||
modules [i].Dispose ();
|
||||
}
|
||||
|
||||
#if !READ_ONLY
|
||||
public static AssemblyDefinition CreateAssembly (AssemblyNameDefinition assemblyName, string moduleName, ModuleKind kind)
|
||||
{
|
||||
return CreateAssembly (assemblyName, moduleName, new ModuleParameters { Kind = kind });
|
||||
}
|
||||
|
||||
public static AssemblyDefinition CreateAssembly (AssemblyNameDefinition assemblyName, string moduleName, ModuleParameters parameters)
|
||||
{
|
||||
if (assemblyName == null)
|
||||
throw new ArgumentNullException ("assemblyName");
|
||||
if (moduleName == null)
|
||||
throw new ArgumentNullException ("moduleName");
|
||||
Mixin.CheckParameters (parameters);
|
||||
if (parameters.Kind == ModuleKind.NetModule)
|
||||
throw new ArgumentException ("kind");
|
||||
|
||||
var assembly = ModuleDefinition.CreateModule (moduleName, parameters).Assembly;
|
||||
assembly.Name = assemblyName;
|
||||
|
||||
return assembly;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static AssemblyDefinition ReadAssembly (string fileName)
|
||||
{
|
||||
return ReadAssembly (ModuleDefinition.ReadModule (fileName));
|
||||
}
|
||||
|
||||
public static AssemblyDefinition ReadAssembly (string fileName, ReaderParameters parameters)
|
||||
{
|
||||
return ReadAssembly (ModuleDefinition.ReadModule (fileName, parameters));
|
||||
}
|
||||
|
||||
public static AssemblyDefinition ReadAssembly (Stream stream)
|
||||
{
|
||||
return ReadAssembly (ModuleDefinition.ReadModule (stream));
|
||||
}
|
||||
|
||||
public static AssemblyDefinition ReadAssembly (Stream stream, ReaderParameters parameters)
|
||||
{
|
||||
return ReadAssembly (ModuleDefinition.ReadModule (stream, parameters));
|
||||
}
|
||||
|
||||
static AssemblyDefinition ReadAssembly (ModuleDefinition module)
|
||||
{
|
||||
var assembly = module.Assembly;
|
||||
if (assembly == null)
|
||||
throw new ArgumentException ();
|
||||
|
||||
return assembly;
|
||||
}
|
||||
|
||||
#if !READ_ONLY
|
||||
|
||||
public void Write (string fileName)
|
||||
{
|
||||
Write (fileName, new WriterParameters ());
|
||||
}
|
||||
|
||||
public void Write (string fileName, WriterParameters parameters)
|
||||
{
|
||||
main_module.Write (fileName, parameters);
|
||||
}
|
||||
|
||||
public void Write ()
|
||||
{
|
||||
main_module.Write ();
|
||||
}
|
||||
|
||||
public void Write (WriterParameters parameters)
|
||||
{
|
||||
main_module.Write (parameters);
|
||||
}
|
||||
|
||||
public void Write (Stream stream)
|
||||
{
|
||||
Write (stream, new WriterParameters ());
|
||||
}
|
||||
|
||||
public void Write (Stream stream, WriterParameters parameters)
|
||||
{
|
||||
main_module.Write (stream, parameters);
|
||||
}
|
||||
#endif
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return this.FullName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
[Flags]
|
||||
public enum AssemblyAttributes : uint {
|
||||
PublicKey = 0x0001,
|
||||
SideBySideCompatible = 0x0000,
|
||||
Retargetable = 0x0100,
|
||||
WindowsRuntime = 0x0200,
|
||||
DisableJITCompileOptimizer = 0x4000,
|
||||
EnableJITCompileTracking = 0x8000,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public enum AssemblyHashAlgorithm : uint {
|
||||
None = 0x0000,
|
||||
Reserved = 0x8003, // MD5
|
||||
SHA1 = 0x8004
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public sealed class AssemblyLinkedResource : Resource {
|
||||
|
||||
AssemblyNameReference reference;
|
||||
|
||||
public AssemblyNameReference Assembly {
|
||||
get { return reference; }
|
||||
set { reference = value; }
|
||||
}
|
||||
|
||||
public override ResourceType ResourceType {
|
||||
get { return ResourceType.AssemblyLinked; }
|
||||
}
|
||||
|
||||
public AssemblyLinkedResource (string name, ManifestResourceAttributes flags)
|
||||
: base (name, flags)
|
||||
{
|
||||
}
|
||||
|
||||
public AssemblyLinkedResource (string name, ManifestResourceAttributes flags, AssemblyNameReference reference)
|
||||
: base (name, flags)
|
||||
{
|
||||
this.reference = reference;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public sealed class AssemblyNameDefinition : AssemblyNameReference {
|
||||
|
||||
public override byte [] Hash {
|
||||
get { return Empty<byte>.Array; }
|
||||
}
|
||||
|
||||
internal AssemblyNameDefinition ()
|
||||
{
|
||||
this.token = new MetadataToken (TokenType.Assembly, 1);
|
||||
}
|
||||
|
||||
public AssemblyNameDefinition (string name, Version version)
|
||||
: base (name, version)
|
||||
{
|
||||
this.token = new MetadataToken (TokenType.Assembly, 1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public class AssemblyNameReference : IMetadataScope {
|
||||
|
||||
string name;
|
||||
string culture;
|
||||
Version version;
|
||||
uint attributes;
|
||||
byte [] public_key;
|
||||
byte [] public_key_token;
|
||||
AssemblyHashAlgorithm hash_algorithm;
|
||||
byte [] hash;
|
||||
|
||||
internal MetadataToken token;
|
||||
|
||||
string full_name;
|
||||
|
||||
public string Name {
|
||||
get { return name; }
|
||||
set {
|
||||
name = value;
|
||||
full_name = null;
|
||||
}
|
||||
}
|
||||
|
||||
public string Culture {
|
||||
get { return culture; }
|
||||
set {
|
||||
culture = value;
|
||||
full_name = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Version Version {
|
||||
get { return version; }
|
||||
set {
|
||||
version = Mixin.CheckVersion (value);
|
||||
full_name = null;
|
||||
}
|
||||
}
|
||||
|
||||
public AssemblyAttributes Attributes {
|
||||
get { return (AssemblyAttributes) attributes; }
|
||||
set { attributes = (uint) value; }
|
||||
}
|
||||
|
||||
public bool HasPublicKey {
|
||||
get { return attributes.GetAttributes ((uint) AssemblyAttributes.PublicKey); }
|
||||
set { attributes = attributes.SetAttributes ((uint) AssemblyAttributes.PublicKey, value); }
|
||||
}
|
||||
|
||||
public bool IsSideBySideCompatible {
|
||||
get { return attributes.GetAttributes ((uint) AssemblyAttributes.SideBySideCompatible); }
|
||||
set { attributes = attributes.SetAttributes ((uint) AssemblyAttributes.SideBySideCompatible, value); }
|
||||
}
|
||||
|
||||
public bool IsRetargetable {
|
||||
get { return attributes.GetAttributes ((uint) AssemblyAttributes.Retargetable); }
|
||||
set { attributes = attributes.SetAttributes ((uint) AssemblyAttributes.Retargetable, value); }
|
||||
}
|
||||
|
||||
public bool IsWindowsRuntime {
|
||||
get { return attributes.GetAttributes ((uint) AssemblyAttributes.WindowsRuntime); }
|
||||
set { attributes = attributes.SetAttributes ((uint) AssemblyAttributes.WindowsRuntime, value); }
|
||||
}
|
||||
|
||||
public byte [] PublicKey {
|
||||
get { return public_key ?? Empty<byte>.Array; }
|
||||
set {
|
||||
public_key = value;
|
||||
HasPublicKey = !public_key.IsNullOrEmpty ();
|
||||
public_key_token = Empty<byte>.Array;
|
||||
full_name = null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte [] PublicKeyToken {
|
||||
get {
|
||||
if (public_key_token.IsNullOrEmpty () && !public_key.IsNullOrEmpty ()) {
|
||||
var hash = HashPublicKey ();
|
||||
// we need the last 8 bytes in reverse order
|
||||
var local_public_key_token = new byte [8];
|
||||
Array.Copy (hash, (hash.Length - 8), local_public_key_token, 0, 8);
|
||||
Array.Reverse (local_public_key_token, 0, 8);
|
||||
public_key_token = local_public_key_token; // publish only once finished (required for thread-safety)
|
||||
}
|
||||
return public_key_token ?? Empty<byte>.Array;
|
||||
}
|
||||
set {
|
||||
public_key_token = value;
|
||||
full_name = null;
|
||||
}
|
||||
}
|
||||
|
||||
byte [] HashPublicKey ()
|
||||
{
|
||||
HashAlgorithm algorithm;
|
||||
|
||||
switch (hash_algorithm) {
|
||||
case AssemblyHashAlgorithm.Reserved:
|
||||
algorithm = MD5.Create ();
|
||||
break;
|
||||
default:
|
||||
// None default to SHA1
|
||||
algorithm = SHA1.Create ();
|
||||
break;
|
||||
}
|
||||
|
||||
using (algorithm)
|
||||
return algorithm.ComputeHash (public_key);
|
||||
}
|
||||
|
||||
public virtual MetadataScopeType MetadataScopeType {
|
||||
get { return MetadataScopeType.AssemblyNameReference; }
|
||||
}
|
||||
|
||||
public string FullName {
|
||||
get {
|
||||
if (full_name != null)
|
||||
return full_name;
|
||||
|
||||
const string sep = ", ";
|
||||
|
||||
var builder = new StringBuilder ();
|
||||
builder.Append (name);
|
||||
builder.Append (sep);
|
||||
builder.Append ("Version=");
|
||||
builder.Append (version.ToString (fieldCount: 4));
|
||||
builder.Append (sep);
|
||||
builder.Append ("Culture=");
|
||||
builder.Append (string.IsNullOrEmpty (culture) ? "neutral" : culture);
|
||||
builder.Append (sep);
|
||||
builder.Append ("PublicKeyToken=");
|
||||
|
||||
var pk_token = PublicKeyToken;
|
||||
if (!pk_token.IsNullOrEmpty () && pk_token.Length > 0) {
|
||||
for (int i = 0 ; i < pk_token.Length ; i++) {
|
||||
builder.Append (pk_token [i].ToString ("x2"));
|
||||
}
|
||||
} else
|
||||
builder.Append ("null");
|
||||
|
||||
if (IsRetargetable) {
|
||||
builder.Append (sep);
|
||||
builder.Append ("Retargetable=Yes");
|
||||
}
|
||||
|
||||
return full_name = builder.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
public static AssemblyNameReference Parse (string fullName)
|
||||
{
|
||||
if (fullName == null)
|
||||
throw new ArgumentNullException ("fullName");
|
||||
if (fullName.Length == 0)
|
||||
throw new ArgumentException ("Name can not be empty");
|
||||
|
||||
var name = new AssemblyNameReference ();
|
||||
var tokens = fullName.Split (',');
|
||||
for (int i = 0; i < tokens.Length; i++) {
|
||||
var token = tokens [i].Trim ();
|
||||
|
||||
if (i == 0) {
|
||||
name.Name = token;
|
||||
continue;
|
||||
}
|
||||
|
||||
var parts = token.Split ('=');
|
||||
if (parts.Length != 2)
|
||||
throw new ArgumentException ("Malformed name");
|
||||
|
||||
switch (parts [0].ToLowerInvariant ()) {
|
||||
case "version":
|
||||
name.Version = new Version (parts [1]);
|
||||
break;
|
||||
case "culture":
|
||||
name.Culture = parts [1] == "neutral" ? "" : parts [1];
|
||||
break;
|
||||
case "publickeytoken":
|
||||
var pk_token = parts [1];
|
||||
if (pk_token == "null")
|
||||
break;
|
||||
|
||||
name.PublicKeyToken = new byte [pk_token.Length / 2];
|
||||
for (int j = 0; j < name.PublicKeyToken.Length; j++)
|
||||
name.PublicKeyToken [j] = Byte.Parse (pk_token.Substring (j * 2, 2), NumberStyles.HexNumber);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public AssemblyHashAlgorithm HashAlgorithm {
|
||||
get { return hash_algorithm; }
|
||||
set { hash_algorithm = value; }
|
||||
}
|
||||
|
||||
public virtual byte [] Hash {
|
||||
get { return hash; }
|
||||
set { hash = value; }
|
||||
}
|
||||
|
||||
public MetadataToken MetadataToken {
|
||||
get { return token; }
|
||||
set { token = value; }
|
||||
}
|
||||
|
||||
internal AssemblyNameReference ()
|
||||
{
|
||||
this.version = Mixin.ZeroVersion;
|
||||
this.token = new MetadataToken (TokenType.AssemblyRef);
|
||||
}
|
||||
|
||||
public AssemblyNameReference (string name, Version version)
|
||||
{
|
||||
Mixin.CheckName (name);
|
||||
|
||||
this.name = name;
|
||||
this.version = Mixin.CheckVersion (version);
|
||||
this.hash_algorithm = AssemblyHashAlgorithm.None;
|
||||
this.token = new MetadataToken (TokenType.AssemblyRef);
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return this.FullName;
|
||||
}
|
||||
}
|
||||
|
||||
partial class Mixin {
|
||||
|
||||
public static Version ZeroVersion = new Version (0, 0, 0 ,0);
|
||||
|
||||
public static Version CheckVersion (Version version)
|
||||
{
|
||||
if (version == null)
|
||||
return ZeroVersion;
|
||||
|
||||
if (version.Build == -1)
|
||||
return new Version (version.Major, version.Minor, 0, 0);
|
||||
|
||||
if (version.Revision == -1)
|
||||
return new Version (version.Major, version.Minor, version.Build, 0);
|
||||
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,411 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public delegate AssemblyDefinition AssemblyResolveEventHandler (object sender, AssemblyNameReference reference);
|
||||
|
||||
public sealed class AssemblyResolveEventArgs : EventArgs {
|
||||
|
||||
readonly AssemblyNameReference reference;
|
||||
|
||||
public AssemblyNameReference AssemblyReference {
|
||||
get { return reference; }
|
||||
}
|
||||
|
||||
public AssemblyResolveEventArgs (AssemblyNameReference reference)
|
||||
{
|
||||
this.reference = reference;
|
||||
}
|
||||
}
|
||||
|
||||
#if !NET_CORE
|
||||
[Serializable]
|
||||
#endif
|
||||
public sealed class AssemblyResolutionException : FileNotFoundException {
|
||||
|
||||
readonly AssemblyNameReference reference;
|
||||
|
||||
public AssemblyNameReference AssemblyReference {
|
||||
get { return reference; }
|
||||
}
|
||||
|
||||
public AssemblyResolutionException (AssemblyNameReference reference)
|
||||
: this (reference, null)
|
||||
{
|
||||
}
|
||||
|
||||
public AssemblyResolutionException (AssemblyNameReference reference, Exception innerException)
|
||||
: base (string.Format ("Failed to resolve assembly: '{0}'", reference), innerException)
|
||||
{
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
#if !NET_CORE
|
||||
AssemblyResolutionException (
|
||||
System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context)
|
||||
: base (info, context)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public abstract class BaseAssemblyResolver : IAssemblyResolver {
|
||||
|
||||
static readonly bool on_mono = Type.GetType ("Mono.Runtime") != null;
|
||||
|
||||
readonly Collection<string> directories;
|
||||
|
||||
#if NET_CORE
|
||||
// Maps file names of available trusted platform assemblies to their full paths.
|
||||
// Internal for testing.
|
||||
internal static readonly Lazy<Dictionary<string, string>> TrustedPlatformAssemblies = new Lazy<Dictionary<string, string>> (CreateTrustedPlatformAssemblyMap);
|
||||
#else
|
||||
Collection<string> gac_paths;
|
||||
#endif
|
||||
|
||||
public void AddSearchDirectory (string directory)
|
||||
{
|
||||
directories.Add (directory);
|
||||
}
|
||||
|
||||
public void RemoveSearchDirectory (string directory)
|
||||
{
|
||||
directories.Remove (directory);
|
||||
}
|
||||
|
||||
public string [] GetSearchDirectories ()
|
||||
{
|
||||
var directories = new string [this.directories.size];
|
||||
Array.Copy (this.directories.items, directories, directories.Length);
|
||||
return directories;
|
||||
}
|
||||
|
||||
public event AssemblyResolveEventHandler ResolveFailure;
|
||||
|
||||
protected BaseAssemblyResolver ()
|
||||
{
|
||||
directories = new Collection<string> (2) { ".", "bin" };
|
||||
}
|
||||
|
||||
AssemblyDefinition GetAssembly (string file, ReaderParameters parameters)
|
||||
{
|
||||
if (parameters.AssemblyResolver == null)
|
||||
parameters.AssemblyResolver = this;
|
||||
|
||||
return ModuleDefinition.ReadModule (file, parameters).Assembly;
|
||||
}
|
||||
|
||||
public virtual AssemblyDefinition Resolve (AssemblyNameReference name)
|
||||
{
|
||||
return Resolve (name, new ReaderParameters ());
|
||||
}
|
||||
|
||||
public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
|
||||
{
|
||||
Mixin.CheckName (name);
|
||||
Mixin.CheckParameters (parameters);
|
||||
|
||||
var assembly = SearchDirectory (name, directories, parameters);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
|
||||
if (name.IsRetargetable) {
|
||||
// if the reference is retargetable, zero it
|
||||
name = new AssemblyNameReference (name.Name, Mixin.ZeroVersion) {
|
||||
PublicKeyToken = Empty<byte>.Array,
|
||||
};
|
||||
}
|
||||
|
||||
#if NET_CORE
|
||||
assembly = SearchTrustedPlatformAssemblies (name, parameters);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
#else
|
||||
var framework_dir = Path.GetDirectoryName (typeof (object).Module.FullyQualifiedName);
|
||||
var framework_dirs = on_mono
|
||||
? new [] { framework_dir, Path.Combine (framework_dir, "Facades") }
|
||||
: new [] { framework_dir };
|
||||
|
||||
if (IsZero (name.Version)) {
|
||||
assembly = SearchDirectory (name, framework_dirs, parameters);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
}
|
||||
|
||||
if (name.Name == "mscorlib") {
|
||||
assembly = GetCorlib (name, parameters);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
}
|
||||
|
||||
assembly = GetAssemblyInGac (name, parameters);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
|
||||
assembly = SearchDirectory (name, framework_dirs, parameters);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
#endif
|
||||
if (ResolveFailure != null) {
|
||||
assembly = ResolveFailure (this, name);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
}
|
||||
|
||||
throw new AssemblyResolutionException (name);
|
||||
}
|
||||
|
||||
#if NET_CORE
|
||||
AssemblyDefinition SearchTrustedPlatformAssemblies (AssemblyNameReference name, ReaderParameters parameters)
|
||||
{
|
||||
if (name.IsWindowsRuntime)
|
||||
return null;
|
||||
|
||||
if (TrustedPlatformAssemblies.Value.TryGetValue (name.Name, out string path))
|
||||
return GetAssembly (path, parameters);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Dictionary<string, string> CreateTrustedPlatformAssemblyMap ()
|
||||
{
|
||||
var result = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
string paths;
|
||||
|
||||
try {
|
||||
// AppContext is only available on platforms that implement .NET Standard 1.6
|
||||
var appContextType = Type.GetType ("System.AppContext, System.AppContext, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", throwOnError: false);
|
||||
var getData = appContextType?.GetTypeInfo ().GetDeclaredMethod ("GetData");
|
||||
paths = (string) getData?.Invoke (null, new [] { "TRUSTED_PLATFORM_ASSEMBLIES" });
|
||||
} catch {
|
||||
paths = null;
|
||||
}
|
||||
|
||||
if (paths == null)
|
||||
return result;
|
||||
|
||||
foreach (var path in paths.Split (Path.PathSeparator))
|
||||
if (string.Equals (Path.GetExtension (path), ".dll", StringComparison.OrdinalIgnoreCase))
|
||||
result [Path.GetFileNameWithoutExtension (path)] = path;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected virtual AssemblyDefinition SearchDirectory (AssemblyNameReference name, IEnumerable<string> directories, ReaderParameters parameters)
|
||||
{
|
||||
var extensions = name.IsWindowsRuntime ? new [] { ".winmd", ".dll" } : new [] { ".exe", ".dll" };
|
||||
foreach (var directory in directories) {
|
||||
foreach (var extension in extensions) {
|
||||
string file = Path.Combine (directory, name.Name + extension);
|
||||
if (!File.Exists (file))
|
||||
continue;
|
||||
try {
|
||||
return GetAssembly (file, parameters);
|
||||
} catch (System.BadImageFormatException) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool IsZero (Version version)
|
||||
{
|
||||
return version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0;
|
||||
}
|
||||
|
||||
#if !NET_CORE
|
||||
AssemblyDefinition GetCorlib (AssemblyNameReference reference, ReaderParameters parameters)
|
||||
{
|
||||
var version = reference.Version;
|
||||
var corlib = typeof (object).Assembly.GetName ();
|
||||
if (corlib.Version == version || IsZero (version))
|
||||
return GetAssembly (typeof (object).Module.FullyQualifiedName, parameters);
|
||||
|
||||
var path = Directory.GetParent (
|
||||
Directory.GetParent (
|
||||
typeof (object).Module.FullyQualifiedName).FullName
|
||||
).FullName;
|
||||
|
||||
if (on_mono) {
|
||||
if (version.Major == 1)
|
||||
path = Path.Combine (path, "1.0");
|
||||
else if (version.Major == 2) {
|
||||
if (version.MajorRevision == 5)
|
||||
path = Path.Combine (path, "2.1");
|
||||
else
|
||||
path = Path.Combine (path, "2.0");
|
||||
} else if (version.Major == 4)
|
||||
path = Path.Combine (path, "4.0");
|
||||
else
|
||||
throw new NotSupportedException ("Version not supported: " + version);
|
||||
} else {
|
||||
switch (version.Major) {
|
||||
case 1:
|
||||
if (version.MajorRevision == 3300)
|
||||
path = Path.Combine (path, "v1.0.3705");
|
||||
else
|
||||
path = Path.Combine (path, "v1.1.4322");
|
||||
break;
|
||||
case 2:
|
||||
path = Path.Combine (path, "v2.0.50727");
|
||||
break;
|
||||
case 4:
|
||||
path = Path.Combine (path, "v4.0.30319");
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException ("Version not supported: " + version);
|
||||
}
|
||||
}
|
||||
|
||||
var file = Path.Combine (path, "mscorlib.dll");
|
||||
if (File.Exists (file))
|
||||
return GetAssembly (file, parameters);
|
||||
|
||||
if (on_mono && Directory.Exists (path + "-api")) {
|
||||
file = Path.Combine (path + "-api", "mscorlib.dll");
|
||||
if (File.Exists (file))
|
||||
return GetAssembly (file, parameters);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static Collection<string> GetGacPaths ()
|
||||
{
|
||||
if (on_mono)
|
||||
return GetDefaultMonoGacPaths ();
|
||||
|
||||
var paths = new Collection<string> (2);
|
||||
var windir = Environment.GetEnvironmentVariable ("WINDIR");
|
||||
if (windir == null)
|
||||
return paths;
|
||||
|
||||
paths.Add (Path.Combine (windir, "assembly"));
|
||||
paths.Add (Path.Combine (windir, Path.Combine ("Microsoft.NET", "assembly")));
|
||||
return paths;
|
||||
}
|
||||
|
||||
static Collection<string> GetDefaultMonoGacPaths ()
|
||||
{
|
||||
var paths = new Collection<string> (1);
|
||||
var gac = GetCurrentMonoGac ();
|
||||
if (gac != null)
|
||||
paths.Add (gac);
|
||||
|
||||
var gac_paths_env = Environment.GetEnvironmentVariable ("MONO_GAC_PREFIX");
|
||||
if (string.IsNullOrEmpty (gac_paths_env))
|
||||
return paths;
|
||||
|
||||
var prefixes = gac_paths_env.Split (Path.PathSeparator);
|
||||
foreach (var prefix in prefixes) {
|
||||
if (string.IsNullOrEmpty (prefix))
|
||||
continue;
|
||||
|
||||
var gac_path = Path.Combine (Path.Combine (Path.Combine (prefix, "lib"), "mono"), "gac");
|
||||
if (Directory.Exists (gac_path) && !paths.Contains (gac))
|
||||
paths.Add (gac_path);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
static string GetCurrentMonoGac ()
|
||||
{
|
||||
if (Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName) != "")
|
||||
return Path.Combine(
|
||||
Directory.GetParent(Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName)).FullName,
|
||||
"gac");
|
||||
return null;
|
||||
}
|
||||
|
||||
AssemblyDefinition GetAssemblyInGac (AssemblyNameReference reference, ReaderParameters parameters)
|
||||
{
|
||||
if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
|
||||
return null;
|
||||
|
||||
if (gac_paths == null)
|
||||
gac_paths = GetGacPaths ();
|
||||
|
||||
if (on_mono)
|
||||
return GetAssemblyInMonoGac (reference, parameters);
|
||||
|
||||
return GetAssemblyInNetGac (reference, parameters);
|
||||
}
|
||||
|
||||
AssemblyDefinition GetAssemblyInMonoGac (AssemblyNameReference reference, ReaderParameters parameters)
|
||||
{
|
||||
for (int i = 0; i < gac_paths.Count; i++) {
|
||||
var gac_path = gac_paths [i];
|
||||
var file = GetAssemblyFile (reference, string.Empty, gac_path);
|
||||
if (File.Exists (file))
|
||||
return GetAssembly (file, parameters);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
AssemblyDefinition GetAssemblyInNetGac (AssemblyNameReference reference, ReaderParameters parameters)
|
||||
{
|
||||
var gacs = new [] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" };
|
||||
var prefixes = new [] { string.Empty, "v4.0_" };
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < gacs.Length; j++) {
|
||||
var gac = Path.Combine (gac_paths [i], gacs [j]);
|
||||
var file = GetAssemblyFile (reference, prefixes [i], gac);
|
||||
if (Directory.Exists (gac) && File.Exists (file))
|
||||
return GetAssembly (file, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
|
||||
{
|
||||
var gac_folder = new StringBuilder ()
|
||||
.Append (prefix)
|
||||
.Append (reference.Version)
|
||||
.Append ("__");
|
||||
|
||||
for (int i = 0; i < reference.PublicKeyToken.Length; i++)
|
||||
gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
|
||||
|
||||
return Path.Combine (
|
||||
Path.Combine (
|
||||
Path.Combine (gac, reference.Name), gac_folder.ToString ()),
|
||||
reference.Name + ".dll");
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose (true);
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public sealed class CallSite : IMethodSignature {
|
||||
|
||||
readonly MethodReference signature;
|
||||
|
||||
public bool HasThis {
|
||||
get { return signature.HasThis; }
|
||||
set { signature.HasThis = value; }
|
||||
}
|
||||
|
||||
public bool ExplicitThis {
|
||||
get { return signature.ExplicitThis; }
|
||||
set { signature.ExplicitThis = value; }
|
||||
}
|
||||
|
||||
public MethodCallingConvention CallingConvention {
|
||||
get { return signature.CallingConvention; }
|
||||
set { signature.CallingConvention = value; }
|
||||
}
|
||||
|
||||
public bool HasParameters {
|
||||
get { return signature.HasParameters; }
|
||||
}
|
||||
|
||||
public Collection<ParameterDefinition> Parameters {
|
||||
get { return signature.Parameters; }
|
||||
}
|
||||
|
||||
public TypeReference ReturnType {
|
||||
get { return signature.MethodReturnType.ReturnType; }
|
||||
set { signature.MethodReturnType.ReturnType = value; }
|
||||
}
|
||||
|
||||
public MethodReturnType MethodReturnType {
|
||||
get { return signature.MethodReturnType; }
|
||||
}
|
||||
|
||||
public string Name {
|
||||
get { return string.Empty; }
|
||||
set { throw new InvalidOperationException (); }
|
||||
}
|
||||
|
||||
public string Namespace {
|
||||
get { return string.Empty; }
|
||||
set { throw new InvalidOperationException (); }
|
||||
}
|
||||
|
||||
public ModuleDefinition Module {
|
||||
get { return ReturnType.Module; }
|
||||
}
|
||||
|
||||
public IMetadataScope Scope {
|
||||
get { return signature.ReturnType.Scope; }
|
||||
}
|
||||
|
||||
public MetadataToken MetadataToken {
|
||||
get { return signature.token; }
|
||||
set { signature.token = value; }
|
||||
}
|
||||
|
||||
public string FullName {
|
||||
get {
|
||||
var signature = new StringBuilder ();
|
||||
signature.Append (ReturnType.FullName);
|
||||
this.MethodSignatureFullName (signature);
|
||||
return signature.ToString ();
|
||||
}
|
||||
}
|
||||
|
||||
internal CallSite ()
|
||||
{
|
||||
this.signature = new MethodReference ();
|
||||
this.signature.token = new MetadataToken (TokenType.Signature, 0);
|
||||
}
|
||||
|
||||
public CallSite (TypeReference returnType)
|
||||
: this ()
|
||||
{
|
||||
if (returnType == null)
|
||||
throw new ArgumentNullException ("returnType");
|
||||
|
||||
this.signature.ReturnType = returnType;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return FullName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public enum Code {
|
||||
Nop,
|
||||
Break,
|
||||
Ldarg_0,
|
||||
Ldarg_1,
|
||||
Ldarg_2,
|
||||
Ldarg_3,
|
||||
Ldloc_0,
|
||||
Ldloc_1,
|
||||
Ldloc_2,
|
||||
Ldloc_3,
|
||||
Stloc_0,
|
||||
Stloc_1,
|
||||
Stloc_2,
|
||||
Stloc_3,
|
||||
Ldarg_S,
|
||||
Ldarga_S,
|
||||
Starg_S,
|
||||
Ldloc_S,
|
||||
Ldloca_S,
|
||||
Stloc_S,
|
||||
Ldnull,
|
||||
Ldc_I4_M1,
|
||||
Ldc_I4_0,
|
||||
Ldc_I4_1,
|
||||
Ldc_I4_2,
|
||||
Ldc_I4_3,
|
||||
Ldc_I4_4,
|
||||
Ldc_I4_5,
|
||||
Ldc_I4_6,
|
||||
Ldc_I4_7,
|
||||
Ldc_I4_8,
|
||||
Ldc_I4_S,
|
||||
Ldc_I4,
|
||||
Ldc_I8,
|
||||
Ldc_R4,
|
||||
Ldc_R8,
|
||||
Dup,
|
||||
Pop,
|
||||
Jmp,
|
||||
Call,
|
||||
Calli,
|
||||
Ret,
|
||||
Br_S,
|
||||
Brfalse_S,
|
||||
Brtrue_S,
|
||||
Beq_S,
|
||||
Bge_S,
|
||||
Bgt_S,
|
||||
Ble_S,
|
||||
Blt_S,
|
||||
Bne_Un_S,
|
||||
Bge_Un_S,
|
||||
Bgt_Un_S,
|
||||
Ble_Un_S,
|
||||
Blt_Un_S,
|
||||
Br,
|
||||
Brfalse,
|
||||
Brtrue,
|
||||
Beq,
|
||||
Bge,
|
||||
Bgt,
|
||||
Ble,
|
||||
Blt,
|
||||
Bne_Un,
|
||||
Bge_Un,
|
||||
Bgt_Un,
|
||||
Ble_Un,
|
||||
Blt_Un,
|
||||
Switch,
|
||||
Ldind_I1,
|
||||
Ldind_U1,
|
||||
Ldind_I2,
|
||||
Ldind_U2,
|
||||
Ldind_I4,
|
||||
Ldind_U4,
|
||||
Ldind_I8,
|
||||
Ldind_I,
|
||||
Ldind_R4,
|
||||
Ldind_R8,
|
||||
Ldind_Ref,
|
||||
Stind_Ref,
|
||||
Stind_I1,
|
||||
Stind_I2,
|
||||
Stind_I4,
|
||||
Stind_I8,
|
||||
Stind_R4,
|
||||
Stind_R8,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Div_Un,
|
||||
Rem,
|
||||
Rem_Un,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
Shl,
|
||||
Shr,
|
||||
Shr_Un,
|
||||
Neg,
|
||||
Not,
|
||||
Conv_I1,
|
||||
Conv_I2,
|
||||
Conv_I4,
|
||||
Conv_I8,
|
||||
Conv_R4,
|
||||
Conv_R8,
|
||||
Conv_U4,
|
||||
Conv_U8,
|
||||
Callvirt,
|
||||
Cpobj,
|
||||
Ldobj,
|
||||
Ldstr,
|
||||
Newobj,
|
||||
Castclass,
|
||||
Isinst,
|
||||
Conv_R_Un,
|
||||
Unbox,
|
||||
Throw,
|
||||
Ldfld,
|
||||
Ldflda,
|
||||
Stfld,
|
||||
Ldsfld,
|
||||
Ldsflda,
|
||||
Stsfld,
|
||||
Stobj,
|
||||
Conv_Ovf_I1_Un,
|
||||
Conv_Ovf_I2_Un,
|
||||
Conv_Ovf_I4_Un,
|
||||
Conv_Ovf_I8_Un,
|
||||
Conv_Ovf_U1_Un,
|
||||
Conv_Ovf_U2_Un,
|
||||
Conv_Ovf_U4_Un,
|
||||
Conv_Ovf_U8_Un,
|
||||
Conv_Ovf_I_Un,
|
||||
Conv_Ovf_U_Un,
|
||||
Box,
|
||||
Newarr,
|
||||
Ldlen,
|
||||
Ldelema,
|
||||
Ldelem_I1,
|
||||
Ldelem_U1,
|
||||
Ldelem_I2,
|
||||
Ldelem_U2,
|
||||
Ldelem_I4,
|
||||
Ldelem_U4,
|
||||
Ldelem_I8,
|
||||
Ldelem_I,
|
||||
Ldelem_R4,
|
||||
Ldelem_R8,
|
||||
Ldelem_Ref,
|
||||
Stelem_I,
|
||||
Stelem_I1,
|
||||
Stelem_I2,
|
||||
Stelem_I4,
|
||||
Stelem_I8,
|
||||
Stelem_R4,
|
||||
Stelem_R8,
|
||||
Stelem_Ref,
|
||||
Ldelem_Any,
|
||||
Stelem_Any,
|
||||
Unbox_Any,
|
||||
Conv_Ovf_I1,
|
||||
Conv_Ovf_U1,
|
||||
Conv_Ovf_I2,
|
||||
Conv_Ovf_U2,
|
||||
Conv_Ovf_I4,
|
||||
Conv_Ovf_U4,
|
||||
Conv_Ovf_I8,
|
||||
Conv_Ovf_U8,
|
||||
Refanyval,
|
||||
Ckfinite,
|
||||
Mkrefany,
|
||||
Ldtoken,
|
||||
Conv_U2,
|
||||
Conv_U1,
|
||||
Conv_I,
|
||||
Conv_Ovf_I,
|
||||
Conv_Ovf_U,
|
||||
Add_Ovf,
|
||||
Add_Ovf_Un,
|
||||
Mul_Ovf,
|
||||
Mul_Ovf_Un,
|
||||
Sub_Ovf,
|
||||
Sub_Ovf_Un,
|
||||
Endfinally,
|
||||
Leave,
|
||||
Leave_S,
|
||||
Stind_I,
|
||||
Conv_U,
|
||||
Arglist,
|
||||
Ceq,
|
||||
Cgt,
|
||||
Cgt_Un,
|
||||
Clt,
|
||||
Clt_Un,
|
||||
Ldftn,
|
||||
Ldvirtftn,
|
||||
Ldarg,
|
||||
Ldarga,
|
||||
Starg,
|
||||
Ldloc,
|
||||
Ldloca,
|
||||
Stloc,
|
||||
Localloc,
|
||||
Endfilter,
|
||||
Unaligned,
|
||||
Volatile,
|
||||
Tail,
|
||||
Initobj,
|
||||
Constrained,
|
||||
Cpblk,
|
||||
Initblk,
|
||||
No,
|
||||
Rethrow,
|
||||
Sizeof,
|
||||
Refanytype,
|
||||
Readonly,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,671 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Mono.Cecil.PE;
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
using RVA = System.UInt32;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
sealed class CodeReader : BinaryStreamReader {
|
||||
|
||||
readonly internal MetadataReader reader;
|
||||
|
||||
int start;
|
||||
|
||||
MethodDefinition method;
|
||||
MethodBody body;
|
||||
|
||||
int Offset {
|
||||
get { return Position - start; }
|
||||
}
|
||||
|
||||
public CodeReader (MetadataReader reader)
|
||||
: base (reader.image.Stream.value)
|
||||
{
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public int MoveTo (MethodDefinition method)
|
||||
{
|
||||
this.method = method;
|
||||
this.reader.context = method;
|
||||
var position = this.Position;
|
||||
this.Position = (int) reader.image.ResolveVirtualAddress ((uint) method.RVA);
|
||||
return position;
|
||||
}
|
||||
|
||||
public void MoveBackTo (int position)
|
||||
{
|
||||
this.reader.context = null;
|
||||
this.Position = position;
|
||||
}
|
||||
|
||||
public MethodBody ReadMethodBody (MethodDefinition method)
|
||||
{
|
||||
var position = MoveTo (method);
|
||||
this.body = new MethodBody (method);
|
||||
|
||||
ReadMethodBody ();
|
||||
|
||||
MoveBackTo (position);
|
||||
return this.body;
|
||||
}
|
||||
|
||||
public int ReadCodeSize (MethodDefinition method)
|
||||
{
|
||||
var position = MoveTo (method);
|
||||
|
||||
var code_size = ReadCodeSize ();
|
||||
|
||||
MoveBackTo (position);
|
||||
return code_size;
|
||||
}
|
||||
|
||||
int ReadCodeSize ()
|
||||
{
|
||||
var flags = ReadByte ();
|
||||
switch (flags & 0x3) {
|
||||
case 0x2: // tiny
|
||||
return flags >> 2;
|
||||
case 0x3: // fat
|
||||
Advance (-1 + 2 + 2); // go back, 2 bytes flags, 2 bytes stack size
|
||||
return (int) ReadUInt32 ();
|
||||
default:
|
||||
throw new InvalidOperationException ();
|
||||
}
|
||||
}
|
||||
|
||||
void ReadMethodBody ()
|
||||
{
|
||||
var flags = ReadByte ();
|
||||
switch (flags & 0x3) {
|
||||
case 0x2: // tiny
|
||||
body.code_size = flags >> 2;
|
||||
body.MaxStackSize = 8;
|
||||
ReadCode ();
|
||||
break;
|
||||
case 0x3: // fat
|
||||
Advance (-1);
|
||||
ReadFatMethod ();
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException ();
|
||||
}
|
||||
|
||||
var symbol_reader = reader.module.symbol_reader;
|
||||
|
||||
if (symbol_reader != null && method.debug_info == null)
|
||||
method.debug_info = symbol_reader.Read (method);
|
||||
|
||||
if (method.debug_info != null)
|
||||
ReadDebugInfo ();
|
||||
}
|
||||
|
||||
void ReadFatMethod ()
|
||||
{
|
||||
var flags = ReadUInt16 ();
|
||||
body.max_stack_size = ReadUInt16 ();
|
||||
body.code_size = (int) ReadUInt32 ();
|
||||
body.local_var_token = new MetadataToken (ReadUInt32 ());
|
||||
body.init_locals = (flags & 0x10) != 0;
|
||||
|
||||
if (body.local_var_token.RID != 0)
|
||||
body.variables = ReadVariables (body.local_var_token);
|
||||
|
||||
ReadCode ();
|
||||
|
||||
if ((flags & 0x8) != 0)
|
||||
ReadSection ();
|
||||
}
|
||||
|
||||
public VariableDefinitionCollection ReadVariables (MetadataToken local_var_token)
|
||||
{
|
||||
var position = reader.position;
|
||||
var variables = reader.ReadVariables (local_var_token);
|
||||
reader.position = position;
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
void ReadCode ()
|
||||
{
|
||||
start = Position;
|
||||
var code_size = body.code_size;
|
||||
|
||||
if (code_size < 0 || Length <= (uint) (code_size + Position))
|
||||
code_size = 0;
|
||||
|
||||
var end = start + code_size;
|
||||
var instructions = body.instructions = new InstructionCollection (method, (code_size + 1) / 2);
|
||||
|
||||
while (Position < end) {
|
||||
var offset = Position - start;
|
||||
var opcode = ReadOpCode ();
|
||||
var current = new Instruction (offset, opcode);
|
||||
|
||||
if (opcode.OperandType != OperandType.InlineNone)
|
||||
current.operand = ReadOperand (current);
|
||||
|
||||
instructions.Add (current);
|
||||
}
|
||||
|
||||
ResolveBranches (instructions);
|
||||
}
|
||||
|
||||
OpCode ReadOpCode ()
|
||||
{
|
||||
var il_opcode = ReadByte ();
|
||||
return il_opcode != 0xfe
|
||||
? OpCodes.OneByteOpCode [il_opcode]
|
||||
: OpCodes.TwoBytesOpCode [ReadByte ()];
|
||||
}
|
||||
|
||||
object ReadOperand (Instruction instruction)
|
||||
{
|
||||
switch (instruction.opcode.OperandType) {
|
||||
case OperandType.InlineSwitch:
|
||||
var length = ReadInt32 ();
|
||||
var base_offset = Offset + (4 * length);
|
||||
var branches = new int [length];
|
||||
for (int i = 0; i < length; i++)
|
||||
branches [i] = base_offset + ReadInt32 ();
|
||||
return branches;
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
return ReadSByte () + Offset;
|
||||
case OperandType.InlineBrTarget:
|
||||
return ReadInt32 () + Offset;
|
||||
case OperandType.ShortInlineI:
|
||||
if (instruction.opcode == OpCodes.Ldc_I4_S)
|
||||
return ReadSByte ();
|
||||
|
||||
return ReadByte ();
|
||||
case OperandType.InlineI:
|
||||
return ReadInt32 ();
|
||||
case OperandType.ShortInlineR:
|
||||
return ReadSingle ();
|
||||
case OperandType.InlineR:
|
||||
return ReadDouble ();
|
||||
case OperandType.InlineI8:
|
||||
return ReadInt64 ();
|
||||
case OperandType.ShortInlineVar:
|
||||
return GetVariable (ReadByte ());
|
||||
case OperandType.InlineVar:
|
||||
return GetVariable (ReadUInt16 ());
|
||||
case OperandType.ShortInlineArg:
|
||||
return GetParameter (ReadByte ());
|
||||
case OperandType.InlineArg:
|
||||
return GetParameter (ReadUInt16 ());
|
||||
case OperandType.InlineSig:
|
||||
return GetCallSite (ReadToken ());
|
||||
case OperandType.InlineString:
|
||||
return GetString (ReadToken ());
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineField:
|
||||
return reader.LookupToken (ReadToken ());
|
||||
default:
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public string GetString (MetadataToken token)
|
||||
{
|
||||
return reader.image.UserStringHeap.Read (token.RID);
|
||||
}
|
||||
|
||||
public ParameterDefinition GetParameter (int index)
|
||||
{
|
||||
return body.GetParameter (index);
|
||||
}
|
||||
|
||||
public VariableDefinition GetVariable (int index)
|
||||
{
|
||||
return body.GetVariable (index);
|
||||
}
|
||||
|
||||
public CallSite GetCallSite (MetadataToken token)
|
||||
{
|
||||
return reader.ReadCallSite (token);
|
||||
}
|
||||
|
||||
void ResolveBranches (Collection<Instruction> instructions)
|
||||
{
|
||||
var items = instructions.items;
|
||||
var size = instructions.size;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
var instruction = items [i];
|
||||
switch (instruction.opcode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
instruction.operand = GetInstruction ((int) instruction.operand);
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
var offsets = (int []) instruction.operand;
|
||||
var branches = new Instruction [offsets.Length];
|
||||
for (int j = 0; j < offsets.Length; j++)
|
||||
branches [j] = GetInstruction (offsets [j]);
|
||||
|
||||
instruction.operand = branches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instruction GetInstruction (int offset)
|
||||
{
|
||||
return GetInstruction (body.Instructions, offset);
|
||||
}
|
||||
|
||||
static Instruction GetInstruction (Collection<Instruction> instructions, int offset)
|
||||
{
|
||||
var size = instructions.size;
|
||||
var items = instructions.items;
|
||||
if (offset < 0 || offset > items [size - 1].offset)
|
||||
return null;
|
||||
|
||||
int min = 0;
|
||||
int max = size - 1;
|
||||
while (min <= max) {
|
||||
int mid = min + ((max - min) / 2);
|
||||
var instruction = items [mid];
|
||||
var instruction_offset = instruction.offset;
|
||||
|
||||
if (offset == instruction_offset)
|
||||
return instruction;
|
||||
|
||||
if (offset < instruction_offset)
|
||||
max = mid - 1;
|
||||
else
|
||||
min = mid + 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void ReadSection ()
|
||||
{
|
||||
Align (4);
|
||||
|
||||
const byte fat_format = 0x40;
|
||||
const byte more_sects = 0x80;
|
||||
|
||||
var flags = ReadByte ();
|
||||
if ((flags & fat_format) == 0)
|
||||
ReadSmallSection ();
|
||||
else
|
||||
ReadFatSection ();
|
||||
|
||||
if ((flags & more_sects) != 0)
|
||||
ReadSection ();
|
||||
}
|
||||
|
||||
void ReadSmallSection ()
|
||||
{
|
||||
var count = ReadByte () / 12;
|
||||
Advance (2);
|
||||
|
||||
ReadExceptionHandlers (
|
||||
count,
|
||||
() => (int) ReadUInt16 (),
|
||||
() => (int) ReadByte ());
|
||||
}
|
||||
|
||||
void ReadFatSection ()
|
||||
{
|
||||
Advance (-1);
|
||||
var count = (ReadInt32 () >> 8) / 24;
|
||||
|
||||
ReadExceptionHandlers (
|
||||
count,
|
||||
ReadInt32,
|
||||
ReadInt32);
|
||||
}
|
||||
|
||||
// inline ?
|
||||
void ReadExceptionHandlers (int count, Func<int> read_entry, Func<int> read_length)
|
||||
{
|
||||
for (int i = 0; i < count; i++) {
|
||||
var handler = new ExceptionHandler (
|
||||
(ExceptionHandlerType) (read_entry () & 0x7));
|
||||
|
||||
handler.TryStart = GetInstruction (read_entry ());
|
||||
handler.TryEnd = GetInstruction (handler.TryStart.Offset + read_length ());
|
||||
|
||||
handler.HandlerStart = GetInstruction (read_entry ());
|
||||
handler.HandlerEnd = GetInstruction (handler.HandlerStart.Offset + read_length ());
|
||||
|
||||
ReadExceptionHandlerSpecific (handler);
|
||||
|
||||
this.body.ExceptionHandlers.Add (handler);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadExceptionHandlerSpecific (ExceptionHandler handler)
|
||||
{
|
||||
switch (handler.HandlerType) {
|
||||
case ExceptionHandlerType.Catch:
|
||||
handler.CatchType = (TypeReference) reader.LookupToken (ReadToken ());
|
||||
break;
|
||||
case ExceptionHandlerType.Filter:
|
||||
handler.FilterStart = GetInstruction (ReadInt32 ());
|
||||
break;
|
||||
default:
|
||||
Advance (4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public MetadataToken ReadToken ()
|
||||
{
|
||||
return new MetadataToken (ReadUInt32 ());
|
||||
}
|
||||
|
||||
void ReadDebugInfo ()
|
||||
{
|
||||
if (method.debug_info.sequence_points != null)
|
||||
ReadSequencePoints ();
|
||||
|
||||
if (method.debug_info.scope != null)
|
||||
ReadScope (method.debug_info.scope);
|
||||
|
||||
if (method.custom_infos != null)
|
||||
ReadCustomDebugInformations (method);
|
||||
}
|
||||
|
||||
void ReadCustomDebugInformations (MethodDefinition method)
|
||||
{
|
||||
var custom_infos = method.custom_infos;
|
||||
|
||||
for (int i = 0; i < custom_infos.Count; i++) {
|
||||
var state_machine_scope = custom_infos [i] as StateMachineScopeDebugInformation;
|
||||
if (state_machine_scope != null)
|
||||
ReadStateMachineScope (state_machine_scope);
|
||||
|
||||
var async_method = custom_infos [i] as AsyncMethodBodyDebugInformation;
|
||||
if (async_method != null)
|
||||
ReadAsyncMethodBody (async_method);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadAsyncMethodBody (AsyncMethodBodyDebugInformation async_method)
|
||||
{
|
||||
if (async_method.catch_handler.Offset > -1)
|
||||
async_method.catch_handler = new InstructionOffset (GetInstruction (async_method.catch_handler.Offset));
|
||||
|
||||
if (!async_method.yields.IsNullOrEmpty ())
|
||||
for (int i = 0; i < async_method.yields.Count; i++)
|
||||
async_method.yields [i] = new InstructionOffset (GetInstruction (async_method.yields [i].Offset));
|
||||
|
||||
if (!async_method.resumes.IsNullOrEmpty ())
|
||||
for (int i = 0; i < async_method.resumes.Count; i++)
|
||||
async_method.resumes [i] = new InstructionOffset (GetInstruction (async_method.resumes [i].Offset));
|
||||
}
|
||||
|
||||
void ReadStateMachineScope (StateMachineScopeDebugInformation state_machine_scope)
|
||||
{
|
||||
if (state_machine_scope.scopes.IsNullOrEmpty ())
|
||||
return;
|
||||
|
||||
foreach (var scope in state_machine_scope.scopes) {
|
||||
scope.start = new InstructionOffset (GetInstruction (scope.start.Offset));
|
||||
|
||||
var end_instruction = GetInstruction (scope.end.Offset);
|
||||
scope.end = end_instruction == null
|
||||
? new InstructionOffset ()
|
||||
: new InstructionOffset (end_instruction);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadSequencePoints ()
|
||||
{
|
||||
var symbol = method.debug_info;
|
||||
|
||||
for (int i = 0; i < symbol.sequence_points.Count; i++) {
|
||||
var sequence_point = symbol.sequence_points [i];
|
||||
var instruction = GetInstruction (sequence_point.Offset);
|
||||
if (instruction != null)
|
||||
sequence_point.offset = new InstructionOffset (instruction);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadScopes (Collection<ScopeDebugInformation> scopes)
|
||||
{
|
||||
for (int i = 0; i < scopes.Count; i++)
|
||||
ReadScope (scopes [i]);
|
||||
}
|
||||
|
||||
void ReadScope (ScopeDebugInformation scope)
|
||||
{
|
||||
var start_instruction = GetInstruction (scope.Start.Offset);
|
||||
if (start_instruction != null)
|
||||
scope.Start = new InstructionOffset (start_instruction);
|
||||
|
||||
var end_instruction = GetInstruction (scope.End.Offset);
|
||||
scope.End = end_instruction != null
|
||||
? new InstructionOffset (end_instruction)
|
||||
: new InstructionOffset ();
|
||||
|
||||
if (!scope.variables.IsNullOrEmpty ()) {
|
||||
for (int i = 0; i < scope.variables.Count; i++) {
|
||||
var variable_info = scope.variables [i];
|
||||
var variable = GetVariable (variable_info.Index);
|
||||
if (variable != null)
|
||||
variable_info.index = new VariableIndex (variable);
|
||||
}
|
||||
}
|
||||
|
||||
if (!scope.scopes.IsNullOrEmpty ())
|
||||
ReadScopes (scope.scopes);
|
||||
}
|
||||
|
||||
#if !READ_ONLY
|
||||
|
||||
public ByteBuffer PatchRawMethodBody (MethodDefinition method, CodeWriter writer, out int code_size, out MetadataToken local_var_token)
|
||||
{
|
||||
var position = MoveTo (method);
|
||||
|
||||
var buffer = new ByteBuffer ();
|
||||
|
||||
var flags = ReadByte ();
|
||||
|
||||
switch (flags & 0x3) {
|
||||
case 0x2: // tiny
|
||||
buffer.WriteByte (flags);
|
||||
local_var_token = MetadataToken.Zero;
|
||||
code_size = flags >> 2;
|
||||
PatchRawCode (buffer, code_size, writer);
|
||||
break;
|
||||
case 0x3: // fat
|
||||
Advance (-1);
|
||||
PatchRawFatMethod (buffer, writer, out code_size, out local_var_token);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
|
||||
MoveBackTo (position);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void PatchRawFatMethod (ByteBuffer buffer, CodeWriter writer, out int code_size, out MetadataToken local_var_token)
|
||||
{
|
||||
var flags = ReadUInt16 ();
|
||||
buffer.WriteUInt16 (flags);
|
||||
buffer.WriteUInt16 (ReadUInt16 ());
|
||||
code_size = ReadInt32 ();
|
||||
buffer.WriteInt32 (code_size);
|
||||
local_var_token = ReadToken ();
|
||||
|
||||
if (local_var_token.RID > 0) {
|
||||
var variables = ReadVariables (local_var_token);
|
||||
buffer.WriteUInt32 (variables != null
|
||||
? writer.GetStandAloneSignature (variables).ToUInt32 ()
|
||||
: 0);
|
||||
} else
|
||||
buffer.WriteUInt32 (0);
|
||||
|
||||
PatchRawCode (buffer, code_size, writer);
|
||||
|
||||
if ((flags & 0x8) != 0)
|
||||
PatchRawSection (buffer, writer.metadata);
|
||||
}
|
||||
|
||||
void PatchRawCode (ByteBuffer buffer, int code_size, CodeWriter writer)
|
||||
{
|
||||
var metadata = writer.metadata;
|
||||
buffer.WriteBytes (ReadBytes (code_size));
|
||||
var end = buffer.position;
|
||||
buffer.position -= code_size;
|
||||
|
||||
while (buffer.position < end) {
|
||||
OpCode opcode;
|
||||
var il_opcode = buffer.ReadByte ();
|
||||
if (il_opcode != 0xfe) {
|
||||
opcode = OpCodes.OneByteOpCode [il_opcode];
|
||||
} else {
|
||||
var il_opcode2 = buffer.ReadByte ();
|
||||
opcode = OpCodes.TwoBytesOpCode [il_opcode2];
|
||||
}
|
||||
|
||||
switch (opcode.OperandType) {
|
||||
case OperandType.ShortInlineI:
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.ShortInlineVar:
|
||||
case OperandType.ShortInlineArg:
|
||||
buffer.position += 1;
|
||||
break;
|
||||
case OperandType.InlineVar:
|
||||
case OperandType.InlineArg:
|
||||
buffer.position += 2;
|
||||
break;
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.ShortInlineR:
|
||||
case OperandType.InlineI:
|
||||
buffer.position += 4;
|
||||
break;
|
||||
case OperandType.InlineI8:
|
||||
case OperandType.InlineR:
|
||||
buffer.position += 8;
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
var length = buffer.ReadInt32 ();
|
||||
buffer.position += length * 4;
|
||||
break;
|
||||
case OperandType.InlineString:
|
||||
var @string = GetString (new MetadataToken (buffer.ReadUInt32 ()));
|
||||
buffer.position -= 4;
|
||||
buffer.WriteUInt32 (
|
||||
new MetadataToken (
|
||||
TokenType.String,
|
||||
metadata.user_string_heap.GetStringIndex (@string)).ToUInt32 ());
|
||||
break;
|
||||
case OperandType.InlineSig:
|
||||
var call_site = GetCallSite (new MetadataToken (buffer.ReadUInt32 ()));
|
||||
buffer.position -= 4;
|
||||
buffer.WriteUInt32 (writer.GetStandAloneSignature (call_site).ToUInt32 ());
|
||||
break;
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineField:
|
||||
var provider = reader.LookupToken (new MetadataToken (buffer.ReadUInt32 ()));
|
||||
buffer.position -= 4;
|
||||
buffer.WriteUInt32 (metadata.LookupToken (provider).ToUInt32 ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PatchRawSection (ByteBuffer buffer, MetadataBuilder metadata)
|
||||
{
|
||||
var position = Position;
|
||||
Align (4);
|
||||
buffer.WriteBytes (Position - position);
|
||||
|
||||
const byte fat_format = 0x40;
|
||||
const byte more_sects = 0x80;
|
||||
|
||||
var flags = ReadByte ();
|
||||
if ((flags & fat_format) == 0) {
|
||||
buffer.WriteByte (flags);
|
||||
PatchRawSmallSection (buffer, metadata);
|
||||
} else
|
||||
PatchRawFatSection (buffer, metadata);
|
||||
|
||||
if ((flags & more_sects) != 0)
|
||||
PatchRawSection (buffer, metadata);
|
||||
}
|
||||
|
||||
void PatchRawSmallSection (ByteBuffer buffer, MetadataBuilder metadata)
|
||||
{
|
||||
var length = ReadByte ();
|
||||
buffer.WriteByte (length);
|
||||
Advance (2);
|
||||
|
||||
buffer.WriteUInt16 (0);
|
||||
|
||||
var count = length / 12;
|
||||
|
||||
PatchRawExceptionHandlers (buffer, metadata, count, false);
|
||||
}
|
||||
|
||||
void PatchRawFatSection (ByteBuffer buffer, MetadataBuilder metadata)
|
||||
{
|
||||
Advance (-1);
|
||||
var length = ReadInt32 ();
|
||||
buffer.WriteInt32 (length);
|
||||
|
||||
var count = (length >> 8) / 24;
|
||||
|
||||
PatchRawExceptionHandlers (buffer, metadata, count, true);
|
||||
}
|
||||
|
||||
void PatchRawExceptionHandlers (ByteBuffer buffer, MetadataBuilder metadata, int count, bool fat_entry)
|
||||
{
|
||||
const int fat_entry_size = 16;
|
||||
const int small_entry_size = 6;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
ExceptionHandlerType handler_type;
|
||||
if (fat_entry) {
|
||||
var type = ReadUInt32 ();
|
||||
handler_type = (ExceptionHandlerType) (type & 0x7);
|
||||
buffer.WriteUInt32 (type);
|
||||
} else {
|
||||
var type = ReadUInt16 ();
|
||||
handler_type = (ExceptionHandlerType) (type & 0x7);
|
||||
buffer.WriteUInt16 (type);
|
||||
}
|
||||
|
||||
buffer.WriteBytes (ReadBytes (fat_entry ? fat_entry_size : small_entry_size));
|
||||
|
||||
switch (handler_type) {
|
||||
case ExceptionHandlerType.Catch:
|
||||
var exception = reader.LookupToken (ReadToken ());
|
||||
buffer.WriteUInt32 (metadata.LookupToken (exception).ToUInt32 ());
|
||||
break;
|
||||
default:
|
||||
buffer.WriteUInt32 (ReadUInt32 ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,659 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
using Mono.Cecil.Metadata;
|
||||
using Mono.Cecil.PE;
|
||||
|
||||
using RVA = System.UInt32;
|
||||
|
||||
#if !READ_ONLY
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
sealed class CodeWriter : ByteBuffer {
|
||||
|
||||
readonly RVA code_base;
|
||||
internal readonly MetadataBuilder metadata;
|
||||
readonly Dictionary<uint, MetadataToken> standalone_signatures;
|
||||
readonly Dictionary<ByteBuffer, RVA> tiny_method_bodies;
|
||||
|
||||
MethodBody body;
|
||||
|
||||
public CodeWriter (MetadataBuilder metadata)
|
||||
: base (0)
|
||||
{
|
||||
this.code_base = metadata.text_map.GetNextRVA (TextSegment.CLIHeader);
|
||||
this.metadata = metadata;
|
||||
this.standalone_signatures = new Dictionary<uint, MetadataToken> ();
|
||||
this.tiny_method_bodies = new Dictionary<ByteBuffer, RVA> (new ByteBufferEqualityComparer ());
|
||||
}
|
||||
|
||||
public RVA WriteMethodBody (MethodDefinition method)
|
||||
{
|
||||
RVA rva;
|
||||
|
||||
if (IsUnresolved (method)) {
|
||||
if (method.rva == 0)
|
||||
return 0;
|
||||
|
||||
rva = WriteUnresolvedMethodBody (method);
|
||||
} else {
|
||||
if (IsEmptyMethodBody (method.Body))
|
||||
return 0;
|
||||
|
||||
rva = WriteResolvedMethodBody (method);
|
||||
}
|
||||
|
||||
return rva;
|
||||
}
|
||||
|
||||
static bool IsEmptyMethodBody (MethodBody body)
|
||||
{
|
||||
return body.instructions.IsNullOrEmpty ()
|
||||
&& body.variables.IsNullOrEmpty ();
|
||||
}
|
||||
|
||||
static bool IsUnresolved (MethodDefinition method)
|
||||
{
|
||||
return method.HasBody && method.HasImage && method.body == null;
|
||||
}
|
||||
|
||||
RVA WriteUnresolvedMethodBody (MethodDefinition method)
|
||||
{
|
||||
var code_reader = metadata.module.reader.code;
|
||||
|
||||
int code_size;
|
||||
MetadataToken local_var_token;
|
||||
var raw_body = code_reader.PatchRawMethodBody (method, this, out code_size, out local_var_token);
|
||||
var fat_header = (raw_body.buffer [0] & 0x3) == 0x3;
|
||||
if (fat_header)
|
||||
Align (4);
|
||||
|
||||
var rva = BeginMethod ();
|
||||
|
||||
if (fat_header || !GetOrMapTinyMethodBody (raw_body, ref rva)) {
|
||||
WriteBytes (raw_body);
|
||||
}
|
||||
|
||||
if (method.debug_info == null)
|
||||
return rva;
|
||||
|
||||
var symbol_writer = metadata.symbol_writer;
|
||||
if (symbol_writer != null) {
|
||||
method.debug_info.code_size = code_size;
|
||||
method.debug_info.local_var_token = local_var_token;
|
||||
symbol_writer.Write (method.debug_info);
|
||||
}
|
||||
|
||||
return rva;
|
||||
}
|
||||
|
||||
RVA WriteResolvedMethodBody(MethodDefinition method)
|
||||
{
|
||||
RVA rva;
|
||||
|
||||
body = method.Body;
|
||||
ComputeHeader ();
|
||||
if (RequiresFatHeader ()) {
|
||||
Align (4);
|
||||
rva = BeginMethod ();
|
||||
WriteFatHeader ();
|
||||
WriteInstructions ();
|
||||
|
||||
if (body.HasExceptionHandlers)
|
||||
WriteExceptionHandlers ();
|
||||
} else {
|
||||
rva = BeginMethod ();
|
||||
WriteByte ((byte) (0x2 | (body.CodeSize << 2))); // tiny
|
||||
WriteInstructions ();
|
||||
|
||||
var start_position = (int) (rva - code_base);
|
||||
var body_size = position - start_position;
|
||||
var body_bytes = new byte [body_size];
|
||||
|
||||
Array.Copy (buffer, start_position, body_bytes, 0, body_size);
|
||||
|
||||
if (GetOrMapTinyMethodBody (new ByteBuffer (body_bytes), ref rva))
|
||||
position = start_position;
|
||||
}
|
||||
|
||||
var symbol_writer = metadata.symbol_writer;
|
||||
if (symbol_writer != null && method.debug_info != null) {
|
||||
method.debug_info.code_size = body.CodeSize;
|
||||
method.debug_info.local_var_token = body.local_var_token;
|
||||
symbol_writer.Write (method.debug_info);
|
||||
}
|
||||
|
||||
return rva;
|
||||
}
|
||||
|
||||
bool GetOrMapTinyMethodBody (ByteBuffer body, ref RVA rva)
|
||||
{
|
||||
RVA existing_rva;
|
||||
if (tiny_method_bodies.TryGetValue (body, out existing_rva)) {
|
||||
rva = existing_rva;
|
||||
return true;
|
||||
}
|
||||
|
||||
tiny_method_bodies.Add (body, rva);
|
||||
return false;
|
||||
}
|
||||
|
||||
void WriteFatHeader ()
|
||||
{
|
||||
var body = this.body;
|
||||
byte flags = 0x3; // fat
|
||||
if (body.InitLocals)
|
||||
flags |= 0x10; // init locals
|
||||
if (body.HasExceptionHandlers)
|
||||
flags |= 0x8; // more sections
|
||||
|
||||
WriteByte (flags);
|
||||
WriteByte (0x30);
|
||||
WriteInt16 ((short) body.max_stack_size);
|
||||
WriteInt32 (body.code_size);
|
||||
body.local_var_token = body.HasVariables
|
||||
? GetStandAloneSignature (body.Variables)
|
||||
: MetadataToken.Zero;
|
||||
WriteMetadataToken (body.local_var_token);
|
||||
}
|
||||
|
||||
void WriteInstructions ()
|
||||
{
|
||||
var instructions = body.Instructions;
|
||||
var items = instructions.items;
|
||||
var size = instructions.size;
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
var instruction = items [i];
|
||||
WriteOpCode (instruction.opcode);
|
||||
WriteOperand (instruction);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteOpCode (OpCode opcode)
|
||||
{
|
||||
if (opcode.Size == 1) {
|
||||
WriteByte (opcode.Op2);
|
||||
} else {
|
||||
WriteByte (opcode.Op1);
|
||||
WriteByte (opcode.Op2);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteOperand (Instruction instruction)
|
||||
{
|
||||
var opcode = instruction.opcode;
|
||||
var operand_type = opcode.OperandType;
|
||||
if (operand_type == OperandType.InlineNone)
|
||||
return;
|
||||
|
||||
var operand = instruction.operand;
|
||||
if (operand == null && !(operand_type == OperandType.InlineBrTarget || operand_type == OperandType.ShortInlineBrTarget)) {
|
||||
throw new ArgumentException ();
|
||||
}
|
||||
|
||||
switch (operand_type) {
|
||||
case OperandType.InlineSwitch: {
|
||||
var targets = (Instruction []) operand;
|
||||
WriteInt32 (targets.Length);
|
||||
var diff = instruction.Offset + opcode.Size + (4 * (targets.Length + 1));
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
WriteInt32 (GetTargetOffset (targets [i]) - diff);
|
||||
break;
|
||||
}
|
||||
case OperandType.ShortInlineBrTarget: {
|
||||
var target = (Instruction) operand;
|
||||
var offset = target != null ? GetTargetOffset (target) : body.code_size;
|
||||
WriteSByte ((sbyte) (offset - (instruction.Offset + opcode.Size + 1)));
|
||||
break;
|
||||
}
|
||||
case OperandType.InlineBrTarget: {
|
||||
var target = (Instruction) operand;
|
||||
var offset = target != null ? GetTargetOffset (target) : body.code_size;
|
||||
WriteInt32 (offset - (instruction.Offset + opcode.Size + 4));
|
||||
break;
|
||||
}
|
||||
case OperandType.ShortInlineVar:
|
||||
WriteByte ((byte) GetVariableIndex ((VariableDefinition) operand));
|
||||
break;
|
||||
case OperandType.ShortInlineArg:
|
||||
WriteByte ((byte) GetParameterIndex ((ParameterDefinition) operand));
|
||||
break;
|
||||
case OperandType.InlineVar:
|
||||
WriteInt16 ((short) GetVariableIndex ((VariableDefinition) operand));
|
||||
break;
|
||||
case OperandType.InlineArg:
|
||||
WriteInt16 ((short) GetParameterIndex ((ParameterDefinition) operand));
|
||||
break;
|
||||
case OperandType.InlineSig:
|
||||
WriteMetadataToken (GetStandAloneSignature ((CallSite) operand));
|
||||
break;
|
||||
case OperandType.ShortInlineI:
|
||||
if (opcode == OpCodes.Ldc_I4_S)
|
||||
WriteSByte ((sbyte) operand);
|
||||
else
|
||||
WriteByte ((byte) operand);
|
||||
break;
|
||||
case OperandType.InlineI:
|
||||
WriteInt32 ((int) operand);
|
||||
break;
|
||||
case OperandType.InlineI8:
|
||||
WriteInt64 ((long) operand);
|
||||
break;
|
||||
case OperandType.ShortInlineR:
|
||||
WriteSingle ((float) operand);
|
||||
break;
|
||||
case OperandType.InlineR:
|
||||
WriteDouble ((double) operand);
|
||||
break;
|
||||
case OperandType.InlineString:
|
||||
WriteMetadataToken (
|
||||
new MetadataToken (
|
||||
TokenType.String,
|
||||
GetUserStringIndex ((string) operand)));
|
||||
break;
|
||||
case OperandType.InlineType:
|
||||
case OperandType.InlineField:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineTok:
|
||||
WriteMetadataToken (metadata.LookupToken ((IMetadataTokenProvider) operand));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException ();
|
||||
}
|
||||
}
|
||||
|
||||
int GetTargetOffset (Instruction instruction)
|
||||
{
|
||||
if (instruction == null) {
|
||||
var last = body.instructions [body.instructions.size - 1];
|
||||
return last.offset + last.GetSize ();
|
||||
}
|
||||
|
||||
return instruction.offset;
|
||||
}
|
||||
|
||||
uint GetUserStringIndex (string @string)
|
||||
{
|
||||
if (@string == null)
|
||||
return 0;
|
||||
|
||||
return metadata.user_string_heap.GetStringIndex (@string);
|
||||
}
|
||||
|
||||
static int GetVariableIndex (VariableDefinition variable)
|
||||
{
|
||||
return variable.Index;
|
||||
}
|
||||
|
||||
int GetParameterIndex (ParameterDefinition parameter)
|
||||
{
|
||||
if (body.method.HasThis) {
|
||||
if (parameter == body.this_parameter)
|
||||
return 0;
|
||||
|
||||
return parameter.Index + 1;
|
||||
}
|
||||
|
||||
return parameter.Index;
|
||||
}
|
||||
|
||||
bool RequiresFatHeader ()
|
||||
{
|
||||
var body = this.body;
|
||||
return body.CodeSize >= 64
|
||||
|| body.InitLocals
|
||||
|| body.HasVariables
|
||||
|| body.HasExceptionHandlers
|
||||
|| body.MaxStackSize > 8;
|
||||
}
|
||||
|
||||
void ComputeHeader ()
|
||||
{
|
||||
int offset = 0;
|
||||
var instructions = body.instructions;
|
||||
var items = instructions.items;
|
||||
var count = instructions.size;
|
||||
var stack_size = 0;
|
||||
var max_stack = 0;
|
||||
Dictionary<Instruction, int> stack_sizes = null;
|
||||
|
||||
if (body.HasExceptionHandlers)
|
||||
ComputeExceptionHandlerStackSize (ref stack_sizes);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
var instruction = items [i];
|
||||
instruction.offset = offset;
|
||||
offset += instruction.GetSize ();
|
||||
|
||||
ComputeStackSize (instruction, ref stack_sizes, ref stack_size, ref max_stack);
|
||||
}
|
||||
|
||||
body.code_size = offset;
|
||||
body.max_stack_size = max_stack;
|
||||
}
|
||||
|
||||
void ComputeExceptionHandlerStackSize (ref Dictionary<Instruction, int> stack_sizes)
|
||||
{
|
||||
var exception_handlers = body.ExceptionHandlers;
|
||||
|
||||
for (int i = 0; i < exception_handlers.Count; i++) {
|
||||
var exception_handler = exception_handlers [i];
|
||||
|
||||
switch (exception_handler.HandlerType) {
|
||||
case ExceptionHandlerType.Catch:
|
||||
AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes);
|
||||
break;
|
||||
case ExceptionHandlerType.Filter:
|
||||
AddExceptionStackSize (exception_handler.FilterStart, ref stack_sizes);
|
||||
AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void AddExceptionStackSize (Instruction handler_start, ref Dictionary<Instruction, int> stack_sizes)
|
||||
{
|
||||
if (handler_start == null)
|
||||
return;
|
||||
|
||||
if (stack_sizes == null)
|
||||
stack_sizes = new Dictionary<Instruction, int> ();
|
||||
|
||||
stack_sizes [handler_start] = 1;
|
||||
}
|
||||
|
||||
static void ComputeStackSize (Instruction instruction, ref Dictionary<Instruction, int> stack_sizes, ref int stack_size, ref int max_stack)
|
||||
{
|
||||
int computed_size;
|
||||
if (stack_sizes != null && stack_sizes.TryGetValue (instruction, out computed_size))
|
||||
stack_size = computed_size;
|
||||
|
||||
max_stack = System.Math.Max (max_stack, stack_size);
|
||||
ComputeStackDelta (instruction, ref stack_size);
|
||||
max_stack = System.Math.Max (max_stack, stack_size);
|
||||
|
||||
CopyBranchStackSize (instruction, ref stack_sizes, stack_size);
|
||||
ComputeStackSize (instruction, ref stack_size);
|
||||
}
|
||||
|
||||
static void CopyBranchStackSize (Instruction instruction, ref Dictionary<Instruction, int> stack_sizes, int stack_size)
|
||||
{
|
||||
if (stack_size == 0)
|
||||
return;
|
||||
|
||||
switch (instruction.opcode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
CopyBranchStackSize (ref stack_sizes, (Instruction) instruction.operand, stack_size);
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
var targets = (Instruction []) instruction.operand;
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
CopyBranchStackSize (ref stack_sizes, targets [i], stack_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void CopyBranchStackSize (ref Dictionary<Instruction, int> stack_sizes, Instruction target, int stack_size)
|
||||
{
|
||||
if (stack_sizes == null)
|
||||
stack_sizes = new Dictionary<Instruction, int> ();
|
||||
|
||||
int branch_stack_size = stack_size;
|
||||
|
||||
int computed_size;
|
||||
if (stack_sizes.TryGetValue (target, out computed_size))
|
||||
branch_stack_size = System.Math.Max (branch_stack_size, computed_size);
|
||||
|
||||
stack_sizes [target] = branch_stack_size;
|
||||
}
|
||||
|
||||
static void ComputeStackSize (Instruction instruction, ref int stack_size)
|
||||
{
|
||||
switch (instruction.opcode.FlowControl) {
|
||||
case FlowControl.Branch:
|
||||
case FlowControl.Break:
|
||||
case FlowControl.Throw:
|
||||
case FlowControl.Return:
|
||||
stack_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeStackDelta (Instruction instruction, ref int stack_size)
|
||||
{
|
||||
switch (instruction.opcode.FlowControl) {
|
||||
case FlowControl.Call: {
|
||||
var method = (IMethodSignature) instruction.operand;
|
||||
// pop 'this' argument
|
||||
if (method.HasImplicitThis() && instruction.opcode.Code != Code.Newobj)
|
||||
stack_size--;
|
||||
// pop normal arguments
|
||||
if (method.HasParameters)
|
||||
stack_size -= method.Parameters.Count;
|
||||
// pop function pointer
|
||||
if (instruction.opcode.Code == Code.Calli)
|
||||
stack_size--;
|
||||
// push return value
|
||||
if (method.ReturnType.etype != ElementType.Void || instruction.opcode.Code == Code.Newobj)
|
||||
stack_size++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ComputePopDelta (instruction.opcode.StackBehaviourPop, ref stack_size);
|
||||
ComputePushDelta (instruction.opcode.StackBehaviourPush, ref stack_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputePopDelta (StackBehaviour pop_behavior, ref int stack_size)
|
||||
{
|
||||
switch (pop_behavior) {
|
||||
case StackBehaviour.Popi:
|
||||
case StackBehaviour.Popref:
|
||||
case StackBehaviour.Pop1:
|
||||
stack_size--;
|
||||
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:
|
||||
stack_size -= 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:
|
||||
stack_size -= 3;
|
||||
break;
|
||||
case StackBehaviour.PopAll:
|
||||
stack_size = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputePushDelta (StackBehaviour push_behaviour, ref int stack_size)
|
||||
{
|
||||
switch (push_behaviour) {
|
||||
case StackBehaviour.Push1:
|
||||
case StackBehaviour.Pushi:
|
||||
case StackBehaviour.Pushi8:
|
||||
case StackBehaviour.Pushr4:
|
||||
case StackBehaviour.Pushr8:
|
||||
case StackBehaviour.Pushref:
|
||||
stack_size++;
|
||||
break;
|
||||
case StackBehaviour.Push1_push1:
|
||||
stack_size += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteExceptionHandlers ()
|
||||
{
|
||||
Align (4);
|
||||
|
||||
var handlers = body.ExceptionHandlers;
|
||||
|
||||
if (handlers.Count < 0x15 && !RequiresFatSection (handlers))
|
||||
WriteSmallSection (handlers);
|
||||
else
|
||||
WriteFatSection (handlers);
|
||||
}
|
||||
|
||||
static bool RequiresFatSection (Collection<ExceptionHandler> handlers)
|
||||
{
|
||||
for (int i = 0; i < handlers.Count; i++) {
|
||||
var handler = handlers [i];
|
||||
|
||||
if (IsFatRange (handler.TryStart, handler.TryEnd))
|
||||
return true;
|
||||
|
||||
if (IsFatRange (handler.HandlerStart, handler.HandlerEnd))
|
||||
return true;
|
||||
|
||||
if (handler.HandlerType == ExceptionHandlerType.Filter
|
||||
&& IsFatRange (handler.FilterStart, handler.HandlerStart))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsFatRange (Instruction start, Instruction end)
|
||||
{
|
||||
if (start == null)
|
||||
throw new ArgumentException ();
|
||||
|
||||
if (end == null)
|
||||
return true;
|
||||
|
||||
return end.Offset - start.Offset > 255 || start.Offset > 65535;
|
||||
}
|
||||
|
||||
void WriteSmallSection (Collection<ExceptionHandler> handlers)
|
||||
{
|
||||
const byte eh_table = 0x1;
|
||||
|
||||
WriteByte (eh_table);
|
||||
WriteByte ((byte) (handlers.Count * 12 + 4));
|
||||
WriteBytes (2);
|
||||
|
||||
WriteExceptionHandlers (
|
||||
handlers,
|
||||
i => WriteUInt16 ((ushort) i),
|
||||
i => WriteByte ((byte) i));
|
||||
}
|
||||
|
||||
void WriteFatSection (Collection<ExceptionHandler> handlers)
|
||||
{
|
||||
const byte eh_table = 0x1;
|
||||
const byte fat_format = 0x40;
|
||||
|
||||
WriteByte (eh_table | fat_format);
|
||||
|
||||
int size = handlers.Count * 24 + 4;
|
||||
WriteByte ((byte) (size & 0xff));
|
||||
WriteByte ((byte) ((size >> 8) & 0xff));
|
||||
WriteByte ((byte) ((size >> 16) & 0xff));
|
||||
|
||||
WriteExceptionHandlers (handlers, WriteInt32, WriteInt32);
|
||||
}
|
||||
|
||||
void WriteExceptionHandlers (Collection<ExceptionHandler> handlers, Action<int> write_entry, Action<int> write_length)
|
||||
{
|
||||
for (int i = 0; i < handlers.Count; i++) {
|
||||
var handler = handlers [i];
|
||||
|
||||
write_entry ((int) handler.HandlerType);
|
||||
|
||||
write_entry (handler.TryStart.Offset);
|
||||
write_length (GetTargetOffset (handler.TryEnd) - handler.TryStart.Offset);
|
||||
|
||||
write_entry (handler.HandlerStart.Offset);
|
||||
write_length (GetTargetOffset (handler.HandlerEnd) - handler.HandlerStart.Offset);
|
||||
|
||||
WriteExceptionHandlerSpecific (handler);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteExceptionHandlerSpecific (ExceptionHandler handler)
|
||||
{
|
||||
switch (handler.HandlerType) {
|
||||
case ExceptionHandlerType.Catch:
|
||||
WriteMetadataToken (metadata.LookupToken (handler.CatchType));
|
||||
break;
|
||||
case ExceptionHandlerType.Filter:
|
||||
WriteInt32 (handler.FilterStart.Offset);
|
||||
break;
|
||||
default:
|
||||
WriteInt32 (0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public MetadataToken GetStandAloneSignature (Collection<VariableDefinition> variables)
|
||||
{
|
||||
var signature = metadata.GetLocalVariableBlobIndex (variables);
|
||||
|
||||
return GetStandAloneSignatureToken (signature);
|
||||
}
|
||||
|
||||
public MetadataToken GetStandAloneSignature (CallSite call_site)
|
||||
{
|
||||
var signature = metadata.GetCallSiteBlobIndex (call_site);
|
||||
var token = GetStandAloneSignatureToken (signature);
|
||||
call_site.MetadataToken = token;
|
||||
return token;
|
||||
}
|
||||
|
||||
MetadataToken GetStandAloneSignatureToken (uint signature)
|
||||
{
|
||||
MetadataToken token;
|
||||
if (standalone_signatures.TryGetValue (signature, out token))
|
||||
return token;
|
||||
|
||||
token = new MetadataToken (TokenType.Signature, metadata.AddStandAloneSignature (signature));
|
||||
standalone_signatures.Add (signature, token);
|
||||
return token;
|
||||
}
|
||||
|
||||
RVA BeginMethod ()
|
||||
{
|
||||
return (RVA)(code_base + position);
|
||||
}
|
||||
|
||||
void WriteMetadataToken (MetadataToken token)
|
||||
{
|
||||
WriteUInt32 (token.ToUInt32 ());
|
||||
}
|
||||
|
||||
void Align (int align)
|
||||
{
|
||||
align--;
|
||||
WriteBytes (((position + align) & ~align) - position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public enum DocumentType {
|
||||
Other,
|
||||
Text,
|
||||
}
|
||||
|
||||
public enum DocumentHashAlgorithm {
|
||||
None,
|
||||
MD5,
|
||||
SHA1,
|
||||
SHA256,
|
||||
}
|
||||
|
||||
public enum DocumentLanguage {
|
||||
Other,
|
||||
C,
|
||||
Cpp,
|
||||
CSharp,
|
||||
Basic,
|
||||
Java,
|
||||
Cobol,
|
||||
Pascal,
|
||||
Cil,
|
||||
JScript,
|
||||
Smc,
|
||||
MCpp,
|
||||
FSharp,
|
||||
}
|
||||
|
||||
public enum DocumentLanguageVendor {
|
||||
Other,
|
||||
Microsoft,
|
||||
}
|
||||
|
||||
public sealed class Document : DebugInformation {
|
||||
|
||||
string url;
|
||||
|
||||
Guid type;
|
||||
Guid hash_algorithm;
|
||||
Guid language;
|
||||
Guid language_vendor;
|
||||
|
||||
byte [] hash;
|
||||
byte [] embedded_source;
|
||||
|
||||
public string Url {
|
||||
get { return url; }
|
||||
set { url = value; }
|
||||
}
|
||||
|
||||
public DocumentType Type {
|
||||
get { return type.ToType (); }
|
||||
set { type = value.ToGuid (); }
|
||||
}
|
||||
|
||||
public Guid TypeGuid {
|
||||
get { return type; }
|
||||
set { type = value; }
|
||||
}
|
||||
|
||||
public DocumentHashAlgorithm HashAlgorithm {
|
||||
get { return hash_algorithm.ToHashAlgorithm (); }
|
||||
set { hash_algorithm = value.ToGuid (); }
|
||||
}
|
||||
|
||||
public Guid HashAlgorithmGuid {
|
||||
get { return hash_algorithm; }
|
||||
set { hash_algorithm = value; }
|
||||
}
|
||||
|
||||
public DocumentLanguage Language {
|
||||
get { return language.ToLanguage (); }
|
||||
set { language = value.ToGuid (); }
|
||||
}
|
||||
|
||||
public Guid LanguageGuid {
|
||||
get { return language; }
|
||||
set { language = value; }
|
||||
}
|
||||
|
||||
public DocumentLanguageVendor LanguageVendor {
|
||||
get { return language_vendor.ToVendor (); }
|
||||
set { language_vendor = value.ToGuid (); }
|
||||
}
|
||||
|
||||
public Guid LanguageVendorGuid {
|
||||
get { return language_vendor; }
|
||||
set { language_vendor = value; }
|
||||
}
|
||||
|
||||
public byte [] Hash {
|
||||
get { return hash; }
|
||||
set { hash = value; }
|
||||
}
|
||||
|
||||
public byte[] EmbeddedSource {
|
||||
get { return embedded_source; }
|
||||
set { embedded_source = value; }
|
||||
}
|
||||
|
||||
public Document (string url)
|
||||
{
|
||||
this.url = url;
|
||||
this.hash = Empty<byte>.Array;
|
||||
this.embedded_source = Empty<byte>.Array;
|
||||
this.token = new MetadataToken (TokenType.Document);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public enum ExceptionHandlerType {
|
||||
Catch = 0,
|
||||
Filter = 1,
|
||||
Finally = 2,
|
||||
Fault = 4,
|
||||
}
|
||||
|
||||
public sealed class ExceptionHandler {
|
||||
|
||||
Instruction try_start;
|
||||
Instruction try_end;
|
||||
Instruction filter_start;
|
||||
Instruction handler_start;
|
||||
Instruction handler_end;
|
||||
|
||||
TypeReference catch_type;
|
||||
ExceptionHandlerType handler_type;
|
||||
|
||||
public Instruction TryStart {
|
||||
get { return try_start; }
|
||||
set { try_start = value; }
|
||||
}
|
||||
|
||||
public Instruction TryEnd {
|
||||
get { return try_end; }
|
||||
set { try_end = value; }
|
||||
}
|
||||
|
||||
public Instruction FilterStart {
|
||||
get { return filter_start; }
|
||||
set { filter_start = value; }
|
||||
}
|
||||
|
||||
public Instruction HandlerStart {
|
||||
get { return handler_start; }
|
||||
set { handler_start = value; }
|
||||
}
|
||||
|
||||
public Instruction HandlerEnd {
|
||||
get { return handler_end; }
|
||||
set { handler_end = value; }
|
||||
}
|
||||
|
||||
public TypeReference CatchType {
|
||||
get { return catch_type; }
|
||||
set { catch_type = value; }
|
||||
}
|
||||
|
||||
public ExceptionHandlerType HandlerType {
|
||||
get { return handler_type; }
|
||||
set { handler_type = value; }
|
||||
}
|
||||
|
||||
public ExceptionHandler (ExceptionHandlerType handlerType)
|
||||
{
|
||||
this.handler_type = handlerType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,260 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public sealed class ILProcessor {
|
||||
|
||||
readonly MethodBody body;
|
||||
readonly Collection<Instruction> instructions;
|
||||
|
||||
public MethodBody Body {
|
||||
get { return body; }
|
||||
}
|
||||
|
||||
internal ILProcessor (MethodBody body)
|
||||
{
|
||||
this.body = body;
|
||||
this.instructions = body.Instructions;
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode)
|
||||
{
|
||||
return Instruction.Create (opcode);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, TypeReference type)
|
||||
{
|
||||
return Instruction.Create (opcode, type);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, CallSite site)
|
||||
{
|
||||
return Instruction.Create (opcode, site);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, MethodReference method)
|
||||
{
|
||||
return Instruction.Create (opcode, method);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, FieldReference field)
|
||||
{
|
||||
return Instruction.Create (opcode, field);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, string value)
|
||||
{
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, sbyte value)
|
||||
{
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, byte value)
|
||||
{
|
||||
if (opcode.OperandType == OperandType.ShortInlineVar)
|
||||
return Instruction.Create (opcode, body.Variables [value]);
|
||||
|
||||
if (opcode.OperandType == OperandType.ShortInlineArg)
|
||||
return Instruction.Create (opcode, body.GetParameter (value));
|
||||
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, int value)
|
||||
{
|
||||
if (opcode.OperandType == OperandType.InlineVar)
|
||||
return Instruction.Create (opcode, body.Variables [value]);
|
||||
|
||||
if (opcode.OperandType == OperandType.InlineArg)
|
||||
return Instruction.Create (opcode, body.GetParameter (value));
|
||||
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, long value)
|
||||
{
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, float value)
|
||||
{
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, double value)
|
||||
{
|
||||
return Instruction.Create (opcode, value);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, Instruction target)
|
||||
{
|
||||
return Instruction.Create (opcode, target);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, Instruction [] targets)
|
||||
{
|
||||
return Instruction.Create (opcode, targets);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, VariableDefinition variable)
|
||||
{
|
||||
return Instruction.Create (opcode, variable);
|
||||
}
|
||||
|
||||
public Instruction Create (OpCode opcode, ParameterDefinition parameter)
|
||||
{
|
||||
return Instruction.Create (opcode, parameter);
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode)
|
||||
{
|
||||
Append (Create (opcode));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, TypeReference type)
|
||||
{
|
||||
Append (Create (opcode, type));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, MethodReference method)
|
||||
{
|
||||
Append (Create (opcode, method));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, CallSite site)
|
||||
{
|
||||
Append (Create (opcode, site));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, FieldReference field)
|
||||
{
|
||||
Append (Create (opcode, field));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, string value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, byte value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, sbyte value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, int value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, long value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, float value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, double value)
|
||||
{
|
||||
Append (Create (opcode, value));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, Instruction target)
|
||||
{
|
||||
Append (Create (opcode, target));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, Instruction [] targets)
|
||||
{
|
||||
Append (Create (opcode, targets));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, VariableDefinition variable)
|
||||
{
|
||||
Append (Create (opcode, variable));
|
||||
}
|
||||
|
||||
public void Emit (OpCode opcode, ParameterDefinition parameter)
|
||||
{
|
||||
Append (Create (opcode, parameter));
|
||||
}
|
||||
|
||||
public void InsertBefore (Instruction target, Instruction instruction)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException ("target");
|
||||
if (instruction == null)
|
||||
throw new ArgumentNullException ("instruction");
|
||||
|
||||
var index = instructions.IndexOf (target);
|
||||
if (index == -1)
|
||||
throw new ArgumentOutOfRangeException ("target");
|
||||
|
||||
instructions.Insert (index, instruction);
|
||||
}
|
||||
|
||||
public void InsertAfter (Instruction target, Instruction instruction)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException ("target");
|
||||
if (instruction == null)
|
||||
throw new ArgumentNullException ("instruction");
|
||||
|
||||
var index = instructions.IndexOf (target);
|
||||
if (index == -1)
|
||||
throw new ArgumentOutOfRangeException ("target");
|
||||
|
||||
instructions.Insert (index + 1, instruction);
|
||||
}
|
||||
|
||||
public void Append (Instruction instruction)
|
||||
{
|
||||
if (instruction == null)
|
||||
throw new ArgumentNullException ("instruction");
|
||||
|
||||
instructions.Add (instruction);
|
||||
}
|
||||
|
||||
public void Replace (Instruction target, Instruction instruction)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException ("target");
|
||||
if (instruction == null)
|
||||
throw new ArgumentNullException ("instruction");
|
||||
|
||||
InsertAfter (target, instruction);
|
||||
Remove (target);
|
||||
}
|
||||
|
||||
public void Remove (Instruction instruction)
|
||||
{
|
||||
if (instruction == null)
|
||||
throw new ArgumentNullException ("instruction");
|
||||
|
||||
if (!instructions.Remove (instruction))
|
||||
throw new ArgumentOutOfRangeException ("instruction");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public sealed class Instruction {
|
||||
|
||||
internal int offset;
|
||||
internal OpCode opcode;
|
||||
internal object operand;
|
||||
|
||||
internal Instruction previous;
|
||||
internal Instruction next;
|
||||
|
||||
public int Offset {
|
||||
get { return offset; }
|
||||
set { offset = value; }
|
||||
}
|
||||
|
||||
public OpCode OpCode {
|
||||
get { return opcode; }
|
||||
set { opcode = value; }
|
||||
}
|
||||
|
||||
public object Operand {
|
||||
get { return operand; }
|
||||
set { operand = value; }
|
||||
}
|
||||
|
||||
public Instruction Previous {
|
||||
get { return previous; }
|
||||
set { previous = value; }
|
||||
}
|
||||
|
||||
public Instruction Next {
|
||||
get { return next; }
|
||||
set { next = value; }
|
||||
}
|
||||
|
||||
internal Instruction (int offset, OpCode opCode)
|
||||
{
|
||||
this.offset = offset;
|
||||
this.opcode = opCode;
|
||||
}
|
||||
|
||||
internal Instruction (OpCode opcode, object operand)
|
||||
{
|
||||
this.opcode = opcode;
|
||||
this.operand = operand;
|
||||
}
|
||||
|
||||
public int GetSize ()
|
||||
{
|
||||
int size = opcode.Size;
|
||||
|
||||
switch (opcode.OperandType) {
|
||||
case OperandType.InlineSwitch:
|
||||
return size + (1 + ((Instruction []) operand).Length) * 4;
|
||||
case OperandType.InlineI8:
|
||||
case OperandType.InlineR:
|
||||
return size + 8;
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.InlineField:
|
||||
case OperandType.InlineI:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineString:
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.ShortInlineR:
|
||||
case OperandType.InlineSig:
|
||||
return size + 4;
|
||||
case OperandType.InlineArg:
|
||||
case OperandType.InlineVar:
|
||||
return size + 2;
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.ShortInlineI:
|
||||
case OperandType.ShortInlineArg:
|
||||
case OperandType.ShortInlineVar:
|
||||
return size + 1;
|
||||
default:
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
var instruction = new StringBuilder ();
|
||||
|
||||
AppendLabel (instruction, this);
|
||||
instruction.Append (':');
|
||||
instruction.Append (' ');
|
||||
instruction.Append (opcode.Name);
|
||||
|
||||
if (operand == null)
|
||||
return instruction.ToString ();
|
||||
|
||||
instruction.Append (' ');
|
||||
|
||||
switch (opcode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
AppendLabel (instruction, (Instruction) operand);
|
||||
break;
|
||||
case OperandType.InlineSwitch:
|
||||
var labels = (Instruction []) operand;
|
||||
for (int i = 0; i < labels.Length; i++) {
|
||||
if (i > 0)
|
||||
instruction.Append (',');
|
||||
|
||||
AppendLabel (instruction, labels [i]);
|
||||
}
|
||||
break;
|
||||
case OperandType.InlineString:
|
||||
instruction.Append ('\"');
|
||||
instruction.Append (operand);
|
||||
instruction.Append ('\"');
|
||||
break;
|
||||
default:
|
||||
instruction.Append (operand);
|
||||
break;
|
||||
}
|
||||
|
||||
return instruction.ToString ();
|
||||
}
|
||||
|
||||
static void AppendLabel (StringBuilder builder, Instruction instruction)
|
||||
{
|
||||
builder.Append ("IL_");
|
||||
builder.Append (instruction.offset.ToString ("x4"));
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.InlineNone)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, null);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, TypeReference type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException ("type");
|
||||
if (opcode.OperandType != OperandType.InlineType &&
|
||||
opcode.OperandType != OperandType.InlineTok)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, type);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, CallSite site)
|
||||
{
|
||||
if (site == null)
|
||||
throw new ArgumentNullException ("site");
|
||||
if (opcode.Code != Code.Calli)
|
||||
throw new ArgumentException ("code");
|
||||
|
||||
return new Instruction (opcode, site);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, MethodReference method)
|
||||
{
|
||||
if (method == null)
|
||||
throw new ArgumentNullException ("method");
|
||||
if (opcode.OperandType != OperandType.InlineMethod &&
|
||||
opcode.OperandType != OperandType.InlineTok)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, method);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, FieldReference field)
|
||||
{
|
||||
if (field == null)
|
||||
throw new ArgumentNullException ("field");
|
||||
if (opcode.OperandType != OperandType.InlineField &&
|
||||
opcode.OperandType != OperandType.InlineTok)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, field);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException ("value");
|
||||
if (opcode.OperandType != OperandType.InlineString)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, sbyte value)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.ShortInlineI &&
|
||||
opcode != OpCodes.Ldc_I4_S)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, byte value)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.ShortInlineI ||
|
||||
opcode == OpCodes.Ldc_I4_S)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, int value)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.InlineI)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, long value)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.InlineI8)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, float value)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.ShortInlineR)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, double value)
|
||||
{
|
||||
if (opcode.OperandType != OperandType.InlineR)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, value);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, Instruction target)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException ("target");
|
||||
if (opcode.OperandType != OperandType.InlineBrTarget &&
|
||||
opcode.OperandType != OperandType.ShortInlineBrTarget)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, target);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, Instruction [] targets)
|
||||
{
|
||||
if (targets == null)
|
||||
throw new ArgumentNullException ("targets");
|
||||
if (opcode.OperandType != OperandType.InlineSwitch)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, targets);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, VariableDefinition variable)
|
||||
{
|
||||
if (variable == null)
|
||||
throw new ArgumentNullException ("variable");
|
||||
if (opcode.OperandType != OperandType.ShortInlineVar &&
|
||||
opcode.OperandType != OperandType.InlineVar)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, variable);
|
||||
}
|
||||
|
||||
public static Instruction Create (OpCode opcode, ParameterDefinition parameter)
|
||||
{
|
||||
if (parameter == null)
|
||||
throw new ArgumentNullException ("parameter");
|
||||
if (opcode.OperandType != OperandType.ShortInlineArg &&
|
||||
opcode.OperandType != OperandType.InlineArg)
|
||||
throw new ArgumentException ("opcode");
|
||||
|
||||
return new Instruction (opcode, parameter);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public sealed class MethodBody {
|
||||
|
||||
readonly internal MethodDefinition method;
|
||||
|
||||
internal ParameterDefinition this_parameter;
|
||||
internal int max_stack_size;
|
||||
internal int code_size;
|
||||
internal bool init_locals;
|
||||
internal MetadataToken local_var_token;
|
||||
|
||||
internal Collection<Instruction> instructions;
|
||||
internal Collection<ExceptionHandler> exceptions;
|
||||
internal Collection<VariableDefinition> variables;
|
||||
|
||||
public MethodDefinition Method {
|
||||
get { return method; }
|
||||
}
|
||||
|
||||
public int MaxStackSize {
|
||||
get { return max_stack_size; }
|
||||
set { max_stack_size = value; }
|
||||
}
|
||||
|
||||
public int CodeSize {
|
||||
get { return code_size; }
|
||||
}
|
||||
|
||||
public bool InitLocals {
|
||||
get { return init_locals; }
|
||||
set { init_locals = value; }
|
||||
}
|
||||
|
||||
public MetadataToken LocalVarToken {
|
||||
get { return local_var_token; }
|
||||
set { local_var_token = value; }
|
||||
}
|
||||
|
||||
public Collection<Instruction> Instructions {
|
||||
get { return instructions ?? (instructions = new InstructionCollection (method)); }
|
||||
}
|
||||
|
||||
public bool HasExceptionHandlers {
|
||||
get { return !exceptions.IsNullOrEmpty (); }
|
||||
}
|
||||
|
||||
public Collection<ExceptionHandler> ExceptionHandlers {
|
||||
get { return exceptions ?? (exceptions = new Collection<ExceptionHandler> ()); }
|
||||
}
|
||||
|
||||
public bool HasVariables {
|
||||
get { return !variables.IsNullOrEmpty (); }
|
||||
}
|
||||
|
||||
public Collection<VariableDefinition> Variables {
|
||||
get { return variables ?? (variables = new VariableDefinitionCollection ()); }
|
||||
}
|
||||
|
||||
public ParameterDefinition ThisParameter {
|
||||
get {
|
||||
if (method == null || method.DeclaringType == null)
|
||||
throw new NotSupportedException ();
|
||||
|
||||
if (!method.HasThis)
|
||||
return null;
|
||||
|
||||
if (this_parameter == null)
|
||||
Interlocked.CompareExchange (ref this_parameter, CreateThisParameter (method), null);
|
||||
|
||||
return this_parameter;
|
||||
}
|
||||
}
|
||||
|
||||
static ParameterDefinition CreateThisParameter (MethodDefinition method)
|
||||
{
|
||||
var parameter_type = method.DeclaringType as TypeReference;
|
||||
|
||||
if (parameter_type.HasGenericParameters) {
|
||||
var instance = new GenericInstanceType (parameter_type);
|
||||
for (int i = 0; i < parameter_type.GenericParameters.Count; i++)
|
||||
instance.GenericArguments.Add (parameter_type.GenericParameters [i]);
|
||||
|
||||
parameter_type = instance;
|
||||
|
||||
}
|
||||
|
||||
if (parameter_type.IsValueType || parameter_type.IsPrimitive)
|
||||
parameter_type = new ByReferenceType (parameter_type);
|
||||
|
||||
return new ParameterDefinition (parameter_type, method);
|
||||
}
|
||||
|
||||
public MethodBody (MethodDefinition method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public ILProcessor GetILProcessor ()
|
||||
{
|
||||
return new ILProcessor (this);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class VariableDefinitionCollection : Collection<VariableDefinition> {
|
||||
|
||||
internal VariableDefinitionCollection ()
|
||||
{
|
||||
}
|
||||
|
||||
internal VariableDefinitionCollection (int capacity)
|
||||
: base (capacity)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnAdd (VariableDefinition item, int index)
|
||||
{
|
||||
item.index = index;
|
||||
}
|
||||
|
||||
protected override void OnInsert (VariableDefinition item, int index)
|
||||
{
|
||||
item.index = index;
|
||||
|
||||
for (int i = index; i < size; i++)
|
||||
items [i].index = i + 1;
|
||||
}
|
||||
|
||||
protected override void OnSet (VariableDefinition item, int index)
|
||||
{
|
||||
item.index = index;
|
||||
}
|
||||
|
||||
protected override void OnRemove (VariableDefinition item, int index)
|
||||
{
|
||||
item.index = -1;
|
||||
|
||||
for (int i = index + 1; i < size; i++)
|
||||
items [i].index = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
class InstructionCollection : Collection<Instruction> {
|
||||
|
||||
readonly MethodDefinition method;
|
||||
|
||||
internal InstructionCollection (MethodDefinition method)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
internal InstructionCollection (MethodDefinition method, int capacity)
|
||||
: base (capacity)
|
||||
{
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
protected override void OnAdd (Instruction item, int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return;
|
||||
|
||||
var previous = items [index - 1];
|
||||
previous.next = item;
|
||||
item.previous = previous;
|
||||
}
|
||||
|
||||
protected override void OnInsert (Instruction item, int index)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
var current = items [index];
|
||||
if (current == null) {
|
||||
var last = items [index - 1];
|
||||
last.next = item;
|
||||
item.previous = last;
|
||||
return;
|
||||
}
|
||||
|
||||
var previous = current.previous;
|
||||
if (previous != null) {
|
||||
previous.next = item;
|
||||
item.previous = previous;
|
||||
}
|
||||
|
||||
current.previous = item;
|
||||
item.next = current;
|
||||
}
|
||||
|
||||
protected override void OnSet (Instruction item, int index)
|
||||
{
|
||||
var current = items [index];
|
||||
|
||||
item.previous = current.previous;
|
||||
item.next = current.next;
|
||||
|
||||
current.previous = null;
|
||||
current.next = null;
|
||||
}
|
||||
|
||||
protected override void OnRemove (Instruction item, int index)
|
||||
{
|
||||
var previous = item.previous;
|
||||
if (previous != null)
|
||||
previous.next = item.next;
|
||||
|
||||
var next = item.next;
|
||||
if (next != null)
|
||||
next.previous = item.previous;
|
||||
|
||||
RemoveSequencePoint (item);
|
||||
|
||||
item.previous = null;
|
||||
item.next = null;
|
||||
}
|
||||
|
||||
void RemoveSequencePoint (Instruction instruction)
|
||||
{
|
||||
var debug_info = method.debug_info;
|
||||
if (debug_info == null || !debug_info.HasSequencePoints)
|
||||
return;
|
||||
|
||||
var sequence_points = debug_info.sequence_points;
|
||||
for (int i = 0; i < sequence_points.Count; i++) {
|
||||
if (sequence_points [i].Offset == instruction.offset) {
|
||||
sequence_points.RemoveAt (i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public enum FlowControl {
|
||||
Branch,
|
||||
Break,
|
||||
Call,
|
||||
Cond_Branch,
|
||||
Meta,
|
||||
Next,
|
||||
Phi,
|
||||
Return,
|
||||
Throw,
|
||||
}
|
||||
|
||||
public enum OpCodeType {
|
||||
Annotation,
|
||||
Macro,
|
||||
Nternal,
|
||||
Objmodel,
|
||||
Prefix,
|
||||
Primitive,
|
||||
}
|
||||
|
||||
public enum OperandType {
|
||||
InlineBrTarget,
|
||||
InlineField,
|
||||
InlineI,
|
||||
InlineI8,
|
||||
InlineMethod,
|
||||
InlineNone,
|
||||
InlinePhi,
|
||||
InlineR,
|
||||
InlineSig,
|
||||
InlineString,
|
||||
InlineSwitch,
|
||||
InlineTok,
|
||||
InlineType,
|
||||
InlineVar,
|
||||
InlineArg,
|
||||
ShortInlineBrTarget,
|
||||
ShortInlineI,
|
||||
ShortInlineR,
|
||||
ShortInlineVar,
|
||||
ShortInlineArg,
|
||||
}
|
||||
|
||||
public enum StackBehaviour {
|
||||
Pop0,
|
||||
Pop1,
|
||||
Pop1_pop1,
|
||||
Popi,
|
||||
Popi_pop1,
|
||||
Popi_popi,
|
||||
Popi_popi8,
|
||||
Popi_popi_popi,
|
||||
Popi_popr4,
|
||||
Popi_popr8,
|
||||
Popref,
|
||||
Popref_pop1,
|
||||
Popref_popi,
|
||||
Popref_popi_popi,
|
||||
Popref_popi_popi8,
|
||||
Popref_popi_popr4,
|
||||
Popref_popi_popr8,
|
||||
Popref_popi_popref,
|
||||
PopAll,
|
||||
Push0,
|
||||
Push1,
|
||||
Push1_push1,
|
||||
Pushi,
|
||||
Pushi8,
|
||||
Pushr4,
|
||||
Pushr8,
|
||||
Pushref,
|
||||
Varpop,
|
||||
Varpush,
|
||||
}
|
||||
|
||||
public struct OpCode : IEquatable<OpCode> {
|
||||
|
||||
readonly byte op1;
|
||||
readonly byte op2;
|
||||
readonly byte code;
|
||||
readonly byte flow_control;
|
||||
readonly byte opcode_type;
|
||||
readonly byte operand_type;
|
||||
readonly byte stack_behavior_pop;
|
||||
readonly byte stack_behavior_push;
|
||||
|
||||
public string Name {
|
||||
get { return OpCodeNames.names [(int) Code]; }
|
||||
}
|
||||
|
||||
public int Size {
|
||||
get { return op1 == 0xff ? 1 : 2; }
|
||||
}
|
||||
|
||||
public byte Op1 {
|
||||
get { return op1; }
|
||||
}
|
||||
|
||||
public byte Op2 {
|
||||
get { return op2; }
|
||||
}
|
||||
|
||||
public short Value {
|
||||
get { return op1 == 0xff ? op2 : (short) ((op1 << 8) | op2); }
|
||||
}
|
||||
|
||||
public Code Code {
|
||||
get { return (Code) code; }
|
||||
}
|
||||
|
||||
public FlowControl FlowControl {
|
||||
get { return (FlowControl) flow_control; }
|
||||
}
|
||||
|
||||
public OpCodeType OpCodeType {
|
||||
get { return (OpCodeType) opcode_type; }
|
||||
}
|
||||
|
||||
public OperandType OperandType {
|
||||
get { return (OperandType) operand_type; }
|
||||
}
|
||||
|
||||
public StackBehaviour StackBehaviourPop {
|
||||
get { return (StackBehaviour) stack_behavior_pop; }
|
||||
}
|
||||
|
||||
public StackBehaviour StackBehaviourPush {
|
||||
get { return (StackBehaviour) stack_behavior_push; }
|
||||
}
|
||||
|
||||
internal OpCode (int x, int y)
|
||||
{
|
||||
this.op1 = (byte) ((x >> 0) & 0xff);
|
||||
this.op2 = (byte) ((x >> 8) & 0xff);
|
||||
this.code = (byte) ((x >> 16) & 0xff);
|
||||
this.flow_control = (byte) ((x >> 24) & 0xff);
|
||||
|
||||
this.opcode_type = (byte) ((y >> 0) & 0xff);
|
||||
this.operand_type = (byte) ((y >> 8) & 0xff);
|
||||
this.stack_behavior_pop = (byte) ((y >> 16) & 0xff);
|
||||
this.stack_behavior_push = (byte) ((y >> 24) & 0xff);
|
||||
|
||||
if (op1 == 0xff)
|
||||
OpCodes.OneByteOpCode [op2] = this;
|
||||
else
|
||||
OpCodes.TwoBytesOpCode [op2] = this;
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public override bool Equals (object obj)
|
||||
{
|
||||
if (!(obj is OpCode))
|
||||
return false;
|
||||
|
||||
var opcode = (OpCode) obj;
|
||||
return op1 == opcode.op1 && op2 == opcode.op2;
|
||||
}
|
||||
|
||||
public bool Equals (OpCode opcode)
|
||||
{
|
||||
return op1 == opcode.op1 && op2 == opcode.op2;
|
||||
}
|
||||
|
||||
public static bool operator == (OpCode one, OpCode other)
|
||||
{
|
||||
return one.op1 == other.op1 && one.op2 == other.op2;
|
||||
}
|
||||
|
||||
public static bool operator != (OpCode one, OpCode other)
|
||||
{
|
||||
return one.op1 != other.op1 || one.op2 != other.op2;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
static class OpCodeNames {
|
||||
|
||||
internal static readonly string [] names;
|
||||
|
||||
static OpCodeNames ()
|
||||
{
|
||||
var table = new byte [] {
|
||||
3, 110, 111, 112,
|
||||
5, 98, 114, 101, 97, 107,
|
||||
7, 108, 100, 97, 114, 103, 46, 48,
|
||||
7, 108, 100, 97, 114, 103, 46, 49,
|
||||
7, 108, 100, 97, 114, 103, 46, 50,
|
||||
7, 108, 100, 97, 114, 103, 46, 51,
|
||||
7, 108, 100, 108, 111, 99, 46, 48,
|
||||
7, 108, 100, 108, 111, 99, 46, 49,
|
||||
7, 108, 100, 108, 111, 99, 46, 50,
|
||||
7, 108, 100, 108, 111, 99, 46, 51,
|
||||
7, 115, 116, 108, 111, 99, 46, 48,
|
||||
7, 115, 116, 108, 111, 99, 46, 49,
|
||||
7, 115, 116, 108, 111, 99, 46, 50,
|
||||
7, 115, 116, 108, 111, 99, 46, 51,
|
||||
7, 108, 100, 97, 114, 103, 46, 115,
|
||||
8, 108, 100, 97, 114, 103, 97, 46, 115,
|
||||
7, 115, 116, 97, 114, 103, 46, 115,
|
||||
7, 108, 100, 108, 111, 99, 46, 115,
|
||||
8, 108, 100, 108, 111, 99, 97, 46, 115,
|
||||
7, 115, 116, 108, 111, 99, 46, 115,
|
||||
6, 108, 100, 110, 117, 108, 108,
|
||||
9, 108, 100, 99, 46, 105, 52, 46, 109, 49,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 48,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 49,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 50,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 51,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 52,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 53,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 54,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 55,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 56,
|
||||
8, 108, 100, 99, 46, 105, 52, 46, 115,
|
||||
6, 108, 100, 99, 46, 105, 52,
|
||||
6, 108, 100, 99, 46, 105, 56,
|
||||
6, 108, 100, 99, 46, 114, 52,
|
||||
6, 108, 100, 99, 46, 114, 56,
|
||||
3, 100, 117, 112,
|
||||
3, 112, 111, 112,
|
||||
3, 106, 109, 112,
|
||||
4, 99, 97, 108, 108,
|
||||
5, 99, 97, 108, 108, 105,
|
||||
3, 114, 101, 116,
|
||||
4, 98, 114, 46, 115,
|
||||
9, 98, 114, 102, 97, 108, 115, 101, 46, 115,
|
||||
8, 98, 114, 116, 114, 117, 101, 46, 115,
|
||||
5, 98, 101, 113, 46, 115,
|
||||
5, 98, 103, 101, 46, 115,
|
||||
5, 98, 103, 116, 46, 115,
|
||||
5, 98, 108, 101, 46, 115,
|
||||
5, 98, 108, 116, 46, 115,
|
||||
8, 98, 110, 101, 46, 117, 110, 46, 115,
|
||||
8, 98, 103, 101, 46, 117, 110, 46, 115,
|
||||
8, 98, 103, 116, 46, 117, 110, 46, 115,
|
||||
8, 98, 108, 101, 46, 117, 110, 46, 115,
|
||||
8, 98, 108, 116, 46, 117, 110, 46, 115,
|
||||
2, 98, 114,
|
||||
7, 98, 114, 102, 97, 108, 115, 101,
|
||||
6, 98, 114, 116, 114, 117, 101,
|
||||
3, 98, 101, 113,
|
||||
3, 98, 103, 101,
|
||||
3, 98, 103, 116,
|
||||
3, 98, 108, 101,
|
||||
3, 98, 108, 116,
|
||||
6, 98, 110, 101, 46, 117, 110,
|
||||
6, 98, 103, 101, 46, 117, 110,
|
||||
6, 98, 103, 116, 46, 117, 110,
|
||||
6, 98, 108, 101, 46, 117, 110,
|
||||
6, 98, 108, 116, 46, 117, 110,
|
||||
6, 115, 119, 105, 116, 99, 104,
|
||||
8, 108, 100, 105, 110, 100, 46, 105, 49,
|
||||
8, 108, 100, 105, 110, 100, 46, 117, 49,
|
||||
8, 108, 100, 105, 110, 100, 46, 105, 50,
|
||||
8, 108, 100, 105, 110, 100, 46, 117, 50,
|
||||
8, 108, 100, 105, 110, 100, 46, 105, 52,
|
||||
8, 108, 100, 105, 110, 100, 46, 117, 52,
|
||||
8, 108, 100, 105, 110, 100, 46, 105, 56,
|
||||
7, 108, 100, 105, 110, 100, 46, 105,
|
||||
8, 108, 100, 105, 110, 100, 46, 114, 52,
|
||||
8, 108, 100, 105, 110, 100, 46, 114, 56,
|
||||
9, 108, 100, 105, 110, 100, 46, 114, 101, 102,
|
||||
9, 115, 116, 105, 110, 100, 46, 114, 101, 102,
|
||||
8, 115, 116, 105, 110, 100, 46, 105, 49,
|
||||
8, 115, 116, 105, 110, 100, 46, 105, 50,
|
||||
8, 115, 116, 105, 110, 100, 46, 105, 52,
|
||||
8, 115, 116, 105, 110, 100, 46, 105, 56,
|
||||
8, 115, 116, 105, 110, 100, 46, 114, 52,
|
||||
8, 115, 116, 105, 110, 100, 46, 114, 56,
|
||||
3, 97, 100, 100,
|
||||
3, 115, 117, 98,
|
||||
3, 109, 117, 108,
|
||||
3, 100, 105, 118,
|
||||
6, 100, 105, 118, 46, 117, 110,
|
||||
3, 114, 101, 109,
|
||||
6, 114, 101, 109, 46, 117, 110,
|
||||
3, 97, 110, 100,
|
||||
2, 111, 114,
|
||||
3, 120, 111, 114,
|
||||
3, 115, 104, 108,
|
||||
3, 115, 104, 114,
|
||||
6, 115, 104, 114, 46, 117, 110,
|
||||
3, 110, 101, 103,
|
||||
3, 110, 111, 116,
|
||||
7, 99, 111, 110, 118, 46, 105, 49,
|
||||
7, 99, 111, 110, 118, 46, 105, 50,
|
||||
7, 99, 111, 110, 118, 46, 105, 52,
|
||||
7, 99, 111, 110, 118, 46, 105, 56,
|
||||
7, 99, 111, 110, 118, 46, 114, 52,
|
||||
7, 99, 111, 110, 118, 46, 114, 56,
|
||||
7, 99, 111, 110, 118, 46, 117, 52,
|
||||
7, 99, 111, 110, 118, 46, 117, 56,
|
||||
8, 99, 97, 108, 108, 118, 105, 114, 116,
|
||||
5, 99, 112, 111, 98, 106,
|
||||
5, 108, 100, 111, 98, 106,
|
||||
5, 108, 100, 115, 116, 114,
|
||||
6, 110, 101, 119, 111, 98, 106,
|
||||
9, 99, 97, 115, 116, 99, 108, 97, 115, 115,
|
||||
6, 105, 115, 105, 110, 115, 116,
|
||||
9, 99, 111, 110, 118, 46, 114, 46, 117, 110,
|
||||
5, 117, 110, 98, 111, 120,
|
||||
5, 116, 104, 114, 111, 119,
|
||||
5, 108, 100, 102, 108, 100,
|
||||
6, 108, 100, 102, 108, 100, 97,
|
||||
5, 115, 116, 102, 108, 100,
|
||||
6, 108, 100, 115, 102, 108, 100,
|
||||
7, 108, 100, 115, 102, 108, 100, 97,
|
||||
6, 115, 116, 115, 102, 108, 100,
|
||||
5, 115, 116, 111, 98, 106,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 49, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 50, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 52, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 56, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 49, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 50, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 52, 46, 117, 110,
|
||||
14, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 56, 46, 117, 110,
|
||||
13, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 46, 117, 110,
|
||||
13, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 46, 117, 110,
|
||||
3, 98, 111, 120,
|
||||
6, 110, 101, 119, 97, 114, 114,
|
||||
5, 108, 100, 108, 101, 110,
|
||||
7, 108, 100, 101, 108, 101, 109, 97,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 105, 49,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 117, 49,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 105, 50,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 117, 50,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 105, 52,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 117, 52,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 105, 56,
|
||||
8, 108, 100, 101, 108, 101, 109, 46, 105,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 114, 52,
|
||||
9, 108, 100, 101, 108, 101, 109, 46, 114, 56,
|
||||
10, 108, 100, 101, 108, 101, 109, 46, 114, 101, 102,
|
||||
8, 115, 116, 101, 108, 101, 109, 46, 105,
|
||||
9, 115, 116, 101, 108, 101, 109, 46, 105, 49,
|
||||
9, 115, 116, 101, 108, 101, 109, 46, 105, 50,
|
||||
9, 115, 116, 101, 108, 101, 109, 46, 105, 52,
|
||||
9, 115, 116, 101, 108, 101, 109, 46, 105, 56,
|
||||
9, 115, 116, 101, 108, 101, 109, 46, 114, 52,
|
||||
9, 115, 116, 101, 108, 101, 109, 46, 114, 56,
|
||||
10, 115, 116, 101, 108, 101, 109, 46, 114, 101, 102,
|
||||
10, 108, 100, 101, 108, 101, 109, 46, 97, 110, 121,
|
||||
10, 115, 116, 101, 108, 101, 109, 46, 97, 110, 121,
|
||||
9, 117, 110, 98, 111, 120, 46, 97, 110, 121,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 49,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 49,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 50,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 50,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 52,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 52,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105, 56,
|
||||
11, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117, 56,
|
||||
9, 114, 101, 102, 97, 110, 121, 118, 97, 108,
|
||||
8, 99, 107, 102, 105, 110, 105, 116, 101,
|
||||
8, 109, 107, 114, 101, 102, 97, 110, 121,
|
||||
7, 108, 100, 116, 111, 107, 101, 110,
|
||||
7, 99, 111, 110, 118, 46, 117, 50,
|
||||
7, 99, 111, 110, 118, 46, 117, 49,
|
||||
6, 99, 111, 110, 118, 46, 105,
|
||||
10, 99, 111, 110, 118, 46, 111, 118, 102, 46, 105,
|
||||
10, 99, 111, 110, 118, 46, 111, 118, 102, 46, 117,
|
||||
7, 97, 100, 100, 46, 111, 118, 102,
|
||||
10, 97, 100, 100, 46, 111, 118, 102, 46, 117, 110,
|
||||
7, 109, 117, 108, 46, 111, 118, 102,
|
||||
10, 109, 117, 108, 46, 111, 118, 102, 46, 117, 110,
|
||||
7, 115, 117, 98, 46, 111, 118, 102,
|
||||
10, 115, 117, 98, 46, 111, 118, 102, 46, 117, 110,
|
||||
10, 101, 110, 100, 102, 105, 110, 97, 108, 108, 121,
|
||||
5, 108, 101, 97, 118, 101,
|
||||
7, 108, 101, 97, 118, 101, 46, 115,
|
||||
7, 115, 116, 105, 110, 100, 46, 105,
|
||||
6, 99, 111, 110, 118, 46, 117,
|
||||
7, 97, 114, 103, 108, 105, 115, 116,
|
||||
3, 99, 101, 113,
|
||||
3, 99, 103, 116,
|
||||
6, 99, 103, 116, 46, 117, 110,
|
||||
3, 99, 108, 116,
|
||||
6, 99, 108, 116, 46, 117, 110,
|
||||
5, 108, 100, 102, 116, 110,
|
||||
9, 108, 100, 118, 105, 114, 116, 102, 116, 110,
|
||||
5, 108, 100, 97, 114, 103,
|
||||
6, 108, 100, 97, 114, 103, 97,
|
||||
5, 115, 116, 97, 114, 103,
|
||||
5, 108, 100, 108, 111, 99,
|
||||
6, 108, 100, 108, 111, 99, 97,
|
||||
5, 115, 116, 108, 111, 99,
|
||||
8, 108, 111, 99, 97, 108, 108, 111, 99,
|
||||
9, 101, 110, 100, 102, 105, 108, 116, 101, 114,
|
||||
10, 117, 110, 97, 108, 105, 103, 110, 101, 100, 46,
|
||||
9, 118, 111, 108, 97, 116, 105, 108, 101, 46,
|
||||
5, 116, 97, 105, 108, 46,
|
||||
7, 105, 110, 105, 116, 111, 98, 106,
|
||||
12, 99, 111, 110, 115, 116, 114, 97, 105, 110, 101, 100, 46,
|
||||
5, 99, 112, 98, 108, 107,
|
||||
7, 105, 110, 105, 116, 98, 108, 107,
|
||||
3, 110, 111, 46,
|
||||
7, 114, 101, 116, 104, 114, 111, 119,
|
||||
6, 115, 105, 122, 101, 111, 102,
|
||||
10, 114, 101, 102, 97, 110, 121, 116, 121, 112, 101,
|
||||
9, 114, 101, 97, 100, 111, 110, 108, 121, 46,
|
||||
};
|
||||
|
||||
names = new string [219];
|
||||
|
||||
for (int i = 0, p = 0; i < names.Length; i++) {
|
||||
var buffer = new char [table [p++]];
|
||||
|
||||
for (int j = 0; j < buffer.Length; j++)
|
||||
buffer [j] = (char) table [p++];
|
||||
|
||||
names [i] = new string (buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,894 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public static class OpCodes {
|
||||
|
||||
internal static readonly OpCode [] OneByteOpCode = new OpCode [0xe0 + 1];
|
||||
internal static readonly OpCode [] TwoBytesOpCode = new OpCode [0x1e + 1];
|
||||
|
||||
public static readonly OpCode Nop = new OpCode (
|
||||
0xff << 0 | 0x00 << 8 | (byte) Code.Nop << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Break = new OpCode (
|
||||
0xff << 0 | 0x01 << 8 | (byte) Code.Break << 16 | (byte) FlowControl.Break << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldarg_0 = new OpCode (
|
||||
0xff << 0 | 0x02 << 8 | (byte) Code.Ldarg_0 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldarg_1 = new OpCode (
|
||||
0xff << 0 | 0x03 << 8 | (byte) Code.Ldarg_1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldarg_2 = new OpCode (
|
||||
0xff << 0 | 0x04 << 8 | (byte) Code.Ldarg_2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldarg_3 = new OpCode (
|
||||
0xff << 0 | 0x05 << 8 | (byte) Code.Ldarg_3 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldloc_0 = new OpCode (
|
||||
0xff << 0 | 0x06 << 8 | (byte) Code.Ldloc_0 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldloc_1 = new OpCode (
|
||||
0xff << 0 | 0x07 << 8 | (byte) Code.Ldloc_1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldloc_2 = new OpCode (
|
||||
0xff << 0 | 0x08 << 8 | (byte) Code.Ldloc_2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldloc_3 = new OpCode (
|
||||
0xff << 0 | 0x09 << 8 | (byte) Code.Ldloc_3 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Stloc_0 = new OpCode (
|
||||
0xff << 0 | 0x0a << 8 | (byte) Code.Stloc_0 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stloc_1 = new OpCode (
|
||||
0xff << 0 | 0x0b << 8 | (byte) Code.Stloc_1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stloc_2 = new OpCode (
|
||||
0xff << 0 | 0x0c << 8 | (byte) Code.Stloc_2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stloc_3 = new OpCode (
|
||||
0xff << 0 | 0x0d << 8 | (byte) Code.Stloc_3 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldarg_S = new OpCode (
|
||||
0xff << 0 | 0x0e << 8 | (byte) Code.Ldarg_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineArg << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldarga_S = new OpCode (
|
||||
0xff << 0 | 0x0f << 8 | (byte) Code.Ldarga_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineArg << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Starg_S = new OpCode (
|
||||
0xff << 0 | 0x10 << 8 | (byte) Code.Starg_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineArg << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldloc_S = new OpCode (
|
||||
0xff << 0 | 0x11 << 8 | (byte) Code.Ldloc_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineVar << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldloca_S = new OpCode (
|
||||
0xff << 0 | 0x12 << 8 | (byte) Code.Ldloca_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineVar << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Stloc_S = new OpCode (
|
||||
0xff << 0 | 0x13 << 8 | (byte) Code.Stloc_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineVar << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldnull = new OpCode (
|
||||
0xff << 0 | 0x14 << 8 | (byte) Code.Ldnull << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_M1 = new OpCode (
|
||||
0xff << 0 | 0x15 << 8 | (byte) Code.Ldc_I4_M1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_0 = new OpCode (
|
||||
0xff << 0 | 0x16 << 8 | (byte) Code.Ldc_I4_0 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_1 = new OpCode (
|
||||
0xff << 0 | 0x17 << 8 | (byte) Code.Ldc_I4_1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_2 = new OpCode (
|
||||
0xff << 0 | 0x18 << 8 | (byte) Code.Ldc_I4_2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_3 = new OpCode (
|
||||
0xff << 0 | 0x19 << 8 | (byte) Code.Ldc_I4_3 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_4 = new OpCode (
|
||||
0xff << 0 | 0x1a << 8 | (byte) Code.Ldc_I4_4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_5 = new OpCode (
|
||||
0xff << 0 | 0x1b << 8 | (byte) Code.Ldc_I4_5 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_6 = new OpCode (
|
||||
0xff << 0 | 0x1c << 8 | (byte) Code.Ldc_I4_6 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_7 = new OpCode (
|
||||
0xff << 0 | 0x1d << 8 | (byte) Code.Ldc_I4_7 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_8 = new OpCode (
|
||||
0xff << 0 | 0x1e << 8 | (byte) Code.Ldc_I4_8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4_S = new OpCode (
|
||||
0xff << 0 | 0x1f << 8 | (byte) Code.Ldc_I4_S << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineI << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I4 = new OpCode (
|
||||
0xff << 0 | 0x20 << 8 | (byte) Code.Ldc_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineI << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldc_I8 = new OpCode (
|
||||
0xff << 0 | 0x21 << 8 | (byte) Code.Ldc_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineI8 << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Ldc_R4 = new OpCode (
|
||||
0xff << 0 | 0x22 << 8 | (byte) Code.Ldc_R4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.ShortInlineR << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushr4 << 24);
|
||||
|
||||
public static readonly OpCode Ldc_R8 = new OpCode (
|
||||
0xff << 0 | 0x23 << 8 | (byte) Code.Ldc_R8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineR << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushr8 << 24);
|
||||
|
||||
public static readonly OpCode Dup = new OpCode (
|
||||
0xff << 0 | 0x25 << 8 | (byte) Code.Dup << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push1_push1 << 24);
|
||||
|
||||
public static readonly OpCode Pop = new OpCode (
|
||||
0xff << 0 | 0x26 << 8 | (byte) Code.Pop << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Jmp = new OpCode (
|
||||
0xff << 0 | 0x27 << 8 | (byte) Code.Jmp << 16 | (byte) FlowControl.Call << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineMethod << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Call = new OpCode (
|
||||
0xff << 0 | 0x28 << 8 | (byte) Code.Call << 16 | (byte) FlowControl.Call << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineMethod << 8 | (byte) StackBehaviour.Varpop << 16 | (byte) StackBehaviour.Varpush << 24);
|
||||
|
||||
public static readonly OpCode Calli = new OpCode (
|
||||
0xff << 0 | 0x29 << 8 | (byte) Code.Calli << 16 | (byte) FlowControl.Call << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineSig << 8 | (byte) StackBehaviour.Varpop << 16 | (byte) StackBehaviour.Varpush << 24);
|
||||
|
||||
public static readonly OpCode Ret = new OpCode (
|
||||
0xff << 0 | 0x2a << 8 | (byte) Code.Ret << 16 | (byte) FlowControl.Return << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Varpop << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Br_S = new OpCode (
|
||||
0xff << 0 | 0x2b << 8 | (byte) Code.Br_S << 16 | (byte) FlowControl.Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Brfalse_S = new OpCode (
|
||||
0xff << 0 | 0x2c << 8 | (byte) Code.Brfalse_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Brtrue_S = new OpCode (
|
||||
0xff << 0 | 0x2d << 8 | (byte) Code.Brtrue_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Beq_S = new OpCode (
|
||||
0xff << 0 | 0x2e << 8 | (byte) Code.Beq_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bge_S = new OpCode (
|
||||
0xff << 0 | 0x2f << 8 | (byte) Code.Bge_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bgt_S = new OpCode (
|
||||
0xff << 0 | 0x30 << 8 | (byte) Code.Bgt_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ble_S = new OpCode (
|
||||
0xff << 0 | 0x31 << 8 | (byte) Code.Ble_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Blt_S = new OpCode (
|
||||
0xff << 0 | 0x32 << 8 | (byte) Code.Blt_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bne_Un_S = new OpCode (
|
||||
0xff << 0 | 0x33 << 8 | (byte) Code.Bne_Un_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bge_Un_S = new OpCode (
|
||||
0xff << 0 | 0x34 << 8 | (byte) Code.Bge_Un_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bgt_Un_S = new OpCode (
|
||||
0xff << 0 | 0x35 << 8 | (byte) Code.Bgt_Un_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ble_Un_S = new OpCode (
|
||||
0xff << 0 | 0x36 << 8 | (byte) Code.Ble_Un_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Blt_Un_S = new OpCode (
|
||||
0xff << 0 | 0x37 << 8 | (byte) Code.Blt_Un_S << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Br = new OpCode (
|
||||
0xff << 0 | 0x38 << 8 | (byte) Code.Br << 16 | (byte) FlowControl.Branch << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Brfalse = new OpCode (
|
||||
0xff << 0 | 0x39 << 8 | (byte) Code.Brfalse << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Brtrue = new OpCode (
|
||||
0xff << 0 | 0x3a << 8 | (byte) Code.Brtrue << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Beq = new OpCode (
|
||||
0xff << 0 | 0x3b << 8 | (byte) Code.Beq << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bge = new OpCode (
|
||||
0xff << 0 | 0x3c << 8 | (byte) Code.Bge << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bgt = new OpCode (
|
||||
0xff << 0 | 0x3d << 8 | (byte) Code.Bgt << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ble = new OpCode (
|
||||
0xff << 0 | 0x3e << 8 | (byte) Code.Ble << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Blt = new OpCode (
|
||||
0xff << 0 | 0x3f << 8 | (byte) Code.Blt << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bne_Un = new OpCode (
|
||||
0xff << 0 | 0x40 << 8 | (byte) Code.Bne_Un << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bge_Un = new OpCode (
|
||||
0xff << 0 | 0x41 << 8 | (byte) Code.Bge_Un << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Bgt_Un = new OpCode (
|
||||
0xff << 0 | 0x42 << 8 | (byte) Code.Bgt_Un << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ble_Un = new OpCode (
|
||||
0xff << 0 | 0x43 << 8 | (byte) Code.Ble_Un << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Blt_Un = new OpCode (
|
||||
0xff << 0 | 0x44 << 8 | (byte) Code.Blt_Un << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Switch = new OpCode (
|
||||
0xff << 0 | 0x45 << 8 | (byte) Code.Switch << 16 | (byte) FlowControl.Cond_Branch << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineSwitch << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldind_I1 = new OpCode (
|
||||
0xff << 0 | 0x46 << 8 | (byte) Code.Ldind_I1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_U1 = new OpCode (
|
||||
0xff << 0 | 0x47 << 8 | (byte) Code.Ldind_U1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_I2 = new OpCode (
|
||||
0xff << 0 | 0x48 << 8 | (byte) Code.Ldind_I2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_U2 = new OpCode (
|
||||
0xff << 0 | 0x49 << 8 | (byte) Code.Ldind_U2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_I4 = new OpCode (
|
||||
0xff << 0 | 0x4a << 8 | (byte) Code.Ldind_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_U4 = new OpCode (
|
||||
0xff << 0 | 0x4b << 8 | (byte) Code.Ldind_U4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_I8 = new OpCode (
|
||||
0xff << 0 | 0x4c << 8 | (byte) Code.Ldind_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Ldind_I = new OpCode (
|
||||
0xff << 0 | 0x4d << 8 | (byte) Code.Ldind_I << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldind_R4 = new OpCode (
|
||||
0xff << 0 | 0x4e << 8 | (byte) Code.Ldind_R4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushr4 << 24);
|
||||
|
||||
public static readonly OpCode Ldind_R8 = new OpCode (
|
||||
0xff << 0 | 0x4f << 8 | (byte) Code.Ldind_R8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushr8 << 24);
|
||||
|
||||
public static readonly OpCode Ldind_Ref = new OpCode (
|
||||
0xff << 0 | 0x50 << 8 | (byte) Code.Ldind_Ref << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Stind_Ref = new OpCode (
|
||||
0xff << 0 | 0x51 << 8 | (byte) Code.Stind_Ref << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_I1 = new OpCode (
|
||||
0xff << 0 | 0x52 << 8 | (byte) Code.Stind_I1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_I2 = new OpCode (
|
||||
0xff << 0 | 0x53 << 8 | (byte) Code.Stind_I2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_I4 = new OpCode (
|
||||
0xff << 0 | 0x54 << 8 | (byte) Code.Stind_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_I8 = new OpCode (
|
||||
0xff << 0 | 0x55 << 8 | (byte) Code.Stind_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi8 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_R4 = new OpCode (
|
||||
0xff << 0 | 0x56 << 8 | (byte) Code.Stind_R4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popr4 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_R8 = new OpCode (
|
||||
0xff << 0 | 0x57 << 8 | (byte) Code.Stind_R8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popr8 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Add = new OpCode (
|
||||
0xff << 0 | 0x58 << 8 | (byte) Code.Add << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Sub = new OpCode (
|
||||
0xff << 0 | 0x59 << 8 | (byte) Code.Sub << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Mul = new OpCode (
|
||||
0xff << 0 | 0x5a << 8 | (byte) Code.Mul << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Div = new OpCode (
|
||||
0xff << 0 | 0x5b << 8 | (byte) Code.Div << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Div_Un = new OpCode (
|
||||
0xff << 0 | 0x5c << 8 | (byte) Code.Div_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Rem = new OpCode (
|
||||
0xff << 0 | 0x5d << 8 | (byte) Code.Rem << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Rem_Un = new OpCode (
|
||||
0xff << 0 | 0x5e << 8 | (byte) Code.Rem_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode And = new OpCode (
|
||||
0xff << 0 | 0x5f << 8 | (byte) Code.And << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Or = new OpCode (
|
||||
0xff << 0 | 0x60 << 8 | (byte) Code.Or << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Xor = new OpCode (
|
||||
0xff << 0 | 0x61 << 8 | (byte) Code.Xor << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Shl = new OpCode (
|
||||
0xff << 0 | 0x62 << 8 | (byte) Code.Shl << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Shr = new OpCode (
|
||||
0xff << 0 | 0x63 << 8 | (byte) Code.Shr << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Shr_Un = new OpCode (
|
||||
0xff << 0 | 0x64 << 8 | (byte) Code.Shr_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Neg = new OpCode (
|
||||
0xff << 0 | 0x65 << 8 | (byte) Code.Neg << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Not = new OpCode (
|
||||
0xff << 0 | 0x66 << 8 | (byte) Code.Not << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Conv_I1 = new OpCode (
|
||||
0xff << 0 | 0x67 << 8 | (byte) Code.Conv_I1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_I2 = new OpCode (
|
||||
0xff << 0 | 0x68 << 8 | (byte) Code.Conv_I2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_I4 = new OpCode (
|
||||
0xff << 0 | 0x69 << 8 | (byte) Code.Conv_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_I8 = new OpCode (
|
||||
0xff << 0 | 0x6a << 8 | (byte) Code.Conv_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Conv_R4 = new OpCode (
|
||||
0xff << 0 | 0x6b << 8 | (byte) Code.Conv_R4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushr4 << 24);
|
||||
|
||||
public static readonly OpCode Conv_R8 = new OpCode (
|
||||
0xff << 0 | 0x6c << 8 | (byte) Code.Conv_R8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushr8 << 24);
|
||||
|
||||
public static readonly OpCode Conv_U4 = new OpCode (
|
||||
0xff << 0 | 0x6d << 8 | (byte) Code.Conv_U4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_U8 = new OpCode (
|
||||
0xff << 0 | 0x6e << 8 | (byte) Code.Conv_U8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Callvirt = new OpCode (
|
||||
0xff << 0 | 0x6f << 8 | (byte) Code.Callvirt << 16 | (byte) FlowControl.Call << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineMethod << 8 | (byte) StackBehaviour.Varpop << 16 | (byte) StackBehaviour.Varpush << 24);
|
||||
|
||||
public static readonly OpCode Cpobj = new OpCode (
|
||||
0xff << 0 | 0x70 << 8 | (byte) Code.Cpobj << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldobj = new OpCode (
|
||||
0xff << 0 | 0x71 << 8 | (byte) Code.Ldobj << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldstr = new OpCode (
|
||||
0xff << 0 | 0x72 << 8 | (byte) Code.Ldstr << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineString << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Newobj = new OpCode (
|
||||
0xff << 0 | 0x73 << 8 | (byte) Code.Newobj << 16 | (byte) FlowControl.Call << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineMethod << 8 | (byte) StackBehaviour.Varpop << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Castclass = new OpCode (
|
||||
0xff << 0 | 0x74 << 8 | (byte) Code.Castclass << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Isinst = new OpCode (
|
||||
0xff << 0 | 0x75 << 8 | (byte) Code.Isinst << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_R_Un = new OpCode (
|
||||
0xff << 0 | 0x76 << 8 | (byte) Code.Conv_R_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushr8 << 24);
|
||||
|
||||
public static readonly OpCode Unbox = new OpCode (
|
||||
0xff << 0 | 0x79 << 8 | (byte) Code.Unbox << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Throw = new OpCode (
|
||||
0xff << 0 | 0x7a << 8 | (byte) Code.Throw << 16 | (byte) FlowControl.Throw << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldfld = new OpCode (
|
||||
0xff << 0 | 0x7b << 8 | (byte) Code.Ldfld << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineField << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldflda = new OpCode (
|
||||
0xff << 0 | 0x7c << 8 | (byte) Code.Ldflda << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineField << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Stfld = new OpCode (
|
||||
0xff << 0 | 0x7d << 8 | (byte) Code.Stfld << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineField << 8 | (byte) StackBehaviour.Popref_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldsfld = new OpCode (
|
||||
0xff << 0 | 0x7e << 8 | (byte) Code.Ldsfld << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineField << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldsflda = new OpCode (
|
||||
0xff << 0 | 0x7f << 8 | (byte) Code.Ldsflda << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineField << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Stsfld = new OpCode (
|
||||
0xff << 0 | 0x80 << 8 | (byte) Code.Stsfld << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineField << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stobj = new OpCode (
|
||||
0xff << 0 | 0x81 << 8 | (byte) Code.Stobj << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popi_pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I1_Un = new OpCode (
|
||||
0xff << 0 | 0x82 << 8 | (byte) Code.Conv_Ovf_I1_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I2_Un = new OpCode (
|
||||
0xff << 0 | 0x83 << 8 | (byte) Code.Conv_Ovf_I2_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I4_Un = new OpCode (
|
||||
0xff << 0 | 0x84 << 8 | (byte) Code.Conv_Ovf_I4_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I8_Un = new OpCode (
|
||||
0xff << 0 | 0x85 << 8 | (byte) Code.Conv_Ovf_I8_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U1_Un = new OpCode (
|
||||
0xff << 0 | 0x86 << 8 | (byte) Code.Conv_Ovf_U1_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U2_Un = new OpCode (
|
||||
0xff << 0 | 0x87 << 8 | (byte) Code.Conv_Ovf_U2_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U4_Un = new OpCode (
|
||||
0xff << 0 | 0x88 << 8 | (byte) Code.Conv_Ovf_U4_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U8_Un = new OpCode (
|
||||
0xff << 0 | 0x89 << 8 | (byte) Code.Conv_Ovf_U8_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I_Un = new OpCode (
|
||||
0xff << 0 | 0x8a << 8 | (byte) Code.Conv_Ovf_I_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U_Un = new OpCode (
|
||||
0xff << 0 | 0x8b << 8 | (byte) Code.Conv_Ovf_U_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Box = new OpCode (
|
||||
0xff << 0 | 0x8c << 8 | (byte) Code.Box << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Newarr = new OpCode (
|
||||
0xff << 0 | 0x8d << 8 | (byte) Code.Newarr << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Ldlen = new OpCode (
|
||||
0xff << 0 | 0x8e << 8 | (byte) Code.Ldlen << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelema = new OpCode (
|
||||
0xff << 0 | 0x8f << 8 | (byte) Code.Ldelema << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_I1 = new OpCode (
|
||||
0xff << 0 | 0x90 << 8 | (byte) Code.Ldelem_I1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_U1 = new OpCode (
|
||||
0xff << 0 | 0x91 << 8 | (byte) Code.Ldelem_U1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_I2 = new OpCode (
|
||||
0xff << 0 | 0x92 << 8 | (byte) Code.Ldelem_I2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_U2 = new OpCode (
|
||||
0xff << 0 | 0x93 << 8 | (byte) Code.Ldelem_U2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_I4 = new OpCode (
|
||||
0xff << 0 | 0x94 << 8 | (byte) Code.Ldelem_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_U4 = new OpCode (
|
||||
0xff << 0 | 0x95 << 8 | (byte) Code.Ldelem_U4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_I8 = new OpCode (
|
||||
0xff << 0 | 0x96 << 8 | (byte) Code.Ldelem_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_I = new OpCode (
|
||||
0xff << 0 | 0x97 << 8 | (byte) Code.Ldelem_I << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_R4 = new OpCode (
|
||||
0xff << 0 | 0x98 << 8 | (byte) Code.Ldelem_R4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushr4 << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_R8 = new OpCode (
|
||||
0xff << 0 | 0x99 << 8 | (byte) Code.Ldelem_R8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushr8 << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_Ref = new OpCode (
|
||||
0xff << 0 | 0x9a << 8 | (byte) Code.Ldelem_Ref << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Pushref << 24);
|
||||
|
||||
public static readonly OpCode Stelem_I = new OpCode (
|
||||
0xff << 0 | 0x9b << 8 | (byte) Code.Stelem_I << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_I1 = new OpCode (
|
||||
0xff << 0 | 0x9c << 8 | (byte) Code.Stelem_I1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_I2 = new OpCode (
|
||||
0xff << 0 | 0x9d << 8 | (byte) Code.Stelem_I2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_I4 = new OpCode (
|
||||
0xff << 0 | 0x9e << 8 | (byte) Code.Stelem_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_I8 = new OpCode (
|
||||
0xff << 0 | 0x9f << 8 | (byte) Code.Stelem_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popi8 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_R4 = new OpCode (
|
||||
0xff << 0 | 0xa0 << 8 | (byte) Code.Stelem_R4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popr4 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_R8 = new OpCode (
|
||||
0xff << 0 | 0xa1 << 8 | (byte) Code.Stelem_R8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popr8 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_Ref = new OpCode (
|
||||
0xff << 0 | 0xa2 << 8 | (byte) Code.Stelem_Ref << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popref_popi_popref << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldelem_Any = new OpCode (
|
||||
0xff << 0 | 0xa3 << 8 | (byte) Code.Ldelem_Any << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref_popi << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Stelem_Any = new OpCode (
|
||||
0xff << 0 | 0xa4 << 8 | (byte) Code.Stelem_Any << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref_popi_popref << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Unbox_Any = new OpCode (
|
||||
0xff << 0 | 0xa5 << 8 | (byte) Code.Unbox_Any << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I1 = new OpCode (
|
||||
0xff << 0 | 0xb3 << 8 | (byte) Code.Conv_Ovf_I1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U1 = new OpCode (
|
||||
0xff << 0 | 0xb4 << 8 | (byte) Code.Conv_Ovf_U1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I2 = new OpCode (
|
||||
0xff << 0 | 0xb5 << 8 | (byte) Code.Conv_Ovf_I2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U2 = new OpCode (
|
||||
0xff << 0 | 0xb6 << 8 | (byte) Code.Conv_Ovf_U2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I4 = new OpCode (
|
||||
0xff << 0 | 0xb7 << 8 | (byte) Code.Conv_Ovf_I4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U4 = new OpCode (
|
||||
0xff << 0 | 0xb8 << 8 | (byte) Code.Conv_Ovf_U4 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I8 = new OpCode (
|
||||
0xff << 0 | 0xb9 << 8 | (byte) Code.Conv_Ovf_I8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U8 = new OpCode (
|
||||
0xff << 0 | 0xba << 8 | (byte) Code.Conv_Ovf_U8 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi8 << 24);
|
||||
|
||||
public static readonly OpCode Refanyval = new OpCode (
|
||||
0xff << 0 | 0xc2 << 8 | (byte) Code.Refanyval << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ckfinite = new OpCode (
|
||||
0xff << 0 | 0xc3 << 8 | (byte) Code.Ckfinite << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushr8 << 24);
|
||||
|
||||
public static readonly OpCode Mkrefany = new OpCode (
|
||||
0xff << 0 | 0xc6 << 8 | (byte) Code.Mkrefany << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldtoken = new OpCode (
|
||||
0xff << 0 | 0xd0 << 8 | (byte) Code.Ldtoken << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineTok << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_U2 = new OpCode (
|
||||
0xff << 0 | 0xd1 << 8 | (byte) Code.Conv_U2 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_U1 = new OpCode (
|
||||
0xff << 0 | 0xd2 << 8 | (byte) Code.Conv_U1 << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_I = new OpCode (
|
||||
0xff << 0 | 0xd3 << 8 | (byte) Code.Conv_I << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_I = new OpCode (
|
||||
0xff << 0 | 0xd4 << 8 | (byte) Code.Conv_Ovf_I << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Conv_Ovf_U = new OpCode (
|
||||
0xff << 0 | 0xd5 << 8 | (byte) Code.Conv_Ovf_U << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Add_Ovf = new OpCode (
|
||||
0xff << 0 | 0xd6 << 8 | (byte) Code.Add_Ovf << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Add_Ovf_Un = new OpCode (
|
||||
0xff << 0 | 0xd7 << 8 | (byte) Code.Add_Ovf_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Mul_Ovf = new OpCode (
|
||||
0xff << 0 | 0xd8 << 8 | (byte) Code.Mul_Ovf << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Mul_Ovf_Un = new OpCode (
|
||||
0xff << 0 | 0xd9 << 8 | (byte) Code.Mul_Ovf_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Sub_Ovf = new OpCode (
|
||||
0xff << 0 | 0xda << 8 | (byte) Code.Sub_Ovf << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Sub_Ovf_Un = new OpCode (
|
||||
0xff << 0 | 0xdb << 8 | (byte) Code.Sub_Ovf_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Endfinally = new OpCode (
|
||||
0xff << 0 | 0xdc << 8 | (byte) Code.Endfinally << 16 | (byte) FlowControl.Return << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Leave = new OpCode (
|
||||
0xff << 0 | 0xdd << 8 | (byte) Code.Leave << 16 | (byte) FlowControl.Branch << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineBrTarget << 8 | (byte) StackBehaviour.PopAll << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Leave_S = new OpCode (
|
||||
0xff << 0 | 0xde << 8 | (byte) Code.Leave_S << 16 | (byte) FlowControl.Branch << 24,
|
||||
(byte) OpCodeType.Macro << 0 | (byte) OperandType.ShortInlineBrTarget << 8 | (byte) StackBehaviour.PopAll << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Stind_I = new OpCode (
|
||||
0xff << 0 | 0xdf << 8 | (byte) Code.Stind_I << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Conv_U = new OpCode (
|
||||
0xff << 0 | 0xe0 << 8 | (byte) Code.Conv_U << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Arglist = new OpCode (
|
||||
0xfe << 0 | 0x00 << 8 | (byte) Code.Arglist << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ceq = new OpCode (
|
||||
0xfe << 0 | 0x01 << 8 | (byte) Code.Ceq << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Cgt = new OpCode (
|
||||
0xfe << 0 | 0x02 << 8 | (byte) Code.Cgt << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Cgt_Un = new OpCode (
|
||||
0xfe << 0 | 0x03 << 8 | (byte) Code.Cgt_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Clt = new OpCode (
|
||||
0xfe << 0 | 0x04 << 8 | (byte) Code.Clt << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Clt_Un = new OpCode (
|
||||
0xfe << 0 | 0x05 << 8 | (byte) Code.Clt_Un << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1_pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldftn = new OpCode (
|
||||
0xfe << 0 | 0x06 << 8 | (byte) Code.Ldftn << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineMethod << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldvirtftn = new OpCode (
|
||||
0xfe << 0 | 0x07 << 8 | (byte) Code.Ldvirtftn << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineMethod << 8 | (byte) StackBehaviour.Popref << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Ldarg = new OpCode (
|
||||
0xfe << 0 | 0x09 << 8 | (byte) Code.Ldarg << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineArg << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldarga = new OpCode (
|
||||
0xfe << 0 | 0x0a << 8 | (byte) Code.Ldarga << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineArg << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Starg = new OpCode (
|
||||
0xfe << 0 | 0x0b << 8 | (byte) Code.Starg << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineArg << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Ldloc = new OpCode (
|
||||
0xfe << 0 | 0x0c << 8 | (byte) Code.Ldloc << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineVar << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push1 << 24);
|
||||
|
||||
public static readonly OpCode Ldloca = new OpCode (
|
||||
0xfe << 0 | 0x0d << 8 | (byte) Code.Ldloca << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineVar << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Stloc = new OpCode (
|
||||
0xfe << 0 | 0x0e << 8 | (byte) Code.Stloc << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineVar << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Localloc = new OpCode (
|
||||
0xfe << 0 | 0x0f << 8 | (byte) Code.Localloc << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Endfilter = new OpCode (
|
||||
0xfe << 0 | 0x11 << 8 | (byte) Code.Endfilter << 16 | (byte) FlowControl.Return << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Unaligned = new OpCode (
|
||||
0xfe << 0 | 0x12 << 8 | (byte) Code.Unaligned << 16 | (byte) FlowControl.Meta << 24,
|
||||
(byte) OpCodeType.Prefix << 0 | (byte) OperandType.ShortInlineI << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Volatile = new OpCode (
|
||||
0xfe << 0 | 0x13 << 8 | (byte) Code.Volatile << 16 | (byte) FlowControl.Meta << 24,
|
||||
(byte) OpCodeType.Prefix << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Tail = new OpCode (
|
||||
0xfe << 0 | 0x14 << 8 | (byte) Code.Tail << 16 | (byte) FlowControl.Meta << 24,
|
||||
(byte) OpCodeType.Prefix << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Initobj = new OpCode (
|
||||
0xfe << 0 | 0x15 << 8 | (byte) Code.Initobj << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Constrained = new OpCode (
|
||||
0xfe << 0 | 0x16 << 8 | (byte) Code.Constrained << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Prefix << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Cpblk = new OpCode (
|
||||
0xfe << 0 | 0x17 << 8 | (byte) Code.Cpblk << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Initblk = new OpCode (
|
||||
0xfe << 0 | 0x18 << 8 | (byte) Code.Initblk << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Popi_popi_popi << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode No = new OpCode (
|
||||
0xfe << 0 | 0x19 << 8 | (byte) Code.No << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Prefix << 0 | (byte) OperandType.ShortInlineI << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Rethrow = new OpCode (
|
||||
0xfe << 0 | 0x1a << 8 | (byte) Code.Rethrow << 16 | (byte) FlowControl.Throw << 24,
|
||||
(byte) OpCodeType.Objmodel << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
|
||||
public static readonly OpCode Sizeof = new OpCode (
|
||||
0xfe << 0 | 0x1c << 8 | (byte) Code.Sizeof << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineType << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Refanytype = new OpCode (
|
||||
0xfe << 0 | 0x1d << 8 | (byte) Code.Refanytype << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Primitive << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop1 << 16 | (byte) StackBehaviour.Pushi << 24);
|
||||
|
||||
public static readonly OpCode Readonly = new OpCode (
|
||||
0xfe << 0 | 0x1e << 8 | (byte) Code.Readonly << 16 | (byte) FlowControl.Next << 24,
|
||||
(byte) OpCodeType.Prefix << 0 | (byte) OperandType.InlineNone << 8 | (byte) StackBehaviour.Pop0 << 16 | (byte) StackBehaviour.Push0 << 24);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,590 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
using Mono.Cecil.Metadata;
|
||||
using Mono.Cecil.PE;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public sealed class PortablePdbReaderProvider : ISymbolReaderProvider {
|
||||
|
||||
public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName)
|
||||
{
|
||||
Mixin.CheckModule (module);
|
||||
Mixin.CheckFileName (fileName);
|
||||
|
||||
var file = File.OpenRead (Mixin.GetPdbFileName (fileName));
|
||||
return GetSymbolReader (module, Disposable.Owned (file as Stream), file.Name);
|
||||
}
|
||||
|
||||
public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream)
|
||||
{
|
||||
Mixin.CheckModule (module);
|
||||
Mixin.CheckStream (symbolStream);
|
||||
|
||||
return GetSymbolReader (module, Disposable.NotOwned (symbolStream), symbolStream.GetFileName ());
|
||||
}
|
||||
|
||||
ISymbolReader GetSymbolReader (ModuleDefinition module, Disposable<Stream> symbolStream, string fileName)
|
||||
{
|
||||
return new PortablePdbReader (ImageReader.ReadPortablePdb (symbolStream, fileName), module);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PortablePdbReader : ISymbolReader {
|
||||
|
||||
readonly Image image;
|
||||
readonly ModuleDefinition module;
|
||||
readonly MetadataReader reader;
|
||||
readonly MetadataReader debug_reader;
|
||||
|
||||
bool IsEmbedded { get { return reader.image == debug_reader.image; } }
|
||||
|
||||
internal PortablePdbReader (Image image, ModuleDefinition module)
|
||||
{
|
||||
this.image = image;
|
||||
this.module = module;
|
||||
this.reader = module.reader;
|
||||
this.debug_reader = new MetadataReader (image, module, this.reader);
|
||||
}
|
||||
|
||||
#if !READ_ONLY
|
||||
public ISymbolWriterProvider GetWriterProvider ()
|
||||
{
|
||||
return new PortablePdbWriterProvider ();
|
||||
}
|
||||
#endif
|
||||
|
||||
public bool ProcessDebugHeader (ImageDebugHeader header)
|
||||
{
|
||||
if (image == module.Image)
|
||||
return true;
|
||||
|
||||
var entry = header.GetCodeViewEntry ();
|
||||
if (entry == null)
|
||||
return false;
|
||||
|
||||
var data = entry.Data;
|
||||
|
||||
if (data.Length < 24)
|
||||
return false;
|
||||
|
||||
var magic = ReadInt32 (data, 0);
|
||||
if (magic != 0x53445352)
|
||||
return false;
|
||||
|
||||
var buffer = new byte [16];
|
||||
Buffer.BlockCopy (data, 4, buffer, 0, 16);
|
||||
|
||||
var module_guid = new Guid (buffer);
|
||||
|
||||
Buffer.BlockCopy (image.PdbHeap.Id, 0, buffer, 0, 16);
|
||||
|
||||
var pdb_guid = new Guid (buffer);
|
||||
|
||||
if (module_guid != pdb_guid)
|
||||
return false;
|
||||
|
||||
ReadModule ();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ReadInt32 (byte [] bytes, int start)
|
||||
{
|
||||
return (bytes [start]
|
||||
| (bytes [start + 1] << 8)
|
||||
| (bytes [start + 2] << 16)
|
||||
| (bytes [start + 3] << 24));
|
||||
}
|
||||
|
||||
void ReadModule ()
|
||||
{
|
||||
module.custom_infos = debug_reader.GetCustomDebugInformation (module);
|
||||
}
|
||||
|
||||
public MethodDebugInformation Read (MethodDefinition method)
|
||||
{
|
||||
var info = new MethodDebugInformation (method);
|
||||
ReadSequencePoints (info);
|
||||
ReadScope (info);
|
||||
ReadStateMachineKickOffMethod (info);
|
||||
ReadCustomDebugInformations (info);
|
||||
return info;
|
||||
}
|
||||
|
||||
void ReadSequencePoints (MethodDebugInformation method_info)
|
||||
{
|
||||
method_info.sequence_points = debug_reader.ReadSequencePoints (method_info.method);
|
||||
}
|
||||
|
||||
void ReadScope (MethodDebugInformation method_info)
|
||||
{
|
||||
method_info.scope = debug_reader.ReadScope (method_info.method);
|
||||
}
|
||||
|
||||
void ReadStateMachineKickOffMethod (MethodDebugInformation method_info)
|
||||
{
|
||||
method_info.kickoff_method = debug_reader.ReadStateMachineKickoffMethod (method_info.method);
|
||||
}
|
||||
|
||||
void ReadCustomDebugInformations (MethodDebugInformation info)
|
||||
{
|
||||
info.method.custom_infos = debug_reader.GetCustomDebugInformation (info.method);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (IsEmbedded)
|
||||
return;
|
||||
|
||||
image.Dispose ();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EmbeddedPortablePdbReaderProvider : ISymbolReaderProvider {
|
||||
|
||||
public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName)
|
||||
{
|
||||
Mixin.CheckModule (module);
|
||||
|
||||
var header = module.GetDebugHeader ();
|
||||
var entry = header.GetEmbeddedPortablePdbEntry ();
|
||||
if (entry == null)
|
||||
throw new InvalidOperationException ();
|
||||
|
||||
return new EmbeddedPortablePdbReader (
|
||||
(PortablePdbReader) new PortablePdbReaderProvider ().GetSymbolReader (module, GetPortablePdbStream (entry)));
|
||||
}
|
||||
|
||||
static Stream GetPortablePdbStream (ImageDebugHeaderEntry entry)
|
||||
{
|
||||
var compressed_stream = new MemoryStream (entry.Data);
|
||||
var reader = new BinaryStreamReader (compressed_stream);
|
||||
reader.ReadInt32 (); // signature
|
||||
var length = reader.ReadInt32 ();
|
||||
var decompressed_stream = new MemoryStream (length);
|
||||
|
||||
using (var deflate_stream = new DeflateStream (compressed_stream, CompressionMode.Decompress, leaveOpen: true))
|
||||
deflate_stream.CopyTo (decompressed_stream);
|
||||
|
||||
return decompressed_stream;
|
||||
}
|
||||
|
||||
public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EmbeddedPortablePdbReader : ISymbolReader
|
||||
{
|
||||
private readonly PortablePdbReader reader;
|
||||
|
||||
internal EmbeddedPortablePdbReader (PortablePdbReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
throw new ArgumentNullException ();
|
||||
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
#if !READ_ONLY
|
||||
public ISymbolWriterProvider GetWriterProvider ()
|
||||
{
|
||||
return new EmbeddedPortablePdbWriterProvider ();
|
||||
}
|
||||
#endif
|
||||
public bool ProcessDebugHeader (ImageDebugHeader header)
|
||||
{
|
||||
return reader.ProcessDebugHeader (header);
|
||||
}
|
||||
|
||||
public MethodDebugInformation Read (MethodDefinition method)
|
||||
{
|
||||
return reader.Read (method);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
reader.Dispose ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !READ_ONLY
|
||||
|
||||
public sealed class PortablePdbWriterProvider : ISymbolWriterProvider
|
||||
{
|
||||
public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
|
||||
{
|
||||
Mixin.CheckModule (module);
|
||||
Mixin.CheckFileName (fileName);
|
||||
|
||||
var file = File.OpenWrite (Mixin.GetPdbFileName (fileName));
|
||||
return GetSymbolWriter (module, Disposable.Owned (file as Stream));
|
||||
}
|
||||
|
||||
public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream)
|
||||
{
|
||||
Mixin.CheckModule (module);
|
||||
Mixin.CheckStream (symbolStream);
|
||||
|
||||
return GetSymbolWriter (module, Disposable.NotOwned (symbolStream));
|
||||
}
|
||||
|
||||
ISymbolWriter GetSymbolWriter (ModuleDefinition module, Disposable<Stream> stream)
|
||||
{
|
||||
var metadata = new MetadataBuilder (module, this);
|
||||
var writer = ImageWriter.CreateDebugWriter (module, metadata, stream);
|
||||
|
||||
return new PortablePdbWriter (metadata, module, writer);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PortablePdbWriter : ISymbolWriter {
|
||||
|
||||
readonly MetadataBuilder pdb_metadata;
|
||||
readonly ModuleDefinition module;
|
||||
readonly ImageWriter writer;
|
||||
|
||||
MetadataBuilder module_metadata;
|
||||
|
||||
bool IsEmbedded { get { return writer == null; } }
|
||||
|
||||
internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition module)
|
||||
{
|
||||
this.pdb_metadata = pdb_metadata;
|
||||
this.module = module;
|
||||
|
||||
this.module_metadata = module.metadata_builder;
|
||||
|
||||
if (module_metadata != pdb_metadata)
|
||||
this.pdb_metadata.metadata_builder = this.module_metadata;
|
||||
|
||||
pdb_metadata.AddCustomDebugInformations (module);
|
||||
}
|
||||
|
||||
internal PortablePdbWriter (MetadataBuilder pdb_metadata, ModuleDefinition module, ImageWriter writer)
|
||||
: this (pdb_metadata, module)
|
||||
{
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public ISymbolReaderProvider GetReaderProvider ()
|
||||
{
|
||||
return new PortablePdbReaderProvider ();
|
||||
}
|
||||
|
||||
public ImageDebugHeader GetDebugHeader ()
|
||||
{
|
||||
if (IsEmbedded)
|
||||
return new ImageDebugHeader ();
|
||||
|
||||
var directory = new ImageDebugDirectory () {
|
||||
MajorVersion = 256,
|
||||
MinorVersion = 20557,
|
||||
Type = ImageDebugType.CodeView,
|
||||
TimeDateStamp = (int) module.timestamp,
|
||||
};
|
||||
|
||||
var buffer = new ByteBuffer ();
|
||||
// RSDS
|
||||
buffer.WriteUInt32 (0x53445352);
|
||||
// Module ID
|
||||
buffer.WriteBytes (module.Mvid.ToByteArray ());
|
||||
// PDB Age
|
||||
buffer.WriteUInt32 (1);
|
||||
// PDB Path
|
||||
buffer.WriteBytes (System.Text.Encoding.UTF8.GetBytes (writer.BaseStream.GetFileName ()));
|
||||
buffer.WriteByte (0);
|
||||
|
||||
var data = new byte [buffer.length];
|
||||
Buffer.BlockCopy (buffer.buffer, 0, data, 0, buffer.length);
|
||||
directory.SizeOfData = data.Length;
|
||||
|
||||
return new ImageDebugHeader (new ImageDebugHeaderEntry (directory, data));
|
||||
}
|
||||
|
||||
public void Write (MethodDebugInformation info)
|
||||
{
|
||||
CheckMethodDebugInformationTable ();
|
||||
|
||||
pdb_metadata.AddMethodDebugInformation (info);
|
||||
}
|
||||
|
||||
void CheckMethodDebugInformationTable ()
|
||||
{
|
||||
var mdi = pdb_metadata.table_heap.GetTable<MethodDebugInformationTable> (Table.MethodDebugInformation);
|
||||
if (mdi.length > 0)
|
||||
return;
|
||||
|
||||
// The MethodDebugInformation table has the same length as the Method table
|
||||
mdi.rows = new Row<uint, uint> [module_metadata.method_rid - 1];
|
||||
mdi.length = mdi.rows.Length;
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (IsEmbedded)
|
||||
return;
|
||||
|
||||
WritePdbFile ();
|
||||
}
|
||||
|
||||
void WritePdbFile ()
|
||||
{
|
||||
WritePdbHeap ();
|
||||
|
||||
WriteTableHeap ();
|
||||
|
||||
writer.BuildMetadataTextMap ();
|
||||
writer.WriteMetadataHeader ();
|
||||
writer.WriteMetadata ();
|
||||
|
||||
writer.Flush ();
|
||||
writer.stream.Dispose ();
|
||||
}
|
||||
|
||||
void WritePdbHeap ()
|
||||
{
|
||||
var pdb_heap = pdb_metadata.pdb_heap;
|
||||
|
||||
pdb_heap.WriteBytes (module.Mvid.ToByteArray ());
|
||||
pdb_heap.WriteUInt32 (module_metadata.timestamp);
|
||||
|
||||
pdb_heap.WriteUInt32 (module_metadata.entry_point.ToUInt32 ());
|
||||
|
||||
var table_heap = module_metadata.table_heap;
|
||||
var tables = table_heap.tables;
|
||||
|
||||
ulong valid = 0;
|
||||
for (int i = 0; i < tables.Length; i++) {
|
||||
if (tables [i] == null || tables [i].Length == 0)
|
||||
continue;
|
||||
|
||||
valid |= (1UL << i);
|
||||
}
|
||||
|
||||
pdb_heap.WriteUInt64 (valid);
|
||||
|
||||
for (int i = 0; i < tables.Length; i++) {
|
||||
if (tables [i] == null || tables [i].Length == 0)
|
||||
continue;
|
||||
|
||||
pdb_heap.WriteUInt32 ((uint) tables [i].Length);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteTableHeap ()
|
||||
{
|
||||
pdb_metadata.table_heap.string_offsets = pdb_metadata.string_heap.WriteStrings ();
|
||||
pdb_metadata.table_heap.ComputeTableInformations ();
|
||||
pdb_metadata.table_heap.WriteTableHeap ();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EmbeddedPortablePdbWriterProvider : ISymbolWriterProvider {
|
||||
|
||||
public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
|
||||
{
|
||||
Mixin.CheckModule (module);
|
||||
Mixin.CheckFileName (fileName);
|
||||
|
||||
var stream = new MemoryStream ();
|
||||
var pdb_writer = (PortablePdbWriter) new PortablePdbWriterProvider ().GetSymbolWriter (module, stream);
|
||||
return new EmbeddedPortablePdbWriter (stream, pdb_writer);
|
||||
}
|
||||
|
||||
public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream)
|
||||
{
|
||||
throw new NotSupportedException ();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EmbeddedPortablePdbWriter : ISymbolWriter {
|
||||
|
||||
readonly Stream stream;
|
||||
readonly PortablePdbWriter writer;
|
||||
|
||||
internal EmbeddedPortablePdbWriter (Stream stream, PortablePdbWriter writer)
|
||||
{
|
||||
this.stream = stream;
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public ISymbolReaderProvider GetReaderProvider ()
|
||||
{
|
||||
return new EmbeddedPortablePdbReaderProvider ();
|
||||
}
|
||||
|
||||
public ImageDebugHeader GetDebugHeader ()
|
||||
{
|
||||
writer.Dispose ();
|
||||
|
||||
var directory = new ImageDebugDirectory {
|
||||
Type = ImageDebugType.EmbeddedPortablePdb,
|
||||
MajorVersion = 0x0100,
|
||||
MinorVersion = 0x0100,
|
||||
};
|
||||
|
||||
var data = new MemoryStream ();
|
||||
|
||||
var w = new BinaryStreamWriter (data);
|
||||
w.WriteByte (0x4d);
|
||||
w.WriteByte (0x50);
|
||||
w.WriteByte (0x44);
|
||||
w.WriteByte (0x42);
|
||||
|
||||
w.WriteInt32 ((int) stream.Length);
|
||||
|
||||
stream.Position = 0;
|
||||
|
||||
using (var compress_stream = new DeflateStream (data, CompressionMode.Compress, leaveOpen: true))
|
||||
stream.CopyTo (compress_stream);
|
||||
|
||||
directory.SizeOfData = (int) data.Length;
|
||||
|
||||
return new ImageDebugHeader (new [] {
|
||||
writer.GetDebugHeader ().Entries [0],
|
||||
new ImageDebugHeaderEntry (directory, data.ToArray ())
|
||||
});
|
||||
}
|
||||
|
||||
public void Write (MethodDebugInformation info)
|
||||
{
|
||||
writer.Write (info);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static class PdbGuidMapping {
|
||||
|
||||
static readonly Dictionary<Guid, DocumentLanguage> guid_language = new Dictionary<Guid, DocumentLanguage> ();
|
||||
static readonly Dictionary<DocumentLanguage, Guid> language_guid = new Dictionary<DocumentLanguage, Guid> ();
|
||||
|
||||
static PdbGuidMapping ()
|
||||
{
|
||||
AddMapping (DocumentLanguage.C, new Guid ("63a08714-fc37-11d2-904c-00c04fa302a1"));
|
||||
AddMapping (DocumentLanguage.Cpp, new Guid ("3a12d0b7-c26c-11d0-b442-00a0244a1dd2"));
|
||||
AddMapping (DocumentLanguage.CSharp, new Guid ("3f5162f8-07c6-11d3-9053-00c04fa302a1"));
|
||||
AddMapping (DocumentLanguage.Basic, new Guid ("3a12d0b8-c26c-11d0-b442-00a0244a1dd2"));
|
||||
AddMapping (DocumentLanguage.Java, new Guid ("3a12d0b4-c26c-11d0-b442-00a0244a1dd2"));
|
||||
AddMapping (DocumentLanguage.Cobol, new Guid ("af046cd1-d0e1-11d2-977c-00a0c9b4d50c"));
|
||||
AddMapping (DocumentLanguage.Pascal, new Guid ("af046cd2-d0e1-11d2-977c-00a0c9b4d50c"));
|
||||
AddMapping (DocumentLanguage.Cil, new Guid ("af046cd3-d0e1-11d2-977c-00a0c9b4d50c"));
|
||||
AddMapping (DocumentLanguage.JScript, new Guid ("3a12d0b6-c26c-11d0-b442-00a0244a1dd2"));
|
||||
AddMapping (DocumentLanguage.Smc, new Guid ("0d9b9f7b-6611-11d3-bd2a-0000f80849bd"));
|
||||
AddMapping (DocumentLanguage.MCpp, new Guid ("4b35fde8-07c6-11d3-9053-00c04fa302a1"));
|
||||
AddMapping (DocumentLanguage.FSharp, new Guid ("ab4f38c9-b6e6-43ba-be3b-58080b2ccce3"));
|
||||
}
|
||||
|
||||
static void AddMapping (DocumentLanguage language, Guid guid)
|
||||
{
|
||||
guid_language.Add (guid, language);
|
||||
language_guid.Add (language, guid);
|
||||
}
|
||||
|
||||
static readonly Guid type_text = new Guid ("5a869d0b-6611-11d3-bd2a-0000f80849bd");
|
||||
|
||||
public static DocumentType ToType (this Guid guid)
|
||||
{
|
||||
if (guid == type_text)
|
||||
return DocumentType.Text;
|
||||
|
||||
return DocumentType.Other;
|
||||
}
|
||||
|
||||
public static Guid ToGuid (this DocumentType type)
|
||||
{
|
||||
if (type == DocumentType.Text)
|
||||
return type_text;
|
||||
|
||||
return new Guid ();
|
||||
}
|
||||
|
||||
static readonly Guid hash_md5 = new Guid ("406ea660-64cf-4c82-b6f0-42d48172a799");
|
||||
static readonly Guid hash_sha1 = new Guid ("ff1816ec-aa5e-4d10-87f7-6f4963833460");
|
||||
static readonly Guid hash_sha256 = new Guid ("8829d00f-11b8-4213-878b-770e8597ac16");
|
||||
|
||||
public static DocumentHashAlgorithm ToHashAlgorithm (this Guid guid)
|
||||
{
|
||||
if (guid == hash_md5)
|
||||
return DocumentHashAlgorithm.MD5;
|
||||
|
||||
if (guid == hash_sha1)
|
||||
return DocumentHashAlgorithm.SHA1;
|
||||
|
||||
if (guid == hash_sha256)
|
||||
return DocumentHashAlgorithm.SHA256;
|
||||
|
||||
return DocumentHashAlgorithm.None;
|
||||
}
|
||||
|
||||
public static Guid ToGuid (this DocumentHashAlgorithm hash_algo)
|
||||
{
|
||||
if (hash_algo == DocumentHashAlgorithm.MD5)
|
||||
return hash_md5;
|
||||
|
||||
if (hash_algo == DocumentHashAlgorithm.SHA1)
|
||||
return hash_sha1;
|
||||
|
||||
if (hash_algo == DocumentHashAlgorithm.SHA256)
|
||||
return hash_sha256;
|
||||
|
||||
return new Guid ();
|
||||
}
|
||||
|
||||
public static DocumentLanguage ToLanguage (this Guid guid)
|
||||
{
|
||||
DocumentLanguage language;
|
||||
if (!guid_language.TryGetValue (guid, out language))
|
||||
return DocumentLanguage.Other;
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
public static Guid ToGuid (this DocumentLanguage language)
|
||||
{
|
||||
Guid guid;
|
||||
if (!language_guid.TryGetValue (language, out guid))
|
||||
return new Guid ();
|
||||
|
||||
return guid;
|
||||
}
|
||||
|
||||
static readonly Guid vendor_ms = new Guid ("994b45c4-e6e9-11d2-903f-00c04fa302a1");
|
||||
|
||||
public static DocumentLanguageVendor ToVendor (this Guid guid)
|
||||
{
|
||||
if (guid == vendor_ms)
|
||||
return DocumentLanguageVendor.Microsoft;
|
||||
|
||||
return DocumentLanguageVendor.Other;
|
||||
}
|
||||
|
||||
public static Guid ToGuid (this DocumentLanguageVendor vendor)
|
||||
{
|
||||
if (vendor == DocumentLanguageVendor.Microsoft)
|
||||
return vendor_ms;
|
||||
|
||||
return new Guid ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public sealed class SequencePoint {
|
||||
|
||||
internal InstructionOffset offset;
|
||||
Document document;
|
||||
|
||||
int start_line;
|
||||
int start_column;
|
||||
int end_line;
|
||||
int end_column;
|
||||
|
||||
public int Offset {
|
||||
get { return offset.Offset; }
|
||||
}
|
||||
|
||||
public int StartLine {
|
||||
get { return start_line; }
|
||||
set { start_line = value; }
|
||||
}
|
||||
|
||||
public int StartColumn {
|
||||
get { return start_column; }
|
||||
set { start_column = value; }
|
||||
}
|
||||
|
||||
public int EndLine {
|
||||
get { return end_line; }
|
||||
set { end_line = value; }
|
||||
}
|
||||
|
||||
public int EndColumn {
|
||||
get { return end_column; }
|
||||
set { end_column = value; }
|
||||
}
|
||||
|
||||
public bool IsHidden {
|
||||
get { return start_line == 0xfeefee && start_line == end_line; }
|
||||
}
|
||||
|
||||
public Document Document {
|
||||
get { return document; }
|
||||
set { document = value; }
|
||||
}
|
||||
|
||||
internal SequencePoint (int offset, Document document)
|
||||
{
|
||||
if (document == null)
|
||||
throw new ArgumentNullException ("document");
|
||||
|
||||
this.offset = new InstructionOffset (offset);
|
||||
this.document = document;
|
||||
}
|
||||
|
||||
public SequencePoint (Instruction instruction, Document document)
|
||||
{
|
||||
if (document == null)
|
||||
throw new ArgumentNullException ("document");
|
||||
|
||||
this.offset = new InstructionOffset (instruction);
|
||||
this.document = document;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public sealed class VariableDefinition : VariableReference {
|
||||
|
||||
public bool IsPinned {
|
||||
get { return variable_type.IsPinned; }
|
||||
}
|
||||
|
||||
public VariableDefinition (TypeReference variableType)
|
||||
: base (variableType)
|
||||
{
|
||||
}
|
||||
|
||||
public override VariableDefinition Resolve ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
namespace Mono.Cecil.Cil {
|
||||
|
||||
public abstract class VariableReference {
|
||||
|
||||
internal int index = -1;
|
||||
protected TypeReference variable_type;
|
||||
|
||||
public TypeReference VariableType {
|
||||
get { return variable_type; }
|
||||
set { variable_type = value; }
|
||||
}
|
||||
|
||||
public int Index {
|
||||
get { return index; }
|
||||
}
|
||||
|
||||
internal VariableReference (TypeReference variable_type)
|
||||
{
|
||||
this.variable_type = variable_type;
|
||||
}
|
||||
|
||||
public abstract VariableDefinition Resolve ();
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
if (index >= 0)
|
||||
return "V_" + index;
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
static class Consts
|
||||
{
|
||||
public const string AssemblyName = "Mono.Cecil";
|
||||
public const string PublicKey = "00240000048000009400000006020000002400005253413100040000010001002b5c9f7f04346c324a3176f8d3ee823bbf2d60efdbc35f86fd9e65ea3e6cd11bcdcba3a353e55133c8ac5c4caaba581b2c6dfff2cc2d0edc43959ddb86b973300a479a82419ef489c3225f1fe429a708507bd515835160e10bc743d20ca33ab9570cfd68d479fcf0bc797a763bec5d1000f0159ef619e709d915975e87beebaf";
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Mono.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public struct CustomAttributeArgument {
|
||||
|
||||
readonly TypeReference type;
|
||||
readonly object value;
|
||||
|
||||
public TypeReference Type {
|
||||
get { return type; }
|
||||
}
|
||||
|
||||
public object Value {
|
||||
get { return value; }
|
||||
}
|
||||
|
||||
public CustomAttributeArgument (TypeReference type, object value)
|
||||
{
|
||||
Mixin.CheckType (type);
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct CustomAttributeNamedArgument {
|
||||
|
||||
readonly string name;
|
||||
readonly CustomAttributeArgument argument;
|
||||
|
||||
public string Name {
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public CustomAttributeArgument Argument {
|
||||
get { return argument; }
|
||||
}
|
||||
|
||||
public CustomAttributeNamedArgument (string name, CustomAttributeArgument argument)
|
||||
{
|
||||
Mixin.CheckName (name);
|
||||
this.name = name;
|
||||
this.argument = argument;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ICustomAttribute {
|
||||
|
||||
TypeReference AttributeType { get; }
|
||||
|
||||
bool HasFields { get; }
|
||||
bool HasProperties { get; }
|
||||
bool HasConstructorArguments { get; }
|
||||
Collection<CustomAttributeNamedArgument> Fields { get; }
|
||||
Collection<CustomAttributeNamedArgument> Properties { get; }
|
||||
Collection<CustomAttributeArgument> ConstructorArguments { get; }
|
||||
}
|
||||
|
||||
[DebuggerDisplay ("{AttributeType}")]
|
||||
public sealed class CustomAttribute : ICustomAttribute {
|
||||
|
||||
internal CustomAttributeValueProjection projection;
|
||||
readonly internal uint signature;
|
||||
internal bool resolved;
|
||||
MethodReference constructor;
|
||||
byte [] blob;
|
||||
internal Collection<CustomAttributeArgument> arguments;
|
||||
internal Collection<CustomAttributeNamedArgument> fields;
|
||||
internal Collection<CustomAttributeNamedArgument> properties;
|
||||
|
||||
public MethodReference Constructor {
|
||||
get { return constructor; }
|
||||
set { constructor = value; }
|
||||
}
|
||||
|
||||
public TypeReference AttributeType {
|
||||
get { return constructor.DeclaringType; }
|
||||
}
|
||||
|
||||
public bool IsResolved {
|
||||
get { return resolved; }
|
||||
}
|
||||
|
||||
public bool HasConstructorArguments {
|
||||
get {
|
||||
Resolve ();
|
||||
|
||||
return !arguments.IsNullOrEmpty ();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<CustomAttributeArgument> ConstructorArguments {
|
||||
get {
|
||||
Resolve ();
|
||||
|
||||
return arguments ?? (arguments = new Collection<CustomAttributeArgument> ());
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasFields {
|
||||
get {
|
||||
Resolve ();
|
||||
|
||||
return !fields.IsNullOrEmpty ();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<CustomAttributeNamedArgument> Fields {
|
||||
get {
|
||||
Resolve ();
|
||||
|
||||
return fields ?? (fields = new Collection<CustomAttributeNamedArgument> ());
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasProperties {
|
||||
get {
|
||||
Resolve ();
|
||||
|
||||
return !properties.IsNullOrEmpty ();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<CustomAttributeNamedArgument> Properties {
|
||||
get {
|
||||
Resolve ();
|
||||
|
||||
return properties ?? (properties = new Collection<CustomAttributeNamedArgument> ());
|
||||
}
|
||||
}
|
||||
|
||||
internal bool HasImage {
|
||||
get { return constructor != null && constructor.HasImage; }
|
||||
}
|
||||
|
||||
internal ModuleDefinition Module {
|
||||
get { return constructor.Module; }
|
||||
}
|
||||
|
||||
internal CustomAttribute (uint signature, MethodReference constructor)
|
||||
{
|
||||
this.signature = signature;
|
||||
this.constructor = constructor;
|
||||
this.resolved = false;
|
||||
}
|
||||
|
||||
public CustomAttribute (MethodReference constructor)
|
||||
{
|
||||
this.constructor = constructor;
|
||||
this.resolved = true;
|
||||
}
|
||||
|
||||
public CustomAttribute (MethodReference constructor, byte [] blob)
|
||||
{
|
||||
this.constructor = constructor;
|
||||
this.resolved = false;
|
||||
this.blob = blob;
|
||||
}
|
||||
|
||||
public byte [] GetBlob ()
|
||||
{
|
||||
if (blob != null)
|
||||
return blob;
|
||||
|
||||
if (!HasImage)
|
||||
throw new NotSupportedException ();
|
||||
|
||||
return Module.Read (ref blob, this, (attribute, reader) => reader.ReadCustomAttributeBlob (attribute.signature));
|
||||
}
|
||||
|
||||
void Resolve ()
|
||||
{
|
||||
if (resolved || !HasImage)
|
||||
return;
|
||||
|
||||
Module.Read (this, (attribute, reader) => {
|
||||
try {
|
||||
reader.ReadCustomAttributeSignature (attribute);
|
||||
resolved = true;
|
||||
} catch (ResolutionException) {
|
||||
if (arguments != null)
|
||||
arguments.Clear ();
|
||||
if (fields != null)
|
||||
fields.Clear ();
|
||||
if (properties != null)
|
||||
properties.Clear ();
|
||||
|
||||
resolved = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public class DefaultAssemblyResolver : BaseAssemblyResolver {
|
||||
|
||||
readonly IDictionary<string, AssemblyDefinition> cache;
|
||||
|
||||
public DefaultAssemblyResolver ()
|
||||
{
|
||||
cache = new Dictionary<string, AssemblyDefinition> (StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public override AssemblyDefinition Resolve (AssemblyNameReference name)
|
||||
{
|
||||
Mixin.CheckName (name);
|
||||
|
||||
AssemblyDefinition assembly;
|
||||
if (cache.TryGetValue (name.FullName, out assembly))
|
||||
return assembly;
|
||||
|
||||
assembly = base.Resolve (name);
|
||||
cache [name.FullName] = assembly;
|
||||
|
||||
return assembly;
|
||||
}
|
||||
|
||||
protected void RegisterAssembly (AssemblyDefinition assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
throw new ArgumentNullException ("assembly");
|
||||
|
||||
var name = assembly.Name.FullName;
|
||||
if (cache.ContainsKey (name))
|
||||
return;
|
||||
|
||||
cache [name] = assembly;
|
||||
}
|
||||
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
foreach (var assembly in cache.Values)
|
||||
assembly.Dispose ();
|
||||
|
||||
cache.Clear ();
|
||||
|
||||
base.Dispose (disposing);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
public sealed class EmbeddedResource : Resource {
|
||||
|
||||
readonly MetadataReader reader;
|
||||
|
||||
uint? offset;
|
||||
byte [] data;
|
||||
Stream stream;
|
||||
|
||||
public override ResourceType ResourceType {
|
||||
get { return ResourceType.Embedded; }
|
||||
}
|
||||
|
||||
public EmbeddedResource (string name, ManifestResourceAttributes attributes, byte [] data) :
|
||||
base (name, attributes)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public EmbeddedResource (string name, ManifestResourceAttributes attributes, Stream stream) :
|
||||
base (name, attributes)
|
||||
{
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal EmbeddedResource (string name, ManifestResourceAttributes attributes, uint offset, MetadataReader reader)
|
||||
: base (name, attributes)
|
||||
{
|
||||
this.offset = offset;
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public Stream GetResourceStream ()
|
||||
{
|
||||
if (stream != null)
|
||||
return stream;
|
||||
|
||||
if (data != null)
|
||||
return new MemoryStream (data);
|
||||
|
||||
if (offset.HasValue)
|
||||
return new MemoryStream (reader.GetManagedResource (offset.Value));
|
||||
|
||||
throw new InvalidOperationException ();
|
||||
}
|
||||
|
||||
public byte [] GetResourceData ()
|
||||
{
|
||||
if (stream != null)
|
||||
return ReadStream (stream);
|
||||
|
||||
if (data != null)
|
||||
return data;
|
||||
|
||||
if (offset.HasValue)
|
||||
return reader.GetManagedResource (offset.Value);
|
||||
|
||||
throw new InvalidOperationException ();
|
||||
}
|
||||
|
||||
static byte [] ReadStream (Stream stream)
|
||||
{
|
||||
int read;
|
||||
|
||||
if (stream.CanSeek) {
|
||||
var length = (int) stream.Length;
|
||||
var data = new byte [length];
|
||||
int offset = 0;
|
||||
|
||||
while ((read = stream.Read (data, offset, length - offset)) > 0)
|
||||
offset += read;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
var buffer = new byte [1024 * 8];
|
||||
var memory = new MemoryStream ();
|
||||
while ((read = stream.Read (buffer, 0, buffer.Length)) > 0)
|
||||
memory.Write (buffer, 0, read);
|
||||
|
||||
return memory.ToArray ();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Author:
|
||||
// Jb Evain (jbevain@gmail.com)
|
||||
//
|
||||
// Copyright (c) 2008 - 2015 Jb Evain
|
||||
// Copyright (c) 2008 - 2011 Novell, Inc.
|
||||
//
|
||||
// Licensed under the MIT/X11 license.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mono.Cecil {
|
||||
|
||||
[Flags]
|
||||
public enum EventAttributes : ushort {
|
||||
None = 0x0000,
|
||||
SpecialName = 0x0200, // Event is special
|
||||
RTSpecialName = 0x0400 // CLI provides 'special' behavior, depending upon the name of the event
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue