UIX Assembly

Instruction Set

Summary

General

Mneumonic Name
COBJ ConstructObject
COBJI ConstructObjectIndirect
COBP ConstructObjectParam
CSTR ConstructFromString
CBIN ConstructFromBinary
INIT InitializeInstance
INITI InitializeInstanceIndirect
LSYM LookupSymbol
WSYM WriteSymbol
WSYMP WriteSymbolPeek
CSYM ClearSymbol
PINI PropertyInitialize
PINII PropertyInitializeIndirect
PLAD PropertyListAdd
PDAD PropertyDictionaryAdd
PASS PropertyAssign
PASST PropertyAssignStatic
PGET PropertyGet
PGETP PropertyGetPeek
PGETT PropertyGetStatic
MINV MethodInvoke
MINVP MethodInvokePeek
MINVT MethodInvokeStatic
MINVA MethodInvokePushLastParam
MINVAT MethodInvokeStaticPushLastParam
VTC VerifyTypeCast
CON ConvertType
OPR Operation
ISC IsCheck
ASC As
TYP TypeOf
PSHN PushNull
PSHC PushConstant
PSHT PushThis
DIS DiscardValue
RET ReturnValue
RETV ReturnVoid
JMPF JumpIfFalse
JMPFP JumpIfFalsePeek
JMPTP JumpIfTruePeek
JMPD JumpIfDictionaryContains
JMPNP JumpIfNullPeek
JMP Jump
CLIS ConstructListenerStorage
LIS Listen
LISD DestructiveListen
DBG EnterDebugState

Math Operators

Mneumonic Name
ADD MathAdd
SUB MathSubtract
MUL MathMultiply
DIV MathDivide
MOD MathModulus
NEG MathNegate
AND LogicalAnd
ORR LogicalOr
NOT LogicalNot
REQ RelationalEquals
RNE RelationalNotEquals
RLT RelationalLessThan
RGT RelationalGreaterThan
RLE RelationalLessThanEquals
RGE RelationalGreaterThanEquals
RIS RelationalIs
INC PostIncrement
DEC PostDecrement

Object instantiation

This family of instructions calls the constructor for the given object. All variants take the index of the object type to construct.

ConstructObject

Calls the default constructor with no parameters.

COBJ <typeIndex>

ConstructObjectIndirect

Used to create 'wrappers' of another constructed object. It is used for type schemas that implement IDynamicConstructionSchema, which appears to only be used by UIClassTypeSchema for creating the root <UI> element. The assignment type is taken from the first operand, while the target type is popped off the stack.

COBJI <assignmentTypeIndex>

ConstructObjectParam

Calls the specified constructor with N parameters popped from the stack, with the Nth parameter at the top of the stack. The number of parameters must match the constructor exactly.

COBP <typeIndex>, <constructorIndex>

ConstructFromString

Uses the target's type converter to construct an instance from a constant string.

CSTR <typeIndex>, <stringIndex>

ConstructFromBinary

Uses the target type's binary decoder to construct an instance from a binary blob. The blob is stored inline and can be any length. The binary decoder is responsible for determining the length and consuming the entire blob.

InitializeInstance

Pops an object from the stack and initializes it using the specified type schema. This instruction is required to initialize UIClass and Class objects immediately after construction.

INIT <typeIndex>

InitializeInstanceIndirect

Identical to InitializeInstance, except the type schema is popped off the stack rather than indexed from the type imports table.

INITI

Symbols

This family of instructions manages symbols, usually local variables.

LookupSymbol

Looks up a symbol in the symbol reference table and pushes its value to the stack.

LSYM <symbolReferenceIndex>

WriteSymbol

Writes a value popped from the stack to the specified symbol in the symbol reference table.

WSYM <symbolReferenceIndex>

WriteSymbolPeek

Identical to WriteSymbol, except the value to be written is peeked instead of popped off the stack.

WSYMP <symbolReferenceIndex>

ClearSymbol

If the specified symbol is a scoped local, it is undeclared. Otherwise, this instruction is a no-op.

CSYM <symbolReferenceIndex>

Properties

PropertyInitialize

Pops a value from the stack and initializes the specified property on the current instance, also popped from the stack.

PINI <propertyIndex>

PropertyInitializeIndirect

Simimlar to PropertyInitialize, except the parent type schema is popped from the stack.

