{"id":19080431,"url":"https://github.com/seeminglyscience/ilassembler","last_synced_at":"2025-08-02T09:05:36.059Z","repository":{"id":87373269,"uuid":"286454093","full_name":"SeeminglyScience/ILAssembler","owner":"SeeminglyScience","description":"ILAsm-like DSL for PowerShell","archived":false,"fork":false,"pushed_at":"2020-10-18T20:49:41.000Z","size":227,"stargazers_count":16,"open_issues_count":15,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-30T13:22:30.349Z","etag":null,"topics":["cil","hacktoberfest","il","ilasm","msil","powershell"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SeeminglyScience.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-10T11:17:10.000Z","updated_at":"2024-05-26T10:07:11.000Z","dependencies_parsed_at":"2023-03-17T05:45:48.571Z","dependency_job_id":null,"html_url":"https://github.com/SeeminglyScience/ILAssembler","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeeminglyScience%2FILAssembler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeeminglyScience%2FILAssembler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeeminglyScience%2FILAssembler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SeeminglyScience%2FILAssembler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SeeminglyScience","download_url":"https://codeload.github.com/SeeminglyScience/ILAssembler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251651231,"owners_count":21621716,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cil","hacktoberfest","il","ilasm","msil","powershell"],"created_at":"2024-11-09T02:23:39.102Z","updated_at":"2025-04-30T06:11:02.824Z","avatar_url":"https://github.com/SeeminglyScience.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eILAssembler\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003csub\u003e\n        Use Common Intermediate Language (CIL/MSIL) in PowerShell to build invokable delegates using a domain specific\n        language similar to ILAsm.\n    \u003c/sub\u003e\n    \u003cbr /\u003e\u003cbr /\u003e\n    \u003ca title=\"Commits\" href=\"https://github.com/SeeminglyScience/ILAssembler/commits/master\"\u003e\n        \u003cimg alt=\"Build Status\" src=\"https://github.com/SeeminglyScience/ILAssembler/workflows/build/badge.svg\" /\u003e\n    \u003c/a\u003e\n    \u003ca title=\"ILAssembler on PowerShell Gallery\" href=\"https://www.powershellgallery.com/packages/ILAssembler\"\u003e\n        \u003cimg alt=\"PowerShell Gallery Version (including pre-releases)\" src=\"https://img.shields.io/powershellgallery/v/ILAssembler?include_prereleases\u0026label=gallery\"\u003e\n    \u003c/a\u003e\n    \u003ca title=\"LICENSE.txt\" href=\"https://github.com/SeeminglyScience/ILAssembler/blob/master/LICENSE.txt\"\u003e\n         \u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/SeeminglyScience/ILAssembler\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n## Features\n\n- All opcodes are supported\n- Help documentation for each instruction (including on hover in editors)\n- Exception regions (`try`/`catch`/`finally`)\n- `.locals` declaration including pinned types and `init` switch\n\n## Getting Started\n\n### Install\n\n```powershell\n# AllowClobber probably not necessary.\nInstall-Module ILAssembler -Scope CurrentUser -Force -AllowClobber\n```\n\n### Use\n\n```powershell\n$delegate = il { [int]([uint32], [uint32]) } {\n    ldarg.0\n    ldarg.1\n    add\n    ret\n}\n\n$delegate.Invoke\n# OverloadDefinitions\n# -------------------\n# int Invoke(uint32 arg1, uint32 arg2)\n\n$delegate.Invoke(10, 10)\n# 20\n```\n\n## Help\n\n- [Help Documentation](https://github.com/SeeminglyScience/ILAssembler/tree/master/docs/en-US)\n  - [All OpCodes](https://github.com/SeeminglyScience/ILAssembler/tree/master/docs/en-US)\n  - [`New-ILDelegate` (alias `il`)](https://github.com/SeeminglyScience/ILAssembler/blob/master/docs/en-US/New-IlDelegate.md)\n  - [about_BadImageFormat](https://github.com/SeeminglyScience/ILAssembler/blob/master/docs/en-US/about_BadImageFormat.md)\n- [Syntax](#syntax)\n  - [`il` blocks](#il-blocks)\n  - [Signatures](#signatures)\n    - [Anonymous Method Signature](#anonymous-method-signature)\n    - [Resolvable Method Signature](#resolvable-method-signature)\n    - [Resolvable Type Signature](#resolvable-type-signature)\n    - [Resolvable Field Signature](#resolvable-field-signature)\n  - [Meta Instructions](#meta-instructions)\n    - [`.maxstack`](#maxstack)\n    - [`.locals`](#locals)\n  - [Branch Labels](#branch-labels)\n    - [Switch Instruction](#switch-instruction)\n  - [Exception Handling](#exception-handling)\n    - [`.try`](#try)\n    - [`catch`](#catch)\n    - [`filter`](#filter)\n    - [`finally`](#finally)\n    - [`fault`](#fault)\n\n## Syntax\n\n### `il` blocks\n\n```powershell\n$delegate = il { [int]([object[]], [int]) } {\n    ldarg.0\n    ldlen\n    ldarg.1\n    add\n    ret\n}\n\n$delegate.GetType().ToString()\n# returns: System.Func`3[System.Object[],System.Int32,System.Int32]\n\n$delegate.Invoke(0..10, 1)\n# returns: 12\n```\n\nCreates a delegate that takes `object[]` and `int` as parameters and returns `int`. The first `ScriptBlock` argument\ndeclares the delegate's signature.\n\n### Signatures\n\nVarious opcodes take a signature that is used to resolve a reference.\n\n#### Anonymous Method Signature\n\nThe same signature used in the first argument of an `il` block, but also for `calli` instructions.\nThink of it like a method invocation (e.g. `[Console]::WriteLine([string])`) but without the subject\nor member name (e.g. `[Console]::WriteLine`), plus the return type.\n\n```powershell\n# Takes no parameters and has a void return.\n{ [void]@() }\n\n# Takes string and double as parameters, returns int\n{ [int]([string], [double]) }\n\n# Takes string and int\u0026 as parameters, returns bool\n{ [bool]([string], [ref] [int]) }\n\n# Same as above, subject, member name, and parameter names are ignored\n{ [bool] $_::TryParse([string] $s, [ref] [int] $result) }\n\n# Takes IntPtr, IntPtr, returns int\n$callMethodPointer = il { [int]([IntPtr] $iunknownPointer, [IntPtr] $methodPointer) } {\n    ldarg.0\n    ldarg.1\n\n    # Takes void*, returns int\n    calli unmanaged stdcall { [int]([void+] $this) }\n    ret\n}\n```\n\n#### Resolvable Method Signature\n\nThese signatures *must* resolve to an existing method defined by a type loaded in the AppDomain at assemble time.\n\n```powershell\n# Resolves to the static method Console.WriteLine(string)\ncall { [void] [Console]::WriteLine([string]) }\n\n# Resolves to Unsafe.AsRef\u003cint\u003e(void*)\n# (use [g[arg1, arg2]] as the first argument to specify generic type arguments)\ncall { [ref] [int] [System.Runtime.CompilerServices.Unsafe]::AsRef([g[int]], [void+]) }\n\n# Resolves to the instance property String.Length\ncallvirt { [int] [string].get_Length() }\n```\n\n#### Resolvable Type Signature\n\nThis is a normal type expression wrapped in a `ScriptBlock` with some extra shortcuts for resolving pointers and byref types.\n\n```powershell\n# System.String\nnewarr { [string] }\n\n# System.Int64**\nldobj { [long++] }\n\n# System.Int32\u0026\nstobj { [ref] [int] }\n```\n\n#### Resolvable Field Signature\n\nMust resolve to an existing field defined by a type loaded in the AppDomain at assemble time.\n\n```powershell\nldsfld { [string] [string]::Empty }\n\nldfld { [int] [ValueTuple[int]].Item1 }\n```\n\n### Meta Instructions\n\n#### `.maxstack`\n\n```powershell\n.maxstack 8\n```\nEquivalent of `.maxstack` in ILAsm. Declares the maximum amount of items that will be on the\nevaluation stack. Defaults to `8` if not specified.\n\n**NOTE:** Must be before any opcodes.\n\n#### `.locals`\n\n```powershell\n.locals { }\n.locals init { }\n```\n\nDeclares local variables, equivalent of `.locals` in ILAsm. Specifying `init` will initialize locals\nto zero on the stack at the start of the method (this is the default behavior in C#).\n\n**NOTE:** Must be before any opcodes.\n\n```powershell\nusing namespace System.Runtime.CompilerServices\n\n$copyArray = il { [void]([int[]] $source, [int[]] $destination) } {\n    .locals init {\n        [int+] $pSource,\n        [int+] $pDest,\n        [pinned] [int[]] $pinnedSource,\n        [pinned] [int[]] $pinnedDest\n    }\n\n    ldarg.0\n    stloc.auto $pinnedSource\n\n    ldarg.1\n    stloc.auto $pinnedDest\n\n    ldloc.auto $pinnedSource\n    ldc.i4.0\n    ldelema { [int] }\n    conv.i\n    stloc.auto $pSource\n\n    ldloc.auto $pinnedDest\n    ldc.i4.0\n    ldelema { [int] }\n    conv.i\n    stloc.auto $pDest\n\n    ldloc.auto $pDest\n    ldloc.auto $pSource\n    ldloc.auto $pinnedSource\n    ldlen\n    sizeof { [int] }\n    mul\n    conv.u4\n    call { [void] [Unsafe]::CopyBlock([void+], [void+], [uint]) }\n    ret\n}\n```\n\n### Branch Labels\n\nLabels work the same as in ILAsm. They can prefix an instruction or be placed on their own line. When\nmarking a label, it must be suffixed with a colon (\u003ckbd\u003e:\u003c/kbd\u003e) character.\n\n```powershell\n            idc.4.0\n            brtrue.s was_true\n            br.s invalid\nwas_true:   ldc.4.s 10\n            ret\n\n            invalid:\n            newobj { [void] [Exception].new() }\n            throw\n```\n\n#### Switch instruction\n\nSince the `switch` opcode doesn't actually fit the syntax of a PowerShell `switch` statement, the syntax\ncannot be used. Since it's a infrequently used opcode, you'll need to force it to be parsed as a command\nwith an invocation operator.\n\n```powershell\nil { [void]([int] $input) } {\n    ldarg.0\n    switch was_0, was_1, was_2, was_3\n    ldstr 'input'\n    newobj { [void] [ArgumentOutOfRangeException].new([string]) }\n    throw\n\n    was_0:      ldstr 'input was 0'; br.s writeline\n    was_1:      ldstr 'input was 1'; br.s writeline\n    was_2:      ldstr 'input was 2'; br.s writeline\n    was_3:      ldstr 'input was 3'\n    write_line: call { [void] [Console]::WriteLine([string]) }\n                ret\n}\n```\n\n### Exception Handling\n\nAll of the exception handling regions are supported (`try`, `catch`, `filter`, `fault`, and `finally`).\n\n#### `.try`\n\n[Protected blocks][protected] (aka `.try` blocks) work as they do in ILAsm. When encoding the exception regions,\nthe first enclosed instruction will be the start offset, directly after the last will be the end offset.\n\nProtected blocks must only have *one* type of exception handler attached, though they can be nested.\n\n#### `catch`\n\n[Catch blocks][catch] must have a catch type declared unless preceeded by a `filter` block. For a \"catch all\", type\nthe catch as `object`.\n\n```powershell\n.try {\n    newobj { [void] [InvalidOperationException].new() }\n    throw\n}\ncatch { [InvalidOperationException] } {\n    pop\n    leave.s afterTry\n}\n# catch all\ncatch { [object] } {\n    rethrow\n}\n\nafterTry: ret\n```\n\n#### `filter`\n\n**NOTE:** Because `filter` is an existing PowerShell keyword with conflicting syntax, it needs to be preceeded by an invocation operator such as \u003ckbd\u003e\u0026\u003c/kbd\u003e or \u003ckbd\u003e.\u003c/kbd\u003e.\n\nA [filter block][filter] determines whether the following catch block should handle the exception.  When leaving\nthe filter, a value is popped from the stack to determine if the catch should be executed.\n\n1. [`exception_continue_search` (`0x0`)][endfilter] indicates the exception did not match\n1. [`exception_execute_handler` (`0x1`)][endfilter] indicates that execution should be transfered to the following `catch` block\n\nBelow is an example of a filter in C# with it's rough translation into CIL.\n\n```csharp\ntry\n{\n    throw new Exception() { HResult = 0x10 };\n}\ncatch (Exception e) when (e.HResult is 0x10)\n{\n    // Do nothing.\n}\ncatch\n{\n    throw;\n}\n```\n\n```powershell\n.try {\n    newobj { [void] [Exception].new() }\n    dup\n    ldc.i4.s 0x10\n    callvirt { [void] [Exception].set_HResult([int]) }\n    throw\n}\n. filter {\n    callvirt { [int] [Exception].get_HResult() }\n    ldc.i4.s 0x10\n    ceq\n    endfilter\n}\ncatch {\n    pop\n    leave.s exitSEH\n}\ncatch { [object] } {\n    rethrow\n}\n\nexitSEH: ret\n```\n\n#### `finally`\n\nThe [finally block][finally] will always run when the `try` completes, even if an exception is thrown.\n\n```powershell\n.try {\n    .try {\n        # The finally will run even if you change this to ArgumentNullException.\n        newobj { [void] [InvalidOperationException].new() }\n        throw\n    }\n    catch { [ArgumentNullException] } {\n        pop\n        leave.s exitSEH\n    }\n}\nfinally {\n    ldstr 'This always runs'\n    call { [void] [Console]::WriteLine([string]) }\n    endfinally\n}\n\nexitSEH: ret\n```\n\n#### `fault`\n\nThe [fault block][fault] acts just like `finally` except it *does not* get executed when the try block completes normally. It is only executed when there was an uncaught exception. In this way it's like a \"catch all\" style `catch` block but it **does not** handle the exception.\n\n```powershell\n.try {\n    .try {\n        # The finally will run even if you change this to ArgumentNullException.\n        newobj { [void] [InvalidOperationException].new() }\n        throw\n    }\n    catch { [ArgumentNullException] } {\n        pop\n        leave.s exitSEH\n    }\n}\nfault {\n    ldstr 'This only runs if the exception is *not* caught above.'\n    call { [void] [Console]::WriteLine([string]) }\n    endfault\n}\n\nexitSEH: ret\n```\n\n[protected]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=227\u0026zoom=100,116,722 \"EMCA §II.19.1\"\n[catch]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=228\u0026zoom=100,116,210 \"EMCA §II.19.3\"\n[filter]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=228\u0026zoom=100,116,509 \"EMCA §II.19.4\"\n[finally]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=229\u0026zoom=100,116,120 \"EMCA §II.19.5\"\n[fault]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=229\u0026zoom=100,116,429 \"EMCA §II.19.6\"\n[endfilter]: https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=385\u0026zoom=100,116,104 \"EMCA §III.3.34\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseeminglyscience%2Filassembler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseeminglyscience%2Filassembler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseeminglyscience%2Filassembler/lists"}