|
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