// 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. #ifndef V8_CCTEST_SCOPE_TEST_HELPER_H_ #define V8_CCTEST_SCOPE_TEST_HELPER_H_ #include "src/ast/scopes.h" #include "src/ast/variables.h" namespace v8 { namespace internal { class ScopeTestHelper { public: static bool MustAllocateInContext(Variable* var) { return var->scope()->MustAllocateInContext(var); } static void CompareScopes(Scope* baseline, Scope* scope, bool precise_maybe_assigned) { CHECK_EQ(baseline->scope_type(), scope->scope_type()); CHECK_IMPLIES(baseline->is_declaration_scope(), baseline->AsDeclarationScope()->function_kind() == scope->AsDeclarationScope()->function_kind()); if (!PreparseDataBuilder::ScopeNeedsData(baseline)) return; if (scope->is_declaration_scope() && scope->AsDeclarationScope()->is_skipped_function()) { return; } if (baseline->is_function_scope()) { Variable* function = baseline->AsDeclarationScope()->function_var(); if (function != nullptr) { CompareVariables(function, scope->AsDeclarationScope()->function_var(), precise_maybe_assigned); } else { CHECK_NULL(scope->AsDeclarationScope()->function_var()); } } for (auto baseline_local = baseline->locals()->begin(), scope_local = scope->locals()->begin(); baseline_local != baseline->locals()->end(); ++baseline_local, ++scope_local) { if (scope_local->mode() == VariableMode::kVar || scope_local->mode() == VariableMode::kLet || scope_local->mode() == VariableMode::kConst) { CompareVariables(*baseline_local, *scope_local, precise_maybe_assigned); } } for (Scope *baseline_inner = baseline->inner_scope(), *scope_inner = scope->inner_scope(); scope_inner != nullptr; scope_inner = scope_inner->sibling(), baseline_inner = baseline_inner->sibling()) { CompareScopes(baseline_inner, scope_inner, precise_maybe_assigned); } } static void CompareVariables(Variable* baseline_local, Variable* scope_local, bool precise_maybe_assigned) { // Sanity check the variable name. If this fails, the variable order // is not deterministic. CHECK_EQ(scope_local->raw_name()->length(), baseline_local->raw_name()->length()); for (int i = 0; i < scope_local->raw_name()->length(); ++i) { CHECK_EQ(scope_local->raw_name()->raw_data()[i], baseline_local->raw_name()->raw_data()[i]); } CHECK_EQ(scope_local->location(), baseline_local->location()); if (precise_maybe_assigned) { CHECK_EQ(scope_local->maybe_assigned(), baseline_local->maybe_assigned()); } else { static_assert(kMaybeAssigned > kNotAssigned); CHECK_GE(scope_local->maybe_assigned(), baseline_local->maybe_assigned()); } } // Finds a scope given a start point and directions to it (which inner scope // to pick). static Scope* FindScope(Scope* scope, const std::vector& location) { for (auto n : location) { scope = scope->inner_scope(); CHECK_NOT_NULL(scope); while (n-- > 0) { scope = scope->sibling(); CHECK_NOT_NULL(scope); } } return scope; } static void MarkInnerFunctionsAsSkipped(Scope* scope) { for (Scope* inner = scope->inner_scope(); inner != nullptr; inner = inner->sibling()) { if (inner->is_function_scope() && !inner->AsDeclarationScope()->is_arrow_scope()) { inner->AsDeclarationScope()->set_is_skipped_function(true); } MarkInnerFunctionsAsSkipped(inner); } } static bool HasSkippedFunctionInside(Scope* scope) { if (scope->is_function_scope() && scope->AsDeclarationScope()->is_skipped_function()) { return true; } for (Scope* inner = scope->inner_scope(); inner != nullptr; inner = inner->sibling()) { if (HasSkippedFunctionInside(inner)) return true; } return false; } }; } // namespace internal } // namespace v8 #endif // V8_CCTEST_SCOPE_TEST_HELPER_H_