Lee Thanh
Upload 3012 files
5641073
raw
history blame
5 kB
import initTrace from "debug";
import { PTR_ADOPT_WRONG_MESSAGE, PTR_ALREADY_ADOPTED, PTR_INVALID_POINTER_TYPE } from "../../errors";
import { format } from "../../util";
import { ListElementSize } from "../list-element-size";
import { ObjectSize, getWordLength } from "../object-size";
import { Segment } from "../segment";
import {
Pointer,
getTargetListLength,
getTargetStructSize,
getTargetPointerType,
getTargetListElementSize,
getTargetCompositeListSize,
getCapabilityId,
getContent,
erasePointer,
initPointer,
erase,
setStructPointer,
setListPointer,
setInterfacePointer,
getListByteLength,
} from "./pointer";
import { PointerType } from "./pointer-type";
const trace = initTrace("capnp:orphan");
trace("load");
export interface _Orphan {
capId: number;
elementSize: ListElementSize;
length: number;
size: ObjectSize;
type: PointerType;
}
// Technically speaking this class doesn't need to be generic, but the extra type checking enforced by this helps to
// make sure you don't accidentally adopt a pointer of the wrong type.
/**
* An orphaned pointer. This object itself is technically a pointer to the original pointer's content, which was left
* untouched in its original message. The original pointer data is encoded as attributes on the Orphan object, ready to
* be reconstructed once another pointer is ready to adopt it.
*
* @export
* @class Orphan
* @extends {Pointer}
* @template T
*/
export class Orphan<T extends Pointer> {
/** If this member is not present then the orphan has already been adopted, or something went very wrong. */
_capnp?: _Orphan;
byteOffset: number;
segment: Segment;
constructor(src: T) {
const c = getContent(src);
this.segment = c.segment;
this.byteOffset = c.byteOffset;
this._capnp = {} as _Orphan;
// Read vital info from the src pointer so we can reconstruct it during adoption.
this._capnp.type = getTargetPointerType(src);
switch (this._capnp.type) {
case PointerType.STRUCT:
this._capnp.size = getTargetStructSize(src);
break;
case PointerType.LIST:
this._capnp.length = getTargetListLength(src);
this._capnp.elementSize = getTargetListElementSize(src);
if (this._capnp.elementSize === ListElementSize.COMPOSITE) {
this._capnp.size = getTargetCompositeListSize(src);
}
break;
case PointerType.OTHER:
this._capnp.capId = getCapabilityId(src);
break;
default:
// COVERAGE: Unreachable code.
/* istanbul ignore next */
throw new Error(PTR_INVALID_POINTER_TYPE);
}
// Zero out the source pointer (but not the contents!).
erasePointer(src);
}
/**
* Adopt (move) this orphan into the target pointer location. This will allocate far pointers in `dst` as needed.
*
* @param {T} dst The destination pointer.
* @returns {void}
*/
_moveTo(dst: T): void {
if (this._capnp === undefined) {
throw new Error(format(PTR_ALREADY_ADOPTED, this));
}
// TODO: Implement copy semantics when this happens.
if (this.segment.message !== dst.segment.message) {
throw new Error(format(PTR_ADOPT_WRONG_MESSAGE, this, dst));
}
// Recursively wipe out the destination pointer first.
erase(dst);
const res = initPointer(this.segment, this.byteOffset, dst);
switch (this._capnp.type) {
case PointerType.STRUCT:
setStructPointer(res.offsetWords, this._capnp.size, res.pointer);
break;
case PointerType.LIST: {
let offsetWords = res.offsetWords;
if (this._capnp.elementSize === ListElementSize.COMPOSITE) {
offsetWords--; // The tag word gets skipped.
}
setListPointer(offsetWords, this._capnp.elementSize, this._capnp.length, res.pointer, this._capnp.size);
break;
}
case PointerType.OTHER:
setInterfacePointer(this._capnp.capId, res.pointer);
break;
/* istanbul ignore next */
default:
throw new Error(PTR_INVALID_POINTER_TYPE);
}
this._capnp = undefined;
}
dispose(): void {
// FIXME: Should this throw?
if (this._capnp === undefined) {
trace("not disposing an already disposed orphan", this);
return;
}
switch (this._capnp.type) {
case PointerType.STRUCT:
this.segment.fillZeroWords(this.byteOffset, getWordLength(this._capnp.size));
break;
case PointerType.LIST: {
const byteLength = getListByteLength(this._capnp.elementSize, this._capnp.length, this._capnp.size);
this.segment.fillZeroWords(this.byteOffset, byteLength);
break;
}
default:
// Other pointer types don't actually have any content.
break;
}
this._capnp = undefined;
}
toString(): string {
return format("Orphan_%d@%a,type:%s", this.segment.id, this.byteOffset, this._capnp && this._capnp.type);
}
}