[ VM / Dart API ] Added additional methods required for Flutter background execution.

Change-Id: Id620ece49124da50bf50af3c1a390fb1f6186691
Reviewed-on: https://dart-review.googlesource.com/61118
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index d7734c1..4d54a54 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -1509,6 +1509,14 @@
 DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance);
 
 /**
+ * Returns the name for the provided class type.
+ *
+ * \return A valid string handle if no error occurs during the
+ *   operation.
+ */
+DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type);
+
+/**
  * Returns the name for the provided function or method.
  *
  * \return A valid string handle if no error occurs during the
@@ -1553,6 +1561,15 @@
  */
 DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure);
 
+/**
+ * Returns a handle to the library which contains class.
+ *
+ * \return A valid handle to the library with owns class, null if the class
+ *   has no library or an error handle if the argument is not a valid handle
+ *   to a class type.
+ */
+DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type);
+
 /*
  * =============================
  * Numbers, Integers and Doubles
@@ -1686,6 +1703,20 @@
 DART_EXPORT Dart_Handle Dart_GetClosure(Dart_Handle library,
                                         Dart_Handle function_name);
 
+/**
+ * Returns a closure of static function 'function_name' in the class 'class_name'
+ * in the exported namespace of specified 'library'.
+ *
+ * \param library Library object
+ * \param cls_type Type object representing a Class
+ * \param function_name Name of the static function in the class
+ *
+ * \return A valid Dart instance if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library,
+                                                    Dart_Handle cls_type,
+                                                    Dart_Handle function_name);
+
 /*
  * ========
  * Booleans
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index ebee057..761e198 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -2066,6 +2066,20 @@
   return Api::NewHandle(T, func.UserVisibleName());
 }
 
+DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type) {
+  DARTSCOPE(Thread::Current());
+  const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type);
+  if (type_obj.IsNull()) {
+    RETURN_TYPE_ERROR(Z, cls_type, Type);
+  }
+  const Class& klass = Class::Handle(Z, type_obj.type_class());
+  if (klass.IsNull()) {
+    return Api::NewError(
+        "cls_type must be a Type object which represents a Class.");
+  }
+  return Api::NewHandle(T, klass.UserVisibleName());
+}
+
 DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function) {
   DARTSCOPE(Thread::Current());
   const Function& func = Api::UnwrapFunctionHandle(Z, function);
@@ -2120,6 +2134,21 @@
   return Api::NewHandle(T, rf);
 }
 
+DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type) {
+  DARTSCOPE(Thread::Current());
+  const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type);
+  const Class& klass = Class::Handle(Z, type_obj.type_class());
+  if (klass.IsNull()) {
+    return Api::NewError(
+        "cls_type must be a Type object which represents a Class.");
+  }
+  const Library& library = Library::Handle(klass.library());
+  if (library.IsNull()) {
+    return Dart_Null();
+  }
+  return Api::NewHandle(Thread::Current(), library.raw());
+}
+
 // --- Numbers, Integers and Doubles ----
 
 DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer,
@@ -2300,6 +2329,53 @@
   return Api::NewHandle(T, lib.GetFunctionClosure(name));
 }
 
+DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library,
+                                                    Dart_Handle cls_type,
+                                                    Dart_Handle function_name) {
+  DARTSCOPE(Thread::Current());
+  const Library& lib = Api::UnwrapLibraryHandle(Z, library);
+  if (lib.IsNull()) {
+    RETURN_TYPE_ERROR(Z, library, Library);
+  }
+
+  const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type);
+  if (type_obj.IsNull()) {
+    RETURN_TYPE_ERROR(Z, cls_type, Type);
+  }
+
+  const Class& klass = Class::Handle(Z, type_obj.type_class());
+  if (klass.IsNull()) {
+    return Api::NewError(
+        "cls_type must be a Type object which represents a Class");
+  }
+
+  const String& func_name = Api::UnwrapStringHandle(Z, function_name);
+  if (func_name.IsNull()) {
+    RETURN_TYPE_ERROR(Z, function_name, String);
+  }
+
+  Function& func =
+      Function::Handle(Z, klass.LookupStaticFunctionAllowPrivate(func_name));
+  if (func.IsNull()) {
+    return Dart_Null();
+  }
+
+  if (!func.is_static()) {
+    return Api::NewError("function_name must refer to a static method.");
+  }
+
+  if (func.kind() != RawFunction::kRegularFunction) {
+    return Api::NewError(
+        "function_name must be the name of a regular function.");
+  }
+  func ^= func.ImplicitClosureFunction();
+  if (func.IsNull()) {
+    return Dart_Null();
+  }
+
+  return Api::NewHandle(T, func.ImplicitStaticClosure());
+}
+
 // --- Booleans ----
 
 DART_EXPORT Dart_Handle Dart_True() {
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index b88dd39..8b7fe6b 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -898,6 +898,77 @@
   EXPECT(strstr(result, "getInt"));
 }
 
+TEST_CASE(DartAPI_GetStaticMethodClosure) {
+  const char* kScriptChars =
+      "class Foo {\n"
+      "  static int getInt() {\n"
+      "    return 1;\n"
+      "  }\n"
+      "  double getDouble() {\n"
+      "    return 1.0;\n"
+      "  }\n"
+      "}\n";
+  // Create a test library and Load up a test script in it.
+  Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
+  EXPECT_VALID(lib);
+  Dart_Handle foo_cls = Dart_GetClass(lib, NewString("Foo"));
+  EXPECT_VALID(foo_cls);
+
+  Dart_Handle closure =
+      Dart_GetStaticMethodClosure(lib, foo_cls, NewString("getInt"));
+  EXPECT_VALID(closure);
+  EXPECT(Dart_IsClosure(closure));
+  Dart_Handle closure_str = Dart_ToString(closure);
+  const char* result = "";
+  Dart_StringToCString(closure_str, &result);
+  EXPECT_SUBSTRING("getInt", result);
+
+  Dart_Handle function = Dart_ClosureFunction(closure);
+  EXPECT_VALID(function);
+  EXPECT(Dart_IsFunction(function));
+  Dart_Handle func_str = Dart_ToString(function);
+  Dart_StringToCString(func_str, &result);
+  EXPECT_SUBSTRING("getInt", result);
+
+  Dart_Handle cls = Dart_FunctionOwner(function);
+  EXPECT_VALID(cls);
+  EXPECT(Dart_IsInstance(cls));
+  Dart_Handle cls_str = Dart_ClassName(cls);
+  Dart_StringToCString(cls_str, &result);
+  EXPECT_SUBSTRING("Foo", result);
+
+  EXPECT_ERROR(Dart_ClassName(Dart_Null()),
+               "Dart_ClassName expects argument 'cls_type' to be non-null.");
+  EXPECT_ERROR(
+      Dart_GetStaticMethodClosure(Dart_Null(), foo_cls, NewString("getInt")),
+      "Dart_GetStaticMethodClosure expects argument 'library' to be non-null.");
+  EXPECT_ERROR(
+      Dart_GetStaticMethodClosure(lib, Dart_Null(), NewString("getInt")),
+      "Dart_GetStaticMethodClosure expects argument 'cls_type' to be "
+      "non-null.");
+  EXPECT_ERROR(Dart_GetStaticMethodClosure(lib, foo_cls, Dart_Null()),
+               "Dart_GetStaticMethodClosure expects argument 'function_name' "
+               "to be non-null.");
+}
+
+TEST_CASE(DartAPI_ClassLibrary) {
+  Dart_Handle lib = Dart_LookupLibrary(NewString("dart:core"));
+  EXPECT_VALID(lib);
+  Dart_Handle type = Dart_GetType(lib, NewString("int"), 0, NULL);
+  EXPECT_VALID(type);
+  Dart_Handle result = Dart_ClassLibrary(type);
+  EXPECT_VALID(result);
+  Dart_Handle lib_url = Dart_LibraryUrl(result);
+  const char* str = NULL;
+  Dart_StringToCString(lib_url, &str);
+  EXPECT_STREQ("dart:core", str);
+
+  // Case with no library.
+  type = Dart_GetType(lib, NewString("dynamic"), 0, NULL);
+  EXPECT_VALID(type);
+  EXPECT(Dart_IsNull(Dart_ClassLibrary(type)));
+}
+
 TEST_CASE(DartAPI_BooleanValues) {
   Dart_Handle str = NewString("test");
   EXPECT(!Dart_IsBoolean(str));