PINII <propertyIndex>

PropertyListAdd

Pops a value from the stack and appends it to the end of a list. The key is read from the constants table. The target object is peeked from the stack. If propertyIndex is 0xFFFF, the target object is the list itself. Otherwise, the list is read from the specified property on the target object.

PLAD <propertyIndex>
// target.Property.Add(value)

PLAD 0xFFFF
// target.Add(value)

PropertyDictionaryAdd

Pops a value from the stack and assigns it to the given key in a dictionary. The same semantics from PropertyListAdd regarding propretyIndex apply.

PDAD <propertyIndex>, <keyConstantIndex>
// target.Property[value] = value

PDAD 0xFFFF, <keyConstantIndex>
// target[key] = value

PropertyAssign

Assigns a value to the specified property. The target object is popped from the stack before the value is peeked.

PASS <propertyIndex>
; object.Property = value

PropertyAssignStatic

Peeks a value from the stack and assigns it to the specified static property.

PASST <propertyIndex>
; StaticInstance.Property = value

PropertyGet

Pops a parent object from the stack and pushes the value of the specified property.

PGET <propertyIndex>
; object.Property

PropertyGetPeek

Peeks an object from the stack and pushes the value of the specified property.

PGETP <propertyIndex>
; object.Property

PropertyGetStatic

Pushes the value of the specified static property to the stack.

PGETT <propertyIndex>
; StaticInstance.Property

Methods

MethodInvoke

Invokes the specified method with N parameters popped from the stack, with the Nth parameter at the top of the stack. The number of parameters must match the method signature exactly. The parent object is popped from the stack. If the method has a return value, it is pushed to the stack.

MINV <methodIndex>

MethodInvokePeek

Identical to MethodInvoke, except the parent object is peeked instead of popped from the stack.

MINVP <methodIndex>

MethodInvokeStatic

Identical to MethodInvoke, except the method must be static and no parent object is popped from the stack.

MINVT <methodIndex>

MethodInvokePushLastParam

Similar to MethodInvoke, except the last parameter is pushed to the stack. When this mode is used, the method return value is not pushed to the stack.

MINVA <methodIndex>

MethodInvokeStaticPushLastParam

Similar to MethodInvokeStatic, except the last parameter is pushed to the stack. When this mode is used, the method return value is not pushed to the stack. Equivalent to MethodInvokePushLastParam but for static methods.

MINVAT <methodIndex>

Type Manipulation

This family of instructions performs operations that manipulate types.

VerifyTypeCast

Throws a script runtime error if the object at the top of the stack cannot be cast to the specified type.

VTC <propertyIndex>

ConvertType

Pops an object from the stack and converts from one specified type to another. The converted object is pushed to the stack. Note that this is not equivalent to a simple cast: see As.

CON <toTypeIndex>, <fromTypeIndex>

IsCheck

Pops an object from the stack and checks whether it is an instance of the specified type. The check result is pushed as a bool to the stack. Returns false is the object is null.

ISC <typeIndex>

As

If the object at the top of the stack is assignable to the specified type, no action is taken. Otherwise, the object is replaced with null.

ASC <typeIndex>

TypeOf

Pushes the schema of the specified type to the stack.

TYP <typeIndex>

Arithmetic and Logical Operations

Operation

Performs the specified arithmetic or logical operation using the provided operation host type schema. Unary operations will pop a single value off the stack and execute the operation. Binary operations will first pop the right operand, followed by the left. The operation result is pushed to the stack.

The operation host, such as Int32Schema, must support the requested operation. Type schemas can declare support for operations using SupportsOperationHandler and PerformOperationHandler.

This is a real instruction. It is recommended to use the alternative virtual instructions described in Arithmetic and Logical Operations for the sake of clarity.

OPR <operationHostTypeIndex>, <operationId>

MathAdd

Pops two values off the stack and pushes their sum. This is a virtual instruction for Operation.

ADD <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 1

MathSubtract

Pops two values, \(a\) then \(b\), off the stack and pushes their difference, \(b - a\). This is a virtual instruction for Operation.

SUB <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 2

MathMultiply

Pops two values off the stack and pushes their product. This is a virtual instruction for Operation.

MUL <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 3

MathDivide

Pops two values, \(a\) then \(b\), off the stack and pushes their quotient, \(b / a\). This is a virtual instruction for Operation.

