|
1 | 1 | /* |
2 | | - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * The Universal Permissive License (UPL), Version 1.0 |
|
49 | 49 | import java.nio.CharBuffer; |
50 | 50 | import java.nio.charset.CharacterCodingException; |
51 | 51 | import java.nio.charset.Charset; |
| 52 | +import java.nio.charset.CoderResult; |
52 | 53 | import java.nio.charset.CodingErrorAction; |
53 | 54 | import java.nio.charset.StandardCharsets; |
54 | 55 | import java.util.Arrays; |
|
67 | 68 | import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetInternalByteArrayNode; |
68 | 69 | import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodesFactory.GetInternalByteArrayNodeGen; |
69 | 70 | import com.oracle.graal.python.builtins.objects.tuple.PTuple; |
| 71 | +import com.oracle.graal.python.nodes.expression.CastToBooleanNode; |
70 | 72 | import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; |
71 | 73 | import com.oracle.graal.python.nodes.function.PythonBuiltinNode; |
72 | 74 | import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; |
73 | 75 | import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes; |
| 76 | +import com.oracle.graal.python.nodes.util.CastToJavaStringNode; |
| 77 | +import com.oracle.graal.python.nodes.util.CastToJavaStringNodeGen; |
74 | 78 | import com.oracle.graal.python.runtime.PythonCore; |
75 | 79 | import com.oracle.truffle.api.CompilerDirectives; |
76 | 80 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; |
@@ -496,90 +500,92 @@ private Object[] encodeString(String self, String errors) { |
496 | 500 |
|
497 | 501 | } |
498 | 502 |
|
499 | | - // _codecs.decode(obj, encoding='utf-8', errors='strict') |
500 | | - @Builtin(name = "__truffle_decode", minNumOfPositionalArgs = 1, parameterNames = {"obj", "encoding", "errors"}) |
| 503 | + // _codecs.decode(obj, encoding='utf-8', errors='strict', final=False) |
| 504 | + @Builtin(name = "__truffle_decode", minNumOfPositionalArgs = 1, parameterNames = {"obj", "encoding", "errors", "final"}) |
501 | 505 | @GenerateNodeFactory |
502 | 506 | abstract static class CodecsDecodeNode extends EncodeBaseNode { |
503 | 507 | @Child private GetInternalByteArrayNode toByteArrayNode; |
| 508 | + @Child private CastToJavaStringNode castEncodingToStringNode; |
| 509 | + @Child private CastToBooleanNode castToBooleanNode; |
504 | 510 |
|
505 | 511 | @Specialization |
506 | | - Object decode(PIBytesLike bytes, @SuppressWarnings("unused") PNone encoding, @SuppressWarnings("unused") PNone errors) { |
507 | | - byte[] decoded = getBytes(bytes); |
508 | | - String string = decodeBytes(decoded, "utf-8", "strict"); |
509 | | - return factory().createTuple(new Object[]{string, decoded.length}); |
| 512 | + Object decode(VirtualFrame frame, PIBytesLike bytes, @SuppressWarnings("unused") PNone encoding, @SuppressWarnings("unused") PNone errors, Object finalData) { |
| 513 | + ByteBuffer decoded = getBytes(bytes); |
| 514 | + String string = decodeBytes(decoded, "utf-8", "strict", castToBoolean(frame, finalData)); |
| 515 | + return factory().createTuple(new Object[]{string, decoded.position()}); |
510 | 516 | } |
511 | 517 |
|
512 | 518 | @Specialization(guards = {"isString(encoding)"}) |
513 | | - Object decode(PIBytesLike bytes, Object encoding, @SuppressWarnings("unused") PNone errors, |
514 | | - @Cached("createClassProfile()") ValueProfile encodingTypeProfile) { |
515 | | - Object profiledEncoding = encodingTypeProfile.profile(encoding); |
516 | | - byte[] decoded = getBytes(bytes); |
517 | | - String string = decodeBytesStrict(decoded, profiledEncoding); |
518 | | - return factory().createTuple(new Object[]{string, decoded.length}); |
| 519 | + Object decode(VirtualFrame frame, PIBytesLike bytes, Object encoding, @SuppressWarnings("unused") PNone errors, Object finalData) { |
| 520 | + ByteBuffer decoded = getBytes(bytes); |
| 521 | + String string = decodeBytes(decoded, castToString(encoding), "strict", castToBoolean(frame, finalData)); |
| 522 | + return factory().createTuple(new Object[]{string, decoded.position()}); |
519 | 523 | } |
520 | 524 |
|
521 | 525 | @Specialization(guards = {"isString(errors)"}) |
522 | | - Object decode(PIBytesLike bytes, @SuppressWarnings("unused") PNone encoding, Object errors, |
523 | | - @Cached("createClassProfile()") ValueProfile errorsTypeProfile) { |
524 | | - Object profiledErrors = errorsTypeProfile.profile(errors); |
525 | | - byte[] decoded = getBytes(bytes); |
526 | | - String string = decodeBytesUTF8(decoded, profiledErrors); |
527 | | - return factory().createTuple(new Object[]{string, decoded.length}); |
| 526 | + Object decode(VirtualFrame frame, PIBytesLike bytes, @SuppressWarnings("unused") PNone encoding, Object errors, Object finalData) { |
| 527 | + ByteBuffer decoded = getBytes(bytes); |
| 528 | + String string = decodeBytes(decoded, "utf-8", castToString(errors), castToBoolean(frame, finalData)); |
| 529 | + return factory().createTuple(new Object[]{string, decoded.position()}); |
528 | 530 | } |
529 | 531 |
|
530 | 532 | @Specialization(guards = {"isString(encoding)", "isString(errors)"}) |
531 | | - Object decode(PIBytesLike bytes, Object encoding, Object errors, |
532 | | - @Cached("createClassProfile()") ValueProfile encodingTypeProfile, |
533 | | - @Cached("createClassProfile()") ValueProfile errorsTypeProfile) { |
534 | | - Object profiledEncoding = encodingTypeProfile.profile(encoding); |
535 | | - Object profiledErrors = errorsTypeProfile.profile(errors); |
536 | | - byte[] decoded = getBytes(bytes); |
537 | | - String string = decodeBytes(decoded, profiledEncoding, profiledErrors); |
538 | | - return factory().createTuple(new Object[]{string, decoded.length}); |
| 533 | + Object decode(VirtualFrame frame, PIBytesLike bytes, Object encoding, Object errors, Object finalData) { |
| 534 | + ByteBuffer decoded = getBytes(bytes); |
| 535 | + String string = decodeBytes(decoded, castToString(encoding), castToString(errors), castToBoolean(frame, finalData)); |
| 536 | + return factory().createTuple(new Object[]{string, decoded.position()}); |
539 | 537 | } |
540 | 538 |
|
541 | 539 | @Fallback |
542 | | - Object decode(Object bytes, @SuppressWarnings("unused") Object encoding, @SuppressWarnings("unused") Object errors) { |
| 540 | + Object decode(Object bytes, @SuppressWarnings("unused") Object encoding, @SuppressWarnings("unused") Object errors, @SuppressWarnings("unused") Object finalData) { |
543 | 541 | throw raise(TypeError, "a bytes-like object is required, not '%p'", bytes); |
544 | 542 | } |
545 | 543 |
|
546 | | - private byte[] getBytes(PIBytesLike bytesLike) { |
547 | | - if (toByteArrayNode == null) { |
548 | | - CompilerDirectives.transferToInterpreterAndInvalidate(); |
549 | | - toByteArrayNode = insert(GetInternalByteArrayNodeGen.create()); |
550 | | - } |
551 | | - return toByteArrayNode.execute(bytesLike.getSequenceStorage()); |
| 544 | + @TruffleBoundary |
| 545 | + private static ByteBuffer wrap(byte[] bytes) { |
| 546 | + return ByteBuffer.wrap(bytes); |
552 | 547 | } |
553 | 548 |
|
554 | 549 | @TruffleBoundary |
555 | | - String decodeBytes(byte[] bytes, Object profiledEncoding, Object profiledErrors) { |
556 | | - return decodeBytes(bytes, profiledEncoding.toString(), profiledErrors.toString()); |
| 550 | + String decodeBytes(ByteBuffer byteBuffer, String encoding, String errors, boolean finalData) { |
| 551 | + CodingErrorAction errorAction = convertCodingErrorAction(errors); |
| 552 | + Charset charset = getCharset(encoding); |
| 553 | + if (charset == null) { |
| 554 | + throw raise(LookupError, "unknown encoding: %s", encoding); |
| 555 | + } |
| 556 | + CharBuffer decoded = CharBuffer.allocate(byteBuffer.capacity()); |
| 557 | + CoderResult result = charset.newDecoder().onMalformedInput(errorAction).onUnmappableCharacter(errorAction).decode(byteBuffer, decoded, finalData); |
| 558 | + if (result.isError()) { |
| 559 | + throw raise(UnicodeDecodeError, result.toString()); |
| 560 | + } |
| 561 | + return String.valueOf(decoded.flip()); |
557 | 562 | } |
558 | 563 |
|
559 | | - @TruffleBoundary |
560 | | - String decodeBytesStrict(byte[] bytes, Object profiledEncoding) { |
561 | | - return decodeBytes(bytes, profiledEncoding.toString(), "strict"); |
| 564 | + private ByteBuffer getBytes(PIBytesLike bytesLike) { |
| 565 | + if (toByteArrayNode == null) { |
| 566 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 567 | + toByteArrayNode = insert(GetInternalByteArrayNodeGen.create()); |
| 568 | + } |
| 569 | + return wrap(toByteArrayNode.execute(bytesLike.getSequenceStorage())); |
562 | 570 | } |
563 | 571 |
|
564 | | - @TruffleBoundary |
565 | | - String decodeBytesUTF8(byte[] bytes, Object profiledErrors) { |
566 | | - return decodeBytes(bytes, "utf-8", profiledErrors.toString()); |
| 572 | + private String castToString(Object encodingObj) { |
| 573 | + if (castEncodingToStringNode == null) { |
| 574 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 575 | + castEncodingToStringNode = insert(CastToJavaStringNodeGen.create()); |
| 576 | + } |
| 577 | + return castEncodingToStringNode.execute(encodingObj); |
567 | 578 | } |
568 | 579 |
|
569 | | - @TruffleBoundary |
570 | | - String decodeBytes(byte[] bytes, String encoding, String errors) { |
571 | | - ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); |
572 | | - CodingErrorAction errorAction = convertCodingErrorAction(errors); |
573 | | - Charset charset = getCharset(encoding); |
574 | | - if (charset == null) { |
575 | | - throw raise(LookupError, "unknown encoding: %s", encoding); |
| 580 | + private boolean castToBoolean(VirtualFrame frame, Object object) { |
| 581 | + if (object == PNone.NO_VALUE) { |
| 582 | + return false; |
576 | 583 | } |
577 | | - try { |
578 | | - CharBuffer decoded = charset.newDecoder().onMalformedInput(errorAction).onUnmappableCharacter(errorAction).decode(byteBuffer); |
579 | | - return String.valueOf(decoded); |
580 | | - } catch (CharacterCodingException e) { |
581 | | - throw raise(UnicodeDecodeError, e); |
| 584 | + if (castToBooleanNode == null) { |
| 585 | + CompilerDirectives.transferToInterpreterAndInvalidate(); |
| 586 | + castToBooleanNode = insert(CastToBooleanNode.createIfTrueNode()); |
582 | 587 | } |
| 588 | + return castToBooleanNode.executeBoolean(frame, object); |
583 | 589 | } |
584 | 590 | } |
585 | 591 |
|
|
0 commit comments