this repo has no description
at trunk 1847 lines 69 kB view raw
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 2#include "under-io-module.h" 3 4#include "builtins.h" 5#include "bytes-builtins.h" 6#include "byteslike.h" 7#include "file.h" 8#include "frame.h" 9#include "globals.h" 10#include "int-builtins.h" 11#include "modules.h" 12#include "object-builtins.h" 13#include "objects.h" 14#include "os.h" 15#include "runtime.h" 16#include "str-builtins.h" 17#include "thread.h" 18#include "type-builtins.h" 19#include "unicode.h" 20 21namespace py { 22 23RawObject FUNC(_io, _BytesIO_guard)(Thread* thread, Arguments args) { 24 HandleScope scope(thread); 25 Object self_obj(&scope, args.get(0)); 26 if (!thread->runtime()->isInstanceOfBytesIO(*self_obj)) { 27 return thread->raiseRequiresType(self_obj, ID(BytesIO)); 28 } 29 return NoneType::object(); 30} 31 32RawObject FUNC(_io, _BytesIO_closed_guard)(Thread* thread, Arguments args) { 33 HandleScope scope(thread); 34 Object self_obj(&scope, args.get(0)); 35 if (!thread->runtime()->isInstanceOfBytesIO(*self_obj)) { 36 return thread->raiseRequiresType(self_obj, ID(BytesIO)); 37 } 38 BytesIO self(&scope, *self_obj); 39 if (self.closed()) { 40 return thread->raiseWithFmt(LayoutId::kValueError, 41 "I/O operation on closed file."); 42 } 43 return NoneType::object(); 44} 45 46RawObject FUNC(_io, _BytesIO_seek)(Thread* thread, Arguments args) { 47 HandleScope scope(thread); 48 49 Runtime* runtime = thread->runtime(); 50 Object offset_obj(&scope, args.get(1)); 51 if (!runtime->isInstanceOfInt(*offset_obj)) { 52 return Unbound::object(); 53 } 54 55 Object whence_obj(&scope, args.get(2)); 56 if (!runtime->isInstanceOfInt(*whence_obj)) { 57 return Unbound::object(); 58 } 59 60 Object self_obj(&scope, args.get(0)); 61 if (!runtime->isInstanceOfBytesIO(*self_obj)) { 62 return thread->raiseRequiresType(self_obj, ID(BytesIO)); 63 } 64 BytesIO self(&scope, *self_obj); 65 if (self.closed()) { 66 return thread->raiseWithFmt(LayoutId::kValueError, 67 "I/O operation on closed file."); 68 } 69 70 Int offset_int(&scope, intUnderlying(*offset_obj)); 71 word offset = offset_int.asWordSaturated(); 72 if (!SmallInt::isValid(offset)) { 73 return thread->raiseWithFmt( 74 LayoutId::kOverflowError, 75 "cannot fit offset into an index-sized integer"); 76 } 77 Int whence_int(&scope, intUnderlying(*whence_obj)); 78 word whence = whence_int.asWordSaturated(); 79 word result; 80 switch (whence) { 81 case 0: 82 if (offset < 0) { 83 return thread->raiseWithFmt(LayoutId::kValueError, 84 "Negative seek value %d", offset); 85 } 86 self.setPos(offset); 87 return SmallInt::fromWord(offset); 88 case 1: 89 result = Utils::maximum(word{0}, self.pos() + offset); 90 self.setPos(result); 91 return SmallInt::fromWord(result); 92 case 2: 93 result = Utils::maximum( 94 word{0}, MutableBytes::cast(self.buffer()).length() + offset); 95 self.setPos(result); 96 return SmallInt::fromWord(result); 97 default: 98 if (SmallInt::isValid(whence)) { 99 return thread->raiseWithFmt(LayoutId::kValueError, 100 "Invalid whence (%w, should be 0, 1 or 2)", 101 whence); 102 } 103 return thread->raiseWithFmt(LayoutId::kOverflowError, 104 "Python int too large to convert to C long"); 105 } 106} 107 108RawObject FUNC(_io, _BytesIO_truncate)(Thread* thread, Arguments args) { 109 HandleScope scope(thread); 110 Object self(&scope, args.get(0)); 111 Runtime* runtime = thread->runtime(); 112 if (!runtime->isInstanceOfBytesIO(*self)) { 113 return thread->raiseRequiresType(self, ID(BytesIO)); 114 } 115 BytesIO bytes_io(&scope, *self); 116 if (bytes_io.closed()) { 117 return thread->raiseWithFmt(LayoutId::kValueError, 118 "I/O operation on closed file."); 119 } 120 Object size_obj(&scope, args.get(1)); 121 word size; 122 if (size_obj.isNoneType()) { 123 size = bytes_io.pos(); 124 } else { 125 if (size_obj.isError()) return *size_obj; 126 Int size_int(&scope, intUnderlying(*size_obj)); 127 // Allow SmallInt, Bool, and subclasses of Int containing SmallInt or Bool 128 if (!size_int.isSmallInt() && !size_int.isBool()) { 129 return thread->raiseWithFmt(LayoutId::kOverflowError, 130 "cannot fit '%T' into an index-sized integer", 131 &size_int); 132 } 133 size = size_int.asWord(); 134 if (size < 0) { 135 return thread->raiseWithFmt(LayoutId::kValueError, 136 "negative size value %d", size); 137 } 138 } 139 if (size < bytes_io.numItems()) { 140 bytes_io.setNumItems(size); 141 bytes_io.setPos(size); 142 } 143 return SmallInt::fromWord(size); 144} 145 146RawObject FUNC(_io, _StringIO_closed_guard)(Thread* thread, Arguments args) { 147 HandleScope scope(thread); 148 Runtime* runtime = thread->runtime(); 149 Object self_obj(&scope, args.get(0)); 150 if (!runtime->isInstanceOfStringIO(*self_obj)) { 151 return thread->raiseRequiresType(self_obj, ID(StringIO)); 152 } 153 StringIO self(&scope, *self_obj); 154 if (self.closed()) { 155 return thread->raiseWithFmt(LayoutId::kValueError, 156 "I/O operation on closed file."); 157 } 158 return NoneType::object(); 159} 160 161RawObject FUNC(_io, _StringIO_seek)(Thread* thread, Arguments args) { 162 HandleScope scope(thread); 163 Object offset_obj(&scope, args.get(1)); 164 Object whence_obj(&scope, args.get(2)); 165 Runtime* runtime = thread->runtime(); 166 if (!runtime->isInstanceOfInt(*offset_obj) || 167 !runtime->isInstanceOfInt(*whence_obj)) { 168 return Unbound::object(); 169 } 170 Object self_obj(&scope, args.get(0)); 171 if (!runtime->isInstanceOfStringIO(*self_obj)) { 172 return thread->raiseRequiresType(self_obj, ID(StringIO)); 173 } 174 StringIO self(&scope, *self_obj); 175 if (self.closed()) { 176 return thread->raiseWithFmt(LayoutId::kValueError, 177 "I/O operation on closed file."); 178 } 179 word offset = intUnderlying(*offset_obj).asWordSaturated(); 180 if (!SmallInt::isValid(offset)) { 181 return thread->raiseWithFmt( 182 LayoutId::kOverflowError, 183 "cannot fit offset into an index-sized integer"); 184 } 185 if (!runtime->isInstanceOfInt(*whence_obj)) { 186 return thread->raiseWithFmt(LayoutId::kTypeError, 187 "Invalid whence (should be 0, 1 or 2)"); 188 } 189 word whence = intUnderlying(*whence_obj).asWordSaturated(); 190 switch (whence) { 191 case 0: 192 if (offset < 0) { 193 return thread->raiseWithFmt(LayoutId::kValueError, 194 "Negative seek position %d", offset); 195 } 196 self.setPos(offset); 197 return *offset_obj; 198 case 1: 199 if (offset != 0) { 200 return thread->raiseWithFmt(LayoutId::kOSError, 201 "Can't do nonzero cur-relative seeks"); 202 } 203 return SmallInt::fromWord(self.pos()); 204 case 2: { 205 if (offset != 0) { 206 return thread->raiseWithFmt(LayoutId::kOSError, 207 "Can't do nonzero end-relative seeks"); 208 } 209 word new_pos = MutableBytes::cast(self.buffer()).length(); 210 self.setPos(new_pos); 211 return SmallInt::fromWord(new_pos); 212 } 213 default: 214 if (SmallInt::isValid(whence)) { 215 return thread->raiseWithFmt(LayoutId::kValueError, 216 "Invalid whence (%w, should be 0, 1 or 2)", 217 whence); 218 } else { 219 return thread->raiseWithFmt( 220 LayoutId::kOverflowError, 221 "Python int too large to convert to C long"); 222 } 223 } 224} 225 226static RawObject initReadBuf(Thread* thread, 227 const BufferedReader& buffered_reader) { 228 HandleScope scope(thread); 229 word buffer_size = buffered_reader.bufferSize(); 230 MutableBytes read_buf( 231 &scope, thread->runtime()->newMutableBytesUninitialized(buffer_size)); 232 buffered_reader.setReadBuf(*read_buf); 233 buffered_reader.setReadPos(0); 234 buffered_reader.setBufferNumBytes(0); 235 return *read_buf; 236} 237 238// If there is no buffer allocated yet, allocate one. If there are remaining 239// bytes in the buffer, move them to position 0; Set buffer read position to 0. 240static RawObject rewindOrInitReadBuf(Thread* thread, 241 const BufferedReader& buffered_reader) { 242 HandleScope scope(thread); 243 Object read_buf_obj(&scope, buffered_reader.readBuf()); 244 word read_pos = buffered_reader.readPos(); 245 if (read_pos > 0) { 246 MutableBytes read_buf(&scope, *read_buf_obj); 247 word buffer_num_bytes = buffered_reader.bufferNumBytes(); 248 read_buf.replaceFromWithStartAt(0, *read_buf, buffer_num_bytes - read_pos, 249 read_pos); 250 buffered_reader.setBufferNumBytes(buffer_num_bytes - read_pos); 251 buffered_reader.setReadPos(0); 252 return *read_buf; 253 } 254 if (read_buf_obj.isNoneType()) { 255 return initReadBuf(thread, buffered_reader); 256 } 257 return *read_buf_obj; 258} 259 260// Perform one read operation to re-fill the buffer. 261static RawObject fillBuffer(Thread* thread, const Object& raw_file, 262 const MutableBytes& buffer, 263 word* buffer_num_bytes) { 264 HandleScope scope(thread); 265 word buffer_size = buffer.length(); 266 word wanted = buffer_size - *buffer_num_bytes; 267 Object wanted_int(&scope, SmallInt::fromWord(wanted)); 268 Object result_obj(&scope, 269 thread->invokeMethod2(raw_file, ID(read), wanted_int)); 270 if (result_obj.isError()) { 271 if (result_obj.isErrorException()) return *result_obj; 272 if (result_obj.isErrorNotFound()) { 273 if (raw_file.isNoneType()) { 274 return thread->raiseWithFmt(LayoutId::kValueError, 275 "raw stream has been detached"); 276 } 277 Object name(&scope, thread->runtime()->symbols()->at(ID(read))); 278 return objectRaiseAttributeError(thread, raw_file, name); 279 } 280 } 281 if (result_obj.isNoneType()) return NoneType::object(); 282 283 Runtime* runtime = thread->runtime(); 284 Bytes bytes(&scope, Bytes::empty()); 285 word length; 286 if (runtime->isInstanceOfBytes(*result_obj)) { 287 bytes = bytesUnderlying(*result_obj); 288 length = bytes.length(); 289 } else if (runtime->isInstanceOfBytearray(*result_obj)) { 290 Bytearray byte_array(&scope, *result_obj); 291 bytes = byte_array.items(); 292 length = byte_array.numItems(); 293 } else if (runtime->isByteslike(*result_obj)) { 294 UNIMPLEMENTED("byteslike"); 295 } else { 296 return thread->raiseWithFmt(LayoutId::kTypeError, 297 "read() should return bytes"); 298 } 299 if (length == 0) return Bytes::empty(); 300 if (length > wanted && wanted != -1) { 301 UNIMPLEMENTED("read() returned too many bytes"); 302 } 303 buffer.replaceFromWithBytes(*buffer_num_bytes, *bytes, length); 304 *buffer_num_bytes += length; 305 return Unbound::object(); 306} 307 308// Helper function for read requests that are bigger (or close to) than the size 309// of the buffer. 310static RawObject readBig(Thread* thread, const BufferedReader& buffered_reader, 311 word num_bytes) { 312 HandleScope scope(thread); 313 Runtime* runtime = thread->runtime(); 314 word available = buffered_reader.bufferNumBytes() - buffered_reader.readPos(); 315 DCHECK(num_bytes == kMaxWord || num_bytes > available, 316 "num_bytes should be big"); 317 318 // TODO(T59000373): We could specialize this to avoid the intermediate 319 // allocations when the size of the result is known and `readinto` is 320 // available. 321 322 word length = available; 323 Object chunks(&scope, NoneType::object()); 324 Object chunk(&scope, NoneType::object()); 325 Object raw_file(&scope, buffered_reader.underlying()); 326 Bytes bytes(&scope, Bytes::empty()); 327 for (;;) { 328 word wanted = (num_bytes == kMaxWord) ? 32 * kKiB : num_bytes - available; 329 Object wanted_int(&scope, SmallInt::fromWord(wanted)); 330 Object result_obj(&scope, 331 thread->invokeMethod2(raw_file, ID(read), wanted_int)); 332 if (result_obj.isError()) { 333 if (result_obj.isErrorException()) return *result_obj; 334 if (result_obj.isErrorNotFound()) { 335 if (raw_file.isNoneType()) { 336 return thread->raiseWithFmt(LayoutId::kValueError, 337 "raw stream has been detached"); 338 } 339 Object name(&scope, runtime->symbols()->at(ID(read))); 340 return objectRaiseAttributeError(thread, raw_file, name); 341 } 342 } 343 if (result_obj.isNoneType()) { 344 if (length == 0) return NoneType::object(); 345 break; 346 } 347 348 word chunk_length; 349 if (runtime->isInstanceOfBytes(*result_obj)) { 350 bytes = bytesUnderlying(*result_obj); 351 chunk = *bytes; 352 chunk_length = bytes.length(); 353 } else if (runtime->isInstanceOfBytearray(*result_obj)) { 354 Bytearray byte_array(&scope, *result_obj); 355 bytes = byte_array.items(); 356 chunk = *byte_array; 357 chunk_length = byte_array.numItems(); 358 } else if (runtime->isByteslike(*result_obj)) { 359 UNIMPLEMENTED("byteslike"); 360 } else { 361 return thread->raiseWithFmt(LayoutId::kTypeError, 362 "read() should return bytes"); 363 } 364 365 if (chunk_length == 0) { 366 if (length == 0) return *chunk; 367 break; 368 } 369 if (chunk_length > wanted) { 370 UNIMPLEMENTED("read() returned too many bytes"); 371 } 372 373 if (chunks.isNoneType()) { 374 chunks = runtime->newList(); 375 } 376 List list(&scope, *chunks); 377 runtime->listAdd(thread, list, chunk); 378 379 length += chunk_length; 380 if (num_bytes != kMaxWord) { 381 num_bytes -= chunk_length; 382 if (num_bytes <= 0) break; 383 } 384 } 385 386 MutableBytes result(&scope, runtime->newMutableBytesUninitialized(length)); 387 word idx = 0; 388 if (available > 0) { 389 result.replaceFromWithStartAt(idx, 390 MutableBytes::cast(buffered_reader.readBuf()), 391 available, buffered_reader.readPos()); 392 idx += available; 393 buffered_reader.setReadPos(0); 394 buffered_reader.setBufferNumBytes(0); 395 } 396 if (!chunks.isNoneType()) { 397 List list(&scope, *chunks); 398 for (word i = 0, num_items = list.numItems(); i < num_items; i++) { 399 chunk = list.at(i); 400 word chunk_length; 401 if (chunk.isBytes()) { 402 bytes = *chunk; 403 chunk_length = bytes.length(); 404 } else { 405 Bytearray byte_array(&scope, *chunk); 406 bytes = byte_array.items(); 407 chunk_length = byte_array.numItems(); 408 } 409 result.replaceFromWithBytes(idx, *bytes, chunk_length); 410 idx += chunk_length; 411 } 412 } 413 DCHECK(idx == length, "mismatched length"); 414 return result.becomeImmutable(); 415} 416 417RawObject FUNC(_io, _buffered_reader_clear_buffer)(Thread* thread, 418 Arguments args) { 419 HandleScope scope(thread); 420 Runtime* runtime = thread->runtime(); 421 Object self_obj(&scope, args.get(0)); 422 if (!runtime->isInstanceOfBufferedReader(*self_obj)) { 423 return thread->raiseRequiresType(self_obj, ID(BufferedReader)); 424 } 425 BufferedReader self(&scope, *self_obj); 426 self.setReadPos(0); 427 self.setBufferNumBytes(0); 428 return NoneType::object(); 429} 430 431RawObject FUNC(_io, _buffered_reader_init)(Thread* thread, Arguments args) { 432 HandleScope scope(thread); 433 Runtime* runtime = thread->runtime(); 434 Object self_obj(&scope, args.get(0)); 435 if (!runtime->isInstanceOfBufferedReader(*self_obj)) { 436 return thread->raiseRequiresType(self_obj, ID(BufferedReader)); 437 } 438 BufferedReader self(&scope, *self_obj); 439 440 Int buffer_size_obj(&scope, intUnderlying(args.get(1))); 441 if (!buffer_size_obj.isSmallInt() && !buffer_size_obj.isBool()) { 442 return thread->raiseWithFmt(LayoutId::kOverflowError, 443 "cannot fit value into an index-sized integer"); 444 } 445 word buffer_size = buffer_size_obj.asWord(); 446 DCHECK(buffer_size > 0, "invalid buffer size"); 447 448 self.setBufferSize(buffer_size); 449 self.setReadPos(0); 450 self.setBufferNumBytes(0); 451 // readBuf() starts out as `None` and is initialized lazily so patterns like 452 // just doing a single `read()` on the whole buffered reader will not even 453 // bother allocating the read buffer. There may however be already a 454 // `_read_buf` allocated previously when `_init` is used to clear the buffer 455 // as part of `seek`. 456 if (!self.readBuf().isNoneType() && 457 MutableBytes::cast(self.readBuf()).length() != buffer_size) { 458 return thread->raiseWithFmt(LayoutId::kValueError, "length mismatch"); 459 } 460 return NoneType::object(); 461} 462 463RawObject FUNC(_io, _buffered_reader_peek)(Thread* thread, Arguments args) { 464 // TODO(T58490915): Investigate what thread safety guarantees python has, 465 // and add locking code as necessary. 466 HandleScope scope(thread); 467 Runtime* runtime = thread->runtime(); 468 Object self_obj(&scope, args.get(0)); 469 if (!runtime->isInstanceOfBufferedReader(*self_obj)) { 470 return thread->raiseRequiresType(self_obj, ID(BufferedReader)); 471 } 472 BufferedReader self(&scope, *self_obj); 473 474 Object num_bytes_obj(&scope, args.get(1)); 475 // TODO(T59004416) Is there a way to push intFromIndex() towards managed? 476 Object num_bytes_int_obj(&scope, intFromIndex(thread, num_bytes_obj)); 477 if (num_bytes_int_obj.isErrorException()) return *num_bytes_int_obj; 478 Int num_bytes_int(&scope, intUnderlying(*num_bytes_int_obj)); 479 if (!num_bytes_int.isSmallInt() && !num_bytes_int.isBool()) { 480 return thread->raiseWithFmt(LayoutId::kOverflowError, 481 "cannot fit value into an index-sized integer"); 482 } 483 word num_bytes = num_bytes_int.asWord(); 484 485 word buffer_num_bytes = self.bufferNumBytes(); 486 word read_pos = self.readPos(); 487 Object read_buf_obj(&scope, self.readBuf()); 488 word available = buffer_num_bytes - read_pos; 489 if (num_bytes <= 0 || num_bytes > available) { 490 // Perform a lightweight "reset" of the read buffer that does not move data 491 // around. 492 if (read_buf_obj.isNoneType()) { 493 read_buf_obj = initReadBuf(thread, self); 494 } else if (available == 0) { 495 buffer_num_bytes = 0; 496 read_pos = 0; 497 self.setReadPos(0); 498 self.setBufferNumBytes(0); 499 } 500 // Attempt a single read to fill the buffer. 501 MutableBytes read_buf(&scope, *read_buf_obj); 502 Object raw_file(&scope, self.underlying()); 503 Object fill_result( 504 &scope, fillBuffer(thread, raw_file, read_buf, &buffer_num_bytes)); 505 if (fill_result.isErrorException()) return *fill_result; 506 self.setBufferNumBytes(buffer_num_bytes); 507 available = buffer_num_bytes - read_pos; 508 } 509 510 Bytes read_buf(&scope, *read_buf_obj); 511 return bytesSubseq(thread, read_buf, read_pos, available); 512} 513 514RawObject FUNC(_io, _buffered_reader_read)(Thread* thread, Arguments args) { 515 // TODO(T58490915): Investigate what thread safety guarantees python has, 516 // and add locking code as necessary. 517 HandleScope scope(thread); 518 Runtime* runtime = thread->runtime(); 519 Object self_obj(&scope, args.get(0)); 520 if (!runtime->isInstanceOfBufferedReader(*self_obj)) { 521 return thread->raiseRequiresType(self_obj, ID(BufferedReader)); 522 } 523 BufferedReader self(&scope, *self_obj); 524 525 Object num_bytes_obj(&scope, args.get(1)); 526 word num_bytes; 527 if (num_bytes_obj.isNoneType()) { 528 num_bytes = kMaxWord; 529 } else { 530 // TODO(T59004416) Is there a way to push intFromIndex() towards managed? 531 Object num_bytes_int_obj(&scope, intFromIndex(thread, num_bytes_obj)); 532 if (num_bytes_int_obj.isErrorException()) return *num_bytes_int_obj; 533 Int num_bytes_int(&scope, intUnderlying(*num_bytes_int_obj)); 534 if (!num_bytes_int.isSmallInt() && !num_bytes_int.isBool()) { 535 return thread->raiseWithFmt( 536 LayoutId::kOverflowError, 537 "cannot fit value into an index-sized integer"); 538 } 539 num_bytes = num_bytes_int.asWord(); 540 if (num_bytes == -1) { 541 num_bytes = kMaxWord; 542 } else if (num_bytes < 0) { 543 return thread->raiseWithFmt(LayoutId::kValueError, 544 "read length must be non-negative or -1"); 545 } 546 } 547 548 word buffer_num_bytes = self.bufferNumBytes(); 549 word read_pos = self.readPos(); 550 551 word available = buffer_num_bytes - read_pos; 552 DCHECK(available >= 0, "invalid state"); 553 if (num_bytes <= available) { 554 word new_read_pos = read_pos + num_bytes; 555 self.setReadPos(new_read_pos); 556 Bytes read_buf(&scope, self.readBuf()); 557 return bytesSubseq(thread, read_buf, read_pos, num_bytes); 558 } 559 560 Object raw_file(&scope, self.underlying()); 561 if (num_bytes == kMaxWord) { 562 Object readall_result(&scope, thread->invokeMethod1(raw_file, ID(readall))); 563 if (readall_result.isErrorException()) return *readall_result; 564 if (!readall_result.isErrorNotFound()) { 565 Bytes bytes(&scope, Bytes::empty()); 566 word bytes_length; 567 if (readall_result.isNoneType()) { 568 if (available == 0) return NoneType::object(); 569 bytes_length = 0; 570 } else if (runtime->isInstanceOfBytes(*readall_result)) { 571 bytes = bytesUnderlying(*readall_result); 572 bytes_length = bytes.length(); 573 } else if (runtime->isInstanceOfBytearray(*readall_result)) { 574 Bytearray byte_array(&scope, *readall_result); 575 bytes = byte_array.items(); 576 bytes_length = byte_array.numItems(); 577 } else if (runtime->isByteslike(*readall_result)) { 578 UNIMPLEMENTED("byteslike"); 579 } else { 580 return thread->raiseWithFmt(LayoutId::kTypeError, 581 "readall() should return bytes"); 582 } 583 word length = bytes_length + available; 584 if (length == 0) return Bytes::empty(); 585 MutableBytes result(&scope, 586 runtime->newMutableBytesUninitialized(length)); 587 word idx = 0; 588 if (available > 0) { 589 result.replaceFromWithStartAt(idx, MutableBytes::cast(self.readBuf()), 590 available, read_pos); 591 idx += available; 592 self.setReadPos(0); 593 self.setBufferNumBytes(0); 594 } 595 if (bytes_length > 0) { 596 result.replaceFromWithBytes(idx, *bytes, bytes_length); 597 idx += bytes_length; 598 } 599 DCHECK(idx == length, "length mismatch"); 600 return result.becomeImmutable(); 601 } 602 } 603 604 // Use alternate reading code for big requests where buffering would not help. 605 // (This is also used for the num_bytes==kMaxWord (aka "readall") case when 606 // the file object does not provide a "readall" method. 607 word buffer_size = self.bufferSize(); 608 if (num_bytes > (buffer_size / 2)) { 609 return readBig(thread, self, num_bytes); 610 } 611 612 // Fill buffer until we have enough bytes available. 613 MutableBytes read_buf(&scope, rewindOrInitReadBuf(thread, self)); 614 buffer_num_bytes = self.bufferNumBytes(); 615 Object fill_result(&scope, NoneType::object()); 616 do { 617 fill_result = fillBuffer(thread, raw_file, read_buf, &buffer_num_bytes); 618 if (fill_result.isErrorException()) return *fill_result; 619 if (!fill_result.isUnbound()) { 620 if (buffer_num_bytes == 0) return *fill_result; 621 break; 622 } 623 } while (buffer_num_bytes < num_bytes); 624 625 word length = Utils::minimum(buffer_num_bytes, num_bytes); 626 self.setBufferNumBytes(buffer_num_bytes); 627 self.setReadPos(length); 628 Bytes read_buf_bytes(&scope, *read_buf); 629 return bytesSubseq(thread, read_buf_bytes, 0, length); 630} 631 632RawObject FUNC(_io, _buffered_reader_readline)(Thread* thread, Arguments args) { 633 // TODO(T58490915): Investigate what thread safety guarantees Python has, 634 // and add locking code as necessary. 635 HandleScope scope(thread); 636 Runtime* runtime = thread->runtime(); 637 Object self_obj(&scope, args.get(0)); 638 if (!runtime->isInstanceOfBufferedReader(*self_obj)) { 639 return thread->raiseRequiresType(self_obj, ID(BufferedReader)); 640 } 641 BufferedReader self(&scope, *self_obj); 642 643 Object max_line_bytes_obj(&scope, args.get(1)); 644 word max_line_bytes = kMaxWord; 645 if (!max_line_bytes_obj.isNoneType()) { 646 // TODO(T59004416) Is there a way to push intFromIndex() towards managed? 647 Object max_line_bytes_int_obj(&scope, 648 intFromIndex(thread, max_line_bytes_obj)); 649 if (max_line_bytes_int_obj.isErrorException()) { 650 return *max_line_bytes_int_obj; 651 } 652 Int max_line_bytes_int(&scope, intUnderlying(*max_line_bytes_int_obj)); 653 if (!max_line_bytes_int.isSmallInt() && !max_line_bytes_int.isBool()) { 654 return thread->raiseWithFmt( 655 LayoutId::kOverflowError, 656 "cannot fit value into an index-sized integer"); 657 } 658 max_line_bytes = max_line_bytes_int.asWord(); 659 if (max_line_bytes == -1) { 660 max_line_bytes = kMaxWord; 661 } else if (max_line_bytes < 0) { 662 return thread->raiseWithFmt(LayoutId::kValueError, 663 "read length must be non-negative or -1"); 664 } 665 } 666 667 word buffer_num_bytes = self.bufferNumBytes(); 668 word read_pos = self.readPos(); 669 word available = buffer_num_bytes - read_pos; 670 if (available > 0) { 671 MutableBytes read_buf(&scope, self.readBuf()); 672 word line_end = -1; 673 word scan_length = available; 674 if (available >= max_line_bytes) { 675 scan_length = max_line_bytes; 676 line_end = read_pos + max_line_bytes; 677 } else { 678 max_line_bytes -= available; 679 } 680 word newline_index = read_buf.findByte('\n', read_pos, scan_length); 681 if (newline_index >= 0) { 682 line_end = newline_index + 1; 683 } 684 if (line_end >= 0) { 685 self.setReadPos(line_end); 686 Bytes read_buf_bytes(&scope, *read_buf); 687 return bytesSubseq(thread, read_buf_bytes, read_pos, line_end - read_pos); 688 } 689 } 690 691 MutableBytes read_buf(&scope, rewindOrInitReadBuf(thread, self)); 692 buffer_num_bytes = self.bufferNumBytes(); 693 word buffer_size = self.bufferSize(); 694 695 Object raw_file(&scope, self.underlying()); 696 Object fill_result(&scope, NoneType::object()); 697 Object chunks(&scope, NoneType::object()); 698 word line_end = -1; 699 // Outer loop in case for case where a line is longer than a single buffer. In 700 // that case we will collect the pieces in the `chunks` list. 701 for (;;) { 702 // Fill buffer until we find a newline character or filled up the whole 703 // buffer. 704 do { 705 word old_buffer_num_bytes = buffer_num_bytes; 706 fill_result = fillBuffer(thread, raw_file, read_buf, &buffer_num_bytes); 707 if (fill_result.isErrorException()) return *fill_result; 708 if (!fill_result.isUnbound()) { 709 if (buffer_num_bytes == 0 && chunks.isNoneType()) return *fill_result; 710 line_end = buffer_num_bytes; 711 break; 712 } 713 714 word scan_start = old_buffer_num_bytes; 715 word scan_length = buffer_num_bytes - old_buffer_num_bytes; 716 if (scan_length >= max_line_bytes) { 717 scan_length = max_line_bytes; 718 line_end = scan_start + max_line_bytes; 719 } else { 720 max_line_bytes -= buffer_num_bytes - old_buffer_num_bytes; 721 } 722 word newline_index = read_buf.findByte('\n', scan_start, scan_length); 723 if (newline_index >= 0) { 724 line_end = newline_index + 1; 725 break; 726 } 727 } while (line_end < 0 && buffer_num_bytes < buffer_size); 728 729 if (line_end < 0) { 730 // The line is longer than the buffer: Add the current buffer to the 731 // chunks list, create a fresh one and repeat scan loop. 732 if (chunks.isNoneType()) { 733 chunks = runtime->newList(); 734 } 735 List list(&scope, *chunks); 736 runtime->listAdd(thread, list, read_buf); 737 738 // Create a fresh buffer and retry. 739 read_buf = initReadBuf(thread, self); 740 buffer_num_bytes = 0; 741 continue; 742 } 743 break; 744 } 745 746 word length = line_end; 747 if (!chunks.isNoneType()) { 748 List list(&scope, *chunks); 749 for (word i = 0, num_items = list.numItems(); i < num_items; i++) { 750 length += MutableBytes::cast(list.at(i)).length(); 751 } 752 } 753 MutableBytes result(&scope, runtime->newMutableBytesUninitialized(length)); 754 word idx = 0; 755 if (!chunks.isNoneType()) { 756 List list(&scope, *chunks); 757 Bytes chunk(&scope, Bytes::empty()); 758 for (word i = 0, num_items = list.numItems(); i < num_items; i++) { 759 chunk = list.at(i); 760 word chunk_length = chunk.length(); 761 result.replaceFromWithBytes(idx, *chunk, chunk_length); 762 idx += chunk_length; 763 } 764 } 765 result.replaceFromWith(idx, *read_buf, line_end); 766 DCHECK(idx + line_end == length, "length mismatch"); 767 self.setReadPos(line_end); 768 self.setBufferNumBytes(buffer_num_bytes); 769 return result.becomeImmutable(); 770} 771 772RawObject FUNC(_io, _TextIOWrapper_attached_guard)(Thread* thread, 773 Arguments args) { 774 HandleScope scope(thread); 775 Runtime* runtime = thread->runtime(); 776 Object self_obj(&scope, args.get(0)); 777 if (!runtime->isInstanceOfTextIOWrapper(*self_obj)) { 778 return thread->raiseRequiresType(self_obj, ID(TextIOWrapper)); 779 } 780 TextIOWrapper self(&scope, *self_obj); 781 if (self.detached()) { 782 return thread->raiseWithFmt(LayoutId::kValueError, 783 "underlying buffer has been detached"); 784 } 785 return NoneType::object(); 786} 787 788RawObject FUNC(_io, _TextIOWrapper_attached_closed_guard)(Thread* thread, 789 Arguments args) { 790 HandleScope scope(thread); 791 Object self_obj(&scope, args.get(0)); 792 Runtime* runtime = thread->runtime(); 793 if (!runtime->isInstanceOfTextIOWrapper(*self_obj)) { 794 return thread->raiseRequiresType(self_obj, ID(TextIOWrapper)); 795 } 796 TextIOWrapper self(&scope, *self_obj); 797 if (self.detached()) { 798 return thread->raiseWithFmt(LayoutId::kValueError, 799 "underlying buffer has been detached"); 800 } 801 Object buffer_obj(&scope, self.buffer()); 802 if (runtime->isInstanceOfBufferedReader(*buffer_obj)) { 803 BufferedReader buffer(&scope, *buffer_obj); 804 if (buffer.closed()) { 805 return thread->raiseWithFmt(LayoutId::kValueError, 806 "I/O operation on closed file."); 807 } 808 return NoneType::object(); 809 } 810 811 if (runtime->isInstanceOfBufferedWriter(*buffer_obj)) { 812 BufferedWriter buffer(&scope, *buffer_obj); 813 if (!buffer.closed()) { 814 return NoneType::object(); 815 } 816 return thread->raiseWithFmt(LayoutId::kValueError, 817 "I/O operation on closed file."); 818 } 819 // TODO(T61927696): Add closed check support for other types of buffer 820 return Unbound::object(); 821} 822 823RawObject FUNC(_io, 824 _TextIOWrapper_attached_closed_seekable_guard)(Thread* thread, 825 Arguments args) { 826 HandleScope scope(thread); 827 Object self_obj(&scope, args.get(0)); 828 Runtime* runtime = thread->runtime(); 829 if (!runtime->isInstanceOfTextIOWrapper(*self_obj)) { 830 return thread->raiseRequiresType(self_obj, ID(TextIOWrapper)); 831 } 832 TextIOWrapper self(&scope, *self_obj); 833 if (self.detached()) { 834 return thread->raiseWithFmt(LayoutId::kValueError, 835 "underlying buffer has been detached"); 836 } 837 Object buffer_obj(&scope, self.buffer()); 838 if (runtime->isInstanceOfBufferedReader(*buffer_obj)) { 839 BufferedReader buffer(&scope, *buffer_obj); 840 if (buffer.closed()) { 841 return thread->raiseWithFmt(LayoutId::kValueError, 842 "I/O operation on closed file."); 843 } 844 // TODO(T61927696): change this when TextIOWrapper.seekable() returns bool 845 Object seekable_obj(&scope, self.seekable()); 846 if (seekable_obj.isNoneType() || seekable_obj == Bool::falseObj()) { 847 return thread->raiseWithFmt(LayoutId::kValueError, 848 "underlying stream is not seekable"); 849 } 850 return NoneType::object(); 851 } 852 853 if (runtime->isInstanceOfBufferedWriter(*buffer_obj)) { 854 BufferedWriter buffer(&scope, *buffer_obj); 855 if (!buffer.closed()) { 856 // TODO(T61927696): change this when TextIOWrapper.seekable() returns bool 857 Object seekable_obj(&scope, self.seekable()); 858 if (seekable_obj.isNoneType() || seekable_obj == Bool::falseObj()) { 859 return thread->raiseWithFmt(LayoutId::kValueError, 860 "underlying stream is not seekable"); 861 } 862 return NoneType::object(); 863 } 864 return thread->raiseWithFmt(LayoutId::kValueError, 865 "I/O operation on closed file."); 866 } 867 868 // TODO(T61927696): Add closed check support for other types of buffer 869 return Unbound::object(); 870} 871 872// Copy the bytes of a UTF-8 encoded string with no surrogates to the write 873// buffer (a Bytearray) of underlying Bufferedwriter of TextIOWrapper 874// If the length of write buffer will be larger than 875// BufferedWriter.bufferSize(), return Unbound to escape to managed code and 876// call BufferedWriter.write() 877// If the newline is "\r\n", return Unbound to use managed code 878// If text_io.lineBuffering() or haslf or "\r" in text, return Unbound to 879// managed code to use flush() 880// TODO(T61927696): Implement native version of BufferedWriter._flush_unlocked() 881// with FileIO as BufferedWriter.raw. With that function, we can do flush in 882// here. 883RawObject FUNC(_io, _TextIOWrapper_write_UTF8)(Thread* thread, Arguments args) { 884 HandleScope scope(thread); 885 886 Object text_obj(&scope, args.get(1)); 887 Runtime* runtime = thread->runtime(); 888 if (!runtime->isInstanceOfStr(*text_obj)) { 889 return thread->raiseWithFmt(LayoutId::kTypeError, 890 "write() argument must be str, not %T", 891 &text_obj); 892 } 893 894 Object self_obj(&scope, args.get(0)); 895 if (!runtime->isInstanceOfTextIOWrapper(*self_obj)) { 896 return thread->raiseRequiresType(self_obj, ID(TextIOWrapper)); 897 } 898 TextIOWrapper text_io(&scope, *self_obj); 899 if (text_io.detached()) { 900 return thread->raiseWithFmt(LayoutId::kValueError, 901 "underlying buffer has been detached"); 902 } 903 904 Object buffer_obj(&scope, text_io.buffer()); 905 if (!buffer_obj.isBufferedWriter()) { 906 return Unbound::object(); 907 } 908 BufferedWriter buffer(&scope, text_io.buffer()); 909 if (buffer.closed()) { 910 return thread->raiseWithFmt(LayoutId::kTypeError, 911 "I/O operation on closed file."); 912 } 913 914 if (text_io.encoding() != SmallStr::fromCStr("utf-8") && 915 text_io.encoding() != SmallStr::fromCStr("UTF-8")) { 916 return Unbound::object(); 917 } 918 Str writenl(&scope, text_io.writenl()); 919 920 // Only allow writenl to be cr or lf in this short cut 921 if (!text_io.writetranslate() || writenl == SmallStr::fromCStr("\r\n")) { 922 return Unbound::object(); 923 } 924 925 Str text(&scope, strUnderlying(*text_obj)); 926 word text_len = text.length(); 927 928 Bytearray write_buffer(&scope, buffer.writeBuf()); 929 word old_len = write_buffer.numItems(); 930 word new_len = old_len + text_len; 931 runtime->bytearrayEnsureCapacity(thread, write_buffer, new_len); 932 MutableBytes write_buffer_bytes(&scope, write_buffer.items()); 933 write_buffer_bytes.replaceFromWithStr(old_len, *text, text_len); 934 write_buffer.setNumItems(new_len); 935 936 int32_t codepoint; 937 word num_bytes; 938 bool hasnl = false; 939 940 if (writenl == SmallStr::fromCStr("\n")) { 941 for (word offset = 0; offset < text_len;) { 942 codepoint = text.codePointAt(offset, &num_bytes); 943 if (Unicode::isSurrogate(codepoint)) { 944 write_buffer.downsize(old_len); 945 return Unbound::object(); 946 } 947 if (num_bytes == 1) { 948 if (text.byteAt(offset) == '\n' || text.byteAt(offset) == '\r') { 949 hasnl = true; 950 } 951 } 952 offset += num_bytes; 953 } 954 } else { 955 for (word offset = 0; offset < text_len;) { 956 codepoint = text.codePointAt(offset, &num_bytes); 957 if (Unicode::isSurrogate(codepoint)) { 958 write_buffer.downsize(old_len); 959 return Unbound::object(); 960 } 961 if (num_bytes == 1) { 962 if (text.byteAt(offset) == '\n') { 963 hasnl = true; 964 write_buffer_bytes.byteAtPut(offset + old_len, byte{'\r'}); 965 offset += num_bytes; 966 continue; 967 } 968 if (text.byteAt(offset) == '\r') { 969 hasnl = true; 970 offset += num_bytes; 971 continue; 972 } 973 } 974 offset += num_bytes; 975 } 976 } 977 978 if (text_io.lineBuffering() && hasnl) { 979 // TODO(T61927696): Implement native support for 980 // BufferedWriter._flush_unlocked to do flush here 981 Object flush_result(&scope, thread->invokeMethod1(buffer, ID(flush))); 982 if (flush_result.isErrorException()) return *flush_result; 983 text_io.setTelling(text_io.seekable()); 984 } 985 986 text_io.setDecodedChars(Str::empty()); 987 text_io.setSnapshot(NoneType::object()); 988 Object decoder_obj(&scope, text_io.decoder()); 989 if (!decoder_obj.isNoneType()) { 990 Object reset_result(&scope, thread->invokeMethod1(decoder_obj, ID(reset))); 991 if (reset_result.isErrorException()) return *reset_result; 992 } 993 994 return SmallInt::fromWord(text_len); 995} 996 997static const BuiltinAttribute kUnderIOBaseAttributes[] = { 998 {ID(_closed), RawUnderIOBase::kClosedOffset}, 999}; 1000 1001static const BuiltinAttribute kIncrementalNewlineDecoderAttributes[] = { 1002 {ID(_errors), RawIncrementalNewlineDecoder::kErrorsOffset}, 1003 {ID(_translate), RawIncrementalNewlineDecoder::kTranslateOffset}, 1004 {ID(_decoder), RawIncrementalNewlineDecoder::kDecoderOffset}, 1005 {ID(_seennl), RawIncrementalNewlineDecoder::kSeennlOffset}, 1006 {ID(_pendingcr), RawIncrementalNewlineDecoder::kPendingcrOffset}, 1007}; 1008 1009static const BuiltinAttribute kUnderBufferedIOMixinAttributes[] = { 1010 {ID(_raw), RawUnderBufferedIOMixin::kUnderlyingOffset}, 1011}; 1012 1013static const BuiltinAttribute kBufferedRandomAttributes[] = { 1014 {ID(buffer_size), RawBufferedRandom::kBufferSizeOffset}, 1015 {ID(_reader), RawBufferedRandom::kReaderOffset}, 1016 {ID(_write_buf), RawBufferedRandom::kWriteBufOffset}, 1017 {ID(_write_lock), RawBufferedRandom::kWriteLockOffset}, 1018}; 1019 1020static const BuiltinAttribute kBufferedReaderAttributes[] = { 1021 {ID(_buffer_size), RawBufferedReader::kBufferSizeOffset, 1022 AttributeFlags::kReadOnly}, 1023 {ID(_buffered_reader__read_buf), RawBufferedReader::kReadBufOffset, 1024 AttributeFlags::kHidden}, 1025 {ID(_read_pos), RawBufferedReader::kReadPosOffset, 1026 AttributeFlags::kReadOnly}, 1027 {ID(_buffer_num_bytes), RawBufferedReader::kBufferNumBytesOffset, 1028 AttributeFlags::kReadOnly}, 1029}; 1030 1031static const BuiltinAttribute kBufferedWriterAttributes[] = { 1032 {ID(buffer_size), RawBufferedWriter::kBufferSizeOffset}, 1033 {ID(_write_buf), RawBufferedWriter::kWriteBufOffset}, 1034 {ID(_write_lock), RawBufferedWriter::kWriteLockOffset}, 1035}; 1036 1037static const BuiltinAttribute kBytesIOAttributes[] = { 1038 {ID(_buffer), RawBytesIO::kBufferOffset}, 1039 {ID(_BytesIO__num_items), RawBytesIO::kNumItemsOffset, 1040 AttributeFlags::kReadOnly}, 1041 {ID(_pos), RawBytesIO::kPosOffset}, 1042}; 1043 1044static void bytesIOEnsureCapacity(Thread* thread, const BytesIO& bytes_io, 1045 word min_capacity) { 1046 DCHECK_BOUND(min_capacity, SmallInt::kMaxValue); 1047 HandleScope scope(thread); 1048 MutableBytes curr_buffer(&scope, bytes_io.buffer()); 1049 word curr_capacity = curr_buffer.length(); 1050 if (min_capacity <= curr_capacity) return; 1051 word new_capacity = Runtime::newCapacity(curr_capacity, min_capacity); 1052 MutableBytes new_buffer( 1053 &scope, thread->runtime()->newMutableBytesUninitialized(new_capacity)); 1054 new_buffer.replaceFromWith(0, *curr_buffer, curr_capacity); 1055 new_buffer.replaceFromWithByte(curr_capacity, 0, 1056 new_capacity - curr_capacity); 1057 bytes_io.setBuffer(*new_buffer); 1058} 1059 1060RawObject METH(BytesIO, __init__)(Thread* thread, Arguments args) { 1061 HandleScope scope(thread); 1062 Object self(&scope, args.get(0)); 1063 Runtime* runtime = thread->runtime(); 1064 if (!runtime->isInstanceOfBytesIO(*self)) { 1065 return thread->raiseRequiresType(self, ID(BytesIO)); 1066 } 1067 BytesIO bytes_io(&scope, *self); 1068 Object initial_bytes(&scope, args.get(1)); 1069 if (initial_bytes.isNoneType() || initial_bytes == Bytes::empty()) { 1070 bytes_io.setBuffer(runtime->emptyMutableBytes()); 1071 bytes_io.setNumItems(0); 1072 bytes_io.setPos(0); 1073 bytes_io.setClosed(false); 1074 return NoneType::object(); 1075 } 1076 1077 Byteslike byteslike(&scope, thread, *initial_bytes); 1078 if (!byteslike.isValid()) { 1079 return thread->raiseWithFmt(LayoutId::kTypeError, 1080 "a bytes-like object is required, not '%T'", 1081 &initial_bytes); 1082 } 1083 word byteslike_length = byteslike.length(); 1084 MutableBytes buffer(&scope, 1085 runtime->newMutableBytesUninitialized(byteslike_length)); 1086 buffer.replaceFromWithByteslike(0, byteslike, byteslike_length); 1087 bytes_io.setBuffer(*buffer); 1088 bytes_io.setClosed(false); 1089 bytes_io.setNumItems(byteslike_length); 1090 bytes_io.setPos(0); 1091 return NoneType::object(); 1092} 1093 1094RawObject METH(BytesIO, getvalue)(Thread* thread, Arguments args) { 1095 HandleScope scope(thread); 1096 Runtime* runtime = thread->runtime(); 1097 Object self(&scope, args.get(0)); 1098 if (!runtime->isInstanceOfBytesIO(*self)) { 1099 return thread->raiseRequiresType(self, ID(BytesIO)); 1100 } 1101 BytesIO bytes_io(&scope, *self); 1102 if (bytes_io.closed()) { 1103 return thread->raiseWithFmt(LayoutId::kValueError, 1104 "I/O operation on closed file."); 1105 } 1106 Bytes buffer(&scope, bytes_io.buffer()); 1107 word num_items = bytes_io.numItems(); 1108 return runtime->bytesCopyWithSize(thread, buffer, num_items); 1109} 1110 1111RawObject METH(BytesIO, read)(Thread* thread, Arguments args) { 1112 HandleScope scope(thread); 1113 Runtime* runtime = thread->runtime(); 1114 Object self(&scope, args.get(0)); 1115 if (!runtime->isInstanceOfBytesIO(*self)) { 1116 return thread->raiseRequiresType(self, ID(BytesIO)); 1117 } 1118 BytesIO bytes_io(&scope, *self); 1119 if (bytes_io.closed()) { 1120 return thread->raiseWithFmt(LayoutId::kValueError, 1121 "I/O operation on closed file."); 1122 } 1123 1124 Object size_obj(&scope, args.get(1)); 1125 MutableBytes buffer(&scope, bytes_io.buffer()); 1126 1127 word size; 1128 word buffer_len = bytes_io.numItems(); 1129 word pos = bytes_io.pos(); 1130 if (size_obj.isNoneType()) { 1131 size = buffer_len; 1132 } else { 1133 size_obj = intFromIndex(thread, size_obj); 1134 if (size_obj.isError()) return *size_obj; 1135 // Allow SmallInt, Bool, and subclasses of Int containing SmallInt or Bool 1136 Int size_int(&scope, intUnderlying(*size_obj)); 1137 if (size_obj.isLargeInt()) { 1138 return thread->raiseWithFmt(LayoutId::kOverflowError, 1139 "cannot fit '%T' into an index-sized integer", 1140 &size_int); 1141 } 1142 if (size_int.asWord() < 0) { 1143 size = buffer_len; 1144 } else { 1145 size = size_int.asWord(); 1146 } 1147 } 1148 if (buffer_len <= pos) { 1149 return Bytes::empty(); 1150 } 1151 word new_pos = Utils::minimum(buffer_len, pos + size); 1152 bytes_io.setPos(new_pos); 1153 Bytes result(&scope, *buffer); 1154 return bytesSubseq(thread, result, pos, new_pos - pos); 1155} 1156 1157RawObject METH(BytesIO, write)(Thread* thread, Arguments args) { 1158 HandleScope scope(thread); 1159 Runtime* runtime = thread->runtime(); 1160 Object self(&scope, args.get(0)); 1161 if (!runtime->isInstanceOfBytesIO(*self)) { 1162 return thread->raiseRequiresType(self, ID(BytesIO)); 1163 } 1164 BytesIO bytes_io(&scope, *self); 1165 if (bytes_io.closed()) { 1166 return thread->raiseWithFmt(LayoutId::kValueError, 1167 "I/O operation on closed file."); 1168 } 1169 1170 Object value_obj(&scope, args.get(1)); 1171 Byteslike value(&scope, thread, *value_obj); 1172 if (!value.isValid()) { 1173 return thread->raiseWithFmt(LayoutId::kTypeError, 1174 "a bytes-like object is required, not '%T'", 1175 &value_obj); 1176 } 1177 1178 word pos = bytes_io.pos(); 1179 word value_length = value.length(); 1180 word new_pos = pos + value_length; 1181 bytesIOEnsureCapacity(thread, bytes_io, new_pos); 1182 1183 MutableBytes::cast(bytes_io.buffer()) 1184 .replaceFromWithByteslike(pos, value, value_length); 1185 if (new_pos > bytes_io.numItems()) { 1186 bytes_io.setNumItems(new_pos); 1187 } 1188 bytes_io.setPos(new_pos); 1189 return SmallInt::fromWord(value_length); 1190} 1191 1192static const BuiltinAttribute kFileIOAttributes[] = { 1193 {ID(_fd), RawFileIO::kFdOffset}, 1194 {ID(name), RawFileIO::kNameOffset}, 1195 {ID(_created), RawFileIO::kCreatedOffset}, 1196 {ID(_readable), RawFileIO::kReadableOffset}, 1197 {ID(_writable), RawFileIO::kWritableOffset}, 1198 {ID(_appending), RawFileIO::kAppendingOffset}, 1199 {ID(_seekable), RawFileIO::kSeekableOffset}, 1200 {ID(_closefd), RawFileIO::kCloseFdOffset}, 1201}; 1202 1203static const word kDefaultBufferSize = 1 * kKiB; // bytes 1204 1205RawObject METH(FileIO, readall)(Thread* thread, Arguments args) { 1206 HandleScope scope(thread); 1207 Runtime* runtime = thread->runtime(); 1208 Object self(&scope, args.get(0)); 1209 if (!runtime->isInstanceOfFileIO(*self)) { 1210 return thread->raiseRequiresType(self, ID(FileIO)); 1211 } 1212 FileIO file_io(&scope, *self); 1213 if (file_io.closed()) { 1214 return thread->raiseWithFmt(LayoutId::kValueError, 1215 "I/O operation on closed file."); 1216 } 1217 Object fd_obj(&scope, file_io.fd()); 1218 DCHECK(fd_obj.isSmallInt(), "fd must be small int"); 1219 int fd = SmallInt::cast(*fd_obj).value(); 1220 // If there is OSError from File::seek or File::size, error will not be 1221 // thrown. This case is handled by the for loop below. 1222 word pos = File::seek(fd, 0, 1); 1223 word end = File::size(fd); 1224 word buffer_size = kDefaultBufferSize; 1225 1226 if (end > 0 && pos >= 0 && end >= pos) { 1227 buffer_size = end - pos + 1; 1228 } 1229 // OSError from getting File::seek or File::size, or end < pos 1230 // read buffer by buffer 1231 Bytearray result_array(&scope, runtime->newBytearray()); 1232 word read_size; 1233 word total_len = 0; 1234 for (;;) { 1235 read_size = buffer_size; 1236 runtime->bytearrayEnsureCapacity(thread, result_array, 1237 total_len + buffer_size); 1238 byte* dst = reinterpret_cast<byte*>( 1239 MutableBytes::cast(result_array.items()).address()); 1240 word result_len = File::read(fd, dst + total_len, read_size); 1241 if (result_len < 0) { 1242 return thread->raiseOSErrorFromErrno(-result_len); 1243 } 1244 total_len += result_len; 1245 // From the glibc manual: "If read returns at least one character, there 1246 // is no way you can tell whether end-of-file was reached. But if you did 1247 // reach the end, the next read will return zero." 1248 // Therefore, we can't stop when the result_len is less than read_len, as 1249 // we still don't know if there's more input that we're blocked on. 1250 if (result_len == 0) { 1251 if (total_len == 0) { 1252 return Bytes::empty(); 1253 } 1254 // TODO(T70612758): Find a way to shorten the MutableBytes object without 1255 // extra allocation 1256 Bytes result_bytes( 1257 &scope, MutableBytes::cast(result_array.items()).becomeImmutable()); 1258 MutableBytes result(&scope, 1259 runtime->newMutableBytesUninitialized(total_len)); 1260 dst = reinterpret_cast<byte*>(result.address()); 1261 result_bytes.copyTo(dst, total_len); 1262 return result.becomeImmutable(); 1263 } 1264 result_array.setNumItems(total_len); 1265 if (total_len == buffer_size) { 1266 buffer_size *= 2; 1267 } 1268 } 1269} 1270 1271static RawObject readintoBytesAddress(Thread* thread, const int fd, byte* dst, 1272 const word dst_len) { 1273 if (dst_len == 0) { 1274 return SmallInt::fromWord(0); 1275 } 1276 word result = File::read(fd, dst, dst_len); 1277 if (result < 0) return thread->raiseOSErrorFromErrno(-result); 1278 return SmallInt::fromWord(result); 1279} 1280 1281RawObject METH(FileIO, readinto)(Thread* thread, Arguments args) { 1282 HandleScope scope(thread); 1283 Object self(&scope, args.get(0)); 1284 Runtime* runtime = thread->runtime(); 1285 if (!runtime->isInstanceOfFileIO(*self)) { 1286 return thread->raiseRequiresType(self, ID(FileIO)); 1287 } 1288 FileIO file_io(&scope, *self); 1289 if (file_io.closed()) { 1290 return thread->raiseWithFmt(LayoutId::kValueError, 1291 "I/O operation on closed file."); 1292 } 1293 Object dst_obj(&scope, args.get(1)); 1294 if (!runtime->isByteslike(*dst_obj) && !runtime->isInstanceOfMmap(*dst_obj)) { 1295 return thread->raiseWithFmt(LayoutId::kTypeError, 1296 "Expected bytes-like object, not %T", &dst_obj); 1297 } 1298 1299 int fd = SmallInt::cast(file_io.fd()).value(); 1300 if (runtime->isInstanceOfBytearray(*dst_obj)) { 1301 Bytearray dst_array(&scope, *dst_obj); 1302 return readintoBytesAddress( 1303 thread, fd, 1304 reinterpret_cast<byte*>( 1305 MutableBytes::cast(dst_array.items()).address()), 1306 dst_array.numItems()); 1307 } 1308 if (dst_obj.isArray()) { 1309 Array array(&scope, *dst_obj); 1310 return readintoBytesAddress( 1311 thread, fd, 1312 reinterpret_cast<byte*>(MutableBytes::cast(array.buffer()).address()), 1313 array.length()); 1314 } 1315 if (dst_obj.isMemoryView()) { 1316 MemoryView dst_memoryview(&scope, *dst_obj); 1317 dst_obj = dst_memoryview.buffer(); 1318 if (runtime->isInstanceOfBytes(*dst_obj)) { 1319 return thread->raiseWithFmt( 1320 LayoutId::kTypeError, "Expected read-write bytes-like object, not %T", 1321 &dst_memoryview); 1322 } 1323 Pointer dst_ptr(&scope, *dst_obj); 1324 return readintoBytesAddress( 1325 thread, fd, reinterpret_cast<byte*>(dst_ptr.cptr()), dst_ptr.length()); 1326 } 1327 if (dst_obj.isMmap()) { 1328 Mmap dst_mmap(&scope, *dst_obj); 1329 if (!dst_mmap.isWritable()) { 1330 return thread->raiseWithFmt( 1331 LayoutId::kTypeError, "Expected read-write bytes-like object, not %T", 1332 &dst_mmap); 1333 } 1334 Pointer dst_ptr(&scope, dst_mmap.data()); 1335 return readintoBytesAddress( 1336 thread, fd, reinterpret_cast<byte*>(dst_ptr.cptr()), dst_ptr.length()); 1337 } 1338 // Bytes, not valid arguments for readinto 1339 return thread->raiseWithFmt(LayoutId::kTypeError, 1340 "Expected read-write bytes-like object, not %T", 1341 &dst_obj); 1342} 1343 1344static const BuiltinAttribute kStringIOAttributes[] = { 1345 {ID(_buffer), RawStringIO::kBufferOffset}, 1346 {ID(_pos), RawStringIO::kPosOffset}, 1347 {ID(_readnl), RawStringIO::kReadnlOffset}, 1348 {ID(_readtranslate), RawStringIO::kReadtranslateOffset}, 1349 {ID(_readuniversal), RawStringIO::kReaduniversalOffset}, 1350 {ID(_seennl), RawStringIO::kSeennlOffset}, 1351 {ID(_writenl), RawStringIO::kWritenlOffset}, 1352 {ID(_writetranslate), RawStringIO::kWritetranslateOffset}, 1353}; 1354 1355enum NewlineFound { kLF = 0x1, kCR = 0x2, kCRLF = 0x4 }; 1356 1357static RawObject stringIOWrite(Thread* thread, const StringIO& string_io, 1358 const Str& value) { 1359 HandleScope scope(thread); 1360 Runtime* runtime = thread->runtime(); 1361 if (*value == Str::empty()) { 1362 return SmallInt::fromWord(0); 1363 } 1364 1365 Str writenl(&scope, string_io.writenl()); 1366 bool long_writenl = writenl.length() == 2; 1367 byte first_writenl_char = writenl.byteAt(0); 1368 bool has_write_translate = 1369 string_io.hasWritetranslate() && first_writenl_char != '\n'; 1370 word original_val_len = value.length(); 1371 word val_len = original_val_len; 1372 1373 // If write_translate is true, read_translate is false 1374 // Contrapositively, if read_translate is true, write_translate is false 1375 // Therefore we don't have to worry about their interactions with each other 1376 if (has_write_translate && long_writenl) { 1377 val_len += value.occurrencesOf(SmallStr::fromCStr("\n")); 1378 } 1379 1380 word start = string_io.pos(); 1381 word new_len = start + val_len; 1382 bool has_read_translate = string_io.hasReadtranslate(); 1383 if (has_read_translate) { 1384 new_len -= value.occurrencesOf(SmallStr::fromCStr("\r\n")); 1385 } 1386 1387 MutableBytes buffer(&scope, string_io.buffer()); 1388 word old_len = buffer.length(); 1389 if (old_len < new_len) { 1390 MutableBytes new_buffer(&scope, 1391 runtime->newMutableBytesUninitialized(new_len)); 1392 new_buffer.replaceFromWith(0, *buffer, old_len); 1393 new_buffer.replaceFromWithByte(old_len, 0, new_len - old_len); 1394 string_io.setBuffer(*new_buffer); 1395 buffer = *new_buffer; 1396 } 1397 1398 if (has_read_translate) { 1399 word new_seen_nl = Int::cast(string_io.seennl()).asWord(); 1400 for (word str_i = 0, byte_i = start; str_i < val_len; ++str_i, ++byte_i) { 1401 byte ch = value.byteAt(str_i); 1402 if (ch == '\r') { 1403 if (val_len > str_i + 1 && value.byteAt(str_i + 1) == '\n') { 1404 new_seen_nl |= NewlineFound::kCRLF; 1405 buffer.byteAtPut(byte_i, '\n'); 1406 str_i++; 1407 continue; 1408 } 1409 new_seen_nl |= NewlineFound::kCR; 1410 buffer.byteAtPut(byte_i, '\n'); 1411 continue; 1412 } 1413 if (ch == '\n') { 1414 new_seen_nl |= NewlineFound::kLF; 1415 } 1416 buffer.byteAtPut(byte_i, ch); 1417 } 1418 string_io.setSeennl(SmallInt::fromWord(new_seen_nl)); 1419 } else if (has_write_translate) { 1420 for (word str_i = 0, byte_i = start; str_i < original_val_len; 1421 ++str_i, ++byte_i) { 1422 byte ch = value.byteAt(str_i); 1423 if (ch == '\n') { 1424 buffer.byteAtPut(byte_i, first_writenl_char); 1425 if (long_writenl) { 1426 buffer.byteAtPut(++byte_i, writenl.byteAt(1)); 1427 } 1428 continue; 1429 } 1430 buffer.byteAtPut(byte_i, ch); 1431 } 1432 } else { 1433 buffer.replaceFromWithStr(start, *value, val_len); 1434 } 1435 string_io.setPos(new_len); 1436 return SmallInt::fromWord(original_val_len); 1437} 1438 1439static bool isValidStringIONewline(const Object& newline) { 1440 if (newline == SmallStr::empty()) return true; 1441 if (newline == SmallStr::fromCodePoint('\n')) return true; 1442 if (newline == SmallStr::fromCodePoint('\r')) return true; 1443 return newline == SmallStr::fromCStr("\r\n"); 1444} 1445 1446RawObject METH(StringIO, __init__)(Thread* thread, Arguments args) { 1447 HandleScope scope(thread); 1448 Runtime* runtime = thread->runtime(); 1449 Object self(&scope, args.get(0)); 1450 if (!runtime->isInstanceOfStringIO(*self)) { 1451 return thread->raiseRequiresType(self, ID(StringIO)); 1452 } 1453 Object newline(&scope, args.get(2)); 1454 if (newline != NoneType::object()) { 1455 if (!runtime->isInstanceOfStr(*newline)) { 1456 return thread->raiseWithFmt(LayoutId::kTypeError, 1457 "newline must be str or None, not %T", 1458 &newline); 1459 } 1460 newline = strUnderlying(*newline); 1461 if (!isValidStringIONewline(newline)) { 1462 return thread->raiseWithFmt(LayoutId::kValueError, 1463 "illegal newline value: %S", &newline); 1464 } 1465 } 1466 StringIO string_io(&scope, *self); 1467 string_io.setBuffer(runtime->emptyMutableBytes()); 1468 string_io.setClosed(false); 1469 string_io.setPos(0); 1470 string_io.setReadnl(*newline); 1471 string_io.setSeennl(SmallInt::fromWord(0)); 1472 if (newline == NoneType::object()) { 1473 string_io.setReadtranslate(true); 1474 string_io.setReaduniversal(true); 1475 string_io.setWritetranslate(false); 1476 string_io.setWritenl(SmallStr::fromCodePoint('\n')); 1477 } else if (newline == Str::empty()) { 1478 string_io.setReadtranslate(false); 1479 string_io.setReaduniversal(true); 1480 string_io.setWritetranslate(false); 1481 string_io.setWritenl(SmallStr::fromCodePoint('\n')); 1482 } else { 1483 string_io.setReadtranslate(false); 1484 string_io.setReaduniversal(false); 1485 string_io.setWritetranslate(true); 1486 string_io.setWritenl(*newline); 1487 } 1488 1489 Object initial_value_obj(&scope, args.get(1)); 1490 if (initial_value_obj != NoneType::object()) { 1491 if (!runtime->isInstanceOfStr(*initial_value_obj)) { 1492 return thread->raiseWithFmt(LayoutId::kTypeError, 1493 "initial_value must be str or None, not %T", 1494 &initial_value_obj); 1495 } 1496 Str initial_value(&scope, strUnderlying(*initial_value_obj)); 1497 stringIOWrite(thread, string_io, initial_value); 1498 string_io.setPos(0); 1499 } 1500 return NoneType::object(); 1501} 1502 1503static word stringIOReadline(Thread* thread, const StringIO& string_io, 1504 word size) { 1505 HandleScope scope(thread); 1506 MutableBytes buffer(&scope, string_io.buffer()); 1507 word buf_len = buffer.length(); 1508 word start = string_io.pos(); 1509 if (start >= buf_len) { 1510 return -1; 1511 } 1512 bool has_read_universal = string_io.hasReaduniversal(); 1513 bool has_read_translate = string_io.hasReadtranslate(); 1514 Object newline_obj(&scope, string_io.readnl()); 1515 if (has_read_translate) { 1516 newline_obj = SmallStr::fromCodePoint('\n'); 1517 } 1518 Str newline(&scope, *newline_obj); 1519 if (size < 0 || (size + start) > buf_len) { 1520 size = buf_len - start; 1521 } 1522 word i = start; 1523 1524 if (has_read_universal) { 1525 const byte crlf[] = {'\r', '\n'}; 1526 i = buffer.indexOfAny(crlf, start); 1527 // when this condition is met, either '\r' or '\n' is found 1528 if (buf_len > i) { 1529 // ch is the '\n' or '\r' 1530 byte ch = buffer.byteAt(i++); 1531 if (ch == '\r') { 1532 if (buf_len > i && buffer.byteAt(i) == '\n') { 1533 i++; 1534 } 1535 } 1536 } 1537 } else { 1538 byte first_nl_byte = newline.byteAt(0); 1539 while (i < start + size) { 1540 word index = buffer.findByte(first_nl_byte, i, (size + start - i)); 1541 if (index == -1) { 1542 i += (size + start - i); 1543 break; 1544 } 1545 i = index + 1; 1546 if (buf_len >= (i + newline.length() - 1)) { 1547 bool match = true; 1548 for (int j = 1; j < newline.length(); j++) { 1549 if (buffer.byteAt(i + j - 1) != newline.byteAt(j)) { 1550 match = false; 1551 } 1552 } 1553 if (match) { 1554 i += (newline.length() - 1); 1555 break; 1556 } 1557 } 1558 } 1559 } 1560 string_io.setPos(i); 1561 return i; 1562} 1563 1564RawObject METH(StringIO, __next__)(Thread* thread, Arguments args) { 1565 HandleScope scope(thread); 1566 Object self(&scope, args.get(0)); 1567 if (!thread->runtime()->isInstanceOfStringIO(*self)) { 1568 return thread->raiseRequiresType(self, ID(StringIO)); 1569 } 1570 StringIO string_io(&scope, *self); 1571 if (string_io.closed()) { 1572 return thread->raiseWithFmt(LayoutId::kValueError, 1573 "I/O operation on closed file."); 1574 } 1575 word start = string_io.pos(); 1576 word end = stringIOReadline(thread, string_io, -1); 1577 if (end == -1) { 1578 return thread->raise(LayoutId::kStopIteration, NoneType::object()); 1579 } 1580 Bytes result(&scope, string_io.buffer()); 1581 result = bytesSubseq(thread, result, start, end - start); 1582 return result.becomeStr(); 1583} 1584 1585RawObject METH(StringIO, close)(Thread* thread, Arguments args) { 1586 HandleScope scope(thread); 1587 Object self_obj(&scope, args.get(0)); 1588 if (!thread->runtime()->isInstanceOfStringIO(*self_obj)) { 1589 return thread->raiseRequiresType(self_obj, ID(StringIO)); 1590 } 1591 StringIO self(&scope, *self_obj); 1592 self.setClosed(true); 1593 return NoneType::object(); 1594} 1595 1596RawObject METH(StringIO, getvalue)(Thread* thread, Arguments args) { 1597 HandleScope scope(thread); 1598 Runtime* runtime = thread->runtime(); 1599 Object self(&scope, args.get(0)); 1600 if (!runtime->isInstanceOfStringIO(*self)) { 1601 return thread->raiseRequiresType(self, ID(StringIO)); 1602 } 1603 StringIO string_io(&scope, *self); 1604 if (string_io.closed()) { 1605 return thread->raiseWithFmt(LayoutId::kValueError, 1606 "I/O operation on closed file."); 1607 } 1608 Bytes buffer(&scope, string_io.buffer()); 1609 buffer = runtime->bytesCopy(thread, buffer); 1610 return buffer.becomeStr(); 1611} 1612 1613RawObject METH(StringIO, read)(Thread* thread, Arguments args) { 1614 HandleScope scope(thread); 1615 Object self(&scope, args.get(0)); 1616 if (!thread->runtime()->isInstanceOfStringIO(*self)) { 1617 return thread->raiseRequiresType(self, ID(StringIO)); 1618 } 1619 StringIO string_io(&scope, *self); 1620 if (string_io.closed()) { 1621 return thread->raiseWithFmt(LayoutId::kValueError, 1622 "I/O operation on closed file."); 1623 } 1624 Object size_obj(&scope, args.get(1)); 1625 word size; 1626 if (size_obj.isNoneType()) { 1627 size = -1; 1628 } else { 1629 size_obj = intFromIndex(thread, size_obj); 1630 if (size_obj.isError()) return *size_obj; 1631 // TODO(T55084422): have a better abstraction for int to word conversion 1632 if (!size_obj.isSmallInt() && !size_obj.isBool()) { 1633 return thread->raiseWithFmt( 1634 LayoutId::kOverflowError, 1635 "cannot fit value into an index-sized integer"); 1636 } 1637 size = Int::cast(*size_obj).asWord(); 1638 } 1639 Bytes result(&scope, string_io.buffer()); 1640 word start = string_io.pos(); 1641 word end = result.length(); 1642 if (start > end) { 1643 return Str::empty(); 1644 } 1645 if (size < 0) { 1646 string_io.setPos(end); 1647 result = bytesSubseq(thread, result, start, end - start); 1648 return result.becomeStr(); 1649 } 1650 word new_pos = Utils::minimum(end, start + size); 1651 string_io.setPos(new_pos); 1652 result = bytesSubseq(thread, result, start, new_pos - start); 1653 return result.becomeStr(); 1654} 1655 1656RawObject METH(StringIO, readline)(Thread* thread, Arguments args) { 1657 HandleScope scope(thread); 1658 Object self(&scope, args.get(0)); 1659 if (!thread->runtime()->isInstanceOfStringIO(*self)) { 1660 return thread->raiseRequiresType(self, ID(StringIO)); 1661 } 1662 StringIO string_io(&scope, *self); 1663 if (string_io.closed()) { 1664 return thread->raiseWithFmt(LayoutId::kValueError, 1665 "I/O operation on closed file."); 1666 } 1667 Object size_obj(&scope, args.get(1)); 1668 word size; 1669 if (size_obj.isNoneType()) { 1670 size = -1; 1671 } else { 1672 size_obj = intFromIndex(thread, size_obj); 1673 if (size_obj.isError()) return *size_obj; 1674 // TODO(T55084422): have a better abstraction for int to word conversion 1675 if (!size_obj.isSmallInt() && !size_obj.isBool()) { 1676 return thread->raiseWithFmt( 1677 LayoutId::kOverflowError, 1678 "cannot fit value into an index-sized integer"); 1679 } 1680 size = Int::cast(*size_obj).asWord(); 1681 } 1682 word start = string_io.pos(); 1683 word end = stringIOReadline(thread, string_io, size); 1684 if (end == -1) { 1685 return Str::empty(); 1686 } 1687 Bytes result(&scope, string_io.buffer()); 1688 result = bytesSubseq(thread, result, start, end - start); 1689 return result.becomeStr(); 1690} 1691 1692RawObject METH(StringIO, truncate)(Thread* thread, Arguments args) { 1693 HandleScope scope(thread); 1694 Object self(&scope, args.get(0)); 1695 Runtime* runtime = thread->runtime(); 1696 if (!runtime->isInstanceOfStringIO(*self)) { 1697 return thread->raiseRequiresType(self, ID(StringIO)); 1698 } 1699 StringIO string_io(&scope, *self); 1700 if (string_io.closed()) { 1701 return thread->raiseWithFmt(LayoutId::kValueError, 1702 "I/O operation on closed file."); 1703 } 1704 Object size_obj(&scope, args.get(1)); 1705 word size; 1706 if (size_obj.isNoneType()) { 1707 size = string_io.pos(); 1708 } else { 1709 size_obj = intFromIndex(thread, size_obj); 1710 if (size_obj.isError()) return *size_obj; 1711 // TODO(T55084422): have a better abstraction for int to word conversion 1712 if (!size_obj.isSmallInt() && !size_obj.isBool()) { 1713 return thread->raiseWithFmt( 1714 LayoutId::kOverflowError, 1715 "cannot fit value into an index-sized integer"); 1716 } 1717 size = Int::cast(*size_obj).asWord(); 1718 if (size < 0) { 1719 return thread->raiseWithFmt(LayoutId::kValueError, 1720 "Negative size value %d", size); 1721 } 1722 } 1723 MutableBytes buffer(&scope, string_io.buffer()); 1724 if (size < buffer.length()) { 1725 MutableBytes new_buffer(&scope, 1726 runtime->newMutableBytesUninitialized(size)); 1727 new_buffer.replaceFromWith(0, *buffer, size); 1728 string_io.setBuffer(*new_buffer); 1729 } 1730 return SmallInt::fromWord(size); 1731} 1732 1733RawObject METH(StringIO, write)(Thread* thread, Arguments args) { 1734 HandleScope scope(thread); 1735 Runtime* runtime = thread->runtime(); 1736 Object self(&scope, args.get(0)); 1737 if (!runtime->isInstanceOfStringIO(*self)) { 1738 return thread->raiseRequiresType(self, ID(StringIO)); 1739 } 1740 StringIO string_io(&scope, *self); 1741 if (string_io.closed()) { 1742 return thread->raiseWithFmt(LayoutId::kValueError, 1743 "I/O operation on closed file."); 1744 } 1745 Object value(&scope, args.get(1)); 1746 if (!runtime->isInstanceOfStr(*value)) { 1747 return thread->raiseRequiresType(value, ID(str)); 1748 } 1749 Str str(&scope, strUnderlying(*value)); 1750 return stringIOWrite(thread, string_io, str); 1751} 1752 1753static const BuiltinAttribute kTextIOWrapperAttributes[] = { 1754 {ID(_buffer), RawTextIOWrapper::kBufferOffset}, 1755 {ID(_line_buffering), RawTextIOWrapper::kLineBufferingOffset}, 1756 {ID(_encoding), RawTextIOWrapper::kEncodingOffset}, 1757 {ID(_errors), RawTextIOWrapper::kErrorsOffset}, 1758 {ID(_readuniversal), RawTextIOWrapper::kReaduniversalOffset}, 1759 {ID(_readtranslate), RawTextIOWrapper::kReadtranslateOffset}, 1760 {ID(_readnl), RawTextIOWrapper::kReadnlOffset}, 1761 {ID(_writetranslate), RawTextIOWrapper::kWritetranslateOffset}, 1762 {ID(_writenl), RawTextIOWrapper::kWritenlOffset}, 1763 {ID(_encoder), RawTextIOWrapper::kEncoderOffset}, 1764 {ID(_decoder), RawTextIOWrapper::kDecoderOffset}, 1765 {ID(_decoded_chars), RawTextIOWrapper::kDecodedCharsOffset}, 1766 {ID(_decoded_chars_used), RawTextIOWrapper::kDecodedCharsUsedOffset}, 1767 {ID(_snapshot), RawTextIOWrapper::kSnapshotOffset}, 1768 {ID(_seekable), RawTextIOWrapper::kSeekableOffset}, 1769 {ID(_has_read1), RawTextIOWrapper::kHasRead1Offset}, 1770 {ID(_b2cratio), RawTextIOWrapper::kB2cratioOffset}, 1771 {ID(_telling), RawTextIOWrapper::kTellingOffset}, 1772 {ID(mode), RawTextIOWrapper::kModeOffset}, // TODO(T54575279): remove 1773}; 1774 1775void initializeUnderIOTypes(Thread* thread) { 1776 HandleScope scope(thread); 1777 Type type(&scope, addBuiltinType(thread, ID(_IOBase), LayoutId::kUnderIOBase, 1778 /*superclass_id=*/LayoutId::kObject, 1779 kUnderIOBaseAttributes, UnderIOBase::kSize, 1780 /*basetype=*/true)); 1781 builtinTypeEnableTupleOverflow(thread, type); 1782 1783 addBuiltinType(thread, ID(IncrementalNewlineDecoder), 1784 LayoutId::kIncrementalNewlineDecoder, 1785 /*superclass_id=*/LayoutId::kObject, 1786 kIncrementalNewlineDecoderAttributes, 1787 IncrementalNewlineDecoder::kSize, /*basetype=*/true); 1788 1789 addBuiltinType(thread, ID(_RawIOBase), LayoutId::kUnderRawIOBase, 1790 /*superclass_id=*/LayoutId::kUnderIOBase, kNoAttributes, 1791 UnderRawIOBase::kSize, /*basetype=*/true); 1792 1793 addBuiltinType(thread, ID(_BufferedIOBase), LayoutId::kUnderBufferedIOBase, 1794 /*superclass_id=*/LayoutId::kUnderIOBase, kNoAttributes, 1795 UnderBufferedIOBase::kSize, /*basetype=*/true); 1796 1797 type = addBuiltinType(thread, ID(BytesIO), LayoutId::kBytesIO, 1798 /*superclass_id=*/LayoutId::kUnderBufferedIOBase, 1799 kBytesIOAttributes, BytesIO::kSize, /*basetype=*/true); 1800 builtinTypeEnableTupleOverflow(thread, type); 1801 1802 addBuiltinType(thread, ID(_BufferedIOMixin), LayoutId::kUnderBufferedIOMixin, 1803 /*superclass_id=*/LayoutId::kUnderBufferedIOBase, 1804 kUnderBufferedIOMixinAttributes, UnderBufferedIOMixin::kSize, 1805 /*basetype=*/true); 1806 1807 type = addBuiltinType(thread, ID(BufferedRandom), LayoutId::kBufferedRandom, 1808 /*superclass_id=*/LayoutId::kUnderBufferedIOMixin, 1809 kBufferedRandomAttributes, BufferedRandom::kSize, 1810 /*basetype=*/true); 1811 builtinTypeEnableTupleOverflow(thread, type); 1812 1813 type = addBuiltinType(thread, ID(BufferedReader), LayoutId::kBufferedReader, 1814 /*superclass_id=*/LayoutId::kUnderBufferedIOMixin, 1815 kBufferedReaderAttributes, BufferedReader::kSize, 1816 /*basetype=*/true); 1817 builtinTypeEnableTupleOverflow(thread, type); 1818 1819 type = addBuiltinType(thread, ID(BufferedWriter), LayoutId::kBufferedWriter, 1820 /*superclass_id=*/LayoutId::kUnderBufferedIOMixin, 1821 kBufferedWriterAttributes, BufferedWriter::kSize, 1822 /*basetype=*/true); 1823 builtinTypeEnableTupleOverflow(thread, type); 1824 1825 type = addBuiltinType(thread, ID(FileIO), LayoutId::kFileIO, 1826 /*superclass_id=*/LayoutId::kUnderRawIOBase, 1827 kFileIOAttributes, FileIO::kSize, /*basetype=*/true); 1828 builtinTypeEnableTupleOverflow(thread, type); 1829 1830 addBuiltinType(thread, ID(_TextIOBase), LayoutId::kUnderTextIOBase, 1831 /*superclass_id=*/LayoutId::kUnderIOBase, kNoAttributes, 1832 RawUnderTextIOBase::kSize, /*basetype=*/true); 1833 1834 type = addBuiltinType(thread, ID(TextIOWrapper), LayoutId::kTextIOWrapper, 1835 /*superclass_id=*/LayoutId::kUnderTextIOBase, 1836 kTextIOWrapperAttributes, TextIOWrapper::kSize, 1837 /*basetype=*/true); 1838 builtinTypeEnableTupleOverflow(thread, type); 1839 1840 type = 1841 addBuiltinType(thread, ID(StringIO), LayoutId::kStringIO, 1842 /*superclass_id=*/LayoutId::kUnderTextIOBase, 1843 kStringIOAttributes, StringIO::kSize, /*basetype=*/true); 1844 builtinTypeEnableTupleOverflow(thread, type); 1845} 1846 1847} // namespace py