test-source-positions.cc 7.69 KB
Newer Older
1 2 3 4 5 6
// Copyright 2015 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/v8.h"

7
#include "src/api-inl.h"
8 9 10 11 12
#include "src/compiler/pipeline.h"
#include "src/handles.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate.h"
13
#include "src/objects-inl.h"
14 15 16 17 18 19 20 21 22
#include "test/cctest/cctest.h"
#include "test/cctest/interpreter/source-position-matcher.h"

namespace v8 {
namespace internal {
namespace interpreter {

// Flags enabling optimizations that change generated bytecode array.
// Format is <command-line flag> <flag name> <bit index>
23 24 25
#define OPTIMIZATION_FLAGS(V)      \
  V(FLAG_ignition_reo, kUseReo, 0) \
  V(FLAG_ignition_filter_expression_positions, kUseFilterExpressionPositions, 2)
26 27 28 29 30 31 32 33

#define DECLARE_BIT(_, Name, BitIndex) static const int Name = 1 << BitIndex;
OPTIMIZATION_FLAGS(DECLARE_BIT)
#undef DECLARE_BIT

// Test cases source positions are checked for. Please ensure all
// combinations of flags are present here. This is done manually
// because it provides easier to comprehend failure case for humans.
34 35 36
#define TEST_CASES(V)                                              \
  V(UsingReo, kUseReo)                                             \
  V(UsingFilterExpressionPositions, kUseFilterExpressionPositions) \
37
  V(UsingAllOptimizations, kUseReo | kUseFilterExpressionPositions)
38 39 40 41 42 43 44 45 46

struct TestCaseData {
  TestCaseData(const char* const script,
               const char* const declaration_parameters = "",
               const char* const arguments = "")
      : script_(script),
        declaration_parameters_(declaration_parameters),
        arguments_(arguments) {}

47 48 49
  const char* script() const { return script_; }
  const char* declaration_parameters() const { return declaration_parameters_; }
  const char* arguments() const { return arguments_; }
50 51

 private:
52
  TestCaseData() = delete;
53 54 55 56 57 58 59 60 61 62 63 64 65 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

  const char* const script_;
  const char* const declaration_parameters_;
  const char* const arguments_;
};

static const TestCaseData kTestCaseData[] = {
    {"var x = (y = 3) + (x = y); return x + y;"},
    {"var x = 55;\n"
     "var y = x + (x = 1) + (x = 2) + (x = 3);\n"
     "return y;"},
    {"var x = 10; return x >>> 3;\n"},
    {"var x = 0; return x || (1, 2, 3);\n"},
    {"return a || (a, b, a, b, c = 5, 3);\n"},
    {"var a = 3; var b = 4; a = b; b = a; a = b; return a;\n"},
    {"var a = 1; return [[a, 2], [a + 2]];\n"},
    {"var a = 1; if (a || a < 0) { return 1; }\n"},
    {"var b;"
     "b = a.name;"
     "b = a.name;"
     "a.name = a;"
     "b = a.name;"
     "a.name = a;"
     "return b;"},
    {"var sum = 0;\n"
     "outer: {\n"
     "  for (var x = 0; x < 10; ++x) {\n"
     "    for (var y = 0; y < 3; ++y) {\n"
     "      ++sum;\n"
     "      if (x + y == 12) { break outer; }\n"
     "    }\n"
     "  }\n"
     "}\n"
     "return sum;\n"},
    {"var a = 1;"
     "switch (a) {"
     "  case 1: return a * a + 1;"
     "  case 1: break;"
     "  case 2: return (a = 3) * a + (a = 4);"
     "  case 3:"
     "}"
     "return a;"},
    {"for (var p of [0, 1, 2]) {}"},
    {"var x = { 'a': 1, 'b': 2 };"
     "for (x['a'] of [1,2,3]) { return x['a']; }"},
    {"while (x == 4) {\n"
     "  var y = x + 1;\n"
     "  if (y == 2) break;\n"
     "  for (z['a'] of [0]) {\n"
     "    x += (x *= 3) + y;"
     "  }\n"
     "}\n"},
    {"function g(a, b) { return a.func(b + b, b); }\n"
     "g(new (function Obj() { this.func = function() { return; }})(), 1)\n"},
    {"return some_global[name];", "name", "'a'"}};
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

class OptimizedBytecodeSourcePositionTester final {
 public:
  explicit OptimizedBytecodeSourcePositionTester(Isolate* isolate)
      : isolate_(isolate) {
    SaveOptimizationFlags();
    saved_flag_always_opt_ = FLAG_always_opt;
    FLAG_always_opt = false;
  }

