From cf3117d82dd97ee527bd5fcc4087073db3e2f6db Mon Sep 17 00:00:00 2001 From: nikic Date: Sun, 15 Jan 2012 16:28:25 +0100 Subject: [PATCH] Fix parsing of integers that overflow into floats Integers in hex/oct/bin notation that overflowed into floats were parsed incorrectly. --- grammar/rebuildParser.php | 6 ----- grammar/zend_language_parser.phpy | 2 +- lib/PHPParser/Node/Scalar/DNumber.php | 36 +++++++++++++++++++++++++++ lib/PHPParser/Parser.php | 2 +- test/code/scalar/float.test | 22 ++++++++++++++++ 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/grammar/rebuildParser.php b/grammar/rebuildParser.php index f6b049e..d5ab952 100644 --- a/grammar/rebuildParser.php +++ b/grammar/rebuildParser.php @@ -138,12 +138,6 @@ function resolveMacros($code) { return 'substr(' . $args[0] . ', 1)'; } - if ('parseDNumber' == $name) { - assertArgs(1, $args, $name); - - return '(double) ' . $args[0]; - } - if ('parseEncapsed' == $name) { assertArgs(2, $args, $name); diff --git a/grammar/zend_language_parser.phpy b/grammar/zend_language_parser.phpy index 5cfa12c..1c8c04e 100644 --- a/grammar/zend_language_parser.phpy +++ b/grammar/zend_language_parser.phpy @@ -659,7 +659,7 @@ ctor_arguments: common_scalar: T_LNUMBER { $$ = Scalar_LNumber[Scalar_LNumber::parse($1)]; } - | T_DNUMBER { $$ = Scalar_DNumber[parseDNumber($1)]; } + | T_DNUMBER { $$ = Scalar_DNumber[Scalar_DNumber::parse($1)]; } | T_CONSTANT_ENCAPSED_STRING { $$ = Scalar_String::create($1, $line, $docComment); } | T_LINE { $$ = Scalar_LineConst[]; } | T_FILE { $$ = Scalar_FileConst[]; } diff --git a/lib/PHPParser/Node/Scalar/DNumber.php b/lib/PHPParser/Node/Scalar/DNumber.php index 039fc5e..4ba172b 100644 --- a/lib/PHPParser/Node/Scalar/DNumber.php +++ b/lib/PHPParser/Node/Scalar/DNumber.php @@ -20,4 +20,40 @@ class PHPParser_Node_Scalar_DNumber extends PHPParser_Node_Scalar $line, $docComment ); } + + /** + * Parses a DNUMBER token like PHP would. + * + * @param string $str A string number + * + * @return float The parsed number + */ + public static function parse($str) { + // if string contains any of .eE just cast it to float + if (false !== strpbrk($str, '.eE')) { + return (float) $str; + } + + // otherwise it's an integer notation that overflowed into a float + // if it starts with 0 it's one of the special integer notations + if ('0' === $str[0]) { + // hex + if ('x' === $str[1] || 'X' === $str[1]) { + return hexdec($str); + } + + // bin + if ('b' === $str[1] || 'B' === $str[1]) { + return bindec($str); + } + + // oct + // substr($str, 0, strcspn($str, '89')) cuts the string at the first invalid digit (8 or 9) + // so that only the digits before that are used + return octdec(substr($str, 0, strcspn($str, '89'))); + } + + // dec + return (float) $str; + } } \ No newline at end of file diff --git a/lib/PHPParser/Parser.php b/lib/PHPParser/Parser.php index e79a17a..79b6c0e 100644 --- a/lib/PHPParser/Parser.php +++ b/lib/PHPParser/Parser.php @@ -2157,7 +2157,7 @@ class PHPParser_Parser } protected function yyn284($line, $docComment) { - $this->yyval = new PHPParser_Node_Scalar_DNumber((double) $this->yyastk[$this->yysp-(1-1)], $line, $docComment); + $this->yyval = new PHPParser_Node_Scalar_DNumber(PHPParser_Node_Scalar_DNumber::parse($this->yyastk[$this->yysp-(1-1)]), $line, $docComment); } protected function yyn285($line, $docComment) { diff --git a/test/code/scalar/float.test b/test/code/scalar/float.test index 1aabaa0..6bdccf3 100644 --- a/test/code/scalar/float.test +++ b/test/code/scalar/float.test @@ -12,6 +12,13 @@ Different float syntaxes 30.20e10; 300.200e100; 1e10000; + +// various integer -> float overflows +9999999999999999999; +0xFFFFFFFFFFFFFFFF; +07777777777777777777777; +0777777777777777777777787; +0b1111111111111111111111111111111111111111111111111111111111111111; ----- array( 0: Scalar_DNumber( @@ -44,4 +51,19 @@ array( 9: Scalar_DNumber( value: INF ) + 10: Scalar_DNumber( + value: 1.0E+19 + ) + 11: Scalar_DNumber( + value: 1.844674407371E+19 + ) + 12: Scalar_DNumber( + value: 7.3786976294838E+19 + ) + 13: Scalar_DNumber( + value: 7.3786976294838E+19 + ) + 14: Scalar_DNumber( + value: 1.844674407371E+19 + ) ) \ No newline at end of file