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               90x   90x     90x 100x     100x 4x                 96x     90x 643x 643x   643x 636x     7x 3x   7x                   90x 173x 173x 173x 172x     1x         1x               90x 260x 260x 260x   260x 255x     5x 5x         5x     6x   5x                                
/**
 * 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,
            };
          },
      };
    },
  };
}