xref: /PHP-8.0/ext/opcache/tests/jit/bug81249.phpt (revision c0e49328)
1--TEST--
2Bug #81249: Intermittent property assignment failure with JIT enabled
3--INI--
4opcache.enable=1
5opcache.enable_cli=1
6opcache.jit_buffer_size=1M
7opcache.jit=tracing
8--SKIPIF--
9<?php require_once('skipif.inc'); ?>
10<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
11--FILE--
12<?php
13
14declare(strict_types=1);
15
16final class EucJpDecoder
17{
18    private const JIS0212_INDEX = [108 => 728];
19    private const JIS0208_INDEX = [];
20
21    private const CONTINUE = -1;
22    private const FINISHED = -2;
23    private const ERROR = -3;
24
25    /**
26     * @var int
27     */
28    private $lead;
29
30    /**
31     * @var bool
32     */
33    private $isJis0212;
34
35    public function __construct()
36    {
37        $this->lead = 0x00;
38        $this->isJis0212 = false;
39    }
40
41    public function handle(array &$ioQueue, string $byte): int
42    {
43        if ($byte === '') {
44            if ($this->lead !== 0x00) {
45                $this->lead = 0x00;
46
47                return self::ERROR;
48            }
49
50            return self::FINISHED;
51        }
52
53        $byte = ord($byte);
54
55        if ($this->lead === 0x8E && ($byte >= 0xA1 && $byte <= 0xDF)) {
56            $this->lead = 0x00;
57
58            return 0xFF61 - 0xA1 + $byte;
59        }
60
61        if ($this->lead === 0x8F && ($byte >= 0xA1 && $byte <= 0xFE)) {
62            $this->isJis0212 = true;
63            $this->lead = $byte;
64
65            return self::CONTINUE;
66        }
67
68        if ($this->lead !== 0x00) {
69            $lead = $this->lead;
70            $this->lead = 0x00;
71            $codePoint = null;
72
73            if (($lead >= 0xA1 && $lead <= 0xFE) && ($byte >= 0xA1 && $byte <= 0xFE)) {
74                $index = self::JIS0208_INDEX;
75
76                if ($this->isJis0212) {
77                    $index = self::JIS0212_INDEX;
78                }
79
80                $codePoint = $index[($lead - 0xA1) * 94 + $byte - 0xA1] ?? null;
81            }
82
83            $this->isJis0212 = false;
84
85            if ($codePoint !== null) {
86                return $codePoint;
87            }
88
89            if ($byte <= 0x7F) {
90                array_unshift($ioQueue, chr($byte));
91            }
92
93            return self::ERROR;
94        }
95
96        if ($byte <= 0x7F) {
97            return $byte;
98        }
99
100        if ($byte === 0x8E || $byte === 0x8F || ($byte >= 0xA1 && $byte <= 0xFE)) {
101            $this->lead = $byte;
102
103            return self::CONTINUE;
104        }
105
106        return self::ERROR;
107    }
108}
109
110for ($i = 0; $i < 2000; ++$i) {
111    $decoder = new EucJpDecoder();
112
113    $bytes = ["\x8F", "\xA2", "\xAF", ''];
114    $out = null;
115
116    foreach ($bytes as $byte) {
117        $result = $decoder->handle($bytes, $byte);
118
119        if ($result >= 0) {
120            $out = $result;
121        }
122    }
123
124    // $bytes array should be decoded to U+2D8, decimal 728
125    assert($out === 728);
126}
127?>
128OK
129--EXPECT--
130OK
131