v8/test/unittests/asmjs/asm-scanner-unittest.cc
Toon Verwaest 2d40e2f445 [scanner] Prepare CharacterStreams for specializing scanner and parser by character type
This templatizes CharacterStream by char type, and makes them subclass ScannerStream.
Methods that are widely used by tests are marked virtual on ScannerStream and final on
CharacterStream<T> so the specialized scanner will know what to call. ParseInfo passes
around ScannerStream, but the scanner requires the explicit CharacterStream<T>. Since
AdvanceUntil is templatized by FunctionType, I couldn't mark that virtual; so instead
I adjusted those tests to operate directly on ucs2 (not utf8 since we'll drop that in
the future).

In the end no functionality was changed. Some calls became virtual in tests. This is
mainly just preparation.

Change-Id: I0b4def65d3eb8fa5c806027c7e9123a590ebbdb5
Reviewed-on: https://chromium-review.googlesource.com/1156690
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54848}
2018-08-01 15:11:50 +00:00

315 lines
6.5 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 SetupScanner(const char* source) {
stream = ScannerStream::ForTesting(source);
scanner.reset(new AsmJsScanner(
static_cast<CharacterStream<uint16_t>*>(stream.get()), 0));
}
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_EQ(scanner->Token(), AsmJsScanner::kEndOfInput); }
void CheckForParseError() {
CHECK_EQ(scanner->Token(), AsmJsScanner::kParseError);
}
std::unique_ptr<ScannerStream> stream;
std::unique_ptr<AsmJsScanner> scanner;
};
TEST_F(AsmJsScannerTest, SimpleFunction) {
SetupScanner("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) {
SetupScanner(
"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) {
SetupScanner(
"+ - * / % & | ^ ~ << >> >>>\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) {
SetupScanner(
"+-*/%&|^~<<>> >>>\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) {
SetupScanner("'use asm' \"use asm\"\n");
Skip(TOK(UseAsm));
Skip(TOK(UseAsm));
CheckForEnd();
}
TEST_F(AsmJsScannerTest, DefaultGlobalScope) {
SetupScanner("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) {
SetupScanner("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) {
SetupScanner("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) {
SetupScanner("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) {
SetupScanner("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) {
SetupScanner(".123fe");
Skip('.');
CheckForParseError();
}
TEST_F(AsmJsScannerTest, Rewind1) {
SetupScanner("+ - * /");
Skip('+');
scanner->Rewind();
Skip('+');
Skip('-');
scanner->Rewind();
Skip('-');
Skip('*');
scanner->Rewind();
Skip('*');
Skip('/');
scanner->Rewind();
Skip('/');
CheckForEnd();
}
TEST_F(AsmJsScannerTest, Comments) {
SetupScanner(
"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) {
SetupScanner("var /* test\n");
Skip(TOK(var));
CheckForParseError();
}
TEST_F(AsmJsScannerTest, Seeking) {
SetupScanner("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) {
SetupScanner(
"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