owned this note
owned this note
Published
Linked with GitHub
---
lintConfig:
MD013: false
MD025: false
---
# DASM Spec
> [TOC]
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [IETF RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). Except "SHOULD", it shouldn't exist :>
Authors:
[NotStirred](https://github.com/NotStirred/)
[Flames](https://github.com/CursedFlames/)
## Required tests to meet spec: [Dasm Tests](/iDj2EjQCQfWszbdf7DA5qw)
# Definitions
## General Definitions
- Simple Class Name: `Object`
- Class Name (or fully qualified class name): `java.lang.Object`, `java.lang.String[]`
- Internal Name: `java/lang/Object`, `I`, `Z` WARNING: asm's `getInternalName()` returns **descriptors** if given an array type
- Descriptor
- Type: `Ljava/lang/Object;`, `Z`, `I`, `[I`, `[[I`, `[Ljava/lang/String;`
- Method: `(II)Ljava/lang/Object;`, `(Ljava/lang/String;)V`
## DASM Definitions
- Redirect: A redirect specifies a substitution from one field/method/type to another when applying a transform.
- Redirect set: A set of redirects that are used when applying a transform.
- Transform: A transform copies a method/type and substitutes all fields/methods/types specified in the redirect sets used for that transform.
## Example Stages for Mixin
- annotation processing
- member validation
- pre apply
- member + bytecode validation
- pre apply transforms
- member + bytecode validation
- post apply
- member + bytecode validation
- post apply transforms
- full validation
# Internal Spec
## Stages
### Annotation Processing Stage
Collect all redirects and transforms from relevant classes
### Validation Stage
Validate that no [invariants](##Validation) are broken
### Transformation Stage pre/post
Apply transforms with redirects
## Redirects
(Impl detail: Applied in listed order)
### FieldToMethod: ChunkPos.x → CloPos.getX()
- Src owner
- Src type
- Field name
- Dst owner
- Getter name (must be zero args)
- Setter name (must be one arg) (must exist if and only if the field is non-final)
:::warning
The field to method redirect **must** set the correct INVOKE instruction for the destination type; interfaces should be INVOKEINTERFACE and classes INVOKEVIRTUAL
:::
### ConstructorToFactory: ChunkAccess.\<init\> → CloAccess.construct
- Src owner
- Constructor method signature (no name, it's `<init>` :))
- Dst owner
- New method name
### Field: ChunkAccess.chunkPos → CubeAccess.cloPos
- Src owner
- Src type
- Field name
- Dst owner
- New field name
### Method
- Src owner
- Method name+signature
- Dst owner
- New method name
### Type: ChunkAccess → CloAccess
- Src type
- Dst type
:::warning
The type redirect **must** set the correct INVOKE instruction for the destination type; interfaces should be INVOKEINTERFACE and classes INVOKEVIRTUAL
:::
### No Redirect Chaining
Redirects for a given transform **must** be applied in a single pass. For example:
- Given type redirects A→B and B→A it results in swapping the types.
- Given type redirects A→B and B→C it results in replacing B with C, and A with B (**not** replacing A with C)
## Transform
- Field ? should we have this?
- Method
- MethodFromOther
- Class
### Transform Chaining
If the destination of a transform is itself used as the source for a second transform, the second destination **must** be the result of first applying the first set of redirects to the original source, followed by the second set of redirects. This **must** hold for any combination of arbitrarily many method and class transforms.
:::warning
["No Redirect Chaining"](###No-Redirect-Chaining) only applies for a single transform. For example: if one transform redirects types A→B and B→A, and a chained transform applies the same, the resulting method **must** be identical to the pre-transformed original method.
:::
## Validation
Validation **shall** be applied in all cases.
In development environments, validation failures **shall** cause an immediate error. In production, validation failures **must** either cause an error or log the failure; by default, production environments **shall** log rather than erroring, this **may** be configured (e.g. with a flag).
### Validation pass types
- Member validation - apply
- Bytecode validation
- Full validation - perform both member and bytecode validation, and also validate that ALL fields/methods/types referenced anywhere actually exist.
### Disallowed errors
The following errors **must not** occur at runtime when validation is enabled; they **shall** be detected and reported during validation, using the invariants specified in this section.
- VerifyError
- UnsatisfiedLinkError
- NoSuchFieldError
- NoSuchMethodError
- AbstractMethodError
- IncompatibleClassChangeError
- ClassCircularityError
### Member Validation
#### Invariant 1 (destination/redirected source type parity)
All field/method/fieldToMethod/constructorToFactory redirects **must** have corresponding type redirects such that the expected types/signatures specified in code match with those produced by redirecting the source field/method. Not doing so **shall** cause an error in [Validation](###Validation-Stage).
:::warning
**This includes the `this` parameter** for non-static methods and field accesses.
:::
For example, a field redirect from `ChunkAccess.chunkPos` to `CubeAccess.cloPos` **must** have a corresponding type redirect from `ChunkPos` to `CloPos`.
#### Invariant 2 (type redirect field and method completeness)
The destination type of a type redirect **must** have corresponding fields and methods for all fields and methods on the source type, **including supertypes**, and **including constructors of the source type** (but not constructors of supertypes), such that it is not possible for transformed code to attempt to access nonexistent fields/methods after all redirects are applied. Not doing so **shall** cause an error in [Validation](###Validation-Stage).
(TODO: this should be disableable for constructors - possibly for other cases? worth considering. Invariant 6 will still apply regardless.)
:::info
Note that this does not impose any additional limitations on the contents of the destination type; there may be extra fields and methods.
:::
For example, for a type redirect from `ChunkAccess` to `CubeAccess`, fields/methods retaining the same name do not require an explicit redirect (eg: `ChunkAccess.unsaved` to `CubeAccess.unsaved`), while fields/methods with differing names do (eg: `ChunkAccess.getPos()` to `CubeAccess.cc_getCloPos()`).
#### Invariant 3 (Subclasses)
If there exists a type redirect for a given source class, subclasses of that class **must** have a type redirect, such that the destination class for the subclass extends the destination class for the original class.
(Note that a class and its subclass **may** be redirected to the same type, i.e. if `B extends A`, `A` and `B` may both be redirected to `C`)
(TODO a way to disable this if wanted? we want this for CC, but in simpler cases we might not.)
#### Invariant 4 (Overridden subclass members)
When a field/method is redirected, this redirect **shall** also be applied for subclasses that do not shadow/override the field/method. (TODO implementation detail; move elsewhere?).
For fields/methods on subclasses that **do** shadow/override those of the superclass, there **must** also exist a redirect for the shadowed/overridden field/method.
Not doing so **shall** cause an error in [Validation](###Validation-Stage). (TODO maybe have a flag to disable this? not sure if we always want this)
:::info
Observe that this requirement is not relevant in cases where there is a type redirect without a corresponding field/method redirect; for example `ChunkAccess.setBlockState` is type-redirected to `CubeAccess.setBlockState` without a method redirect, meaning (TODO wording) no things are required for overriding methods such as `LevelChunk.setBlockState`.
:::
### Bytecode Validation
#### Invariant 5 (constructor to factory TODO name)
If a constructor is redirected to a factory method, all other constructors that call that constructor **must** also have redirects to factory methods.
(Impl detail: this **shall** be checked during mixin init, after preApply, and after postApply)
#### Invariant 6 (Method body type/member redirect parity)
All member accesses within a method body **must** have field/method redirects such that the destination members match the descriptors obtained by applying type redirects to the source members. This should respect field-to-method and constructor-to-factory redirects.
(Impl detail: this **shall** be checked during mixin init, after preApply, and after postApply)
### Full Validation
#### Existence Validation
During a full validation pass, all fields/methods/types referenced in transformed code **must** exist and have access modifiers such that the transformed code is legal (i.e. visibility and field finality).
## Application Stage
# Implementation/API Spec
- ClassProvider
- MappingsProvider
## Annotations
- `@Dasm`
### Redirect sets
- `@RedirectSet`
- `@RedirectContainer`
### Redirects
(TODO naming)
- `@FieldToMethodRedirect`
- `@ConstructorToFactory`
- `@FieldRedirect`
- `@MethodRedirect`
- `@TypeRedirect`
- `@AddFieldToSets`
- `@AddMethodToSets`
Add transform destination to a set:
- `@AddTransformToSets`
### Transforms
- `@TransformFromMethod`
- `@TransformFromClass`
### Other
- `@FieldSig`
- `@MethodSig`
- `@ConstructorMethodSig`
- `@Ref`
# Implementation Oddities