Uint8Array.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. * Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Base64.h>
  7. #include <LibJS/Runtime/Temporal/AbstractOperations.h>
  8. #include <LibJS/Runtime/TypedArray.h>
  9. #include <LibJS/Runtime/Uint8Array.h>
  10. #include <LibJS/Runtime/VM.h>
  11. #include <LibJS/Runtime/ValueInlines.h>
  12. namespace JS {
  13. void Uint8ArrayPrototypeHelpers::initialize(Realm& realm, Object& prototype)
  14. {
  15. auto& vm = prototype.vm();
  16. static constexpr u8 attr = Attribute::Writable | Attribute::Configurable;
  17. prototype.define_native_function(realm, vm.names.toBase64, to_base64, 0, attr);
  18. }
  19. static ThrowCompletionOr<Alphabet> parse_alphabet(VM& vm, Object& options)
  20. {
  21. // Let alphabet be ? Get(opts, "alphabet").
  22. auto alphabet = TRY(options.get(vm.names.alphabet));
  23. // If alphabet is undefined, set alphabet to "base64".
  24. if (alphabet.is_undefined())
  25. return Alphabet::Base64;
  26. // If alphabet is neither "base64" nor "base64url", throw a TypeError exception.
  27. if (alphabet.is_string()) {
  28. if (alphabet.as_string().utf8_string_view() == "base64"sv)
  29. return Alphabet::Base64;
  30. if (alphabet.as_string().utf8_string_view() == "base64url"sv)
  31. return Alphabet::Base64URL;
  32. }
  33. return vm.throw_completion<TypeError>(ErrorType::OptionIsNotValidValue, alphabet, "alphabet"sv);
  34. }
  35. // 1 Uint8Array.prototype.toBase64 ( [ options ] ), https://tc39.es/proposal-arraybuffer-base64/spec/#sec-uint8array.prototype.tobase64
  36. JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_base64)
  37. {
  38. auto options_value = vm.argument(0);
  39. // 1. Let O be the this value.
  40. // 2. Perform ? ValidateUint8Array(O).
  41. auto typed_array = TRY(validate_uint8_array(vm));
  42. // 3. Let opts be ? GetOptionsObject(options).
  43. auto* options = TRY(Temporal::get_options_object(vm, options_value));
  44. // 4. Let alphabet be ? Get(opts, "alphabet").
  45. // 5. If alphabet is undefined, set alphabet to "base64".
  46. // 6. If alphabet is neither "base64" nor "base64url", throw a TypeError exception.
  47. auto alphabet = TRY(parse_alphabet(vm, *options));
  48. // 7. Let omitPadding be ToBoolean(? Get(opts, "omitPadding")).
  49. auto omit_padding_value = TRY(options->get(vm.names.omitPadding)).to_boolean();
  50. auto omit_padding = omit_padding_value ? AK::OmitPadding::Yes : AK::OmitPadding::No;
  51. // 8. Let toEncode be ? GetUint8ArrayBytes(O).
  52. auto to_encode = TRY(get_uint8_array_bytes(vm, typed_array));
  53. String out_ascii;
  54. // 9. If alphabet is "base64", then
  55. if (alphabet == Alphabet::Base64) {
  56. // a. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64
  57. // encoding specified in section 4 of RFC 4648. Padding is included if and only if omitPadding is false.
  58. out_ascii = MUST(encode_base64(to_encode, omit_padding));
  59. }
  60. // 10. Else,
  61. else {
  62. // a. Assert: alphabet is "base64url".
  63. // b. Let outAscii be the sequence of code points which results from encoding toEncode according to the base64url
  64. // encoding specified in section 5 of RFC 4648. Padding is included if and only if omitPadding is false.
  65. out_ascii = MUST(encode_base64url(to_encode, omit_padding));
  66. }
  67. // 11. Return CodePointsToString(outAscii).
  68. return PrimitiveString::create(vm, move(out_ascii));
  69. }
  70. // 7 ValidateUint8Array ( ta ), https://tc39.es/proposal-arraybuffer-base64/spec/#sec-validateuint8array
  71. ThrowCompletionOr<NonnullGCPtr<TypedArrayBase>> validate_uint8_array(VM& vm)
  72. {
  73. auto this_object = TRY(vm.this_value().to_object(vm));
  74. // 1. Perform ? RequireInternalSlot(ta, [[TypedArrayName]]).
  75. if (!this_object->is_typed_array())
  76. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Uint8Array");
  77. auto& typed_array = static_cast<TypedArrayBase&>(*this_object.ptr());
  78. // 2. If ta.[[TypedArrayName]] is not "Uint8Array", throw a TypeError exception.
  79. if (typed_array.kind() != TypedArrayBase::Kind::Uint8Array)
  80. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Uint8Array");
  81. // 3. Return UNUSED.
  82. return typed_array;
  83. }
  84. // 8 GetUint8ArrayBytes ( ta ), https://tc39.es/proposal-arraybuffer-base64/spec/#sec-getuint8arraybytes
  85. ThrowCompletionOr<ByteBuffer> get_uint8_array_bytes(VM& vm, TypedArrayBase const& typed_array)
  86. {
  87. // 1. Let buffer be ta.[[ViewedArrayBuffer]].
  88. // 2. Let taRecord be MakeTypedArrayWithBufferWitnessRecord(ta, SEQ-CST).
  89. auto typed_array_record = make_typed_array_with_buffer_witness_record(typed_array, ArrayBuffer::Order::SeqCst);
  90. // 3. If IsTypedArrayOutOfBounds(taRecord) is true, throw a TypeError exception.
  91. if (is_typed_array_out_of_bounds(typed_array_record))
  92. return vm.throw_completion<TypeError>(ErrorType::BufferOutOfBounds, "TypedArray"sv);
  93. // 4. Let len be TypedArrayLength(taRecord).
  94. auto length = typed_array_length(typed_array_record);
  95. // 5. Let byteOffset be ta.[[ByteOffset]].
  96. auto byte_offset = typed_array.byte_offset();
  97. // 6. Let bytes be a new empty List.
  98. ByteBuffer bytes;
  99. // 7. Let index be 0.
  100. // 8. Repeat, while index < len,
  101. for (u32 index = 0; index < length; ++index) {
  102. // a. Let byteIndex be byteOffset + index.
  103. auto byte_index = byte_offset + index;
  104. // b. Let byte be ℝ(GetValueFromBuffer(buffer, byteIndex, UINT8, true, UNORDERED)).
  105. auto byte = typed_array.get_value_from_buffer(byte_index, ArrayBuffer::Order::Unordered);
  106. // c. Append byte to bytes.
  107. bytes.append(MUST(byte.to_u8(vm)));
  108. // d. Set index to index + 1.
  109. }
  110. // 9. Return bytes.
  111. return bytes;
  112. }
  113. }