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)); |
} |