fix: alt syntax for positive but faster assertions
MODIFY
packages/captp/src/atomics.js
packages/captp/src/atomics.js
@@ -23,8 +23,8 @@ const STATUS_FLAG_REJECT = 4;
* @param {SharedArrayBuffer} transferBuffer the backing buffer
*/
const splitTransferBuffer = transferBuffer => {
- assert(
- transferBuffer.byteLength >= MIN_TRANSFER_BUFFER_LENGTH,
+ transferBuffer.byteLength >= MIN_TRANSFER_BUFFER_LENGTH ||
+ assert.fail(
X`Transfer buffer of ${transferBuffer.byteLength} bytes is smaller than MIN_TRANSFER_BUFFER_LENGTH ${MIN_TRANSFER_BUFFER_LENGTH}`,
);
const lenbuf = new BigUint64Array(transferBuffer, 0, 1);
@@ -40,8 +40,8 @@ const splitTransferBuffer = transferBuffer => {
X`Internal error; actual overhead ${overheadLength} of bytes is not TRANSFER_OVERHEAD_LENGTH ${TRANSFER_OVERHEAD_LENGTH}`,
);
const databuf = new Uint8Array(transferBuffer, overheadLength);
- assert(
- databuf.byteLength >= MIN_DATA_BUFFER_LENGTH,
+ databuf.byteLength >= MIN_DATA_BUFFER_LENGTH ||
+ assert.fail(
X`Transfer buffer of size ${transferBuffer.byteLength} only supports ${databuf.byteLength} data bytes; need at least ${MIN_DATA_BUFFER_LENGTH}`,
);
return harden({ statusbuf, lenbuf, databuf });

MODIFY
packages/captp/src/captp.js
packages/captp/src/captp.js
@@ -64,8 +64,8 @@ export const makeCapTP = (
// encounter deadlock. Without a lot more bookkeeping, we can't detect it for
// more general networks of CapTPs, but we are conservative for at least this
// one case.
- assert(
- !(trapHost && trapGuest),
+ !(trapHost && trapGuest) ||
+ assert.fail(
X`CapTP ${ourId} can only be one of either trapGuest or trapHost`,
);
@@ -391,8 +391,8 @@ export const makeCapTP = (
});
};
if (trap) {
- assert(
- exportedTrapHandlers.has(val),
+ exportedTrapHandlers.has(val) ||
+ assert.fail(
X`Refused Trap(${val}) because target was not registered with makeTrapHandler`,
);
assert.typeof(
@@ -607,18 +607,16 @@ export const makeCapTP = (
// Create the Trap proxy maker.
const makeTrapImpl = implMethod => (target, ...implArgs) => {
- assert(
- Promise.resolve(target) !== target,
- X`Trap(${target}) target cannot be a promise`,
- );
+ Promise.resolve(target) !== target ||
+ assert.fail(X`Trap(${target}) target cannot be a promise`);
const slot = valToSlot.get(target);
- assert(
- slot && slot[1] === '-',
- X`Trap(${target}) target was not imported`,
- );
- assert(
- slot[0] === 't',
+ (slot && slot[1] === '-') ||
+ assert.fail(X`Trap(${target}) target was not imported`);
+ // @ts-expect-error TS apparently confused about `||` control flow
+ // https://github.com/microsoft/TypeScript/issues/50739
+ slot[0] === 't' ||
+ assert.fail(
X`Trap(${target}) imported target was not created with makeTrapHandler`,
);
@@ -653,6 +651,8 @@ export const makeCapTP = (
// messages over the current CapTP data channel.
const [isException, serialized] = trapGuest({
trapMethod: implMethod,
+ // @ts-expect-error TS apparently confused about `||` control flow
+ // https://github.com/microsoft/TypeScript/issues/50739
slot,
trapArgs: implArgs,
startTrap: () => {
@@ -685,8 +685,8 @@ export const makeCapTP = (
});
const value = unserialize(serialized);
- assert(
- !isThenable(value),
+ !isThenable(value) ||
+ assert.fail(
X`Trap(${target}) reply cannot be a Thenable; have ${value}`,
);

MODIFY
packages/import-bundle/test/test-compartment-wrapper.js
packages/import-bundle/test/test-compartment-wrapper.js
@@ -12,10 +12,8 @@ const { details: X } = assert;
// use cases that don't involve voluntary participation by oldSrc.
function milageTransform(oldSrc) {
- assert(
- oldSrc.indexOf('getOdometer') === -1,
- X`forbidden access to 'getOdometer' in oldSrc`,
- );
+ oldSrc.indexOf('getOdometer') === -1 ||
+ assert.fail(X`forbidden access to 'getOdometer' in oldSrc`);
return oldSrc.replace(/addMilage\(\)/g, 'getOdometer().add(1)');
}

MODIFY
packages/marshal/src/encodeToCapData.js
packages/marshal/src/encodeToCapData.js
@@ -372,10 +372,8 @@ export const makeDecodeFromCapData = ({
// @ts-ignore inadequate type inference
// See https://github.com/endojs/endo/pull/1259#discussion_r954561901
const { original, rest } = jsonEncoded;
- assert(
- hasOwnPropertyOf(jsonEncoded, 'original'),
- X`Invalid Hilbert Hotel encoding ${jsonEncoded}`,
- );
+ hasOwnPropertyOf(jsonEncoded, 'original') ||
+ assert.fail(X`Invalid Hilbert Hotel encoding ${jsonEncoded}`);
// Don't harden since we're not done mutating it
const result = { [QCLASS]: decodeFromCapData(original) };
if (hasOwnPropertyOf(jsonEncoded, 'rest')) {
@@ -389,8 +387,8 @@ export const makeDecodeFromCapData = ({
// TODO really should assert that `passStyleOf(rest)` is
// `'copyRecord'` but we'd have to harden it and it is too
// early to do that.
- assert(
- !hasOwnPropertyOf(restObj, QCLASS),
+ !hasOwnPropertyOf(restObj, QCLASS) ||
+ assert.fail(
X`Rest must not contain its own definition of ${q(QCLASS)}`,
);
defineProperties(result, getOwnPropertyDescriptors(restObj));
@@ -399,8 +397,8 @@ export const makeDecodeFromCapData = ({
}
default: {
- assert(
- qclass !== 'ibid',
+ qclass !== 'ibid' ||
+ assert.fail(
X`The protocol no longer supports ibid encoding: ${jsonEncoded}.`,
);
assert.fail(X`unrecognized ${q(QCLASS)} ${q(qclass)}`, TypeError);

MODIFY
packages/marshal/src/helpers/passStyle-helpers.js
packages/marshal/src/helpers/passStyle-helpers.js
@@ -81,10 +81,11 @@ export const checkNormalProperty = (
nameType,
)}-named property: ${candidate}`,
) &&
+ (hasOwnPropertyOf(desc, 'value') ||
check(
- hasOwnPropertyOf(desc, 'value'),
+ false,
X`${q(propertyName)} must not be an accessor property: ${candidate}`,
- ) &&
+ )) &&
(shouldBeEnumerable
? check(
!!desc.enumerable,
@@ -111,15 +112,11 @@ harden(getTag);
*/
export const checkTagRecord = (tagRecord, passStyle, check) => {
return (
- check(
- typeof tagRecord === 'object' && tagRecord !== null,
- X`A non-object cannot be a tagRecord: ${tagRecord}`,
- ) &&
+ ((typeof tagRecord === 'object' && tagRecord !== null) ||
+ check(false, X`A non-object cannot be a tagRecord: ${tagRecord}`)) &&
check(isFrozen(tagRecord), X`A tagRecord must be frozen: ${tagRecord}`) &&
- check(
- !isArray(tagRecord),
- X`An array cannot be a tagRecords: ${tagRecord}`,
- ) &&
+ (!isArray(tagRecord) ||
+ check(false, X`An array cannot be a tagRecords: ${tagRecord}`)) &&
checkNormalProperty(tagRecord, PASS_STYLE, 'symbol', false, check) &&
check(
tagRecord[PASS_STYLE] === passStyle,
@@ -134,10 +131,11 @@ export const checkTagRecord = (tagRecord, passStyle, check) => {
false,
check,
) &&
+ (typeof getTag(tagRecord) === 'string' ||
check(
- typeof getTag(tagRecord) === 'string',
+ false,
X`A [Symbol.toString]-named property must be a string: ${tagRecord}`,
- )
+ ))
);
};
harden(checkTagRecord);

MODIFY
packages/marshal/src/helpers/remotable.js
packages/marshal/src/helpers/remotable.js
@@ -36,10 +36,11 @@ const {
const checkIface = (iface, check) => {
return (
// TODO other possible ifaces, once we have third party veracity
+ (typeof iface === 'string' ||
check(
- typeof iface === 'string',
+ false,
X`For now, interface ${iface} must be a string; unimplemented`,
- ) &&
+ )) &&
check(
iface === 'Remotable' || iface.startsWith('Alleged: '),
X`For now, iface ${q(
@@ -144,10 +145,12 @@ const checkRemotableProtoOf = (original, check) => {
} = proto;
return (
+ (ownKeys(rest).length === 0 ||
check(
- ownKeys(rest).length === 0,
+ false,
X`Unexpected properties on Remotable Proto ${ownKeys(rest)}`,
- ) && checkIface(iface, check)
+ )) &&
+ checkIface(iface, check)
);
};
@@ -195,10 +198,9 @@ export const RemotableHelper = harden({
canBeValid: (candidate, check) => {
if (
!(
- check(
- isObject(candidate),
- X`cannot serialize non-objects like ${candidate}`,
- ) && check(!isArray(candidate), X`Arrays cannot be pass-by-remote`)
+ (isObject(candidate) ||
+ check(false, X`cannot serialize non-objects like ${candidate}`)) &&
+ check(!isArray(candidate), X`Arrays cannot be pass-by-remote`)
)
) {
return false;
@@ -222,10 +224,8 @@ export const RemotableHelper = harden({
String(key),
)} in ${candidate}`,
) &&
- check(
- key !== PASS_STYLE,
- X`A pass-by-remote cannot shadow ${q(PASS_STYLE)}`,
- ),
+ (key !== PASS_STYLE ||
+ check(false, X`A pass-by-remote cannot shadow ${q(PASS_STYLE)}`)),
);
} else if (typeof candidate === 'function') {
// Far functions cannot be methods, and cannot have methods.
@@ -237,14 +237,16 @@ export const RemotableHelper = harden({
nameDesc && typeof nameDesc.value === 'string',
X`Far function name must be a string, in ${candidate}`,
) &&
+ ((lengthDesc && typeof lengthDesc.value === 'number') ||
check(
- lengthDesc && typeof lengthDesc.value === 'number',
+ false,
X`Far function length must be a number, in ${candidate}`,
- ) &&
+ )) &&
+ (restKeys.length === 0 ||
check(
- restKeys.length === 0,
+ false,
X`Far functions unexpected properties besides .name and .length ${restKeys}`,
- ))
+ )))
);
} else {
return check(false, X`unrecognized typeof ${candidate}`);

MODIFY
packages/marshal/src/helpers/safe-promise.js
packages/marshal/src/helpers/safe-promise.js
@@ -104,10 +104,11 @@ const checkPromiseOwnKeys = (pr, check) => {
const checkSafePromise = (pr, check) =>
check(isFrozen(pr), X`${pr} - Must be frozen`) &&
check(isPromise(pr), X`${pr} - Must be a promise`) &&
+ (getPrototypeOf(pr) === Promise.prototype ||
check(
- getPrototypeOf(pr) === Promise.prototype,
+ false,
X`${pr} - Must inherit from Promise.prototype: ${q(getPrototypeOf(pr))}`,
- ) &&
+ )) &&
checkPromiseOwnKeys(/** @type {Promise} */ (pr), check);
harden(checkSafePromise);

MODIFY
packages/marshal/src/helpers/symbol.js
packages/marshal/src/helpers/symbol.js
@@ -39,8 +39,8 @@ export const isPassableSymbol = sym =>
harden(isPassableSymbol);
export const assertPassableSymbol = sym =>
- assert(
- isPassableSymbol(sym),
+ isPassableSymbol(sym) ||
+ assert.fail(
X`Only registered symbols or well-known symbols are passable: ${q(sym)}`,
);
harden(assertPassableSymbol);

MODIFY
packages/marshal/src/helpers/tagged.js
packages/marshal/src/helpers/tagged.js
@@ -36,11 +36,8 @@ export const TaggedHelper = harden({
payload: _payload, // value checked by recursive walk at the end
...rest
} = candidate;
-
- assert(
- ownKeys(rest).length === 0,
- X`Unexpected properties on Remotable Proto ${ownKeys(rest)}`,
- );
+ (ownKeys(rest).length === 0) ||
+ assert.fail(X`Unexpected properties on Remotable Proto ${ownKeys(rest)}`);
checkNormalProperty(candidate, 'payload', 'string', true, assertChecker);

MODIFY
packages/marshal/src/make-far.js
packages/marshal/src/make-far.js
@@ -38,13 +38,14 @@ const makeRemotableProto = (remotable, iface) => {
if (oldProto === null) {
oldProto = objectPrototype;
}
- assert(
- oldProto === objectPrototype || oldProto === null,
+ oldProto === objectPrototype ||
+ oldProto === null ||
+ assert.fail(
X`For now, remotables cannot inherit from anything unusual, in ${remotable}`,
);
} else if (typeof remotable === 'function') {
- assert(
- oldProto !== null,
+ oldProto !== null ||
+ assert.fail(
X`Original function must not inherit from null: ${remotable}`,
);
assert(

MODIFY
packages/marshal/src/marshal-justin.js
packages/marshal/src/marshal-justin.js
@@ -189,10 +189,8 @@ const decodeToJustin = (encoding, shouldIndent = false) => {
}
case 'hilbert': {
const { original, rest } = rawTree;
- assert(
- 'original' in rawTree,
- X`Invalid Hilbert Hotel encoding ${rawTree}`,
- );
+ 'original' in rawTree ||
+ assert.fail(X`Invalid Hilbert Hotel encoding ${rawTree}`);
prepare(original);
if ('rest' in rawTree) {
assert.typeof(
@@ -201,12 +199,10 @@ const decodeToJustin = (encoding, shouldIndent = false) => {
X`Rest ${rest} encoding must be an object`,
);
assert(rest !== null, X`Rest ${rest} encoding must not be null`);
- assert(
- !isArray(rest),
- X`Rest ${rest} encoding must not be an array`,
- );
- assert(
- !(QCLASS in rest),
+ !isArray(rest) ||
+ assert.fail(X`Rest ${rest} encoding must not be an array`);
+ !(QCLASS in rest) ||
+ assert.fail(
X`Rest encoding ${rest} must not contain ${q(QCLASS)}`,
);
const names = ownKeys(rest);
@@ -228,10 +224,8 @@ const decodeToJustin = (encoding, shouldIndent = false) => {
'string',
X`invalid error name typeof ${q(typeof name)}`,
);
- assert(
- getErrorConstructor(name) !== undefined,
- X`Must be the name of an Error constructor ${name}`,
- );
+ getErrorConstructor(name) !== undefined ||
+ assert.fail(X`Must be the name of an Error constructor ${name}`);
assert.typeof(
message,
'string',

MODIFY
packages/marshal/src/marshal.js
packages/marshal/src/marshal.js
@@ -52,8 +52,9 @@ export const makeMarshal = (
} = {},
) => {
assert.typeof(marshalName, 'string');
- assert(
- errorTagging === 'on' || errorTagging === 'off',
+ errorTagging === 'on' ||
+ errorTagging === 'off' ||
+ assert.fail(
X`The errorTagging option can only be "on" or "off" ${errorTagging}`,
);
const nextErrorId = () => {
@@ -231,10 +232,8 @@ export const makeMarshal = (
'string',
X`unserialize() given non-capdata (.body is ${data.body}, not string)`,
);
- assert(
- isArray(data.slots),
- X`unserialize() given non-capdata (.slots are not Array)`,
- );
+ (isArray(data.slots)) ||
+ assert.fail(X`unserialize() given non-capdata (.slots are not Array)`);
const rawTree = harden(JSON.parse(data.body));
const fullRevive = makeFullRevive(data.slots);
const result = harden(fullRevive(rawTree));

MODIFY
packages/marshal/src/passStyleOf.js
packages/marshal/src/passStyleOf.js
@@ -52,10 +52,8 @@ const makeHelperTable = passStyleHelpers => {
HelperTable[styleName] = helper;
}
for (const styleName of ownKeys(HelperTable)) {
- assert(
- HelperTable[styleName] !== undefined,
- X`missing helper for ${q(styleName)}`,
- );
+ HelperTable[styleName] !== undefined ||
+ assert.fail(X`missing helper for ${q(styleName)}`);
}
return harden(HelperTable);
@@ -109,10 +107,8 @@ const makePassStyleOf = passStyleHelpers => {
// @ts-ignore TypeScript doesn't know that `get` after `has` is safe
return passStyleMemo.get(inner);
}
- assert(
- !inProgress.has(inner),
- X`Pass-by-copy data cannot be cyclic ${inner}`,
- );
+ (!inProgress.has(inner)) ||
+ assert.fail(X`Pass-by-copy data cannot be cyclic ${inner}`);
inProgress.add(inner);
}
// eslint-disable-next-line no-use-before-define
@@ -145,26 +141,22 @@ const makePassStyleOf = passStyleHelpers => {
if (inner === null) {
return 'null';
}
- assert(
- isFrozen(inner),
+ (isFrozen(inner)) ||
+ assert.fail(
X`Cannot pass non-frozen objects like ${inner}. Use harden()`,
);
if (isPromise(inner)) {
assertSafePromise(inner);
return 'promise';
}
- assert(
- typeof inner.then !== 'function',
- X`Cannot pass non-promise thenables`,
- );
+ (typeof inner.then !== 'function') ||
+ assert.fail(X`Cannot pass non-promise thenables`);
const passStyleTag = inner[PASS_STYLE];
if (passStyleTag !== undefined) {
assert.typeof(passStyleTag, 'string');
const helper = HelperTable[passStyleTag];
- assert(
- helper !== undefined,
- X`Unrecognized PassStyle: ${q(passStyleTag)}`,
- );
+ (helper !== undefined) ||
+ assert.fail(X`Unrecognized PassStyle: ${q(passStyleTag)}`);
helper.assertValid(inner, passStyleOfRecur);
return /** @type {PassStyle} */ (passStyleTag);
}
@@ -178,14 +170,12 @@ const makePassStyleOf = passStyleHelpers => {
return 'remotable';
}
case 'function': {
- assert(
- isFrozen(inner),
+ (isFrozen(inner)) ||
+ assert.fail(
X`Cannot pass non-frozen objects like ${inner}. Use harden()`,
);
- assert(
- typeof inner.then !== 'function',
- X`Cannot pass non-promise thenables`,
- );
+ (typeof inner.then !== 'function') ||
+ assert.fail(X`Cannot pass non-promise thenables`);
remotableHelper.assertValid(inner, passStyleOfRecur);
return 'remotable';
}

MODIFY
packages/ses/src/environment-options.js
packages/ses/src/environment-options.js
@@ -107,9 +107,13 @@ export const makeEnvironmentCaptor = aGlobal => {
}
}
}
- assert(
- setting === undefined || typeof setting === 'string',
- X`Environment option value ${q(setting)}, if present, must be a string.`,
+ setting === undefined ||
+ typeof setting === 'string' ||
+ // eslint-disable-next-line @endo/no-polymorphic-call
+ assert.fail(
+ X`Environment option value ${q(
+ setting,
+ )}, if present, must be a string.`,
);
return setting;
};

MODIFY
packages/stream-node/reader.js
packages/stream-node/reader.js
@@ -15,8 +15,8 @@ const { details: X, quote: q } = assert;
* @returns {import('@endo/stream').Reader<Uint8Array>}
*/
export const makeNodeReader = input => {
- assert(
- !input.readableObjectMode,
+ !input.readableObjectMode ||
+ assert.fail(
X`Cannot convert Node.js object mode Reader to AsyncIterator<Uint8Array>`,
);
assert(

MODIFY
packages/stream-node/writer.js
packages/stream-node/writer.js
@@ -17,8 +17,8 @@ const { details: X } = assert;
* @returns {import('@endo/stream').Writer<Uint8Array>}
*/
export const makeNodeWriter = writer => {
- assert(
- !writer.writableObjectMode,
+ !writer.writableObjectMode ||
+ assert.fail(
X`Cannot convert Node.js object mode Writer to AsyncIterator<undefined, Uint8Array>`,
);

fix: alt syntax for positive but faster assertions (#1280)