All files / src/core statics.js

100% Statements 47/47
100% Branches 6/6
100% Functions 14/14
100% Lines 46/46
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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146          23x 23x 23x 23x     23x                       23x               2x 2x 2x 6x     2x       90x 90x 90x 90x 2x   2x 2x   2x   6x 6x 6x 6x 6x 6x                     2x 2x   2x               2x     2x                                 90x 42x 21x 17x     4x   4x             90x 68x   68x                   90x 181x 181x 181x     181x 84x           181x          
/**
 * Static value plugin
 *
 * @flow
 */
import Syntax from 'walt-syntax';
import { stringEncoder } from '../utils/string';
import OutputStream from '../utils/output-stream';
import wasmTypes from 'wasm-types';
import type { SemanticPlugin } from '../flow/types';
 
const escapeMap = {
  ['\\0']: 0x00,
  ['\\a']: 0x07,
  ['\\b']: 0x08,
  ['\\t']: 0x09,
  ['\\n']: 0x0a,
  ['\\v']: 0x0b,
  ['\\f']: 0x0c,
  ['\\r']: 0x0d,
  ["\\'"]: 0x27,
};
 
const sizeMap = {
  i64: 8,
  f64: 8,
  i32: 4,
  f32: 4,
};
 
function encodeArray(array, type) {
  const stream = new OutputStream();
  const encodeType = wasmTypes[type];
  array.forEach(v => {
    stream.push(encodeType, v, String(v));
  });
 
  return stream;
}
 
export default function Strings(): SemanticPlugin {
  let count = 0;
  return {
    semantics: ({ stmt }) => ({
      [Syntax.StaticDeclaration]: _next => ([node, context], transform) => {
        const { userTypes, statics } = context;
 
        const bareType = String(node.type).slice(0, -2);
        const typeSize = sizeMap[bareType];
 
        const meta = node.params.reduce(
          (acc, v, i) => {
            const n = transform([v, context]);
            acc.OBJECT_SIZE += typeSize;
            acc.TYPE_OBJECT[i] = i * typeSize;
            acc.OBJECT_KEY_TYPES[i] = bareType;
            acc.VALUES.push(Number(n.value));
            return acc;
          },
          {
            OBJECT_SIZE: 0,
            TYPE_OBJECT: {},
            OBJECT_KEY_TYPES: {},
            VALUES: [],
            STATIC: bareType,
          }
        );
 
        const uid = `__auto_gen_${node.value}_${count}`;
        count += 1;
 
        userTypes[uid] = {
          ...node,
          value: uid,
          Type: Syntax.Type,
          meta,
          params: [],
        };
 
        statics[uid] = encodeArray(meta.VALUES, bareType);
 
        // Short circuit the middleware and instead transform a declaration
        return transform([
          {
            ...node,
            meta,
            type: uid,
            Type: Syntax.ImmutableDeclaration,
            params: [
              {
                ...node.params[0],
                value: uid,
                Type: Syntax.StaticValueList,
              },
            ],
          },
          context,
        ]);
      },
      [Syntax.ArraySubscript]: next => ([node, context], transform) => {
        const [target, offset] = node.params.map(p => transform([p, context]));
        if (!target.meta.STATIC) {
          return next([node, context]);
        }
 
        const shift = { i32: 2, f32: 2, i64: 3, f64: 3 }[target.meta.STATIC];
 
        return transform([
          stmt`${
            target.meta.STATIC
          }.load(${target} + (${offset} << ${shift}));`,
          context,
        ]);
      },
      [Syntax.CharacterLiteral]: _ => ([node, context], transform) => {
        const codePoint = escapeMap[node.value] || node.value.codePointAt(0);
 
        return transform([
          {
            ...node,
            Type: 'Constant',
            type: 'i32',
            value: String(codePoint),
          },
          context,
        ]);
      },
      [Syntax.StringLiteral]: _ignore => args => {
        const [stringLiteral, context] = args;
        const { statics } = context;
        const { value } = stringLiteral;
 
        // did we already encode the static?
        if (!(value in statics)) {
          statics[value] = stringEncoder(value);
        }
 
        // It's too early to transform a string at this point
        // we need additional information, only available in the generator.
        // This also avoids doing the work in two places, in semantics AND gen
        return stringLiteral;
      },
    }),
  };
}