# All files / src/core function-pointer.js

100% Statements 37/37
100% Branches 16/16
100% Functions 11/11
100% Lines 37/37

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117

    23x
    23x
    23x
    23x

    81x

    81x

    81x
    90x

    90x
    3x

    87x

    81x
    648x
    648x

    648x
    641x

    7x
    3x

    7x

    81x
    158x
    158x
    158x
    157x

    1x

    1x

    81x
    244x
    244x
    244x

    244x
    240x

    4x
    4x

    4x

    4x

    4x

     /**
     * Function pointer plugin.
     * Handles function pointer declaration and indirect calls.
     *
     * @flow
     */
    import Syntax from 'walt-syntax';
    import { find } from 'walt-parser-tools/scope';
    import { extendNode } from '../utils/extend-node';
    import {
      TYPE_INDEX,
      GLOBAL_INDEX,
      FUNCTION_INDEX,
    } from '../semantics/metadata';
    import type { SemanticPlugin } from '../flow/types';

    export default function functionPointer(): SemanticPlugin {
      return {
        semantics() {
          return {
            // Handle Table definitions
            [Syntax.ImmutableDeclaration]: next =>
              function defineTable(args) {
                const [decl, context] = args;

                // Short circuit since memory is a special type of declaration
                if (!context.locals && decl.type === 'Table') {
                  return {
                    ...decl,
                    meta: {
                      ...decl.meta,
                      [GLOBAL_INDEX]: -1,
                    },
                  };
                }

                return next(args);
              },
            [Syntax.Identifier]: next =>
              function pointer(args) {
                const [node, context] = args;
                const { functions, table, scopes } = context;

                if (find(scopes, node.value) || !functions[node.value]) {
                  return next(args);
                }

                if (table[node.value] == null) {
                  table[node.value] = functions[node.value];
                }
                return {
                  ...node,
                  type: 'i32',
                  meta: {
                    [FUNCTION_INDEX]: functions[node.value].meta[FUNCTION_INDEX],
                  },
                  value: Object.keys(table).indexOf(node.value),
                  Type: Syntax.FunctionPointer,
                };
              },
            [Syntax.FunctionResult]: next => (args, transform) => {
              const [node, context] = args;
              const { types } = context;
              if (!types[node.type]) {
                return next(args);
              }

              return next([
                extendNode(
                  {
                    type: 'i32',
                    meta: { ALIAS: node.type },
                    params: node.params.map(p => transform([p, context])),
                  },
                  node
                ),
                context,
              ]);
            },
            [Syntax.FunctionCall]: next =>
              function indirectCall(args, transform) {
                const [call, context] = args;
                const { scopes, types } = context;
                const ref = find(scopes, call.value);
                // Nothing we need transform
                if (!ref) {
                  return next(args);
                }

                const typedef = types[ref.type];
                const typeIndex = Object.keys(types).indexOf(ref.type);

                // We will short all of the other middleware so transform the parameters
                // here and append an identifier which will be used to get the table
                // value
                const params = [
                  ...call.params.slice(1),
                  { ...ref, Type: Syntax.Identifier },
                ].map(p => transform([p, context]));

                return {
                  ...call,
                  meta: {
                    ...call.meta,
                    ...ref.meta,
                    [TYPE_INDEX]: typeIndex,
                  },
                  type: typedef != null ? typedef.type : call.type,
                  params,
                  Type: Syntax.IndirectFunctionCall,
                };
              },
          };
        },
      };
    }