  ~OptimizedBytecodeSourcePositionTester() {
    RestoreOptimizationFlags();
    FLAG_always_opt = saved_flag_always_opt_;
  }

  bool SourcePositionsMatch(int optimization_bitmap, const char* function_body,
124 125
                            const char* function_decl_params,
                            const char* function_args);
126 127 128 129 130 131

 private:
  Handle<BytecodeArray> MakeBytecode(int optimization_bitmap,
                                     const char* function_body,
                                     const char* function_decl_params,
                                     const char* function_args);
132
  static std::string MakeScript(const char* function_body,
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
                                const char* function_decl_params,
                                const char* function_args);

  void SetOptimizationFlags(int optimization_bitmap);
  void SaveOptimizationFlags();
  void RestoreOptimizationFlags();

  Isolate* isolate() const { return isolate_; }

  Isolate* isolate_;
  int saved_optimization_bitmap_;
  bool saved_flag_always_opt_;
};

// static
std::string OptimizedBytecodeSourcePositionTester::MakeScript(
149 150
    const char* function_body, const char* function_decl_params,
    const char* function_args) {
151
  std::ostringstream os;
152 153
  os << "function test_function"
     << "(" << function_decl_params << ") {";
154 155
  os << function_body;
  os << "}";
156
  os << "test_function(" << function_args << ");";
157 158 159 160 161 162
  return os.str();
}

Handle<BytecodeArray> OptimizedBytecodeSourcePositionTester::MakeBytecode(
    int optimization_bitmap, const char* function_body,
    const char* function_decl_params, const char* function_args) {
163 164
  std::string script =
      MakeScript(function_body, function_decl_params, function_args);
165 166 167
  SetOptimizationFlags(optimization_bitmap);
  CompileRun(script.c_str());

168 169 170 171
  Local<Function> api_function = Local<Function>::Cast(
      CcTest::global()
          ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("test_function"))
          .ToLocalChecked());
172 173
  Handle<JSFunction> function =
      Handle<JSFunction>::cast(v8::Utils::OpenHandle(*api_function));
174
  return handle(function->shared()->GetBytecodeArray(), isolate_);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
}

void OptimizedBytecodeSourcePositionTester::SetOptimizationFlags(
    int optimization_bitmap) {
#define SET_FLAG(V8Flag, BitName, _) \
  V8Flag = (optimization_bitmap & BitName) ? true : false;
  OPTIMIZATION_FLAGS(SET_FLAG)
#undef SET_FLAG
}

void OptimizedBytecodeSourcePositionTester::SaveOptimizationFlags() {
  saved_optimization_bitmap_ = 0;
#define SAVE_FLAG(V8Flag, BitName, _) \
  if (V8Flag) saved_optimization_bitmap_ |= BitName;
#undef SET_FLAG
}

void OptimizedBytecodeSourcePositionTester::RestoreOptimizationFlags() {
  SetOptimizationFlags(saved_optimization_bitmap_);
}

bool OptimizedBytecodeSourcePositionTester::SourcePositionsMatch(
    int optimization_bitmap, const char* function_body,
    const char* function_decl_params, const char* function_args) {
  Handle<BytecodeArray> unoptimized_bytecode =
      MakeBytecode(0, function_body, function_decl_params, function_args);
  Handle<BytecodeArray> optimized_bytecode = MakeBytecode(
      optimization_bitmap, function_body, function_decl_params, function_args);
  SourcePositionMatcher matcher;
  if (!matcher.Match(unoptimized_bytecode, optimized_bytecode)) {
    return false;
  }
  return true;
}

void TestSourcePositionsEquivalent(int optimization_bitmap) {
  HandleAndZoneScope handles;
  OptimizedBytecodeSourcePositionTester tester(handles.main_isolate());
213 214 215 216
  for (auto test_case_data : kTestCaseData) {
    CHECK(tester.SourcePositionsMatch(
        optimization_bitmap, test_case_data.script(),
        test_case_data.declaration_parameters(), test_case_data.arguments()));
217 218 219 220 221 222 223 224 225 226 227 228 229
  }
}

#define MAKE_TEST(Name, Bitmap)               \
  TEST(TestSourcePositionsEquivalent##Name) { \
    TestSourcePositionsEquivalent(Bitmap);    \
  }
TEST_CASES(MAKE_TEST)
#undef MAKE_TEST

}  // namespace interpreter
}  // namespace internal
}  // namespace v8