Chromium Code Reviews| 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)); |
| } |