a0c3797461
TBR=bmeurer@chromium.org,leszeks@chromium.org Bug: v8:9247 Change-Id: I8d14d0192ea8c705f8274e8e61a162531826edb6 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1624220 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Hannes Payer <hpayer@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#61769}
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/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
|