Skip to content

Commit

Permalink
implement TypedArray.prototype.with
Browse files Browse the repository at this point in the history
  • Loading branch information
robik committed Mar 29, 2024
1 parent 686f233 commit fa46507
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/hermes/VM/NativeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ NATIVE_FUNCTION(typedArrayPrototypeSort)
NATIVE_FUNCTION(typedArrayPrototypeSubarray)
NATIVE_FUNCTION(typedArrayPrototypeSymbolToStringTag)
NATIVE_FUNCTION(typedArrayPrototypeToLocaleString)
NATIVE_FUNCTION(typedArrayPrototypeWith)
NATIVE_FUNCTION(unescape)
NATIVE_FUNCTION(weakMapConstructor)
NATIVE_FUNCTION(weakMapPrototypeDelete)
Expand Down
127 changes: 127 additions & 0 deletions lib/VM/JSLib/TypedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,126 @@ typedArrayPrototypeToLocaleString(void *, Runtime &runtime, NativeArgs args) {
return HermesValue::encodeStringValue(*builder->getStringPrimitive());
}

inline static bool isValidIntegerIndex(
Runtime &runtime,
Handle<JSTypedArrayBase> O,
const HermesValue &index) {
// 1. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
if (!O->attached(runtime)) {
return false;
}

// 2. If IsIntegralNumber(index) is false, return false.
if (LLVM_UNLIKELY(!index.isNumber())) {
return false;
}

// 3. If index is -0𝔽, return false.
if (LLVM_UNLIKELY(
index.getDouble() == 0 && std::signbit(index.getDouble()))) {
return false;
}

// 4. If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false.
if (index.getNumber() < 0 || index.getNumber() >= O->getLength()) {
return false;
}

return true;
}

/// ES14.0 23.2.3.36
CallResult<HermesValue>
typedArrayPrototypeWith(void *, Runtime &runtime, NativeArgs args) {
// 2. Perform ? ValidateTypedArray(O).
if (JSTypedArrayBase::validateTypedArray(runtime, args.getThisHandle()) ==
ExecutionStatus::EXCEPTION) {
return ExecutionStatus::EXCEPTION;
}

// 1. Let O be this value
auto self = args.vmcastThis<JSTypedArrayBase>();

// 3. Let len be O.[[ArrayLength]].
double len = self->getLength();

// 4. Let relativeIndex be ? ToIntegerOrInfinity(index).
auto relativeIndexRes = toIntegerOrInfinity(runtime, args.getArgHandle(0));
if (LLVM_UNLIKELY(relativeIndexRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}

// Use double here, because ToInteger may return Infinity.
double relativeIndex = relativeIndexRes->getNumber();

// 5. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
// 6. Else, let actualIndex be len + relativeIndex.
double actualIndex =
convertNegativeBoundsRelativeToLength(relativeIndex, len);

// 7. If O.[[ContentType]] is BigInt, let numericValueBe ? ToBigInt(value)
// 8. Else, let numericValue be ? ToNumber(value)
CallResult<HermesValue> res = ExecutionStatus::EXCEPTION;
switch (self->getKind()) {
default:
res = toNumber_RJS(runtime, args.getArgHandle(1));
break;
case CellKind::BigInt64ArrayKind:
case CellKind::BigUint64ArrayKind:
res = toBigInt_RJS(runtime, args.getArgHandle(1));
break;
}
if (res == ExecutionStatus::EXCEPTION) {
return ExecutionStatus::EXCEPTION;
}

HermesValue actualIndexValue =
HermesValue::encodeTrustedNumberValue(actualIndex);
// 9. If IsValidIntegerIndex(O, 𝔽(actualIndex)) is false, throw a RangeError
// exception.
if (LLVM_UNLIKELY(!isValidIntegerIndex(runtime, self, actualIndexValue))) {
return runtime.raiseRangeError("index invalid or out of range");
}

// 10. Let A be ? TypedArrayCreateSameType(O, « 𝔽(len) »).
auto status = JSTypedArrayBase::allocateSpecies(runtime, self, len);
if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}

auto A = status.getValue();
if (actualIndex > 0) {
if (LLVM_UNLIKELY(
JSTypedArrayBase::setToCopyOfTypedArray(
runtime, A, 0, self, 0, actualIndex) ==
ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
}

auto targetValue = runtime.makeHandle(res.getValue());
if (LLVM_UNLIKELY(
A->setOwnIndexed(A, runtime, actualIndex, targetValue) ==
ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}

if (actualIndex + 1 < len) {
if (LLVM_UNLIKELY(
JSTypedArrayBase::setToCopyOfTypedArray(
runtime,
A,
actualIndex + 1,
self,
actualIndex + 1,
len - actualIndex - 1) == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
}

return A.getHermesValue();
}

Handle<JSObject> createTypedArrayBaseConstructor(Runtime &runtime) {
auto proto = Handle<JSObject>::vmcast(&runtime.typedArrayBasePrototype);

Expand Down Expand Up @@ -2007,6 +2127,13 @@ Handle<JSObject> createTypedArrayBaseConstructor(Runtime &runtime) {
(void *)IterationKind::Entry,
typedArrayPrototypeIterator,
0);
defineMethod(
runtime,
proto,
Predefined::getSymbolID(Predefined::with),
nullptr,
typedArrayPrototypeWith,
2);

DefinePropertyFlags dpf = DefinePropertyFlags::getNewNonEnumerableFlags();

Expand Down
15 changes: 15 additions & 0 deletions test/hermes/TypedArray.js
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,21 @@ cons.forEach(function(TypedArray) {
});
/// @}

/// @name TypedArray.prototype.with
/// @{
cons.forEach(function(TypedArray) {
var arr = new TypedArray([ 0, 1, 1, 3 ]);

assert.arrayEqual(arr.with(2, 2), [ 0, 1, 2, 3 ]);
assert.arrayEqual(arr.with(-2, 2), [ 0, 1, 2, 3 ]);
assert.arrayEqual(new TypedArray(1).with(0, 100), [ 100 ]);

assert.throws(function() {
arr.with(4, 5);
}, RangeError);
});
/// @}

/// @name Exception cases
/// @{

Expand Down

0 comments on commit fa46507

Please sign in to comment.