v8/test/unittests/asmjs/asm-scanner-unittest.cc
Toon Verwaest fcfd995aa1 [scanner] Go back to untemplatized scanning with buffering
This reverts the following 3 CLs:

Revert "[scanner] Templatize scan functions by encoding"
Revert "[asm] Remove invalid static cast of character stream"
Revert "[scanner] Prepare CharacterStreams for specializing scanner and parser by character type"

The original idea behind this work was to avoid copying, converting and
buffering characters to be scanned by specializing the scanner functions. The
additional benefit was for scanner functions to have a bigger window over the
input. Even though we can get a pretty nice speedup from having a larger
window, in practice this rarely helps. The cost is a larger binary.

Since we can't eagerly convert utf8 to utf16 due to memory overhead, we'd also
need to have a specialized version of the scanner just for utf8. That's pretty
complex, and likely won't be better than simply bulk converting and buffering
utf8 as utf16.

Change-Id: Ic3564683932a0097e3f9f51cd88f62c6ac879dcb
Reviewed-on: https://chromium-review.googlesource.com/1183190
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55258}
2018-08-21 10:52:52 +00:00

314 lines
6.4 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(stream.get()));
}
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<Utf16CharacterStream> 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