Index: trunk/phase3/tests/qunit/index.html |
— | — | @@ -79,6 +79,7 @@ |
80 | 80 | <script src="data/testrunner.js"></script> |
81 | 81 | |
82 | 82 | <!-- QUnit: Load test suites (maintain the same order as above please) --> |
| 83 | + <script src="suites/resources/mediawiki/mediawiki.jscompat.test.js"></script> |
83 | 84 | <script src="suites/resources/mediawiki/mediawiki.test.js"></script> |
84 | 85 | <script src="suites/resources/mediawiki/mediawiki.user.test.js"></script> |
85 | 86 | |
Index: trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js |
— | — | @@ -0,0 +1,35 @@ |
| 2 | +/* Some misc JavaScript compatibility tests, just to make sure the environments we run in are consistent */ |
| 3 | + |
| 4 | +module( 'mediawiki.jscompat' ); |
| 5 | + |
| 6 | +test( 'Variable with Unicode letter in name', function() { |
| 7 | + expect(3); |
| 8 | + var orig = "some token"; |
| 9 | + var ŝablono = orig; |
| 10 | + deepEqual( ŝablono, orig, 'ŝablono' ); |
| 11 | + deepEqual( \u015dablono, orig, '\\u015dablono' ); |
| 12 | + deepEqual( \u015Dablono, orig, '\\u015Dablono' ); |
| 13 | +}); |
| 14 | + |
| 15 | +/* |
| 16 | +// Not that we need this. ;) |
| 17 | +// This fails on IE 6-8 |
| 18 | +// Works on IE 9, Firefox 6, Chrome 14 |
| 19 | +test( 'Keyword workaround: "if" as variable name using Unicode escapes', function() { |
| 20 | + var orig = "another token"; |
| 21 | + \u0069\u0066 = orig; |
| 22 | + deepEqual( \u0069\u0066, orig, '\\u0069\\u0066' ); |
| 23 | +}); |
| 24 | +*/ |
| 25 | + |
| 26 | +/* |
| 27 | +// Not that we need this. ;) |
| 28 | +// This fails on IE 6-9 |
| 29 | +// Works on Firefox 6, Chrome 14 |
| 30 | +test( 'Keyword workaround: "if" as member variable name using Unicode escapes', function() { |
| 31 | + var orig = "another token"; |
| 32 | + var foo = {}; |
| 33 | + foo.\u0069\u0066 = orig; |
| 34 | + deepEqual( foo.\u0069\u0066, orig, 'foo.\\u0069\\u0066' ); |
| 35 | +}); |
| 36 | +*/ |
Property changes on: trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 37 | + native |
Index: trunk/phase3/tests/phpunit/includes/libs/JavaScriptMinifierTest.php |
— | — | @@ -78,6 +78,12 @@ |
79 | 79 | |
80 | 80 | // newline insertion after 1000 chars: break after the "++", not before |
81 | 81 | array( str_repeat( ';', 996 ) . "if(x++);", str_repeat( ';', 996 ) . "if(x++\n);" ), |
| 82 | + |
| 83 | + // Unicode letter characters should pass through ok in identifiers (bug 31187) |
| 84 | + array( "var KaŝSkatolVal = {}", 'var KaŝSkatolVal={}'), |
| 85 | + // And also per spec unicode char escape values should work in identifiers, |
| 86 | + // as long as it's a valid char. In future it might get normalized. |
| 87 | + array( "var Ka\\u015dSkatolVal = {}", 'var Ka\\u015dSkatolVal={}'), |
82 | 88 | ); |
83 | 89 | } |
84 | 90 | |
Index: trunk/phase3/includes/libs/jsminplus.php |
— | — | @@ -2029,13 +2029,55 @@ |
2030 | 2030 | break; |
2031 | 2031 | |
2032 | 2032 | default: |
2033 | | - // FIXME: add support for unicode and unicode escape sequence \uHHHH |
2034 | | - if (preg_match('/^[$\w]+/', $input, $match)) |
| 2033 | + // Fast path for identifiers: word chars followed by whitespace or various other tokens. |
| 2034 | + // Note we don't need to exclude digits in the first char, as they've already been found |
| 2035 | + // above. |
| 2036 | + if (!preg_match('/^[$\w]+(?=[\s\/\|\^\&<>\+\-\*%=!.;,\?:~\[\]\{\}\(\)@])/', $input, $match)) |
2035 | 2037 | { |
2036 | | - $tt = in_array($match[0], $this->keywords) ? $match[0] : TOKEN_IDENTIFIER; |
| 2038 | + // Character classes per ECMA-262 edition 5.1 section 7.6 |
| 2039 | + // Per spec, must accept Unicode 3.0, *may* accept later versions. |
| 2040 | + // We'll take whatever PCRE understands, which should be more recent. |
| 2041 | + $identifierStartChars = "\\p{L}\\p{Nl}" . # UnicodeLetter |
| 2042 | + "\$" . |
| 2043 | + "_"; |
| 2044 | + $identifierPartChars = $identifierStartChars . |
| 2045 | + "\\p{Mn}\\p{Mc}" . # UnicodeCombiningMark |
| 2046 | + "\\p{Nd}" . # UnicodeDigit |
| 2047 | + "\\p{Pc}"; # UnicodeConnectorPunctuation |
| 2048 | + $unicodeEscape = "\\\\u[0-9A-F-a-f]{4}"; |
| 2049 | + $identifierRegex = "/^" . |
| 2050 | + "(?:[$identifierStartChars]|$unicodeEscape)" . |
| 2051 | + "(?:[$identifierPartChars]|$unicodeEscape)*" . |
| 2052 | + "/uS"; |
| 2053 | + if (preg_match($identifierRegex, $input, $match)) |
| 2054 | + { |
| 2055 | + if (strpos($match[0], '\\') !== false) { |
| 2056 | + // Per ECMA-262 edition 5.1, section 7.6 escape sequences should behave as if they were |
| 2057 | + // the original chars, but only within the boundaries of the identifier. |
| 2058 | + $decoded = preg_replace_callback('/\\\\u([0-9A-Fa-f]{4})/', |
| 2059 | + array(__CLASS__, 'unicodeEscapeCallback'), |
| 2060 | + $match[0]); |
| 2061 | + |
| 2062 | + // Since our original regex didn't de-escape the originals, we need to check for validity again. |
| 2063 | + // No need to worry about token boundaries, as anything outside the identifier is illegal! |
| 2064 | + if (!preg_match("/^[$identifierStartChars][$identifierPartChars]*$/u", $decoded)) { |
| 2065 | + throw $this->newSyntaxError('Illegal token'); |
| 2066 | + } |
| 2067 | + |
| 2068 | + // Per spec it _ought_ to work to use these escapes for keywords words as well... |
| 2069 | + // but IE rejects them as invalid, while Firefox and Chrome treat them as identifiers |
| 2070 | + // that don't match the keyword. |
| 2071 | + if (in_array($decoded, $this->keywords)) { |
| 2072 | + throw $this->newSyntaxError('Illegal token'); |
| 2073 | + } |
| 2074 | + |
| 2075 | + // TODO: save the decoded form for output? |
| 2076 | + } |
| 2077 | + } |
| 2078 | + else |
| 2079 | + throw $this->newSyntaxError('Illegal token'); |
2037 | 2080 | } |
2038 | | - else |
2039 | | - throw $this->newSyntaxError('Illegal token'); |
| 2081 | + $tt = in_array($match[0], $this->keywords) ? $match[0] : TOKEN_IDENTIFIER; |
2040 | 2082 | } |
2041 | 2083 | } |
2042 | 2084 | |
— | — | @@ -2073,6 +2115,11 @@ |
2074 | 2116 | { |
2075 | 2117 | return new Exception('Parse error: ' . $m . ' in file \'' . $this->filename . '\' on line ' . $this->lineno); |
2076 | 2118 | } |
| 2119 | + |
| 2120 | + public static function unicodeEscapeCallback($m) |
| 2121 | + { |
| 2122 | + return html_entity_decode('&#x' . $m[1]. ';', ENT_QUOTES, 'UTF-8'); |
| 2123 | + } |
2077 | 2124 | } |
2078 | 2125 | |
2079 | 2126 | class JSToken |