DIV <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 4

MathModulus

Pops two values, \(a\) then \(b\), off the stack and pushes their remainder, \(b \bmod a\). This is a virtual instruction for Operation.

MOD <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 5

MathNegate

Pops a value \(a\) off the stack and pushes its negation, \(-a\). This is a virtual instruction for Operation.

NEG <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 16

LogicalAnd

Pops two values, \(a\) then \(b\), off the stack and pushes their logical AND, \(a \land b\). This is a virtual instruction for Operation.

AND <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 6

LogicalOr

Pops two values, \(a\) then \(b\), off the stack and pushes their logical OR, \(b \lor a\). This is a virtual instruction for Operation.

ORR <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 7

LogicalNot

Pops a value \(a\) off the stack and pushes its logical negation, \(\lnot a\). This is a virtual instruction for Operation.

NOT <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 15

RelationalEquals

Pops two values, \(a\) then \(b\), off the stack and pushes whether they are equal, \(b = a\). This is a virtual instruction for Operation.

REQ <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 8

RelationalNotEquals

Pops two values, \(a\) then \(b\), off the stack and pushes whether they are not equal, \(b \neq a\). This is a virtual instruction for Operation.

RNE <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 9

RelationalLessThan

Pops two values, \(a\) then \(b\), off the stack and pushes whether the result of \(b \lt a\). This is a virtual instruction for Operation.

RLT <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 10

RelationalGreaterThan

Pops two values, \(a\) then \(b\), off the stack and pushes whether they are not equal, \(b \gt a\). This is a virtual instruction for Operation.

RGT <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 11

RelationalLessThanEquals

Pops two values, \(a\) then \(b\), off the stack and pushes whether they are less than or equal, \(b \le a\). This is a virtual instruction for Operation.

RLE <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 12

RelationalGreaterThanEquals

Pops two values, \(a\) then \(b\), off the stack and pushes whether they are greater than or equal, \(b \ge a\). This is a virtual instruction for Operation.

RGE <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 13

RelationalIs

Pops two values, a then b, off the stack and pushes whether b is a. This is a virtual instruction for Operation, though it does not appear to be used anywhere.

RIS <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 14

PostIncrement

Pops a value \(a\) off the stack and pushes \(a + 1\). This is a virtual instruction for Operation.

INC <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 17

PostDecrement

Pops a value \(a\) off the stack and pushes \(a - 1\). This is a virtual instruction for Operation.

DEC <operationHostTypeIndex>
OPR <operationHostTypeIndex>, 18

Stack Manipulation

PushNull

Pushes a null value to the stack.

PSHN

PushConstant

Pushes the specified constant to the stack.

PSHC <constantIndex>
PSHC @<constantName>

PushThis

Pushes the current markup instance, an IMarkupTypeBase, to the stack.

PSHT

DiscardValue

Pops an object off the stack and does nothing.

DIS

Control Flow

ReturnValue

Returns the topmost item from the stack.

RET

ReturnVoid

Returns from the script.

RETV

JumpIfFalse

Pops a boolean value off the stack and jumps to the specified offset if false.

JMPF <offset>

JumpIfFalsePeek

Peeks a boolean value from the top of the stack and jumps to the specified offset if false.

JMPFP <offset>

JumpIfTruePeek

Peeks a boolean value from the top of the stack and jumps to the specified offset if true.

JMPTP <offset>

JumpIfDictionaryContains

Jumps to the specified offset if a dictionary contains the specified key. Follows the same collection semantics as PropertyDictionaryAdd.

JMPD <propertyIndex>, <keyConstantIndex>, <offset>

JumpIfNullPeek

Peeks a value off the stack and jumps to the specified offset if null.

JMPNP <offset>

Jump

Unconditionally jumps to the specified offset.

JMP <offset>

Listeners

ConstructListenerStorage

TODO

IStringEncodable

So I added an interface named IStringEncodable that has what's essentially a specialized ToString() method. Each schema has a matching runtime type, and if the schema supports converting from a string, I modify the runtime class to implement IStringEncodable and returns a valid string that UIX already knows how to parse.

It's a pretty manual process, so I've been implementing that interface as I observe incorrect constants in the disassembly. Turns out the Image schema supports string encoding, but UIImage (the corresponding runtime class) didn't implement IStringEncodable so it has the wrong representation in the disassembly.