Implement method and variable reflection in dart:mirrors.

Review URL: https://chromiumcodereview.appspot.com//10687004

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@9493 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
turnidge@google.com 2012-07-09 23:57:08 +00:00
parent d3a67e8016
commit 22fb45f640
9 changed files with 2221 additions and 164 deletions

View file

@ -15,7 +15,6 @@
// ...the getter is named 'myField' and the setter is named
// 'myField='. This allows us to assign unique names to getters and
// setters for the purposes of member lookup.
// TODO(turnidge): Implement getter/setter lookup.
//
// TODO(turnidge): Finish implementing this api.
@ -48,7 +47,7 @@ Future<IsolateMirror> isolateMirrorOf(SendPort port) {
*/
interface Mirror {
/**
* The isolate of orgin for this [Mirror].
* A mirror on the originating isolate for this [Mirror].
*/
final IsolateMirror isolate;
}
@ -92,8 +91,6 @@ interface ObjectMirror extends Mirror {
* Invokes the named function and returns a mirror on the result.
*
* TODO(turnidge): Properly document.
*
* TODO(turnidge): what to do if invoke causes the death of the reflectee?
*/
Future<InstanceMirror> invoke(String memberName,
List<Object> positionalArguments,
@ -118,17 +115,16 @@ interface InstanceMirror extends ObjectMirror {
* If the [InstanceMirror] refers to a simple value, we provide
* access to the actual value here.
*
* A value is simple if:
* - it is null
* - it is of type [num]
* - it is of type [bool]
* - it is of type [String]
* A value is simple if one of the following holds:
* - the value is null
* - the value is of type [num]
* - the value is of type [bool]
* - the value is of type [String]
*
* If you access [simpleValue] when [hasSimpleValue] is false an
* exception is thrown.
*/
final simpleValue;
}
/**
@ -170,14 +166,26 @@ interface InterfaceMirror extends ObjectMirror {
/**
* An immutable map from from names to mirrors for all members of
* this type, including inherited members.
* this type.
*
* The members of an interface are its constructors, methods,
* fields, getters, and setters.
*
* TODO(turnidge): Currently empty.
* This does not include inherited members.
*/
Map<String, Mirror> members();
/**
* An immutable map from names to mirrors for all method,
* constructor, getter, and setter declarations in this library.
*/
Map<String, MethodMirror> methods();
/**
* An immutable map from names to mirrors for all variable
* declarations in this library.
*/
Map<String, VariableMirror> variables();
}
/**
@ -200,17 +208,159 @@ interface LibraryMirror extends ObjectMirror {
final String url;
/**
* An immutable map from from top-level names to mirrors for all
* members in this library.
* An immutable map from from names to mirrors for all members in
* this library.
*
* The members of a library are its top-level classes, interfaces,
* functions, variables, getters, and setters.
*
* TODO(turnidge): Currently only contains classes and interfaces.
*/
Map<String, Mirror> members();
/**
* An immutable map from names to mirrors for all class and
* interface declarations in this library.
*/
Map<String, InterfaceMirror> classes();
/**
* An immutable map from names to mirrors for all function
* declarations in this library.
*/
Map<String, MethodMirror> functions();
/**
* An immutable map from names to mirrors for all variable
* declarations in this library.
*/
Map<String, VariableMirror> variables();
}
/**
* A [MethodMirror] reflects a Dart language function, method,
* constructor, getter, or setter.
*/
interface MethodMirror {
/**
* The name of this function.
*/
final String simpleName;
/**
* A mirror on the owner of this function. This is the declaration
* immediately surrounding the reflectee.
*
* For top-level functions, this will be a [LibraryMirror] and for
* methods, constructors, getters, and setters, this will be an
* [InterfaceMirror].
*/
Mirror owner;
// Ownership
/**
* Does this mirror reflect a top-level function?
*/
bool isTopLevel;
/**
* Does this mirror reflect a static method?
*
* For the purposes of the mirrors library, a top-level function is
* considered static.
*/
bool isStatic;
// Method kind
/**
* Does this mirror reflect a regular function or method?
*
* A method is regular if it is not a getter, setter, or constructor.
*/
bool isMethod;
/**
* Does this mirror reflect an abstract method?
*/
bool isAbstract;
/**
* Does this mirror reflect a getter?
*/
bool isGetter;
/**
* Does this mirror reflect a setter?
*/
bool isSetter;
/**
* Does this mirror reflect a constructor?
*/
bool isConstructor;
// Constructor kind
/**
* Does this mirror reflect a const constructor?
*/
bool isConstConstructor;
/**
* Does this mirror reflect a generative constructor?
*/
bool isGenerativeConstructor;
/**
* Does this mirror reflect a redirecting constructor?
*/
bool isRedirectingConstructor;
/**
* Does this mirror reflect a factory constructor?
*/
bool isFactoryConstructor;
}
/**
* A [VariableMirror] reflects a Dart language variable.
*/
interface VariableMirror {
/**
* The name of this variable
*/
final String simpleName;
/**
* A mirror on the owner of this method. The owner is the
* declaration immediately surrounding the reflectee.
*
* For top-level variables, this will be a [LibraryMirror] and for
* class and interface variables, this will be an [InterfaceMirror].
*/
Mirror owner;
/**
* Does this mirror reflect a top-level variable?
*/
bool isTopLevel;
/**
* Does this mirror reflect a static variable?
*
* For the purposes of the mirror library, top-level variables are
* implicitly declared static.
*/
bool isStatic;
/**
* Does this mirror reflect a final variable?
*/
bool isFinal;
}
/**
* When an error occurs during the mirrored execution of code, a
* [MirroredError] is thrown.
@ -230,7 +380,7 @@ interface LibraryMirror extends ObjectMirror {
* the reflector and reflectee share the same isolate, then they
* will both suffer. If the reflector and reflectee are in distinct
* isolates, then we hope to provide some information about the
* isolate death, but this is yet to be implemented.
* isolate death, but this has yet to be implemented.
*
* TODO(turnidge): Specify the behavior for remote fatal errors.
*/

View file

@ -2022,6 +2022,165 @@ DART_EXPORT Dart_Handle Dart_ClassGetInterfaceCount(Dart_Handle clazz,
DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz,
intptr_t index);
// --- Function and Variable Declarations ---
/**
* Returns a list of the names of all functions or methods declared in
* a library or class.
*
* \param target A library or class.
*
* \return If no error occurs, a list of strings is returned.
* Otherwise an erorr handle is returned.
*/
DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target);
/**
* Looks up a function or method declaration by name from a library or
* class.
*
* \param target The library or class containing the function.
* \param function_name The name of the function.
*
* \return If an error is encountered, returns an error handle.
* Otherwise returns a function handle if the function is found of
* Dart_Null() if the function is not found.
*/
DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target,
Dart_Handle function_name);
/**
* Is this a function or method declaration handle?
*/
DART_EXPORT bool Dart_IsFunction(Dart_Handle handle);
/**
* Returns the name for the provided function or method.
*
* \return A valid string handle if no error occurs during the
* operation.
*/
DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function);
/**
* Determines whether a function handle refers to an abstract method.
*
* \param function A handle to a function or method declaration.
* \param is_static Returns whether the handle refers to an abstract method.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function,
bool* is_abstract);
/**
* Determines whether a function handle referes to a static function
* of method.
*
* For the purposes of the embedding API, a top-level function is
* implicitly declared static.
*
* \param function A handle to a function or method declaration.
* \param is_static Returns whether the function or method is declared static.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
bool* is_static);
/**
* Determines whether a function handle referes to a constructor.
*
* \param function A handle to a function or method declaration.
* \param is_static Returns whether the function or method is a constructor.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function,
bool* is_constructor);
// TODO(turnidge): Document behavior for factory constructors too.
/**
* Determines whether a function or method is a getter.
*
* \param function A handle to a function or method declaration.
* \param is_static Returns whether the function or method is a getter.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function,
bool* is_getter);
/**
* Determines whether a function or method is a setter.
*
* \param function A handle to a function or method declaration.
* \param is_static Returns whether the function or method is a setter.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function,
bool* is_setter);
/**
* Returns a list of the names of all variables declared in a library
* or class.
*
* \param target A library or class.
*
* \return If no error occurs, a list of strings is returned.
* Otherwise an erorr handle is returned.
*/
DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target);
/**
* Looks up a variable declaration by name from a library or class.
*
* \param library The library or class containing the variable.
* \param variable_name The name of the variable.
*
* \return If an error is encountered, returns an error handle.
* Otherwise returns a variable handle if the variable is found or
* Dart_Null() if the variable is not found.
*/
DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target,
Dart_Handle variable_name);
/**
* Is this a variable declaration handle?
*/
DART_EXPORT bool Dart_IsVariable(Dart_Handle handle);
/**
* Returns the name for the provided variable.
*/
DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable);
/**
* Determines whether a variable is declared static.
*
* For the purposes of the embedding API, a top-level variable is
* implicitly declared static.
*
* \param variable A handle to a variable declaration.
* \param is_static Returns whether the variable is declared static.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
bool* is_static);
/**
* Determines whether a variable is declared final.
*
* \param variable A handle to a variable declaration.
* \param is_final Returns whether the variable is declared final.
*
* \return A valid handle if no error occurs during the operation.
*/
DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable,
bool* is_final);
// --- Constructors, Methods, and Fields ---
/**

View file

@ -51,19 +51,14 @@ static Dart_Handle MapNew() {
static Dart_Handle MapAdd(Dart_Handle map, Dart_Handle key, Dart_Handle value) {
const int kNumArgs = 2;
Dart_Handle args[kNumArgs];
args[0] = key;
args[1] = value;
return Dart_Invoke(map, Dart_NewString("[]="), kNumArgs, args);
Dart_Handle args[] = { key, value };
return Dart_Invoke(map, Dart_NewString("[]="), ARRAY_SIZE(args), args);
}
static Dart_Handle MapGet(Dart_Handle map, Dart_Handle key) {
const int kNumArgs = 1;
Dart_Handle args[kNumArgs];
args[0] = key;
return Dart_Invoke(map, Dart_NewString("[]"), kNumArgs, args);
Dart_Handle args[] = { key };
return Dart_Invoke(map, Dart_NewString("[]"), ARRAY_SIZE(args), args);
}
@ -198,30 +193,31 @@ static Dart_Handle UnwrapArgList(Dart_Handle arg_list,
}
static Dart_Handle CreateLazyLibraryMirror(Dart_Handle lib) {
if (Dart_IsNull(lib)) {
return lib;
static Dart_Handle CreateLazyMirror(Dart_Handle target) {
if (Dart_IsNull(target)) {
return target;
}
Dart_Handle cls_name = Dart_NewString("_LazyLibraryMirror");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
const int kNumArgs = 1;
Dart_Handle args[kNumArgs];
args[0] = Dart_LibraryName(lib);
return Dart_New(cls, Dart_Null(), kNumArgs, args);
}
if (Dart_IsLibrary(target)) {
Dart_Handle cls_name = Dart_NewString("_LazyLibraryMirror");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
Dart_Handle args[] = { Dart_LibraryName(target) };
return Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
} else {
ASSERT(Dart_IsClass(target) || Dart_IsInterface(target));
Dart_Handle cls_name = Dart_NewString("_LazyInterfaceMirror");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
Dart_Handle lib = Dart_ClassGetLibrary(target);
Dart_Handle lib_name;
if (Dart_IsNull(lib)) {
lib_name = Dart_Null();
} else {
lib_name = Dart_LibraryName(lib);
}
static Dart_Handle CreateLazyInterfaceMirror(Dart_Handle intf) {
if (Dart_IsNull(intf)) {
return intf;
Dart_Handle args[] = { lib_name, Dart_ClassName(target) };
return Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
}
Dart_Handle cls_name = Dart_NewString("_LazyInterfaceMirror");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
const int kNumArgs = 2;
Dart_Handle args[kNumArgs];
args[0] = Dart_LibraryName(Dart_ClassGetLibrary(intf));
args[1] = Dart_ClassName(intf);
return Dart_New(cls, Dart_Null(), kNumArgs, args);
}
@ -242,7 +238,7 @@ static Dart_Handle CreateImplementsList(Dart_Handle intf) {
if (Dart_IsError(interface)) {
return interface;
}
Dart_Handle mirror = CreateLazyInterfaceMirror(interface);
Dart_Handle mirror = CreateLazyMirror(interface);
if (Dart_IsError(mirror)) {
return mirror;
}
@ -255,9 +251,13 @@ static Dart_Handle CreateImplementsList(Dart_Handle intf) {
}
static Dart_Handle CreateMemberMap(Dart_Handle owner);
static Dart_Handle CreateInterfaceMirror(Dart_Handle intf,
Dart_Handle intf_name,
Dart_Handle lib) {
Dart_Handle lib,
Dart_Handle lib_mirror) {
ASSERT(Dart_IsClass(intf) || Dart_IsInterface(intf));
Dart_Handle cls_name = Dart_NewString("_LocalInterfaceMirrorImpl");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
@ -271,45 +271,236 @@ static Dart_Handle CreateInterfaceMirror(Dart_Handle intf,
super_class = Dart_GetClass(CoreLib(), Dart_NewString("Object"));
}
Dart_Handle default_class = Dart_ClassGetDefault(intf);
Dart_Handle member_map = CreateMemberMap(intf);
if (Dart_IsError(member_map)) {
return member_map;
}
const int kNumArgs = 7;
Dart_Handle args[kNumArgs];
args[0] = CreateVMReference(intf);
args[1] = intf_name;
args[2] = Dart_NewBoolean(Dart_IsClass(intf));
args[3] = CreateLazyLibraryMirror(lib);
args[4] = CreateLazyInterfaceMirror(super_class);
args[5] = CreateImplementsList(intf);
args[6] = CreateLazyInterfaceMirror(default_class);
Dart_Handle mirror = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = {
CreateVMReference(intf),
intf_name,
Dart_NewBoolean(Dart_IsClass(intf)),
lib_mirror,
CreateLazyMirror(super_class),
CreateImplementsList(intf),
CreateLazyMirror(default_class),
member_map,
};
Dart_Handle mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return mirror;
}
static Dart_Handle CreateLibraryMemberMap(Dart_Handle lib) {
// TODO(turnidge): This should be an immutable map.
Dart_Handle map = MapNew();
static Dart_Handle CreateMethodMirror(Dart_Handle func,
Dart_Handle func_name,
Dart_Handle lib_mirror) {
ASSERT(Dart_IsFunction(func));
Dart_Handle cls_name = Dart_NewString("_LocalMethodMirrorImpl");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
if (Dart_IsError(cls)) {
return cls;
}
Dart_Handle intf_names = Dart_LibraryGetClassNames(lib);
if (Dart_IsError(intf_names)) {
return intf_names;
bool is_static = false;
bool is_abstract = false;
bool is_getter = false;
bool is_setter = false;
bool is_constructor = false;
Dart_Handle result = Dart_FunctionIsStatic(func, &is_static);
if (Dart_IsError(result)) {
return result;
}
result = Dart_FunctionIsAbstract(func, &is_abstract);
if (Dart_IsError(result)) {
return result;
}
result = Dart_FunctionIsGetter(func, &is_getter);
if (Dart_IsError(result)) {
return result;
}
result = Dart_FunctionIsSetter(func, &is_setter);
if (Dart_IsError(result)) {
return result;
}
result = Dart_FunctionIsConstructor(func, &is_constructor);
if (Dart_IsError(result)) {
return result;
}
// TODO(turnidge): Implement constructor kinds (arguments 7 - 10).
Dart_Handle args[] = {
func_name,
lib_mirror,
Dart_NewBoolean(is_static),
Dart_NewBoolean(is_abstract),
Dart_NewBoolean(is_getter),
Dart_NewBoolean(is_setter),
Dart_NewBoolean(is_constructor),
Dart_False(),
Dart_False(),
Dart_False(),
Dart_False(),
};
Dart_Handle mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return mirror;
}
static Dart_Handle CreateVariableMirror(Dart_Handle var,
Dart_Handle var_name,
Dart_Handle lib_mirror) {
ASSERT(Dart_IsVariable(var));
Dart_Handle cls_name = Dart_NewString("_LocalVariableMirrorImpl");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
if (Dart_IsError(cls)) {
return cls;
}
bool is_static = false;
bool is_final = false;
Dart_Handle result = Dart_VariableIsStatic(var, &is_static);
if (Dart_IsError(result)) {
return result;
}
result = Dart_VariableIsFinal(var, &is_final);
if (Dart_IsError(result)) {
return result;
}
Dart_Handle args[] = {
var_name,
lib_mirror,
Dart_NewBoolean(is_static),
Dart_NewBoolean(is_final),
};
Dart_Handle mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return mirror;
}
static Dart_Handle AddMemberClasses(Dart_Handle map,
Dart_Handle owner,
Dart_Handle owner_mirror) {
ASSERT(Dart_IsLibrary(owner));
Dart_Handle result;
Dart_Handle names = Dart_LibraryGetClassNames(owner);
if (Dart_IsError(names)) {
return names;
}
intptr_t len;
Dart_Handle result = Dart_ListLength(intf_names, &len);
result = Dart_ListLength(names, &len);
if (Dart_IsError(result)) {
return result;
}
for (int i = 0; i < len; i++) {
Dart_Handle intf_name = Dart_ListGetAt(intf_names, i);
Dart_Handle intf = Dart_GetClass(lib, intf_name);
Dart_Handle intf_name = Dart_ListGetAt(names, i);
Dart_Handle intf = Dart_GetClass(owner, intf_name);
if (Dart_IsError(intf)) {
return intf;
}
Dart_Handle intf_mirror = CreateInterfaceMirror(intf, intf_name, lib);
Dart_Handle intf_mirror =
CreateInterfaceMirror(intf, intf_name, owner, owner_mirror);
if (Dart_IsError(intf_mirror)) {
return intf_mirror;
}
result = MapAdd(map, intf_name, intf_mirror);
if (Dart_IsError(result)) {
return result;
}
}
return Dart_True();
}
static Dart_Handle AddMemberFunctions(Dart_Handle map,
Dart_Handle owner,
Dart_Handle owner_mirror) {
Dart_Handle result;
Dart_Handle names = Dart_GetFunctionNames(owner);
if (Dart_IsError(names)) {
return names;
}
intptr_t len;
result = Dart_ListLength(names, &len);
if (Dart_IsError(result)) {
return result;
}
for (int i = 0; i < len; i++) {
Dart_Handle func_name = Dart_ListGetAt(names, i);
Dart_Handle func = Dart_LookupFunction(owner, func_name);
if (Dart_IsError(func)) {
return func;
}
ASSERT(!Dart_IsNull(func));
Dart_Handle func_mirror = CreateMethodMirror(func, func_name, owner_mirror);
if (Dart_IsError(func_mirror)) {
return func_mirror;
}
result = MapAdd(map, func_name, func_mirror);
if (Dart_IsError(result)) {
return result;
}
}
return Dart_True();
}
static Dart_Handle AddMemberVariables(Dart_Handle map,
Dart_Handle owner,
Dart_Handle owner_mirror) {
Dart_Handle result;
Dart_Handle names = Dart_GetVariableNames(owner);
if (Dart_IsError(names)) {
return names;
}
intptr_t len;
result = Dart_ListLength(names, &len);
if (Dart_IsError(result)) {
return result;
}
for (int i = 0; i < len; i++) {
Dart_Handle var_name = Dart_ListGetAt(names, i);
Dart_Handle var = Dart_LookupVariable(owner, var_name);
if (Dart_IsError(var)) {
return var;
}
ASSERT(!Dart_IsNull(var));
Dart_Handle var_mirror = CreateVariableMirror(var, var_name, owner_mirror);
if (Dart_IsError(var_mirror)) {
return var_mirror;
}
result = MapAdd(map, var_name, var_mirror);
if (Dart_IsError(result)) {
return result;
}
}
return Dart_True();
}
static Dart_Handle CreateMemberMap(Dart_Handle owner) {
// TODO(turnidge): This should be an immutable map.
Dart_Handle owner_mirror = CreateLazyMirror(owner);
if (Dart_IsError(owner_mirror)) {
return owner_mirror;
}
Dart_Handle result;
Dart_Handle map = MapNew();
if (Dart_IsLibrary(owner)) {
result = AddMemberClasses(map, owner, owner_mirror);
if (Dart_IsError(result)) {
return result;
}
}
result = AddMemberFunctions(map, owner, owner_mirror);
if (Dart_IsError(result)) {
return result;
}
result = AddMemberVariables(map, owner, owner_mirror);
if (Dart_IsError(result)) {
return result;
}
return map;
}
@ -321,17 +512,17 @@ static Dart_Handle CreateLibraryMirror(Dart_Handle lib) {
if (Dart_IsError(cls)) {
return cls;
}
Dart_Handle member_map = CreateLibraryMemberMap(lib);
Dart_Handle member_map = CreateMemberMap(lib);
if (Dart_IsError(member_map)) {
return member_map;
}
const int kNumArgs = 4;
Dart_Handle args[kNumArgs];
args[0] = CreateVMReference(lib);
args[1] = Dart_LibraryName(lib);
args[2] = Dart_LibraryUrl(lib);
args[3] = member_map;
Dart_Handle lib_mirror = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = {
CreateVMReference(lib),
Dart_LibraryName(lib),
Dart_LibraryUrl(lib),
member_map,
};
Dart_Handle lib_mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
if (Dart_IsError(lib_mirror)) {
return lib_mirror;
}
@ -390,12 +581,12 @@ static Dart_Handle CreateIsolateMirror() {
return root_lib_mirror;
}
const int kNumArgs = 3;
Dart_Handle args[kNumArgs];
args[0] = Dart_DebugName();
args[1] = root_lib_mirror;
args[2] = libraries;
Dart_Handle mirror = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = {
Dart_DebugName(),
root_lib_mirror,
libraries,
};
Dart_Handle mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
if (Dart_IsError(mirror)) {
return mirror;
}
@ -414,13 +605,13 @@ static Dart_Handle CreateNullMirror() {
// TODO(turnidge): This is wrong. The Null class is distinct from object.
Dart_Handle object_class = Dart_GetClass(CoreLib(), Dart_NewString("Object"));
const int kNumArgs = 4;
Dart_Handle args[kNumArgs];
args[0] = CreateVMReference(Dart_Null());
args[1] = CreateLazyInterfaceMirror(object_class);
args[2] = Dart_True();
args[3] = Dart_Null();
Dart_Handle mirror = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = {
CreateVMReference(Dart_Null()),
CreateLazyMirror(object_class),
Dart_True(),
Dart_Null(),
};
Dart_Handle mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return mirror;
}
@ -440,13 +631,13 @@ static Dart_Handle CreateInstanceMirror(Dart_Handle instance) {
return instance_cls;
}
bool is_simple = IsSimpleValue(instance);
const int kNumArgs = 4;
Dart_Handle args[kNumArgs];
args[0] = CreateVMReference(instance);
args[1] = CreateLazyInterfaceMirror(instance_cls);
args[2] = Dart_NewBoolean(is_simple);
args[3] = (is_simple ? instance : Dart_Null());
Dart_Handle mirror = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = {
CreateVMReference(instance),
CreateLazyMirror(instance_cls),
Dart_NewBoolean(is_simple),
(is_simple ? instance : Dart_Null()),
};
Dart_Handle mirror = Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return mirror;
}
@ -473,21 +664,21 @@ static Dart_Handle CreateMirroredError(Dart_Handle error) {
}
Dart_Handle cls_name = Dart_NewString("MirroredUncaughtExceptionError");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
const int kNumArgs = 3;
Dart_Handle args[kNumArgs];
args[0] = CreateInstanceMirror(exc);
args[1] = exc_string;
args[2] = stack;
Dart_Handle mirrored_exc = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = {
CreateInstanceMirror(exc),
exc_string,
stack,
};
Dart_Handle mirrored_exc =
Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return Dart_NewUnhandledExceptionError(mirrored_exc);
} else if (Dart_IsApiError(error) ||
Dart_IsCompilationError(error)) {
Dart_Handle cls_name = Dart_NewString("MirroredCompilationError");
Dart_Handle cls = Dart_GetClass(MirrorLib(), cls_name);
const int kNumArgs = 1;
Dart_Handle args[kNumArgs];
args[0] = Dart_NewString(Dart_GetError(error));
Dart_Handle mirrored_exc = Dart_New(cls, Dart_Null(), kNumArgs, args);
Dart_Handle args[] = { Dart_NewString(Dart_GetError(error)) };
Dart_Handle mirrored_exc =
Dart_New(cls, Dart_Null(), ARRAY_SIZE(args), args);
return Dart_NewUnhandledExceptionError(mirrored_exc);
} else {
ASSERT(Dart_IsFatalError(error));

View file

@ -9,6 +9,16 @@ bool isSimpleValue(var value) {
return (value === null || value is num || value is String || value is bool);
}
Map filterMap(Map old_map, bool filter(key, value)) {
Map new_map = new Map();
old_map.forEach((key, value) {
if (filter(key, value)) {
new_map[key] = value;
}
});
return new_map;
}
abstract class _LocalMirrorImpl implements Mirror {
// Local mirrors always return the same IsolateMirror. This field
// is more interesting once we implement remote mirrors.
@ -25,6 +35,28 @@ class _LocalIsolateMirrorImpl extends _LocalMirrorImpl
Map<String, LibraryMirror> libraries() { return _libraries; }
InterfaceMirror _sharedDynamic = null;
InterfaceMirror _dynamicMirror() {
if (_sharedDynamic === null) {
_sharedDynamic =
new _LocalInterfaceMirrorImpl(
null, 'Dynamic', false, null, null, [], null, const {});
}
return _sharedDynamic;
}
InterfaceMirror _sharedVoid = null;
InterfaceMirror _voidMirror() {
if (_sharedVoid === null) {
_sharedVoid =
new _LocalInterfaceMirrorImpl(
null, 'void', false, null, null, [], null, const {});
}
return _sharedVoid;
}
String toString() {
return "IsolateMirror on '$debugName'";
}
@ -92,7 +124,7 @@ String _dartEscape(String str) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < str.length; i++) {
var input = str[i];
String output;
String output;
switch (input) {
case '\\' :
output = @'\\';
@ -176,6 +208,16 @@ class _LazyInterfaceMirror {
_LazyInterfaceMirror(this.libraryName, this.interfaceName) {}
InterfaceMirror resolve(IsolateMirror isolate) {
if (libraryName === null) {
if (interfaceName == 'Dynamic') {
return isolate._dynamicMirror();
} else if (interfaceName == 'void') {
return isolate._dynamicMirror();
} else {
throw new NotImplementedException(
"Mirror for type '$interfaceName' not implemented");
}
}
return isolate.libraries()[libraryName].members()[interfaceName];
}
@ -191,7 +233,8 @@ class _LocalInterfaceMirrorImpl extends _LocalObjectMirrorImpl
this._library,
this._superclass,
this._superinterfaces,
this._defaultFactory) : super(ref) {}
this._defaultFactory,
this._members) : super(ref) {}
final String simpleName;
final bool isClass;
@ -233,6 +276,28 @@ class _LocalInterfaceMirrorImpl extends _LocalObjectMirrorImpl
return _defaultFactory;
}
Map<String, InterfaceMirror> _members;
Map<String, InterfaceMirror> _methods = null;
Map<String, InterfaceMirror> _variables = null;
Map<String, Mirror> members() { return _members; }
Map<String, MethodMirror> methods() {
if (_methods == null) {
_methods = filterMap(members(),
(key, value) => (value is MethodMirror));
}
return _methods;
}
Map<String, VariableMirror> variables() {
if (_variables == null) {
_variables = filterMap(members(),
(key, value) => (value is VariableMirror));
}
return _variables;
}
String toString() {
return "InterfaceMirror on '$simpleName'";
}
@ -258,14 +323,119 @@ class _LocalLibraryMirrorImpl extends _LocalObjectMirrorImpl
final String simpleName;
final String url;
Map<String, InterfaceMirror> _members;
Map<String, InterfaceMirror> _classes = null;
Map<String, InterfaceMirror> _functions = null;
Map<String, InterfaceMirror> _variables = null;
Map<String, Mirror> members() { return _members; }
Map<String, InterfaceMirror> classes() {
if (_classes == null) {
_classes = filterMap(members(),
(key, value) => (value is InterfaceMirror));
}
return _classes;
}
Map<String, MethodMirror> functions() {
if (_functions == null) {
_functions = filterMap(members(),
(key, value) => (value is MethodMirror));
}
return _functions;
}
Map<String, VariableMirror> variables() {
if (_variables == null) {
_variables = filterMap(members(),
(key, value) => (value is VariableMirror));
}
return _variables;
}
String toString() {
return "LibraryMirror on '$simpleName'";
}
}
class _LocalMethodMirrorImpl extends _LocalMirrorImpl
implements MethodMirror {
_LocalMethodMirrorImpl(this.simpleName,
this._owner,
this.isStatic,
this.isAbstract,
this.isGetter,
this.isSetter,
this.isConstructor,
this.isConstConstructor,
this.isGenerativeConstructor,
this.isRedirectingConstructor,
this.isFactoryConstructor) {}
final String simpleName;
var _owner;
Mirror get owner() {
if (_owner is! Mirror) {
_owner = _owner.resolve(isolate);
}
return _owner;
}
bool get isTopLevel() {
return owner is LibraryMirror;
}
final bool isStatic;
bool get isMethod() {
return !isGetter && !isSetter && !isConstructor;
}
final bool isAbstract;
final bool isGetter;
final bool isSetter;
final bool isConstructor;
final bool isConstConstructor;
final bool isGenerativeConstructor;
final bool isRedirectingConstructor;
final bool isFactoryConstructor;
String toString() {
return "MethodMirror on '$simpleName'";
}
}
class _LocalVariableMirrorImpl extends _LocalMirrorImpl
implements VariableMirror {
_LocalVariableMirrorImpl(this.simpleName,
this._owner,
this.isStatic,
this.isFinal) {}
final String simpleName;
var _owner;
Mirror get owner() {
if (_owner is! Mirror) {
_owner = _owner.resolve(isolate);
}
return _owner;
}
bool get isTopLevel() {
return owner is LibraryMirror;
}
final bool isStatic;
final bool isFinal;
String toString() {
return "VariableMirror on '$simpleName'";
}
}
class _Mirrors {
// Does a port refer to our local isolate?
static bool isLocalPort(SendPort port) native 'Mirrors_isLocalPort';

View file

@ -25,6 +25,11 @@ void testDone(String test) {
}
int global_var = 0;
final int final_global_var = 0;
// Top-level getter and setter.
int get myVar() { return 5; }
int set myVar(x) {}
// This function will be invoked reflectively.
int function(int x) {
@ -32,6 +37,58 @@ int function(int x) {
return x + 1;
}
_stringCompare(String a, String b) => a.compareTo(b);
sort(list) => list.sort(_stringCompare);
String buildMethodString(MethodMirror func) {
var result = '${func.simpleName}';
if (func.isTopLevel) {
result = '$result toplevel';
}
if (func.isStatic) {
result = '$result static';
}
if (func.isMethod) {
result = '$result method';
}
if (func.isGetter) {
result = '$result getter';
}
if (func.isSetter) {
result = '$result setter';
}
if (func.isConstructor) {
result = '$result constructor';
}
if (func.isConstConstructor) {
result = '$result const';
}
if (func.isGenerativeConstructor) {
result = '$result generative';
}
if (func.isRedirectingConstructor) {
result = '$result redirecting';
}
if (func.isFactoryConstructor) {
result = '$result factory';
}
return result;
}
String buildVariableString(VariableMirror variable) {
var result = '${variable.simpleName}';
if (variable.isTopLevel) {
result = '$result toplevel';
}
if (variable.isStatic) {
result = '$result static';
}
if (variable.isFinal) {
result = '$result final';
}
return result;
}
void testRootLibraryMirror(LibraryMirror lib_mirror) {
Expect.equals('isolate_mirror_local_test', lib_mirror.simpleName);
Expect.isTrue(lib_mirror.url.contains('isolate_mirror_local_test.dart'));
@ -48,6 +105,123 @@ void testRootLibraryMirror(LibraryMirror lib_mirror) {
Expect.equals(124, retval.simpleValue);
testDone('testRootLibraryMirror');
});
// Check that the members map is complete.
List keys = lib_mirror.members().getKeys();
sort(keys);
Expect.equals('['
'MyClass, '
'MyException, '
'MyInterface, '
'MySuperClass, '
'_stringCompare, '
'buildMethodString, '
'buildVariableString, '
'exit_port, '
'expectedTests, '
'final_global_var, '
'function, '
'global_var, '
'main, '
'methodWithError, '
'methodWithException, '
'myVar, '
'myVar=, '
'sort, '
'testBoolInstanceMirror, '
'testCustomInstanceMirror, '
'testDone, '
'testIntegerInstanceMirror, '
'testIsolateMirror, '
'testLibrariesMap, '
'testMirrorErrors, '
'testNullInstanceMirror, '
'testRootLibraryMirror, '
'testStringInstanceMirror]',
'$keys');
// Check that the classes map is complete.
keys = lib_mirror.classes().getKeys();
sort(keys);
Expect.equals('['
'MyClass, '
'MyException, '
'MyInterface, '
'MySuperClass]',
'$keys');
// Check that the functions map is complete.
keys = lib_mirror.functions().getKeys();
sort(keys);
Expect.equals('['
'_stringCompare, '
'buildMethodString, '
'buildVariableString, '
'function, '
'main, '
'methodWithError, '
'methodWithException, '
'myVar, '
'myVar=, '
'sort, '
'testBoolInstanceMirror, '
'testCustomInstanceMirror, '
'testDone, '
'testIntegerInstanceMirror, '
'testIsolateMirror, '
'testLibrariesMap, '
'testMirrorErrors, '
'testNullInstanceMirror, '
'testRootLibraryMirror, '
'testStringInstanceMirror]',
'$keys');
// Check that the variables map is complete.
keys = lib_mirror.variables().getKeys();
sort(keys);
Expect.equals('['
'exit_port, '
'expectedTests, '
'final_global_var, '
'global_var]',
'$keys');
InterfaceMirror cls_mirror = lib_mirror.members()['MyClass'];
// Test function mirrors.
MethodMirror func = lib_mirror.members()['function'];
Expect.isTrue(func is MethodMirror);
Expect.equals('function toplevel static method', buildMethodString(func));
func = lib_mirror.members()['myVar'];
Expect.isTrue(func is MethodMirror);
Expect.equals('myVar toplevel static getter', buildMethodString(func));
func = lib_mirror.members()['myVar='];
Expect.isTrue(func is MethodMirror);
Expect.equals('myVar= toplevel static setter', buildMethodString(func));
func = cls_mirror.members()['method'];
Expect.isTrue(func is MethodMirror);
Expect.equals('method method', buildMethodString(func));
func = cls_mirror.members()['MyClass'];
Expect.isTrue(func is MethodMirror);
Expect.equals('MyClass constructor', buildMethodString(func));
// Test variable mirrors.
VariableMirror variable = lib_mirror.members()['global_var'];
Expect.isTrue(variable is VariableMirror);
Expect.equals('global_var toplevel static', buildVariableString(variable));
variable = lib_mirror.members()['final_global_var'];
Expect.isTrue(variable is VariableMirror);
Expect.equals('final_global_var toplevel static final',
buildVariableString(variable));
variable = cls_mirror.members()['value'];
Expect.isTrue(variable is VariableMirror);
Expect.equals('value final', buildVariableString(variable));
}
void testLibrariesMap(Map libraries) {

View file

@ -34,6 +34,7 @@ DECLARE_FLAG(bool, print_class_table);
ThreadLocalKey Api::api_native_key_ = Thread::kUnsetThreadLocalKey;
const char* CanonicalFunction(const char* func) {
if (strncmp(func, "dart::", 6) == 0) {
return func + 6;
@ -42,6 +43,7 @@ const char* CanonicalFunction(const char* func) {
}
}
#define RETURN_TYPE_ERROR(isolate, dart_handle, type) \
do { \
const Object& tmp = \
@ -58,6 +60,86 @@ const char* CanonicalFunction(const char* func) {
} while (0)
#define RETURN_NULL_ERROR(parameter) \
return Api::NewError("%s expects argument '%s' to be non-null.", \
CURRENT_FUNC, #parameter);
// Takes a vm internal name and makes it suitable for external user.
//
// Examples:
//
// Internal getter and setter prefices are removed:
//
// get:foo -> foo
// set:foo -> foo
//
// Private name mangling is removed, possibly twice:
//
// _ReceivePortImpl@6be832b -> _ReceivePortImpl
// _ReceivePortImpl@6be832b._internal@6be832b -> +ReceivePortImpl._internal
//
// The trailing . on the default constructor name is dropped:
//
// List. -> List
//
// And so forth:
//
// get:foo@6be832b -> foo
// _MyClass@6b3832b. -> _MyClass
// _MyClass@6b3832b.named -> _MyClass.named
//
static RawString* IdentifierPrettyName(Isolate* isolate, const String& name) {
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 +190,7 @@ Dart_Handle Api::NewHandle(Isolate* isolate, RawObject* raw) {
}
RawObject* Api::UnwrapHandle(Dart_Handle object) {
#ifdef DEBUG
#if defined(DEBUG)
Isolate* isolate = Isolate::Current();
ASSERT(isolate != NULL);
ApiState* state = isolate->api_state();
@ -575,8 +657,7 @@ DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(Dart_Handle* keys,
ApiState* state = isolate->api_state();
ASSERT(state != NULL);
if (keys == NULL) {
return Api::NewError("%s expects argument 'keys' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(keys);
}
if (num_keys <= 0) {
return Api::NewError(
@ -584,8 +665,7 @@ DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(Dart_Handle* keys,
CURRENT_FUNC);
}
if (values == NULL) {
return Api::NewError("%s expects argument 'values' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(values);
}
if (num_values <= 0) {
return Api::NewError(
@ -670,8 +750,7 @@ DART_EXPORT Dart_Handle Dart_HeapProfile(Dart_HeapProfileWriteCallback callback,
Isolate* isolate = Isolate::Current();
CHECK_ISOLATE(isolate);
if (callback == NULL) {
return Api::NewError("%s expects argument 'callback' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(callback);
}
isolate->heap()->Profile(callback, stream);
return Api::Success(isolate);
@ -814,12 +893,10 @@ DART_EXPORT Dart_Handle Dart_CreateSnapshot(uint8_t** buffer,
DARTSCOPE(isolate);
TIMERSCOPE(time_creating_snapshot);
if (buffer == NULL) {
return Api::NewError("%s expects argument 'buffer' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(buffer);
}
if (size == NULL) {
return Api::NewError("%s expects argument 'size' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(size);
}
const char* msg = CheckIsolateState(isolate,
ClassFinalizer::kGeneratingSnapshot);
@ -841,12 +918,10 @@ DART_EXPORT Dart_Handle Dart_CreateScriptSnapshot(uint8_t** buffer,
DARTSCOPE(isolate);
TIMERSCOPE(time_creating_snapshot);
if (buffer == NULL) {
return Api::NewError("%s expects argument 'buffer' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(buffer);
}
if (size == NULL) {
return Api::NewError("%s expects argument 'size' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(size);
}
const char* msg = CheckIsolateState(isolate);
if (msg != NULL) {
@ -1490,8 +1565,7 @@ DART_EXPORT Dart_Handle Dart_NewString(const char* str) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (str == NULL) {
return Api::NewError("%s expects argument 'str' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(str);
}
if (!Utf8::IsValid(str)) {
return Api::NewError("%s expects argument 'str' to be valid UTF-8.",
@ -1544,8 +1618,7 @@ DART_EXPORT Dart_Handle Dart_ExternalStringGetPeer(Dart_Handle object,
CURRENT_FUNC);
}
if (peer == NULL) {
return Api::NewError("%s expects argument 'peer' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(peer);
}
*peer = str.GetPeer();
return Api::Success(isolate);
@ -1559,8 +1632,7 @@ DART_EXPORT Dart_Handle Dart_NewExternalString8(const uint8_t* codepoints,
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (codepoints == NULL && length != 0) {
return Api::NewError("%s expects argument 'codepoints' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(codepoints);
}
if (length < 0) {
return Api::NewError("%s expects argument 'length' to be greater than 0.",
@ -1578,8 +1650,7 @@ DART_EXPORT Dart_Handle Dart_NewExternalString16(const uint16_t* codepoints,
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (codepoints == NULL && length != 0) {
return Api::NewError("%s expects argument 'codepoints' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(codepoints);
}
if (length < 0) {
return Api::NewError("%s expects argument 'length' to be greater than 0.",
@ -1597,8 +1668,7 @@ DART_EXPORT Dart_Handle Dart_NewExternalString32(const uint32_t* codepoints,
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (codepoints == NULL && length != 0) {
return Api::NewError("%s expects argument 'codepoints' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(codepoints);
}
if (length < 0) {
return Api::NewError("%s expects argument 'length' to be greater than 0.",
@ -1701,12 +1771,10 @@ DART_EXPORT Dart_Handle Dart_StringToBytes(Dart_Handle object,
RETURN_TYPE_ERROR(isolate, object, String);
}
if (bytes == NULL) {
return Api::NewError("%s expects argument 'bytes' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(bytes);
}
if (length == NULL) {
return Api::NewError("%s expects argument 'length' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(length);
}
const char* cstring = str.ToCString();
*length = Utf8::Length(str);
@ -2106,8 +2174,7 @@ DART_EXPORT Dart_Handle Dart_NewExternalByteArray(uint8_t* data,
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (data == NULL && length != 0) {
return Api::NewError("%s expects argument 'data' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(data);
}
if (length < 0) {
return Api::NewError("%s expects argument 'length' to be greater than 0.",
@ -2128,8 +2195,7 @@ DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetPeer(Dart_Handle object,
RETURN_TYPE_ERROR(isolate, object, ExternalUint8Array);
}
if (peer == NULL) {
return Api::NewError("%s expects argument 'peer' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(peer);
}
*peer = array.GetPeer();
return Api::Success(isolate);
@ -2407,7 +2473,8 @@ DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle clazz) {
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, IdentifierPrettyName(isolate, cls_name));
}
@ -2419,6 +2486,14 @@ DART_EXPORT Dart_Handle Dart_ClassGetLibrary(Dart_Handle clazz) {
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());
}
@ -2497,6 +2572,417 @@ DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz,
}
// --- 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()) {
const Class& cls = Class::Cast(obj);
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 = IdentifierPrettyName(isolate, name);
if (is_setter) {
name = AddExternalSetterSuffix(name);
}
names.Add(name);
}
}
} else if (obj.IsLibrary()) {
const Library& lib = Library::Cast(obj);
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 = IdentifierPrettyName(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()) {
const Class& cls = Class::Cast(obj);
// Case 1. Lookup the unmodified function name.
func = cls.LookupFunction(func_name);
// Case 2. Lookup the function without the external setter suffix
// '='. Make 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);
}
// Case 3. Lookup the funciton with the getter prefix prepended.
if (func.IsNull()) {
tmp_name = Field::GetterName(func_name);
func = cls.LookupFunction(tmp_name);
}
// Case 4. Lookup the function with a . appended to find the
// unnamed constructor.
if (func.IsNull()) {
const String& dot = String::Handle(String::NewSymbol("."));
tmp_name = String::Concat(func_name, dot);
func = cls.LookupFunction(tmp_name);
}
} else if (obj.IsLibrary()) {
const Library& lib = Library::Cast(obj);
// Case 1. Lookup the unmodified function name.
func = lib.LookupFunctionAllowPrivate(func_name);
// Case 2. Lookup the function without the external setter suffix
// '='. Make 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);
}
// Case 3. Lookup the funciton with the getter prefix prepended.
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 = IdentifierPrettyName(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) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_abstract == NULL) {
RETURN_NULL_ERROR(is_abstract);
}
const Function& func = Api::UnwrapFunctionHandle(isolate, function);
if (func.IsNull()) {
RETURN_TYPE_ERROR(isolate, function, Function);
}
*is_abstract = (func.kind() == RawFunction::kAbstract);
return Api::Success(isolate);
}
DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
bool* is_static) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_static == NULL) {
RETURN_NULL_ERROR(is_static);
}
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) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_constructor == NULL) {
RETURN_NULL_ERROR(is_constructor);
}
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) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_getter == NULL) {
RETURN_NULL_ERROR(is_getter);
}
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) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_setter == NULL) {
RETURN_NULL_ERROR(is_setter);
}
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()) {
const Class& cls = Class::Cast(obj);
const Array& field_array = Array::Handle(cls.fields());
// Some special types like 'Dynamic' have a null fields list.
//
// TODO(turnidge): Fix 'Dynamic' so that it does not have a null
// fields list. This will have to wait until the empty array is
// allocated in the vm isolate.
if (!field_array.IsNull()) {
for (intptr_t i = 0; i < field_array.Length(); ++i) {
field ^= field_array.At(i);
name = field.name();
name = IdentifierPrettyName(isolate, name);
names.Add(name);
}
}
} else if (obj.IsLibrary()) {
const Library& lib = Library::Cast(obj);
DictionaryIterator it(lib);
Object& obj = Object::Handle(isolate);
while (it.HasNext()) {
obj = it.GetNext();
if (obj.IsField()) {
field ^= obj.raw();
name = field.name();
name = IdentifierPrettyName(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()) {
const Class& cls = Class::Cast(obj);
return Api::NewHandle(isolate, cls.LookupField(var_name));
}
if (obj.IsLibrary()) {
const Library& lib = Library::Cast(obj);
return Api::NewHandle(isolate, lib.LookupFieldAllowPrivate(var_name));
}
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;
}
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, IdentifierPrettyName(isolate, var_name));
}
DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable,
bool* is_static) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_static == NULL) {
RETURN_NULL_ERROR(is_static);
}
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) {
Isolate* isolate = Isolate::Current();
DARTSCOPE(isolate);
if (is_final == NULL) {
RETURN_NULL_ERROR(is_final);
}
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 ---
@ -3333,8 +3819,7 @@ DART_EXPORT Dart_Handle Dart_LoadScriptFromSnapshot(const uint8_t* buffer) {
DARTSCOPE(isolate);
TIMERSCOPE(time_script_loading);
if (buffer == NULL) {
return Api::NewError("%s expects argument 'buffer' to be non-null.",
CURRENT_FUNC);
RETURN_NULL_ERROR(buffer);
}
const Snapshot* snapshot = Snapshot::SetupFromBuffer(buffer);
if (!snapshot->IsScriptSnapshot()) {
@ -3464,8 +3949,15 @@ DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library) {
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 = IdentifierPrettyName(isolate, name);
names.Add(name);
}
}
return Api::NewHandle(isolate, Array::MakeArray(names));
}

View file

@ -4,6 +4,7 @@
#include "include/dart_api.h"
#include "platform/assert.h"
#include "platform/json.h"
#include "platform/utils.h"
#include "vm/class_finalizer.h"
#include "vm/dart_api_impl.h"
@ -3917,6 +3918,456 @@ TEST_CASE(GetClass) {
}
static void BuildFunctionDescription(TextBuffer* buffer, Dart_Handle func) {
buffer->Clear();
if (Dart_IsNull(func)) {
WARN("Function not found");
return;
}
Dart_Handle name = Dart_FunctionName(func);
EXPECT_VALID(name);
const char* name_cstr = "";
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
bool is_abstract = false;
bool is_static = false;
bool is_getter = false;
bool is_setter = false;
bool is_constructor = false;
EXPECT_VALID(Dart_FunctionIsAbstract(func, &is_abstract));
EXPECT_VALID(Dart_FunctionIsStatic(func, &is_static));
EXPECT_VALID(Dart_FunctionIsGetter(func, &is_getter));
EXPECT_VALID(Dart_FunctionIsSetter(func, &is_setter));
EXPECT_VALID(Dart_FunctionIsConstructor(func, &is_constructor));
buffer->Printf("%s", name_cstr);
if (is_abstract) {
buffer->Printf(" abstract");
}
if (is_static) {
buffer->Printf(" static");
}
if (is_getter) {
buffer->Printf(" getter");
}
if (is_setter) {
buffer->Printf(" setter");
}
if (is_constructor) {
buffer->Printf(" constructor");
}
}
TEST_CASE(FunctionReflection) {
const char* kScriptChars =
"a() => 'a';\n"
"_b() => '_b';\n"
"get c() => 'bar';\n"
"set d(x) {}\n"
"get _e() => 'bar';\n"
"set _f(x) {}\n"
"class MyClass {\n"
" MyClass() {}\n"
" MyClass.named() {}\n"
" a() => 'a';\n"
" _b() => '_b';\n"
" get c() => 'bar';\n"
" set d(x) {}\n"
" get _e() => 'bar';\n"
" set _f(x) {}\n"
" static g() => 'g';\n"
" static _h() => '_h';\n"
" static get i() => 'i';\n"
" static set j(x) {}\n"
" static get _k() => 'k';\n"
" static set _l(x) {}\n"
" abstract m();\n"
" abstract _n();\n"
" abstract get o();\n"
" abstract set p(x);\n"
" abstract get _q();\n"
" abstract set _r(x);\n"
" operator ==(x) {}\n"
"}\n"
"class _PrivateClass {\n"
" _PrivateClass() {}\n"
" _PrivateClass.named() {}\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle cls = Dart_GetClass(lib, Dart_NewString("MyClass"));
EXPECT_VALID(cls);
Dart_Handle private_cls = Dart_GetClass(lib, Dart_NewString("_PrivateClass"));
EXPECT_VALID(private_cls);
TextBuffer buffer(128);
// Lookup a top-level function.
Dart_Handle func = Dart_LookupFunction(lib, Dart_NewString("a"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("a static", buffer.buf());
// Lookup a private top-level function.
func = Dart_LookupFunction(lib, Dart_NewString("_b"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_b static", buffer.buf());
// Lookup a top-level getter.
func = Dart_LookupFunction(lib, Dart_NewString("c"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("c static getter", buffer.buf());
// Lookup a top-level setter.
func = Dart_LookupFunction(lib, Dart_NewString("d="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("d= static setter", buffer.buf());
// Lookup a private top-level getter.
func = Dart_LookupFunction(lib, Dart_NewString("_e"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_e static getter", buffer.buf());
// Lookup a private top-level setter.
func = Dart_LookupFunction(lib, Dart_NewString("_f="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_f= static setter", buffer.buf());
// Lookup an unnamed constructor
func = Dart_LookupFunction(cls, Dart_NewString("MyClass"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("MyClass constructor", buffer.buf());
// Lookup a named constructor
func = Dart_LookupFunction(cls, Dart_NewString("MyClass.named"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("MyClass.named constructor", buffer.buf());
// Lookup an private unnamed constructor
func = Dart_LookupFunction(private_cls, Dart_NewString("_PrivateClass"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_PrivateClass constructor", buffer.buf());
// Lookup a private named constructor
func = Dart_LookupFunction(private_cls,
Dart_NewString("_PrivateClass.named"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_PrivateClass.named constructor", buffer.buf());
// Lookup a method.
func = Dart_LookupFunction(cls, Dart_NewString("a"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("a", buffer.buf());
// Lookup a private method.
func = Dart_LookupFunction(cls, Dart_NewString("_b"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_b", buffer.buf());
// Lookup a instance getter.
func = Dart_LookupFunction(cls, Dart_NewString("c"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("c getter", buffer.buf());
// Lookup a instance setter.
func = Dart_LookupFunction(cls, Dart_NewString("d="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("d= setter", buffer.buf());
// Lookup a private instance getter.
func = Dart_LookupFunction(cls, Dart_NewString("_e"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_e getter", buffer.buf());
// Lookup a private instance setter.
func = Dart_LookupFunction(cls, Dart_NewString("_f="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_f= setter", buffer.buf());
// Lookup a static method.
func = Dart_LookupFunction(cls, Dart_NewString("g"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("g static", buffer.buf());
// Lookup a private static method.
func = Dart_LookupFunction(cls, Dart_NewString("_h"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_h static", buffer.buf());
// Lookup a static getter.
func = Dart_LookupFunction(cls, Dart_NewString("i"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("i static getter", buffer.buf());
// Lookup a static setter.
func = Dart_LookupFunction(cls, Dart_NewString("j="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("j= static setter", buffer.buf());
// Lookup a private static getter.
func = Dart_LookupFunction(cls, Dart_NewString("_k"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_k static getter", buffer.buf());
// Lookup a private static setter.
func = Dart_LookupFunction(cls, Dart_NewString("_l="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_l= static setter", buffer.buf());
// Lookup an abstract method.
func = Dart_LookupFunction(cls, Dart_NewString("m"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("m abstract", buffer.buf());
// Lookup a private abstract method.
func = Dart_LookupFunction(cls, Dart_NewString("_n"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_n abstract", buffer.buf());
// Lookup a abstract getter.
func = Dart_LookupFunction(cls, Dart_NewString("o"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("o abstract getter", buffer.buf());
// Lookup a abstract setter.
func = Dart_LookupFunction(cls, Dart_NewString("p="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("p= abstract setter", buffer.buf());
// Lookup a private abstract getter.
func = Dart_LookupFunction(cls, Dart_NewString("_q"));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_q abstract getter", buffer.buf());
// Lookup a private abstract setter.
func = Dart_LookupFunction(cls, Dart_NewString("_r="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("_r= abstract setter", buffer.buf());
// Lookup an operator
func = Dart_LookupFunction(cls, Dart_NewString("=="));
EXPECT_VALID(func);
EXPECT(Dart_IsFunction(func));
BuildFunctionDescription(&buffer, func);
EXPECT_STREQ("==", buffer.buf());
// Lookup a function that does not exist from a library.
func = Dart_LookupFunction(lib, Dart_NewString("DoesNotExist"));
EXPECT(Dart_IsNull(func));
// Lookup a function that does not exist from a class.
func = Dart_LookupFunction(cls, Dart_NewString("DoesNotExist"));
EXPECT(Dart_IsNull(func));
// Lookup a class using an error class name. The error propagates.
func = Dart_LookupFunction(cls, Api::NewError("myerror"));
EXPECT_ERROR(func, "myerror");
// Lookup a class from an error library. The error propagates.
func = Dart_LookupFunction(Api::NewError("myerror"), Dart_NewString("foo"));
EXPECT_ERROR(func, "myerror");
}
static void BuildVariableDescription(TextBuffer* buffer, Dart_Handle var) {
buffer->Clear();
Dart_Handle name = Dart_VariableName(var);
EXPECT_VALID(name);
const char* name_cstr = "";
EXPECT_VALID(Dart_StringToCString(name, &name_cstr));
bool is_static = false;
bool is_final = false;
EXPECT_VALID(Dart_VariableIsStatic(var, &is_static));
EXPECT_VALID(Dart_VariableIsFinal(var, &is_final));
buffer->Printf("%s", name_cstr);
if (is_static) {
buffer->Printf(" static");
}
if (is_final) {
buffer->Printf(" final");
}
}
TEST_CASE(VariableReflection) {
const char* kScriptChars =
"var a = 'a';\n"
"var _b = '_b';\n"
"final c = 'c';\n"
"final _d = '_d';\n"
"class MyClass {\n"
" var a = 'a';\n"
" var _b = '_b';\n"
" final c = 'c';\n"
" final _d = '_d';\n"
" static var e = 'e';\n"
" static var _f = '_f';\n"
" static final g = 'g';\n"
" static final _h = '_h';\n"
"}\n";
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
EXPECT_VALID(lib);
Dart_Handle cls = Dart_GetClass(lib, Dart_NewString("MyClass"));
EXPECT_VALID(cls);
TextBuffer buffer(128);
// Lookup a top-level variable.
Dart_Handle var = Dart_LookupVariable(lib, Dart_NewString("a"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("a static", buffer.buf());
// Lookup a private top-level variable.
var = Dart_LookupVariable(lib, Dart_NewString("_b"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("_b static", buffer.buf());
// Lookup a final top-level variable.
var = Dart_LookupVariable(lib, Dart_NewString("c"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("c static final", buffer.buf());
// Lookup a private final top-level variable.
var = Dart_LookupVariable(lib, Dart_NewString("_d"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("_d static final", buffer.buf());
// Lookup a instance variable.
var = Dart_LookupVariable(cls, Dart_NewString("a"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("a", buffer.buf());
// Lookup a private instance variable.
var = Dart_LookupVariable(cls, Dart_NewString("_b"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("_b", buffer.buf());
// Lookup a final instance variable.
var = Dart_LookupVariable(cls, Dart_NewString("c"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("c final", buffer.buf());
// Lookup a private final instance variable.
var = Dart_LookupVariable(cls, Dart_NewString("_d"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("_d final", buffer.buf());
// Lookup a static variable.
var = Dart_LookupVariable(cls, Dart_NewString("e"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("e static", buffer.buf());
// Lookup a private static variable.
var = Dart_LookupVariable(cls, Dart_NewString("_f"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("_f static", buffer.buf());
// Lookup a final static variable.
var = Dart_LookupVariable(cls, Dart_NewString("g"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("g static final", buffer.buf());
// Lookup a private final static variable.
var = Dart_LookupVariable(cls, Dart_NewString("_h"));
EXPECT_VALID(var);
EXPECT(Dart_IsVariable(var));
BuildVariableDescription(&buffer, var);
EXPECT_STREQ("_h static final", buffer.buf());
// Lookup a variable that does not exist from a library.
var = Dart_LookupVariable(lib, Dart_NewString("DoesNotExist"));
EXPECT(Dart_IsNull(var));
// Lookup a variable that does not exist from a class.
var = Dart_LookupVariable(cls, Dart_NewString("DoesNotExist"));
EXPECT(Dart_IsNull(var));
// Lookup a class from an error library. The error propagates.
var = Dart_LookupVariable(Api::NewError("myerror"), Dart_NewString("foo"));
EXPECT_ERROR(var, "myerror");
// Lookup a class using an error class name. The error propagates.
var = Dart_LookupVariable(lib, Api::NewError("myerror"));
EXPECT_ERROR(var, "myerror");
}
TEST_CASE(InstanceOf) {
const char* kScriptChars =
"class OtherClass {\n"
@ -4325,9 +4776,10 @@ TEST_CASE(LibraryGetClassNames) {
"\n"
"class A {}\n"
"class B {}\n"
"class D {}\n"
"interface C {}\n"
"interface E {}\n"
"class _A {}\n"
"class _B {}\n"
"interface _C {}\n"
"\n"
"_compare(String a, String b) => a.compareTo(b);\n"
"sort(list) => list.sort(_compare);\n";
@ -4351,7 +4803,142 @@ TEST_CASE(LibraryGetClassNames) {
EXPECT_VALID(list_string);
const char* list_cstr = "";
EXPECT_VALID(Dart_StringToCString(list_string, &list_cstr));
EXPECT_STREQ("[A, B, C, D, E]", list_cstr);
EXPECT_STREQ("[A, B, C, _A, _B, _C]", list_cstr);
}
TEST_CASE(GetFunctionNames) {
const char* kLibraryChars =
"#library('library_name');\n"
"\n"
"void A() {}\n"
"get B() => 11;\n"
"set C(x) { }\n"
"var D;\n"
"void _A() {}\n"
"get _B() => 11;\n"
"set _C(x) { }\n"
"var _D;\n"
"\n"
"class MyClass {\n"
" void A2() {}\n"
" get B2() => 11;\n"
" set C2(x) { }\n"
" var D2;\n"
" void _A2() {}\n"
" get _B2() => 11;\n"
" set _C2(x) { }\n"
" var _D2;\n"
"}\n"
"\n"
"_compare(String a, String b) => a.compareTo(b);\n"
"sort(list) => list.sort(_compare);\n";
// Get the functions from a library.
Dart_Handle url = Dart_NewString("library_url");
Dart_Handle source = Dart_NewString(kLibraryChars);
Dart_Handle lib = Dart_LoadLibrary(url, source);
EXPECT_VALID(lib);
Dart_Handle list = Dart_GetFunctionNames(lib);
EXPECT_VALID(list);
EXPECT(Dart_IsList(list));
// Sort the list.
const int kNumArgs = 1;
Dart_Handle args[1];
args[0] = list;
EXPECT_VALID(Dart_Invoke(lib, Dart_NewString("sort"), kNumArgs, args));
Dart_Handle list_string = Dart_ToString(list);
EXPECT_VALID(list_string);
const char* list_cstr = "";
EXPECT_VALID(Dart_StringToCString(list_string, &list_cstr));
EXPECT_STREQ("[A, B, C=, _A, _B, _C=, _compare, sort]", list_cstr);
// Get the functions from a class.
Dart_Handle cls = Dart_GetClass(lib, Dart_NewString("MyClass"));
EXPECT_VALID(cls);
list = Dart_GetFunctionNames(cls);
EXPECT_VALID(list);
EXPECT(Dart_IsList(list));
// Sort the list.
args[0] = list;
EXPECT_VALID(Dart_Invoke(lib, Dart_NewString("sort"), kNumArgs, args));
// Check list contents.
list_string = Dart_ToString(list);
EXPECT_VALID(list_string);
list_cstr = "";
EXPECT_VALID(Dart_StringToCString(list_string, &list_cstr));
EXPECT_STREQ("[A2, B2, C2=, MyClass, _A2, _B2, _C2=]", list_cstr);
}
TEST_CASE(GetVariableNames) {
const char* kLibraryChars =
"#library('library_name');\n"
"\n"
"var A;\n"
"get B() => 12;\n"
"set C(x) { }\n"
"D(x) => (x + 1);\n"
"var _A;\n"
"get _B() => 12;\n"
"set _C(x) { }\n"
"_D(x) => (x + 1);\n"
"\n"
"class MyClass {\n"
" var A2;\n"
" var _A2;\n"
"}\n"
"\n"
"_compare(String a, String b) => a.compareTo(b);\n"
"sort(list) => list.sort(_compare);\n";
// Get the variables from a library.
Dart_Handle url = Dart_NewString("library_url");
Dart_Handle source = Dart_NewString(kLibraryChars);
Dart_Handle lib = Dart_LoadLibrary(url, source);
EXPECT_VALID(lib);
Dart_Handle list = Dart_GetVariableNames(lib);
EXPECT_VALID(list);
EXPECT(Dart_IsList(list));
// Sort the list.
const int kNumArgs = 1;
Dart_Handle args[1];
args[0] = list;
EXPECT_VALID(Dart_Invoke(lib, Dart_NewString("sort"), kNumArgs, args));
// Check list contents.
Dart_Handle list_string = Dart_ToString(list);
EXPECT_VALID(list_string);
const char* list_cstr = "";
EXPECT_VALID(Dart_StringToCString(list_string, &list_cstr));
EXPECT_STREQ("[A, _A]", list_cstr);
// Get the variables from a class.
Dart_Handle cls = Dart_GetClass(lib, Dart_NewString("MyClass"));
EXPECT_VALID(cls);
list = Dart_GetVariableNames(cls);
EXPECT_VALID(list);
EXPECT(Dart_IsList(list));
// Sort the list.
args[0] = list;
EXPECT_VALID(Dart_Invoke(lib, Dart_NewString("sort"), kNumArgs, args));
// Check list contents.
list_string = Dart_ToString(list);
EXPECT_VALID(list_string);
list_cstr = "";
EXPECT_VALID(Dart_StringToCString(list_string, &list_cstr));
EXPECT_STREQ("[A2, _A2]", list_cstr);
}

View file

@ -321,6 +321,9 @@ void Object::InitOnce() {
// Therefore, it cannot have a heap allocated name (the name is hard coded,
// see GetSingletonClassIndex) and its array fields cannot be set to the empty
// array, but remain null.
//
// TODO(turnidge): Once the empty array is allocated in the vm
// isolate, use it here.
cls = Class::New<Instance>(kDynamicClassId);
cls.set_is_finalized();
cls.set_is_interface();
@ -1925,26 +1928,51 @@ static bool MatchesAccessorName(const String& name,
}
static bool MatchesPrivateName(const String& name, const String& private_name) {
intptr_t name_len = name.Length();
intptr_t private_len = private_name.Length();
// The private_name must at least have room for the separator and one key
// character.
if ((name_len < (private_len + 2)) || (name_len == 0) || (private_len == 0)) {
// Check to see if mangled_name is equal to bare_name once the private
// key separator is stripped from mangled_name.
//
// Things are made more complicated by the fact that constructors are
// added *after* the private suffix, so "foo@123.named" should match
// "foo.named".
//
// Also, the private suffix can occur more than once in the name, as in:
//
// _ReceivePortImpl@6be832b._internal@6be832b
//
bool EqualsIgnoringPrivate(const String& mangled_name,
const String& bare_name) {
intptr_t mangled_len = mangled_name.Length();
intptr_t bare_len = bare_name.Length();
if (mangled_len < bare_len) {
// No way they can match.
return false;
}
// Check for the private key separator.
if (name.CharAt(private_len) != Scanner::kPrivateKeySeparator) {
return false;
}
intptr_t mangled_pos = 0;
intptr_t bare_pos = 0;
while (mangled_pos < mangled_len) {
int32_t mangled_char = mangled_name.CharAt(mangled_pos);
mangled_pos++;
for (intptr_t i = 0; i < private_len; i++) {
if (name.CharAt(i) != private_name.CharAt(i)) {
if (mangled_char == Scanner::kPrivateKeySeparator) {
// Consume a private key separator.
while (mangled_pos < mangled_len &&
mangled_name.CharAt(mangled_pos) != '.') {
mangled_pos++;
}
// Resume matching characters.
continue;
}
if (bare_pos == bare_len || mangled_char != bare_name.CharAt(bare_pos)) {
return false;
}
bare_pos++;
}
return true;
// The strings match if we have reached the end of both strings.
return (mangled_pos == mangled_len &&
bare_pos == bare_len);
}
@ -1957,7 +1985,8 @@ RawFunction* Class::LookupFunction(const String& name) const {
for (intptr_t i = 0; i < len; i++) {
function ^= funcs.At(i);
function_name ^= function.name();
if (function_name.Equals(name) || MatchesPrivateName(function_name, name)) {
if (function_name.Equals(name) ||
EqualsIgnoringPrivate(function_name, name)) {
return function.raw();
}
}
@ -2059,7 +2088,7 @@ RawField* Class::LookupField(const String& name) const {
for (intptr_t i = 0; i < len; i++) {
field ^= flds.At(i);
field_name ^= field.name();
if (field_name.Equals(name) || MatchesPrivateName(field_name, name)) {
if (field_name.Equals(name) || EqualsIgnoringPrivate(field_name, name)) {
return field.raw();
}
}

View file

@ -2670,6 +2670,111 @@ TEST_CASE(FieldTests) {
String::Handle(Field::NameFromSetter(setter_f)).ToCString());
}
// Expose helper function from object.cc for testing.
bool EqualsIgnoringPrivate(const String& name, const String& private_name);
TEST_CASE(EqualsIgnoringPrivate) {
String& mangled_name = String::Handle();
String& bare_name = String::Handle();
// Simple matches.
mangled_name = String::New("foo");
bare_name = String::New("foo");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
mangled_name = String::New("foo.");
bare_name = String::New("foo.");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
mangled_name = String::New("foo.named");
bare_name = String::New("foo.named");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
// Simple mismatches.
mangled_name = String::New("bar");
bare_name = String::New("foo");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
mangled_name = String::New("foo.");
bare_name = String::New("foo");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
mangled_name = String::New("foo");
bare_name = String::New("foo.");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
mangled_name = String::New("foo.name");
bare_name = String::New("foo.named");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
mangled_name = String::New("foo.named");
bare_name = String::New("foo.name");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Private match.
mangled_name = String::New("foo@12345");
bare_name = String::New("foo");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
// Private mismatch.
mangled_name = String::New("food@12345");
bare_name = String::New("foo");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Private mismatch 2.
mangled_name = String::New("foo@12345");
bare_name = String::New("food");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Private constructor match.
mangled_name = String::New("foo@12345.");
bare_name = String::New("foo.");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
// Private constructor mismatch.
mangled_name = String::New("foo@12345.");
bare_name = String::New("foo");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Private constructor mismatch 2.
mangled_name = String::New("foo@12345");
bare_name = String::New("foo.");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Named private constructor match.
mangled_name = String::New("foo@12345.named");
bare_name = String::New("foo.named");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
// Named private constructor mismatch.
mangled_name = String::New("foo@12345.name");
bare_name = String::New("foo.named");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Named private constructor mismatch 2.
mangled_name = String::New("foo@12345.named");
bare_name = String::New("foo.name");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Named double-private constructor match. Yes, this happens.
mangled_name = String::New("foo@12345.named@12345");
bare_name = String::New("foo.named");
EXPECT(EqualsIgnoringPrivate(mangled_name, bare_name));
// Named double-private constructor mismatch.
mangled_name = String::New("foo@12345.name@12345");
bare_name = String::New("foo.named");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
// Named double-private constructor mismatch.
mangled_name = String::New("foo@12345.named@12345");
bare_name = String::New("foo.name");
EXPECT(!EqualsIgnoringPrivate(mangled_name, bare_name));
}
#endif // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64).
} // namespace dart