e2accb425c
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}
312 lines
6.3 KiB
C++
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
|