// 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/ast_printer.h"
#include "vm/class_finalizer.h"
#include "vm/longjump.h"
#include "vm/object.h"
#include "vm/parser.h"
#include "vm/unit_test.h"

namespace dart {


void DumpFunction(const Library& lib, const char* cname, const char* fname) {
  const String& classname = String::Handle(String::NewSymbol(cname));
  Class& cls = Class::Handle(lib.LookupClass(classname));
  EXPECT(!cls.IsNull());

  String& funcname = String::Handle(String::New(fname));
  Function& function = Function::Handle(cls.LookupStaticFunction(funcname));
  EXPECT(!function.IsNull());

  bool retval;
  Isolate* isolate = Isolate::Current();
  EXPECT(isolate != NULL);
  LongJump* base = isolate->long_jump_base();
  LongJump jump;
  isolate->set_long_jump_base(&jump);
  if (setjmp(*jump.Set()) == 0) {
    ParsedFunction parsed_function(function);
    Parser::ParseFunction(&parsed_function);
    EXPECT(parsed_function.node_sequence() != NULL);
    printf("Class %s function %s:\n", cname, fname);
    AstPrinter::PrintFunctionNodes(parsed_function);
    retval = true;
  } else {
    retval = false;
  }
  EXPECT(retval);
  isolate->set_long_jump_base(base);
}


void CheckField(const Library& lib,
                const char* class_name,
                const char* field_name,
                bool expect_static,
                bool is_final) {
  const String& classname = String::Handle(String::NewSymbol(class_name));
  Class& cls = Class::Handle(lib.LookupClass(classname));
  EXPECT(!cls.IsNull());

  String& fieldname = String::Handle(String::New(field_name));
  String& functionname = String::Handle();
  Function& function = Function::Handle();
  Field& field = Field::Handle();
  if (expect_static) {
    field ^= cls.LookupStaticField(fieldname);
    functionname ^= Field::GetterName(fieldname);
    function ^= cls.LookupStaticFunction(functionname);
    EXPECT(function.IsNull());
    functionname ^= Field::SetterName(fieldname);
    function ^= cls.LookupStaticFunction(functionname);
    EXPECT(function.IsNull());
  } else {
    field ^= cls.LookupInstanceField(fieldname);
    functionname ^= Field::GetterName(fieldname);
    function ^= cls.LookupDynamicFunction(functionname);
    EXPECT(!function.IsNull());
    functionname ^= Field::SetterName(fieldname);
    function ^= cls.LookupDynamicFunction(functionname);
    EXPECT(is_final ? function.IsNull() : !function.IsNull());
  }
  EXPECT(!field.IsNull());

  EXPECT_EQ(field.is_static(), expect_static);
}


void CheckFunction(const Library& lib,
                   const char* class_name,
                   const char* function_name,
                   bool expect_static) {
  const String& classname = String::Handle(String::NewSymbol(class_name));
  Class& cls = Class::Handle(lib.LookupClass(classname));
  EXPECT(!cls.IsNull());

  String& functionname = String::Handle(String::New(function_name));
  Function& function = Function::Handle();
  if (expect_static) {
    function ^= cls.LookupStaticFunction(functionname);
  } else {
    function ^= cls.LookupDynamicFunction(functionname);
  }
  EXPECT(!function.IsNull());
}


TEST_CASE(ParseClassDefinition) {
  const char* script_chars =
      "class C { }  \n"
      "class A {    \n"
      "  var f0;              \n"
      "  int f1;              \n"
      "  final f2;            \n"
      "  final int f3, f4;    \n"
      "  static String s1, s2;   \n"
      "  static final int s3 = 8675309;    \n"
      "  static bar(i, var d = 5) { return 77; } \n"
      "  static foo() native \"native_function_name\";        \n"
      "}  \n";

  String& url = String::Handle(String::New("dart-test:Parser_TopLevel"));
  String& source = String::Handle(String::New(script_chars));
  Script& script = Script::Handle(Script::New(url, source, RawScript::kSource));
  Library& lib = Library::ZoneHandle(Library::CoreLibrary());

  script.Tokenize(String::Handle(String::New("")));

  Parser::ParseCompilationUnit(lib, script);
  EXPECT(ClassFinalizer::FinalizePendingClasses());
  CheckField(lib, "A", "f1", false, false);
  CheckField(lib, "A", "f2", false, true);
  CheckField(lib, "A", "f3", false, true);
  CheckField(lib, "A", "f4", false, true);
  CheckField(lib, "A", "s1", true, false);
  CheckField(lib, "A", "s2", true, false);
  // No field entries are added for static const fields.
  String& fieldname = String::Handle(String::New("s3"));
  const String& classname = String::Handle(String::NewSymbol("A"));
  const Class& cls = Class::Handle(lib.LookupClass(classname));
  const Field& field = Field::Handle(cls.LookupStaticField(fieldname));
  EXPECT(!field.IsNull());
  Object& val = Object::Handle(field.value());
  // The static const field initializer is not evaluated and the field
  // value is not initialized until the field is referenced by code that
  // is compiled.
  EXPECT(val.raw() == Object::sentinel());

  CheckFunction(lib, "A", "bar", true);
  CheckFunction(lib, "A", "foo", true);
}


TEST_CASE(Parser_TopLevel) {
  const char* script_chars =
      "class A extends B {    \n"
      "  static bar(var i, var d = 5) { return 77; } \n"
      "  static foo() { return 42; } \n"
      "  static baz(var i) { var q = 5; return i + q; } \n"
      "}   \n"
      "    \n"
      "class B {  \n"
      "  static bam(k) { return A.foo(); }   \n"
      "}   \n";

  String& url = String::Handle(String::New("dart-test:Parser_TopLevel"));
  String& source = String::Handle(String::New(script_chars));
  Script& script = Script::Handle(Script::New(url, source, RawScript::kSource));
  Library& lib = Library::ZoneHandle(Library::CoreLibrary());

  script.Tokenize(String::Handle(String::New("")));

  Parser::ParseCompilationUnit(lib, script);
  EXPECT(ClassFinalizer::FinalizePendingClasses());

  DumpFunction(lib, "A", "foo");
  DumpFunction(lib, "A", "bar");
  DumpFunction(lib, "A", "baz");
  DumpFunction(lib, "B", "bam");
}

}  // namespace dart
