asm-scanner-unittest.cc 6.44 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 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:
18 19 20
  void SetupScanner(const char* source) {
    stream = ScannerStream::ForTesting(source);
    scanner.reset(new AsmJsScanner(stream.get()));
21 22 23
  }

  void Skip(AsmJsScanner::token_t t) {
24 25
    CHECK_EQ(t, scanner->Token());
    scanner->Next();
26 27 28
  }

  void SkipGlobal() {
29 30
    CHECK(scanner->IsGlobal());
    scanner->Next();
31 32 33
  }

  void SkipLocal() {
34 35
    CHECK(scanner->IsLocal());
    scanner->Next();
36 37
  }

38
  void CheckForEnd() { CHECK_EQ(scanner->Token(), AsmJsScanner::kEndOfInput); }
39 40

  void CheckForParseError() {
41
    CHECK_EQ(scanner->Token(), AsmJsScanner::kParseError);
42 43
  }

44 45
  std::unique_ptr<Utf16CharacterStream> stream;
  std::unique_ptr<AsmJsScanner> scanner;
46 47 48
};

TEST_F(AsmJsScannerTest, SimpleFunction) {
49
  SetupScanner("function foo() { return; }");
50
  Skip(TOK(function));
51
  DCHECK_EQ("foo", scanner->GetIdentifierString());
52 53 54 55 56 57 58 59 60 61 62 63 64
  SkipGlobal();
  Skip('(');
  Skip(')');
  Skip('{');
  // clang-format off
  Skip(TOK(return));
  // clang-format on
  Skip(';');
  Skip('}');
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, JSKeywords) {
65
  SetupScanner(
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
      "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) {
92
  SetupScanner(
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
      "+ - * / % & | ^ ~ << >> >>>\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) {
117
  SetupScanner(
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
      "+-*/%&|^~<<>> >>>\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) {
142
  SetupScanner("'use asm' \"use asm\"\n");
143 144 145 146 147 148
  Skip(TOK(UseAsm));
  Skip(TOK(UseAsm));
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, DefaultGlobalScope) {
149
  SetupScanner("var x = x + x;");
150
  Skip(TOK(var));
151 152
  CHECK_EQ("x", scanner->GetIdentifierString());
  AsmJsScanner::token_t x = scanner->Token();
153 154 155 156 157 158 159 160 161 162
  SkipGlobal();
  Skip('=');
  Skip(x);
  Skip('+');
  Skip(x);
  Skip(';');
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, GlobalScope) {
163 164
  SetupScanner("var x = x + x;");
  scanner->EnterGlobalScope();
165
  Skip(TOK(var));
166 167
  CHECK_EQ("x", scanner->GetIdentifierString());
  AsmJsScanner::token_t x = scanner->Token();
168 169 170 171 172 173 174 175 176 177
  SkipGlobal();
  Skip('=');
  Skip(x);
  Skip('+');
  Skip(x);
  Skip(';');
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, LocalScope) {
178 179
  SetupScanner("var x = x + x;");
  scanner->EnterLocalScope();
180
  Skip(TOK(var));
181 182
  CHECK_EQ("x", scanner->GetIdentifierString());
  AsmJsScanner::token_t x = scanner->Token();
183 184 185 186 187 188 189 190 191 192
  SkipLocal();
  Skip('=');
  Skip(x);
  Skip('+');
  Skip(x);
  Skip(';');
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, Numbers) {
193
  SetupScanner("1 1.2 0x1F 1.e3");
194

195 196 197
  CHECK(scanner->IsUnsigned());
  CHECK_EQ(1, scanner->AsUnsigned());
  scanner->Next();
198

199 200 201
  CHECK(scanner->IsDouble());
  CHECK_EQ(1.2, scanner->AsDouble());
  scanner->Next();
202

203 204 205
  CHECK(scanner->IsUnsigned());
  CHECK_EQ(31, scanner->AsUnsigned());
  scanner->Next();
206

207 208 209
  CHECK(scanner->IsDouble());
  CHECK_EQ(1.0e3, scanner->AsDouble());
  scanner->Next();
210 211 212 213

  CheckForEnd();
}

214
TEST_F(AsmJsScannerTest, UnsignedNumbers) {
215
  SetupScanner("0x7FFFFFFF 0x80000000 0xFFFFFFFF 0x100000000");
216

217
  CHECK(scanner->IsUnsigned());
218
  CHECK_EQ(0x7FFFFFFF, scanner->AsUnsigned());
219
  scanner->Next();
220

221 222 223
  CHECK(scanner->IsUnsigned());
  CHECK_EQ(0x80000000, scanner->AsUnsigned());
  scanner->Next();
224

225
  CHECK(scanner->IsUnsigned());
226
  CHECK_EQ(0xFFFFFFFF, scanner->AsUnsigned());
227
  scanner->Next();
228 229 230 231 232 233

  // 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();
}

234
TEST_F(AsmJsScannerTest, BadNumber) {
235
  SetupScanner(".123fe");
236 237 238 239 240
  Skip('.');
  CheckForParseError();
}

TEST_F(AsmJsScannerTest, Rewind1) {
241
  SetupScanner("+ - * /");
242
  Skip('+');
243
  scanner->Rewind();
244 245
  Skip('+');
  Skip('-');
246
  scanner->Rewind();
247 248
  Skip('-');
  Skip('*');
249
  scanner->Rewind();
250 251
  Skip('*');
  Skip('/');
252
  scanner->Rewind();
253 254 255 256 257
  Skip('/');
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, Comments) {
258
  SetupScanner(
259 260 261 262 263 264 265 266 267 268 269 270
      "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) {
271
  SetupScanner("var /* test\n");
272 273 274 275 276
  Skip(TOK(var));
  CheckForParseError();
}

TEST_F(AsmJsScannerTest, Seeking) {
277
  SetupScanner("var eval do arguments function break\n");
278
  Skip(TOK(var));
279
  size_t old_pos = scanner->Position();
280 281 282
  Skip(TOK(eval));
  Skip(TOK(do));
  Skip(TOK(arguments));
283
  scanner->Rewind();
284
  Skip(TOK(arguments));
285 286
  scanner->Rewind();
  scanner->Seek(old_pos);
287
  Skip(TOK(eval));
288 289 290 291 292 293 294 295
  Skip(TOK(do));
  Skip(TOK(arguments));
  Skip(TOK(function));
  Skip(TOK(break));
  CheckForEnd();
}

TEST_F(AsmJsScannerTest, Newlines) {
296
  SetupScanner(
297 298 299
      "var x = 1\n"
      "var y = 2\n");
  Skip(TOK(var));
300
  scanner->Next();
301
  Skip('=');
302 303
  scanner->Next();
  CHECK(scanner->IsPrecededByNewline());
304
  Skip(TOK(var));
305
  scanner->Next();
306
  Skip('=');
307 308
  scanner->Next();
  CHECK(scanner->IsPrecededByNewline());
309 310 311 312 313
  CheckForEnd();
}

}  // namespace internal
}  // namespace v8