v8/test/unittests/asmjs/asm-scanner-unittest.cc
Michael Starzinger e2accb425c [asm.js] Fix numeric literal bounds checking.
This fixes the bounds checking of "unsigned" numeric literals (those
that do not contains dots) by the parser. In particular this fixes a
bogus truncation to 32-bit in the scanner. It also makes the scanner
more robust by limiting the range of those numeric literals, hence
completely avoiding rounding loss or truncation errors.

R=clemensh@chromium.org
TEST=unittests/AsmJsScannerTest.UnsignedNumbers
BUG=v8:6298

Change-Id: Id31ab3c652e99fa8d3d6663315768e1bfaf3b773
Reviewed-on: https://chromium-review.googlesource.com/486881
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44890}
2017-04-26 13:45:45 +00:00

312 lines
6.3 KiB
C++

// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/asmjs/asm-scanner.h"
#include "src/objects.h"
#include "src/parsing/scanner-character-streams.h"
#include "src/parsing/scanner.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
#define TOK(t) AsmJsScanner::kToken_##t
class AsmJsScannerTest : public ::testing::Test {
protected:
void SetupSource(const char* source) {
scanner.SetStream(ScannerStream::ForTesting(source));
}
void Skip(AsmJsScanner::token_t t) {
CHECK_EQ(t, scanner.Token());
scanner.Next();
}
void SkipGlobal() {
CHECK(scanner.IsGlobal());
scanner.Next();
}
void SkipLocal() {
CHECK(scanner.IsLocal());
scanner.Next();
}
void CheckForEnd() { CHECK(scanner.Token() == AsmJsScanner::kEndOfInput); }
void CheckForParseError() {
CHECK(scanner.Token() == AsmJsScanner::kParseError);
}
AsmJsScanner scanner;
};
TEST_F(AsmJsScannerTest, SimpleFunction) {
SetupSource("function foo() { return; }");
Skip(TOK(function));
DCHECK_EQ("foo", scanner.GetIdentifierString());
SkipGlobal();
Skip('(');
Skip(')');
Skip('{');
// clang-format off
Skip(TOK(return));
// clang-format on
Skip(';');
Skip('}');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, JSKeywords) {
SetupSource(
"arguments break case const continue\n"
"default do else eval for function\n"
"if new return switch var while\n");
Skip(TOK(arguments));
Skip(TOK(break));
Skip(TOK(case));
Skip(TOK(const));
Skip(TOK(continue));
Skip(TOK(default));
Skip(TOK(do));
Skip(TOK(else));
Skip(TOK(eval));
Skip(TOK(for));
Skip(TOK(function));
Skip(TOK(if));
Skip(TOK(new));
// clang-format off
Skip(TOK(return));
// clang-format on
Skip(TOK(switch));
Skip(TOK(var));
Skip(TOK(while));
CheckForEnd();
}
TEST_F(AsmJsScannerTest, JSOperatorsSpread) {
SetupSource(
"+ - * / % & | ^ ~ << >> >>>\n"
"< > <= >= == !=\n");
Skip('+');
Skip('-');
Skip('*');
Skip('/');
Skip('%');
Skip('&');
Skip('|');
Skip('^');
Skip('~');
Skip(TOK(SHL));
Skip(TOK(SAR));
Skip(TOK(SHR));
Skip('<');
Skip('>');
Skip(TOK(LE));
Skip(TOK(GE));
Skip(TOK(EQ));
Skip(TOK(NE));
CheckForEnd();
}
TEST_F(AsmJsScannerTest, JSOperatorsTight) {
SetupSource(
"+-*/%&|^~<<>> >>>\n"
"<><=>= ==!=\n");
Skip('+');
Skip('-');
Skip('*');
Skip('/');
Skip('%');
Skip('&');
Skip('|');
Skip('^');
Skip('~');
Skip(TOK(SHL));
Skip(TOK(SAR));
Skip(TOK(SHR));
Skip('<');
Skip('>');
Skip(TOK(LE));
Skip(TOK(GE));
Skip(TOK(EQ));
Skip(TOK(NE));
CheckForEnd();
}
TEST_F(AsmJsScannerTest, UsesOfAsm) {
SetupSource("'use asm' \"use asm\"\n");
Skip(TOK(UseAsm));
Skip(TOK(UseAsm));
CheckForEnd();
}
TEST_F(AsmJsScannerTest, DefaultGlobalScope) {
SetupSource("var x = x + x;");
Skip(TOK(var));
CHECK_EQ("x", scanner.GetIdentifierString());
AsmJsScanner::token_t x = scanner.Token();
SkipGlobal();
Skip('=');
Skip(x);
Skip('+');
Skip(x);
Skip(';');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, GlobalScope) {
SetupSource("var x = x + x;");
scanner.EnterGlobalScope();
Skip(TOK(var));
CHECK_EQ("x", scanner.GetIdentifierString());
AsmJsScanner::token_t x = scanner.Token();
SkipGlobal();
Skip('=');
Skip(x);
Skip('+');
Skip(x);
Skip(';');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, LocalScope) {
SetupSource("var x = x + x;");
scanner.EnterLocalScope();
Skip(TOK(var));
CHECK_EQ("x", scanner.GetIdentifierString());
AsmJsScanner::token_t x = scanner.Token();
SkipLocal();
Skip('=');
Skip(x);
Skip('+');
Skip(x);
Skip(';');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, Numbers) {
SetupSource("1 1.2 0x1f 1.e3");
CHECK(scanner.IsUnsigned());
CHECK_EQ(1, scanner.AsUnsigned());
scanner.Next();
CHECK(scanner.IsDouble());
CHECK_EQ(1.2, scanner.AsDouble());
scanner.Next();
CHECK(scanner.IsUnsigned());
CHECK_EQ(31, scanner.AsUnsigned());
scanner.Next();
CHECK(scanner.IsDouble());
CHECK_EQ(1.0e3, scanner.AsDouble());
scanner.Next();
CheckForEnd();
}
TEST_F(AsmJsScannerTest, UnsignedNumbers) {
SetupSource("0x7fffffff 0x80000000 0xffffffff 0x100000000");
CHECK(scanner.IsUnsigned());
CHECK_EQ(0x7fffffff, scanner.AsUnsigned());
scanner.Next();
CHECK(scanner.IsUnsigned());
CHECK_EQ(0x80000000, scanner.AsUnsigned());
scanner.Next();
CHECK(scanner.IsUnsigned());
CHECK_EQ(0xffffffff, scanner.AsUnsigned());
scanner.Next();
// Numeric "unsigned" literals with a payload of more than 32-bit are rejected
// by asm.js in all contexts, we hence consider `0x100000000` to be an error.
CheckForParseError();
}
TEST_F(AsmJsScannerTest, BadNumber) {
SetupSource(".123fe");
Skip('.');
CheckForParseError();
}
TEST_F(AsmJsScannerTest, Rewind1) {
SetupSource("+ - * /");
Skip('+');
scanner.Rewind();
Skip('+');
Skip('-');
scanner.Rewind();
Skip('-');
Skip('*');
scanner.Rewind();
Skip('*');
Skip('/');
scanner.Rewind();
Skip('/');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, Comments) {
SetupSource(
"var // This is a test /* */ eval\n"
"var /* test *** test */ eval\n"
"function /* this */ ^");
Skip(TOK(var));
Skip(TOK(var));
Skip(TOK(eval));
Skip(TOK(function));
Skip('^');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, TrailingCComment) {
SetupSource("var /* test\n");
Skip(TOK(var));
CheckForParseError();
}
TEST_F(AsmJsScannerTest, Seeking) {
SetupSource("var eval do arguments function break\n");
Skip(TOK(var));
size_t old_pos = scanner.Position();
Skip(TOK(eval));
Skip(TOK(do));
Skip(TOK(arguments));
scanner.Rewind();
Skip(TOK(arguments));
scanner.Rewind();
scanner.Seek(old_pos);
Skip(TOK(eval));
Skip(TOK(do));
Skip(TOK(arguments));
Skip(TOK(function));
Skip(TOK(break));
CheckForEnd();
}
TEST_F(AsmJsScannerTest, Newlines) {
SetupSource(
"var x = 1\n"
"var y = 2\n");
Skip(TOK(var));
scanner.Next();
Skip('=');
scanner.Next();
CHECK(scanner.IsPrecededByNewline());
Skip(TOK(var));
scanner.Next();
Skip('=');
scanner.Next();
CHECK(scanner.IsPrecededByNewline());
CheckForEnd();
}
} // namespace internal
} // namespace v8