fcfd995aa1
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}
314 lines
6.4 KiB
C++
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
|