The last of the trio, Instruction, is another huge class with 44 methods in total + 5 from its child classes (there will be more!). This here is where you’ll spend most of your time when you write complex glides, trying to generalize the way your code processes different combinations of Solidity instructions expressed through the Glider’s objects.

Children

Instruction comes in many forms, like:

  • AssemblyInstruction
  • IfInstruction
  • NewVariableInstruction
  • ReturnInstruction
  • ThrowInstruction

Each child class represents a type of Solidity instruction. For example, if you filter if condition statements in the declarative query, the resulting instructions will all be of the IfInstruction class:

from glider import *


def query():
    instructions = (
        Instructions()
        .if_instructions()
        .exec(1)
    )

    result = []
    for instruction in instructions:
        print(instruction)
        result.append(instruction)

    return result

Result

Methods

Each child will gradually have more individual methods, but they all inherit from the general Instruction.

The most useful methods are:

  • .get_callee_values() to get all calls that an instruction contains (internal too)
  • .get_operands() to break an instruction down into several separate operands
  • .get_parent() to step up to the parent function or modifier
  • .instruction_data to get the low-level data about an instruction; very interesting and helps you understand more about how Glider works
  • .is_storage_read() and .is_storage_write() to check whether an instruction contains a storage read or write
  • .next_instruction(), .previous_instruction(), and their ’s siblings return a list of instructions that come before or after
  • .backward_df() and .forward_df() are similar to the methods above, but they use dataflow instead and return instructions that come before or after and contain some functions used in the current instruction
  • The group of .is_if(), .is_return(), and other methods repeating the child classes above to check whether a particular instruction is an instance
  • .source_code()

See them all here.

I’ll talk about some of these methods in other articles because they can be pretty tricky to grasp at once, like the dataflow or instruction data.

Example

Check the following glide:

from glider import *


def query():
    instructions = (
        Instructions()
        # .external_calls()
        .exec(10)
    )

    result = []
    for instruction in instructions:
        print(instruction.source_code())

        callees = instruction.get_callee_values()
        print(callees)
        for callee in callees:
            print(callee.expression)

        operands = instruction.get_operands()
        print(operands)
        for operand in operands:
            print(operand.expression)

        print("==========")

    return result

I wrote it to show the difference between .get_callee_values() and .get_operands(). You can play a little and uncomment the external calls filter or add another one to see the difference in instruction types and their operands/callees. Change the offset to see other entries.