defendPrototype = (
  tag,
  contextProvider,
  behaviorMethods,
  thisfulMethods = false,
  interfaceGuard = undefined,
) => {
  const prototype = {};
  const methodNames = getMethodNames(behaviorMethods).filter(
    // By ignoring any method that seems to be a constructor, we can use a
    // class.prototype as a behaviorMethods.
    key => {
      if (key !== 'constructor') {
        return true;
      }
      const constructor = behaviorMethods.constructor;
      return !(
        constructor.prototype &&
        constructor.prototype.constructor === constructor
      );
    },
  );
  /** @type {Record<PropertyKey, MethodGuard> | undefined} */
  let methodGuards;
  /** @type {import('@endo/patterns').DefaultGuardType} */
  let defaultGuards;
  if (interfaceGuard) {
    const {
      interfaceName,
      methodGuards: mg,
      symbolMethodGuards,
      sloppy,
      defaultGuards: dg = sloppy ? 'passable' : defaultGuards,
    } = getInterfaceGuardPayload(interfaceGuard);
    methodGuards = harden({
      ...mg,
      ...(symbolMethodGuards &&
        fromEntries(getCopyMapEntries(symbolMethodGuards))),
    });
    defaultGuards = dg;
    {
      const methodGuardNames = ownKeys(methodGuards);
      const unimplemented = listDifference(methodGuardNames, methodNames);
      unimplemented.length === 0 ||
        Fail`methods ${q(unimplemented)} not implemented by ${q(tag)}`;
      if (defaultGuards === undefined) {
        const unguarded = listDifference(methodNames, methodGuardNames);
        unguarded.length === 0 ||
          Fail`methods ${q(unguarded)} not guarded by ${q(interfaceName)}`;
      }
    }
  }

  for (const prop of methodNames) {
    const originalMethod = behaviorMethods[prop];
    const { shiftedMethod } = {
      shiftedMethod(...args) {
        return originalMethod(this, ...args);
      },
    };
    const behaviorMethod = thisfulMethods ? originalMethod : shiftedMethod;
    // TODO some tool does not yet understand the `?.[` syntax
    // See https://github.com/endojs/endo/pull/2247#discussion_r1583724424
    let methodGuard = methodGuards && methodGuards[prop];
    if (!methodGuard) {
      switch (defaultGuards) {
        case undefined: {
          if (thisfulMethods) {
            methodGuard = PassableMethodGuard;
          } else {
            methodGuard = RawMethodGuard;
          }
          break;
        }
        case 'passable': {
          methodGuard = PassableMethodGuard;
          break;
        }
        case 'raw': {
          methodGuard = RawMethodGuard;
          break;
        }
        default: {
          throw Fail`Unrecognized defaultGuards ${q(defaultGuards)}`;
        }
      }
    }
    prototype[prop] = bindMethod(
      `In ${q(prop)} method of (${tag})`,
      contextProvider,
      behaviorMethod,
      methodGuard,
    );
  }

  if (!hasOwnPropertyOf(prototype, GET_INTERFACE_GUARD)) {
    const getInterfaceGuardMethod = {
      [GET_INTERFACE_GUARD]() {
        // Note: May be `undefined`
        return interfaceGuard;
      },
    }[GET_INTERFACE_GUARD];
    prototype[GET_INTERFACE_GUARD] = bindMethod(
      `In ${q(GET_INTERFACE_GUARD)} method of (${tag})`,
      contextProvider,
      getInterfaceGuardMethod,
      PassableMethodGuard,
    );
  }

  return Far(
    tag,
    /** @type {T & import('./get-interface.js').GetInterfaceGuard<T>} */ (
      prototype
    ),
  );
}
@template {Record<PropertyKey, CallableFunction>} T
@param {string} tag
@param {ContextProvider} contextProvider
@param {T} behaviorMethods
@param {boolean} [thisfulMethods]
@param {InterfaceGuard<{ [M in keyof T]: MethodGuard }>} [interfaceGuard]