// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "vm/assert.h"
#include "vm/ast.h"
#include "vm/scopes.h"
#include "vm/unit_test.h"

namespace dart {

static const intptr_t kPos = 1;  // Dummy token index in non-existing source.


TEST_CASE(LocalScope) {
  // Allocate a couple of local variables first.
  const Type& var_type = Type::ZoneHandle(Type::VarType());
  const String& a = String::ZoneHandle(String::New("a"));
  LocalVariable* var_a = new LocalVariable(kPos, a, var_type);
  LocalVariable* inner_var_a = new LocalVariable(kPos, a, var_type);
  const String& b = String::ZoneHandle(String::New("b"));
  LocalVariable* var_b = new LocalVariable(kPos, b, var_type);
  const String& c = String::ZoneHandle(String::New("c"));
  LocalVariable* var_c = new LocalVariable(kPos, c, var_type);
  const String& L = String::ZoneHandle(String::New("L"));
  SourceLabel* label_L = new SourceLabel(kPos, L, SourceLabel::kFor);

  LocalScope* outer_scope = new LocalScope(NULL, 0, 0);
  LocalScope* inner_scope1 = new LocalScope(outer_scope, 0, 0);
  LocalScope* inner_scope2 = new LocalScope(outer_scope, 0, 0);

  EXPECT(outer_scope->parent() == NULL);
  EXPECT_EQ(outer_scope, inner_scope1->parent());
  EXPECT_EQ(outer_scope, inner_scope2->parent());
  EXPECT_EQ(inner_scope2, outer_scope->child());
  EXPECT_EQ(inner_scope1, inner_scope2->sibling());
  EXPECT(inner_scope1->child() == NULL);
  EXPECT(inner_scope2->child() == NULL);

  // Populate the local scopes as follows:
  // {  // outer_scope
  //   var a;
  //   {  // inner_scope1
  //     var b;
  //   }
  //   L: {  // inner_scope2
  //     var c;
  //   }
  // }
  EXPECT(outer_scope->AddVariable(var_a));
  EXPECT(inner_scope1->AddVariable(var_b));
  EXPECT(inner_scope2->AddVariable(var_c));
  EXPECT(inner_scope2->AddLabel(label_L));
  EXPECT(!outer_scope->AddVariable(var_a));

  // Check the simple layout above.
  EXPECT_EQ(var_a, outer_scope->LocalLookupVariable(a));
  EXPECT_EQ(var_a, inner_scope1->LookupVariable(a, true));
  EXPECT_EQ(label_L, inner_scope2->LookupLabel(L));
  EXPECT(outer_scope->LocalLookupVariable(b) == NULL);
  EXPECT(inner_scope1->LocalLookupVariable(c) == NULL);

  // Modify the local scopes to contain shadowing:
  // {  // outer_scope
  //   var a;
  //   {  // inner_scope1
  //     var b;
  //     var a;  // inner_var_a
  //   }
  //   {  // inner_scope2
  //     var c;
  //     L: ...
  //   }
  // }
  EXPECT(inner_scope1->AddVariable(inner_var_a));
  EXPECT_EQ(inner_var_a, inner_scope1->LookupVariable(a, true));
  EXPECT(inner_scope1->LookupVariable(a, true) != var_a);

  // Modify the local scopes with access of an outer scope variable:
  // {  // outer_scope
  //   var a;
  //   {  // inner_scope1
  //     var b;
  //     var a;  // inner_var_a
  //   }
  //   {  // inner_scope2
  //     var c = a;
  //     L: ...
  //   }
  // }
  EXPECT(inner_scope2->LocalLookupVariable(a) == NULL);
  EXPECT(inner_scope2->AddVariable(var_a));
  EXPECT_EQ(var_a, inner_scope2->LocalLookupVariable(a));

  EXPECT_EQ(1, outer_scope->num_variables());
  EXPECT_EQ(2, inner_scope1->num_variables());
  EXPECT_EQ(2, inner_scope2->num_variables());

  // Cannot depend on the order, but we should find the variables.
  EXPECT(outer_scope->VariableAt(0) == var_a);
  EXPECT((inner_scope1->VariableAt(0) == inner_var_a) ||
         (inner_scope1->VariableAt(1) == inner_var_a));
  EXPECT((inner_scope1->VariableAt(0) == var_b) ||
         (inner_scope1->VariableAt(1) == var_b));
  EXPECT((inner_scope2->VariableAt(0) == var_a) ||
         (inner_scope2->VariableAt(1) == var_a) ||
         (inner_scope2->VariableAt(2) == var_a));
  EXPECT((inner_scope2->VariableAt(0) == var_c) ||
         (inner_scope2->VariableAt(1) == var_c) ||
         (inner_scope2->VariableAt(2) == var_c));
}

}  // namespace dart
