Skip to content
This repository was archived by the owner on Oct 24, 2023. It is now read-only.

Normative: throw whenever creating TA from OOB TA #72

Merged
merged 3 commits into from
Dec 21, 2021

Conversation

jugglinmike
Copy link
Contributor

@jugglinmike jugglinmike commented Aug 26, 2021

Resolves gh-57. Duplicating the boundary-checking step seems like the most straightforward solution to me. Alternatively, we could interrupt the branches to perform the bounds checking that's common to both:

1. If _elementType_ is the same as _srcType_, then
  1. Let _data_ be ? CloneArrayBuffer(_srcData_, _srcByteOffset_, _byteLength_, _bufferConstructor_).
1. Else,
  1. Let _data_ be ? AllocateArrayBuffer(_bufferConstructor_, _byteLength_).
1. <ins>If IsIntegerIndexedObjectOutOfBounds(_srcArray_, _getSrcBufferByteLength_) is *true*, throw a *TypeError* exception.</ins>
1. <ins>If _elementType_ is not the same as _srcType_, then</ins>
  1. <del>If IsDetachedBuffer(_srcData_) is *true*, throw a *TypeError* exception.</del>
  [...]

@jugglinmike
Copy link
Contributor Author

Any thoughts on this, @syg?

@syg
Copy link
Collaborator

syg commented Sep 10, 2021

Thanks for the ping.

Ugh, this is pretty annoying, I don't think that OOB check is enough. The resizing itself could cause the call to CopyDataBlocks in step 5 here to read out of bounds data, which is undefined. This also needs a fix to CloneArrayBuffer along the lines of #74.

@jugglinmike
Copy link
Contributor Author

gh-75 addresses this concern in a different way than what we've discussed so far (see gh-74). Since we won't know which of the approaches the proposal's going to take until after the next TC39 meeting, I'm going to hold off on updating this patch until that decision has been made.

@syg
Copy link
Collaborator

syg commented Nov 1, 2021

@jugglinmike Consensus was reached in committee for #75 and that has been merged, FYI.

@jugglinmike
Copy link
Contributor Author

@syg If I understand correctly, we'd like the new ArrayBuffer to have a length that matches the cached length of the source ArrayBuffer, but we want the actual copying to be limited by the sneakily-updated length.

For example (adapting your code from gh-75):

const rab = new ArrayBuffer(4 * 8, { maxByteLength: 1024 });
const ta = new Float64Array(rab, 0, 4);
for (let i = 0; i < 4; ++i) {
  ta[i] = i;
}

class MyArray extends Uint32Array {
  constructor(...params) {
    super(...params);
   }

  static get [Symbol.species]() {
    rab.resize(2 * 8);
    return Uint32Array;
  }
};

const newTa = new MyArray(ta);
assert(ta.length == 2); // Since it's length-tracking
assert(newTa.length == 4); // Since it snapshotted the length

assert(newTa[0] == 0);
assert(newTa[1] == 1);
assert(newTa[2] == 0);
assert(newTa[3] == 0);

That's what I've tried to implement with the latest commit on this branch.

@syg
Copy link
Collaborator

syg commented Nov 4, 2021

@syg If I understand correctly, we'd like the new ArrayBuffer to have a length that matches the cached length of the source ArrayBuffer, but we want the actual copying to be limited by the sneakily-updated length.

That was my intention, exactly.

1. <ins>Let _data_ be ? AllocateArrayBuffer(_bufferConstructor_, _byteLength_).</ins>
1. <ins>If IsIntegerIndexedObjectOutOfBounds(_srcArray_, _getSrcBufferByteLength_) is *true*, throw a *TypeError* exception.</ins>
1. <ins>Set _elementLength_ to IntegerIndexedObjectLength(_srcArray_, _getSrcBufferByteLength_).</ins>
1. <ins>Set _byteLength_ to _elementSize_ &times; _elementLength_.</ins>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new byteLength might be greater than the old byteLength. byteLength should only be updated if the buffer was shrunk. Alternatively, you could assign the new byte length to a newByteLength, and pass min(newByteLength, _byteLength) to CopyDataBlockBytes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Persisting the variable _byteLength_ might be confusing in what is becoming a somewhat complicated algorithm (readers might not recognize that despite its authoritative name, its value is not the byte length). There's also _elementLength_ to consider, which maybe ought to be complimented with a corresponding _newElementLength_ (more for coherence than a direct need).

I think we can use the new buffer's [[ArrayBufferByteLength]] slot to get the prior value of _byteLength_ without having to communicate "newness" or "oldness" through variable names. I've pushed up a commit to demonstrate what I have in mind. Does that seem appropriate to you?

Copy link
Collaborator

@syg syg Dec 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use [[ArrayBufferByteLength]] for growable SharedArrayBuffers, which stores its byte length in a separate shared block. That's why the idempotent getter exists.

I retract my comment, I missed the operative keyword "new". Using the new buffer's [[ArrayBufferByteLength]]. That works for me.

@jugglinmike
Copy link
Contributor Author

Any thoughts on this latest revision, @syg?

@syg syg merged commit 31bbe76 into tc39:master Dec 21, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Seemingly inconsistent behavior in TypedArray constructor
2 participants