2d40e2f445
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}
315 lines
6.5 KiB
C++
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
|