Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(328)

Unified Diff: runtime/vm/dart_api_impl.cc

Issue 10687004: Implement method and variable reflection in dart:mirrors. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/vm/dart_api_impl.cc
===================================================================
--- runtime/vm/dart_api_impl.cc (revision 9071)
+++ runtime/vm/dart_api_impl.cc (working copy)
@@ -58,6 +58,58 @@
} while (0)
+// Removes internal vm prefixes and suffixes from an identifier.
+static RawString* StripName(Isolate* isolate, const String& name) {
cshapiro 2012/06/28 23:57:47 IdentifierPrettyName? Better comment with example
turnidge 2012/07/09 23:45:17 Done.
+ intptr_t len = name.Length();
+ intptr_t start = 0;
+ intptr_t at_pos = len; // Position of '@' in the name.
+ intptr_t dot_pos = len; // Position of '.' in the name.
+
+ for (int i = 0; i < name.Length(); i++) {
+ if (name.CharAt(i) == ':') {
+ ASSERT(start == 0);
+ start = i + 1;
+ } else if (name.CharAt(i) == '@') {
+ ASSERT(at_pos == len);
+ at_pos = i;
+ } else if (name.CharAt(i) == '.') {
+ dot_pos = i;
+ break;
+ }
+ }
+ intptr_t limit = (at_pos < dot_pos ? at_pos : dot_pos);
+ if (start == 0 && limit == len) {
+ // This name is fine as it is.
+ return name.raw();
+ }
+
+ String& result = String::Handle(isolate);
+ result = String::SubString(name, start, (limit - start));
+
+ // Look for a second '@' now to correctly handle names like
+ // "_ReceivePortImpl@6be832b._internal@6be832b".
+ at_pos = len;
+ for (int i = dot_pos; i < name.Length(); i++) {
+ if (name.CharAt(i) == '@') {
+ ASSERT(at_pos == len);
+ at_pos = i;
+ }
+ }
+
+ intptr_t suffix_len = at_pos - dot_pos;
+ if (suffix_len <= 1) {
+ // The constructor name is of length 0 or 1. That means that
+ // either this isn't a constructor or that this is an unnamed
+ // constructor. In either case, we're done.
+ return result.raw();
+ }
+
+ const String& suffix =
+ String::Handle(isolate, String::SubString(name, dot_pos, suffix_len));
+ return String::Concat(result, suffix);
+}
+
+
// Return error if isolate is in an inconsistent state.
// Return NULL when no error condition exists.
//
@@ -108,7 +160,7 @@
}
RawObject* Api::UnwrapHandle(Dart_Handle object) {
-#ifdef DEBUG
+#if defined(DEBUG)
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
ApiState* state = isolate->api_state();
@@ -2432,7 +2484,8 @@
if (cls.IsNull()) {
RETURN_TYPE_ERROR(isolate, clazz, Class);
}
- return Api::NewHandle(isolate, cls.Name());
+ const String& cls_name = String::Handle(isolate, cls.Name());
+ return Api::NewHandle(isolate, StripName(isolate, cls_name));
}
@@ -2444,6 +2497,14 @@
if (cls.IsNull()) {
RETURN_TYPE_ERROR(isolate, clazz, Class);
}
+
+#if defined(DEBUG)
+ const Library& lib = Library::Handle(cls.library());
+ if (lib.IsNull()) {
+ ASSERT(cls.IsDynamicClass() || cls.IsVoidClass());
+ }
+#endif
+
return Api::NewHandle(isolate, cls.library());
}
@@ -2522,6 +2583,402 @@
}
+// --- Function and Variable Reflection ---
+
+
+// Outside of the vm, we expose setter names with a trailing '='.
+static bool HasExternalSetterSuffix(const String& name) {
+ return name.CharAt(name.Length() - 1) == '=';
+}
+
+
+static RawString* AddExternalSetterSuffix(const String& name) {
+ const String& equals = String::Handle(String::NewSymbol("="));
+ return String::Concat(name, equals);
+}
+
+
+static RawString* RemoveExternalSetterSuffix(const String& name) {
+ ASSERT(HasExternalSetterSuffix(name));
+ return String::SubString(name, 0, name.Length() - 1);
+}
+
+
+DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+
+ const GrowableObjectArray& names =
+ GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
+ Function& func = Function::Handle();
+ String& name = String::Handle();
+
+ if (obj.IsClass()) {
+ Class& cls = Class::Handle(isolate);
+ cls ^= obj.raw();
+ const Array& func_array = Array::Handle(cls.functions());
+
+ // Some special types like 'Dynamic' have a null functions list.
+ if (!func_array.IsNull()) {
+ for (intptr_t i = 0; i < func_array.Length(); ++i) {
+ func ^= func_array.At(i);
+
+ // Skip implicit getters and setters.
+ if (func.kind() == RawFunction::kImplicitGetter ||
+ func.kind() == RawFunction::kImplicitSetter ||
+ func.kind() == RawFunction::kConstImplicitGetter) {
+ continue;
+ }
+
+ name = func.name();
+ bool is_setter = Field::IsSetterName(name);
+ name = StripName(isolate, name);
+
+ if (is_setter) {
+ name = AddExternalSetterSuffix(name);
+ }
+ names.Add(name);
+ }
+ }
+ } else if (obj.IsLibrary()) {
+ Library& lib = Library::Handle(isolate);
+ lib ^= obj.raw();
+ DictionaryIterator it(lib);
+ Object& obj = Object::Handle();
+ while (it.HasNext()) {
+ obj = it.GetNext();
+ if (obj.IsFunction()) {
+ func ^= obj.raw();
+ name = func.name();
+ bool is_setter = Field::IsSetterName(name);
+ name = StripName(isolate, name);
+ if (is_setter) {
+ name = AddExternalSetterSuffix(name);
+ }
+ names.Add(name);
+ }
+ }
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+ return Api::NewHandle(isolate, Array::MakeArray(names));
+}
+
+
+DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target,
+ Dart_Handle function_name) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+ const String& func_name = Api::UnwrapStringHandle(isolate, function_name);
+ if (func_name.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function_name, String);
+ }
+
+ Function& func = Function::Handle(isolate);
+ String& tmp_name = String::Handle(isolate);
+ if (obj.IsClass()) {
+ Class& cls = Class::Handle(isolate);
+ cls ^= obj.raw();
+ func = cls.LookupFunction(func_name);
+
+ // Check for functions with the external setter suffix '='. Make
cshapiro 2012/06/28 23:57:47 Totally optional, maybe make it clear that we are
turnidge 2012/07/09 23:45:17 Done.
turnidge 2012/07/09 23:45:17 Done.
+ // sure to do this check after the regular lookup, so that we
+ // don't interfere with operator lookups (like ==).
+ if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
+ tmp_name = RemoveExternalSetterSuffix(func_name);
+ tmp_name = Field::SetterName(tmp_name);
+ func = cls.LookupFunction(tmp_name);
+ }
+
+ // Try to look up the function as a getter.
+ if (func.IsNull()) {
+ tmp_name = Field::GetterName(func_name);
+ func = cls.LookupFunction(tmp_name);
+ }
+
+ // Special case for the unnamed constructor. We need to add a dot
+ // at the end of the name.
+ if (func.IsNull()) {
+ const String& dot = String::Handle(String::NewSymbol("."));
+ tmp_name = String::Concat(func_name, dot);
+ func = cls.LookupFunction(tmp_name);
+ }
+
+ if (func.IsNull()) {
+ const String& cls_name = String::Handle(cls.Name());
+ fprintf(stderr, "---> Couldn't find method '%s' in class '%s'\n",
cshapiro 2012/06/28 23:57:47 No arrows allowed!
turnidge 2012/07/09 23:45:17 Done.
+ func_name.ToCString(), cls_name.ToCString());
+ }
+ } else if (obj.IsLibrary()) {
+ Library& lib = Library::Handle(isolate);
+ lib ^= obj.raw();
+ func = lib.LookupFunctionAllowPrivate(func_name);
+
+ // Check for functions with the external setter suffix '='. Make
cshapiro 2012/06/28 23:57:47 Ditto.
turnidge 2012/07/09 23:45:17 Done.
+ // sure to do this check after the regular lookup, so that we
+ // don't interfere with operator lookups (like ==).
+ if (func.IsNull() && HasExternalSetterSuffix(func_name)) {
+ tmp_name = RemoveExternalSetterSuffix(func_name);
+ tmp_name = Field::SetterName(tmp_name);
+ func = lib.LookupFunctionAllowPrivate(tmp_name);
+ }
+
+ // Try to look up the function as a getter.
+ if (func.IsNull()) {
+ tmp_name = Field::GetterName(func_name);
+ func = lib.LookupFunctionAllowPrivate(tmp_name);
+ }
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+
+#if defined(DEBUG)
+ if (!func.IsNull()) {
+ // We only provide access to a subset of function kinds.
+ RawFunction::Kind func_kind = func.kind();
+ ASSERT(func_kind == RawFunction::kFunction ||
+ func_kind == RawFunction::kGetterFunction ||
+ func_kind == RawFunction::kSetterFunction ||
+ func_kind == RawFunction::kConstructor ||
+ func_kind == RawFunction::kAbstract);
+ }
+#endif
+ return Api::NewHandle(isolate, func.raw());
+}
+
+
+DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) {
+ return Api::ClassId(handle) == kFunction;
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ String& func_name = String::Handle(isolate);
+ func_name = func.name();
+ bool is_setter = Field::IsSetterName(func_name);
+ func_name = StripName(isolate, func_name);
+ if (is_setter) {
+ func_name = AddExternalSetterSuffix(func_name);
+ }
+ return Api::NewHandle(isolate, func_name.raw());
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function,
+ bool* is_abstract) {
cshapiro 2012/06/28 23:57:47 Null check.
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_abstract = func.kind() == RawFunction::kAbstract;
cshapiro 2012/06/28 23:57:47 Stick parens around the inner double equal.
turnidge 2012/07/09 23:45:17 Done.
+ return Api::Success(isolate);
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
+ bool* is_static) {
cshapiro 2012/06/28 23:57:47 Null check.
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_static = func.is_static();
+ return Api::Success(isolate);
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function,
+ bool* is_constructor) {
cshapiro 2012/06/28 23:57:47 Null check.
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ *is_constructor = func.kind() == RawFunction::kConstructor;
+ return Api::Success(isolate);
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function,
+ bool* is_getter) {
cshapiro 2012/06/28 23:57:47 Null check.
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ // TODO(turnidge): It would be nice if I could just use func.kind()
+ // to check for a getter function here, but unfortunately the only
+ // way to distinguish abstract getter functions is to use the name
+ // itself. Consider adding a RawFunction::kAbstractGetter type.
+ const String& func_name = String::Handle(isolate, func.name());
+ *is_getter = Field::IsGetterName(func_name);
+
+ return Api::Success(isolate);
+}
+
+
+DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function,
+ bool* is_setter) {
cshapiro 2012/06/28 23:57:47 Null check.
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Function& func = Api::UnwrapFunctionHandle(isolate, function);
+ if (func.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, function, Function);
+ }
+ // TODO(turnidge): It would be nice if I could just use func.kind()
+ // to check for a setter function here, but unfortunately the only
+ // way to distinguish abstract setter functions is to use the name
+ // itself. Consider adding a RawFunction::kAbstractSetter type.
+ const String& func_name = String::Handle(isolate, func.name());
+ *is_setter = Field::IsSetterName(func_name);
+
+ return Api::Success(isolate);
+}
+
+
+DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+
+ const GrowableObjectArray& names =
+ GrowableObjectArray::Handle(isolate, GrowableObjectArray::New());
+ Field& field = Field::Handle(isolate);
+ String& name = String::Handle(isolate);
+
+ if (obj.IsClass()) {
+ Class& cls = Class::Handle(isolate);
+ cls ^= obj.raw();
+ const Array& field_array = Array::Handle(cls.fields());
+
+ // Some special types like 'Dynamic' have a null fields list.
cshapiro 2012/06/28 23:57:47 Maybe fix this at the source if it is not too much
turnidge 2012/07/09 23:45:17 Investigated. Since Dynamic is allocated in the v
+ if (!field_array.IsNull()) {
+ for (intptr_t i = 0; i < field_array.Length(); ++i) {
+ field ^= field_array.At(i);
+ name = field.name();
+ name = StripName(isolate, name);
+ names.Add(name);
+ }
+ }
+ } else if (obj.IsLibrary()) {
+ Library& lib = Library::Handle(isolate);
+ lib ^= obj.raw();
+ DictionaryIterator it(lib);
+ Object& obj = Object::Handle(isolate);
+ while (it.HasNext()) {
+ obj = it.GetNext();
+ if (obj.IsField()) {
+ field ^= obj.raw();
+ name = field.name();
+ name = StripName(isolate, name);
+ names.Add(name);
+ }
+ }
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+ return Api::NewHandle(isolate, Array::MakeArray(names));
+}
+
+
+DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target,
+ Dart_Handle variable_name) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target));
+ if (obj.IsError()) {
+ return target;
+ }
+ const String& var_name = Api::UnwrapStringHandle(isolate, variable_name);
+ if (var_name.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable_name, String);
+ }
+ if (obj.IsClass()) {
+ Class& cls = Class::Handle(isolate);
+ cls ^= obj.raw();
+ return Api::NewHandle(isolate, cls.LookupField(var_name));
cshapiro 2012/06/28 23:57:47 If you have a return, why not get rid of the else
turnidge 2012/07/09 23:45:17 Done.
+ } else if (obj.IsLibrary()) {
+ Library& lib = Library::Handle(isolate);
+ lib ^= obj.raw();
+ return Api::NewHandle(isolate, lib.LookupFieldAllowPrivate(var_name));
+ } else {
+ return Api::NewError(
+ "%s expects argument 'target' to be a class or library.",
+ CURRENT_FUNC);
+ }
+}
+
+
+DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) {
+ return Api::ClassId(handle) == kField;
cshapiro 2012/06/28 23:57:47 Maybe make sure this is safe?
turnidge 2012/07/09 23:45:17 We do this a lot.
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable) {
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+ const String& var_name = String::Handle(var.name());
+ return Api::NewHandle(isolate, StripName(isolate, var_name));
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
+ bool* is_static) {
cshapiro 2012/06/28 23:57:47 Do we normally do null check for out variables? A
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+ *is_static = var.is_static();
+ return Api::Success(isolate);
+}
+
+
+DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable,
+ bool* is_final) {
cshapiro 2012/06/28 23:57:47 Null check for is_final too.
turnidge 2012/07/09 23:45:17 Done.
+ Isolate* isolate = Isolate::Current();
+ DARTSCOPE(isolate);
+ const Field& var = Api::UnwrapFieldHandle(isolate, variable);
+ if (var.IsNull()) {
+ RETURN_TYPE_ERROR(isolate, variable, Field);
+ }
+ *is_final = var.is_final();
+ return Api::Success(isolate);
+}
+
// --- Constructors, Methods, and Fields ---
@@ -3499,8 +3956,15 @@
String& name = String::Handle();
while (it.HasNext()) {
cls = it.GetNextClass();
- name = cls.Name();
- names.Add(name);
+ // For now we suppress the signature classes of closures.
+ //
+ // TODO(turnidge): Add this to the unit test.
+ const Function& signature_func = Function::Handle(cls.signature_function());
+ if (signature_func.IsNull()) {
+ name = cls.Name();
+ name = StripName(isolate, name);
+ names.Add(name);
+ }
}
return Api::NewHandle(isolate, Array::MakeArray(names));
}

Powered by Google App Engine
This is Rietveld 408576698