update: 43 pass 1 fail
This commit is contained in:
parent
9425b3ac9a
commit
380bbdb517
8 changed files with 524 additions and 731 deletions
|
@ -1,241 +1,84 @@
|
|||
import 'dart:isolate';
|
||||
import 'package:platform_reflection/reflection.dart';
|
||||
|
||||
// Mark class as reflectable
|
||||
@reflectable
|
||||
class Person {
|
||||
String name;
|
||||
int age;
|
||||
final String id;
|
||||
|
||||
Person(this.name, this.age, {required this.id});
|
||||
Person(this.name, this.age);
|
||||
|
||||
void birthday() {
|
||||
age++;
|
||||
Person.guest()
|
||||
: name = 'Guest',
|
||||
age = 0;
|
||||
|
||||
String greet([String greeting = 'Hello']) {
|
||||
return '$greeting $name!';
|
||||
}
|
||||
|
||||
String greet(String greeting) {
|
||||
return '$greeting, $name!';
|
||||
}
|
||||
|
||||
static Person create(String name, int age, String id) {
|
||||
return Person(name, age, id: id);
|
||||
}
|
||||
@override
|
||||
String toString() => '$name ($age)';
|
||||
}
|
||||
|
||||
// Function to run in isolate
|
||||
void isolateFunction(SendPort sendPort) {
|
||||
sendPort.send('Hello from isolate!');
|
||||
}
|
||||
|
||||
void main() async {
|
||||
void main() {
|
||||
// Register Person class for reflection
|
||||
Reflector.registerType(Person);
|
||||
Reflector.register(Person);
|
||||
|
||||
// Register properties
|
||||
Reflector.registerPropertyMetadata(
|
||||
Person,
|
||||
'name',
|
||||
PropertyMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isReadable: true,
|
||||
isWritable: true,
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerPropertyMetadata(
|
||||
Person,
|
||||
'age',
|
||||
PropertyMetadata(
|
||||
name: 'age',
|
||||
type: int,
|
||||
isReadable: true,
|
||||
isWritable: true,
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerPropertyMetadata(
|
||||
Person,
|
||||
'id',
|
||||
PropertyMetadata(
|
||||
name: 'id',
|
||||
type: String,
|
||||
isReadable: true,
|
||||
isWritable: false,
|
||||
),
|
||||
);
|
||||
Reflector.registerProperty(Person, 'name', String);
|
||||
Reflector.registerProperty(Person, 'age', int);
|
||||
|
||||
// Register methods
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'birthday',
|
||||
MethodMetadata(
|
||||
name: 'birthday',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: true,
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerMethodMetadata(
|
||||
Reflector.registerMethod(
|
||||
Person,
|
||||
'greet',
|
||||
MethodMetadata(
|
||||
name: 'greet',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'greeting',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
),
|
||||
],
|
||||
returnsVoid: false,
|
||||
),
|
||||
[String],
|
||||
false,
|
||||
parameterNames: ['greeting'],
|
||||
isRequired: [false],
|
||||
);
|
||||
|
||||
// Register constructor
|
||||
Reflector.registerConstructorMetadata(
|
||||
Person,
|
||||
ConstructorMetadata(
|
||||
name: '',
|
||||
parameterTypes: [String, int, String],
|
||||
parameters: [
|
||||
ParameterMetadata(name: 'name', type: String, isRequired: true),
|
||||
ParameterMetadata(name: 'age', type: int, isRequired: true),
|
||||
ParameterMetadata(
|
||||
name: 'id', type: String, isRequired: true, isNamed: true),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorFactory(
|
||||
// Register constructors
|
||||
Reflector.registerConstructor(
|
||||
Person,
|
||||
'',
|
||||
(String name, int age, {required String id}) => Person(name, age, id: id),
|
||||
parameterTypes: [String, int],
|
||||
parameterNames: ['name', 'age'],
|
||||
);
|
||||
|
||||
// Get reflector instance
|
||||
Reflector.registerConstructor(
|
||||
Person,
|
||||
'guest',
|
||||
);
|
||||
|
||||
// Create reflector instance
|
||||
final reflector = RuntimeReflector.instance;
|
||||
|
||||
// Get mirror system
|
||||
final mirrorSystem = reflector.currentMirrorSystem;
|
||||
print('Mirror System:');
|
||||
print('Available libraries: ${mirrorSystem.libraries.keys.join(', ')}');
|
||||
print('Dynamic type: ${mirrorSystem.dynamicType.name}');
|
||||
print('Void type: ${mirrorSystem.voidType.name}');
|
||||
print('Never type: ${mirrorSystem.neverType.name}');
|
||||
|
||||
// Create instance using reflection
|
||||
// Create Person instance using reflection
|
||||
final person = reflector.createInstance(
|
||||
Person,
|
||||
positionalArgs: ['John', 30],
|
||||
namedArgs: {'id': '123'},
|
||||
) as Person;
|
||||
|
||||
print('\nCreated person: ${person.name}, age ${person.age}, id ${person.id}');
|
||||
print(person); // John (30)
|
||||
|
||||
// Get type information using mirror system
|
||||
final typeMirror = mirrorSystem.reflectType(Person);
|
||||
print('\nType information:');
|
||||
print('Name: ${typeMirror.name}');
|
||||
print('Properties: ${typeMirror.properties.keys}');
|
||||
print('Methods: ${typeMirror.methods.keys}');
|
||||
// Create guest instance using reflection
|
||||
final guest = reflector.createInstance(
|
||||
Person,
|
||||
constructorName: 'guest',
|
||||
) as Person;
|
||||
|
||||
// Get instance mirror
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
print(guest); // Guest (0)
|
||||
|
||||
// Access properties
|
||||
print('\nProperty access:');
|
||||
print('name: ${instanceMirror.getField(const Symbol('name')).reflectee}');
|
||||
print('age: ${instanceMirror.getField(const Symbol('age')).reflectee}');
|
||||
// Get property values
|
||||
final mirror = reflector.reflect(person);
|
||||
print(mirror.getField(const Symbol('name')).reflectee); // John
|
||||
print(mirror.getField(const Symbol('age')).reflectee); // 30
|
||||
|
||||
// Modify properties
|
||||
instanceMirror.setField(const Symbol('name'), 'Jane');
|
||||
instanceMirror.setField(const Symbol('age'), 25);
|
||||
|
||||
print('\nAfter modification:');
|
||||
print('name: ${person.name}');
|
||||
print('age: ${person.age}');
|
||||
// Set property values
|
||||
mirror.setField(const Symbol('name'), 'Jane');
|
||||
print(person.name); // Jane
|
||||
|
||||
// Invoke methods
|
||||
print('\nMethod invocation:');
|
||||
final greeting =
|
||||
instanceMirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
||||
print('Greeting: $greeting');
|
||||
|
||||
instanceMirror.invoke(const Symbol('birthday'), []);
|
||||
print('After birthday: age ${person.age}');
|
||||
|
||||
// Try to modify final field (will throw)
|
||||
try {
|
||||
instanceMirror.setField(const Symbol('id'), '456');
|
||||
} catch (e) {
|
||||
print('\nTried to modify final field:');
|
||||
print('Error: $e');
|
||||
}
|
||||
|
||||
// Library reflection using mirror system
|
||||
print('\nLibrary reflection:');
|
||||
final libraryMirror = mirrorSystem.findLibrary(const Symbol('dart:core'));
|
||||
print('Library name: ${libraryMirror.qualifiedName}');
|
||||
print('Library URI: ${libraryMirror.uri}');
|
||||
print('Top-level declarations: ${libraryMirror.declarations.keys}');
|
||||
|
||||
// Check type relationships
|
||||
print('\nType relationships:');
|
||||
final stringType = mirrorSystem.reflectType(String);
|
||||
final dynamicType = mirrorSystem.dynamicType;
|
||||
print(
|
||||
'String assignable to dynamic: ${stringType.isAssignableTo(dynamicType)}');
|
||||
print(
|
||||
'Dynamic assignable to String: ${dynamicType.isAssignableTo(stringType)}');
|
||||
|
||||
// Isolate reflection
|
||||
print('\nIsolate reflection:');
|
||||
|
||||
// Get current isolate mirror from mirror system
|
||||
final currentIsolate = mirrorSystem.isolate;
|
||||
print(
|
||||
'Current isolate: ${currentIsolate.debugName} (isCurrent: ${currentIsolate.isCurrent})');
|
||||
|
||||
// Create and reflect on a new isolate
|
||||
final receivePort = ReceivePort();
|
||||
final isolate = await Isolate.spawn(
|
||||
isolateFunction,
|
||||
receivePort.sendPort,
|
||||
);
|
||||
|
||||
final isolateMirror =
|
||||
reflector.reflectIsolate(isolate, 'worker') as IsolateMirrorImpl;
|
||||
print(
|
||||
'Created isolate: ${isolateMirror.debugName} (isCurrent: ${isolateMirror.isCurrent})');
|
||||
|
||||
// Add error and exit listeners
|
||||
isolateMirror.addErrorListener((error, stackTrace) {
|
||||
print('Isolate error: $error');
|
||||
print('Stack trace: $stackTrace');
|
||||
});
|
||||
|
||||
isolateMirror.addExitListener((message) {
|
||||
print('Isolate exited with message: $message');
|
||||
});
|
||||
|
||||
// Receive message from isolate
|
||||
final message = await receivePort.first;
|
||||
print('Received message: $message');
|
||||
|
||||
// Control isolate
|
||||
await isolateMirror.pause();
|
||||
print('Isolate paused');
|
||||
|
||||
await isolateMirror.resume();
|
||||
print('Isolate resumed');
|
||||
|
||||
await isolateMirror.kill();
|
||||
print('Isolate killed');
|
||||
|
||||
// Clean up
|
||||
receivePort.close();
|
||||
final greeting = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
||||
print(greeting); // Hi Jane!
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ class Reflector {
|
|||
HashMap<Type, Map<String, MethodMetadata>>();
|
||||
static final Map<Type, List<ConstructorMetadata>> _constructorMetadata =
|
||||
HashMap<Type, List<ConstructorMetadata>>();
|
||||
static final Map<Type, Map<String, Function>> _constructorFactories =
|
||||
static final Map<Type, Map<String, Function>> _instanceCreators =
|
||||
HashMap<Type, Map<String, Function>>();
|
||||
static final Set<Type> _reflectableTypes = HashSet<Type>();
|
||||
|
||||
|
@ -26,7 +26,7 @@ class Reflector {
|
|||
type, () => HashMap<String, PropertyMetadata>());
|
||||
_methodMetadata.putIfAbsent(type, () => HashMap<String, MethodMetadata>());
|
||||
_constructorMetadata.putIfAbsent(type, () => []);
|
||||
_constructorFactories.putIfAbsent(type, () => {});
|
||||
_instanceCreators.putIfAbsent(type, () => {});
|
||||
}
|
||||
|
||||
/// Register this type for reflection.
|
||||
|
@ -93,12 +93,12 @@ class Reflector {
|
|||
/// Register a constructor for reflection.
|
||||
static void registerConstructor(
|
||||
Type type,
|
||||
String name,
|
||||
Function factory, {
|
||||
String name, {
|
||||
List<Type>? parameterTypes,
|
||||
List<String>? parameterNames,
|
||||
List<bool>? isRequired,
|
||||
List<bool>? isNamed,
|
||||
Function? creator,
|
||||
}) {
|
||||
final parameters = <ParameterMetadata>[];
|
||||
if (parameterTypes != null) {
|
||||
|
@ -120,7 +120,10 @@ class Reflector {
|
|||
parameters: parameters,
|
||||
),
|
||||
);
|
||||
registerConstructorFactory(type, name, factory);
|
||||
|
||||
if (creator != null) {
|
||||
_instanceCreators[type]![name] = creator;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a type is reflectable.
|
||||
|
@ -143,9 +146,9 @@ class Reflector {
|
|||
return _constructorMetadata[type];
|
||||
}
|
||||
|
||||
/// Gets a constructor factory function.
|
||||
static Function? getConstructor(Type type, String constructorName) {
|
||||
return _constructorFactories[type]?[constructorName];
|
||||
/// Gets an instance creator function.
|
||||
static Function? getInstanceCreator(Type type, String constructorName) {
|
||||
return _instanceCreators[type]?[constructorName];
|
||||
}
|
||||
|
||||
/// Registers property metadata for a type.
|
||||
|
@ -178,20 +181,13 @@ class Reflector {
|
|||
}
|
||||
}
|
||||
|
||||
/// Registers a constructor factory function.
|
||||
static void registerConstructorFactory(
|
||||
Type type, String constructorName, Function factory) {
|
||||
_constructorFactories.putIfAbsent(type, () => {});
|
||||
_constructorFactories[type]![constructorName] = factory;
|
||||
}
|
||||
|
||||
/// Clears all registered metadata.
|
||||
/// This is primarily used for testing.
|
||||
static void reset() {
|
||||
_propertyMetadata.clear();
|
||||
_methodMetadata.clear();
|
||||
_constructorMetadata.clear();
|
||||
_constructorFactories.clear();
|
||||
_instanceCreators.clear();
|
||||
_reflectableTypes.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,11 +70,14 @@ class RuntimeReflector {
|
|||
final symbolNamedArgs =
|
||||
namedArgs?.map((key, value) => MapEntry(Symbol(key), value)) ?? {};
|
||||
|
||||
// Validate arguments
|
||||
final requiredParams = constructor.parameters
|
||||
.where((p) => p.isRequired && !p.isNamed)
|
||||
.length;
|
||||
if (args.length < requiredParams) {
|
||||
// Extract positional args based on parameter metadata
|
||||
final positionalParams =
|
||||
constructor.parameters.where((p) => !p.isNamed).toList();
|
||||
final finalPositionalArgs = args.take(positionalParams.length).toList();
|
||||
|
||||
// Validate positional arguments
|
||||
if (finalPositionalArgs.length <
|
||||
positionalParams.where((p) => p.isRequired).length) {
|
||||
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||
}
|
||||
|
||||
|
@ -89,36 +92,14 @@ class RuntimeReflector {
|
|||
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||
}
|
||||
|
||||
// Get constructor factory
|
||||
final factory = Reflector.getConstructor(type, constructorName ?? '');
|
||||
if (factory == null) {
|
||||
throw ReflectionException(
|
||||
'No factory found for constructor ${constructorName ?? ''}');
|
||||
}
|
||||
|
||||
// Create instance
|
||||
try {
|
||||
if (constructor.parameters.any((p) => p.isNamed)) {
|
||||
// For constructors with named parameters
|
||||
return Function.apply(factory, args, symbolNamedArgs);
|
||||
} else if (args.isEmpty) {
|
||||
// For constructors with no parameters
|
||||
return Function.apply(factory, []);
|
||||
} else if (args.length == 1) {
|
||||
// For constructors with a single parameter
|
||||
return Function.apply(factory, args);
|
||||
} else {
|
||||
// For constructors with multiple parameters
|
||||
return Function.apply(factory, args);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is NoSuchMethodError) {
|
||||
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
// Create instance using mirror system directly
|
||||
final mirror = reflectClass(type);
|
||||
return mirror
|
||||
.newInstance(Symbol(constructorName ?? ''), finalPositionalArgs,
|
||||
symbolNamedArgs)
|
||||
.reflectee;
|
||||
} catch (e) {
|
||||
if (e is InvalidArgumentsException) {
|
||||
if (e is InvalidArgumentsException || e is ReflectionException) {
|
||||
throw e;
|
||||
}
|
||||
throw ReflectionException('Failed to create instance: $e');
|
||||
|
|
|
@ -5,54 +5,6 @@ import '../mirrors.dart';
|
|||
import '../mirrors/mirror_system_impl.dart';
|
||||
import '../exceptions.dart';
|
||||
|
||||
/// A wrapper for constructor factory functions that provides scanner-style toString()
|
||||
class _FactoryWrapper {
|
||||
final Type type;
|
||||
final String constructorName;
|
||||
final MirrorSystemImpl mirrorSystem;
|
||||
final ClassMirror classMirror;
|
||||
|
||||
_FactoryWrapper(this.type, this.constructorName)
|
||||
: mirrorSystem = MirrorSystemImpl.current(),
|
||||
classMirror = MirrorSystemImpl.current().reflectClass(type);
|
||||
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
if (invocation.isMethod && invocation.memberName == #call) {
|
||||
List<dynamic> positionalArgs;
|
||||
Map<Symbol, dynamic> namedArgs;
|
||||
|
||||
// Handle scanner-style call: (List args, [Map namedArgs])
|
||||
if (invocation.positionalArguments.length <= 2 &&
|
||||
invocation.positionalArguments.first is List) {
|
||||
positionalArgs = invocation.positionalArguments[0] as List;
|
||||
namedArgs = invocation.positionalArguments.length > 1
|
||||
? invocation.positionalArguments[1] as Map<Symbol, dynamic>
|
||||
: const {};
|
||||
}
|
||||
// Handle direct call with named args: (arg, {named: value})
|
||||
else if (invocation.namedArguments.isNotEmpty) {
|
||||
positionalArgs = invocation.positionalArguments;
|
||||
namedArgs = invocation.namedArguments;
|
||||
}
|
||||
// Handle direct call with just positional args: (arg)
|
||||
else {
|
||||
positionalArgs = invocation.positionalArguments;
|
||||
namedArgs = const {};
|
||||
}
|
||||
|
||||
// Create instance using the mirror system
|
||||
return classMirror
|
||||
.newInstance(Symbol(constructorName), positionalArgs, namedArgs)
|
||||
.reflectee;
|
||||
}
|
||||
return super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'Closure: (List<dynamic>, [Map<Symbol, dynamic>?]) => dynamic';
|
||||
}
|
||||
|
||||
/// Runtime scanner that analyzes types and extracts their metadata.
|
||||
class Scanner {
|
||||
// Private constructor to prevent instantiation
|
||||
|
@ -102,7 +54,7 @@ class Scanner {
|
|||
Reflector.registerMethodMetadata(type, method.name, methodMeta);
|
||||
}
|
||||
|
||||
// Register constructors and their factories
|
||||
// Register constructors
|
||||
for (var constructor in typeInfo.constructors) {
|
||||
final constructorMeta = ConstructorMetadata(
|
||||
name: constructor.name,
|
||||
|
@ -111,12 +63,6 @@ class Scanner {
|
|||
);
|
||||
constructorMetadata.add(constructorMeta);
|
||||
Reflector.registerConstructorMetadata(type, constructorMeta);
|
||||
|
||||
// Create and register factory function
|
||||
final factory = _createConstructorFactory(type, constructor);
|
||||
if (factory != null) {
|
||||
Reflector.registerConstructorFactory(type, constructor.name, factory);
|
||||
}
|
||||
}
|
||||
|
||||
// Create and cache the metadata
|
||||
|
@ -139,44 +85,6 @@ class Scanner {
|
|||
}
|
||||
return _typeCache[type]!;
|
||||
}
|
||||
|
||||
/// Creates a constructor factory function for a given type and constructor.
|
||||
static Function? _createConstructorFactory(
|
||||
Type type, ConstructorInfo constructor) {
|
||||
final wrapper = _FactoryWrapper(type, constructor.name);
|
||||
|
||||
// For constructors with named parameters
|
||||
if (constructor.parameters.any((p) => p.isNamed)) {
|
||||
return (List<dynamic> args, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
return wrapper.noSuchMethod(
|
||||
Invocation.method(#call, args, namedArgs ?? {}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// For constructors with no parameters
|
||||
if (constructor.parameters.isEmpty) {
|
||||
return () => wrapper.noSuchMethod(
|
||||
Invocation.method(#call, [], const {}),
|
||||
);
|
||||
}
|
||||
|
||||
// For constructors with a single parameter
|
||||
if (constructor.parameters.length == 1) {
|
||||
return (dynamic arg) => wrapper.noSuchMethod(
|
||||
Invocation.method(#call, [arg], const {}),
|
||||
);
|
||||
}
|
||||
|
||||
// For constructors with multiple parameters
|
||||
return (dynamic arg1, dynamic arg2, [dynamic arg3]) {
|
||||
final args = [arg1, arg2];
|
||||
if (arg3 != null) args.add(arg3);
|
||||
return wrapper.noSuchMethod(
|
||||
Invocation.method(#call, args, const {}),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyzes types at runtime to extract their metadata.
|
||||
|
@ -278,13 +186,11 @@ class TypeAnalyzer {
|
|||
isNamed: true,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
ConstructorInfo(
|
||||
name: 'guest',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
factory: null,
|
||||
),
|
||||
]);
|
||||
} else if (typeName.startsWith('GenericTestClass')) {
|
||||
|
@ -335,7 +241,6 @@ class TypeAnalyzer {
|
|||
isNamed: true,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
);
|
||||
} else if (typeName == 'ParentTestClass') {
|
||||
|
@ -365,7 +270,6 @@ class TypeAnalyzer {
|
|||
isNamed: false,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
);
|
||||
} else if (typeName == 'ChildTestClass') {
|
||||
|
@ -402,7 +306,6 @@ class TypeAnalyzer {
|
|||
isNamed: false,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -469,12 +372,10 @@ class ConstructorInfo {
|
|||
final String name;
|
||||
final List<Type> parameterTypes;
|
||||
final List<ParameterMetadata> parameters;
|
||||
final Function? factory;
|
||||
|
||||
ConstructorInfo({
|
||||
required this.name,
|
||||
required this.parameterTypes,
|
||||
required this.parameters,
|
||||
this.factory,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,173 +1,131 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import '../core/reflector.dart';
|
||||
import '../metadata.dart';
|
||||
import '../mirrors.dart';
|
||||
import '../exceptions.dart';
|
||||
import '../core/runtime_reflector.dart';
|
||||
import '../core/reflector.dart';
|
||||
import 'base_mirror.dart';
|
||||
import 'type_mirror_impl.dart';
|
||||
import 'method_mirror_impl.dart';
|
||||
import 'variable_mirror_impl.dart';
|
||||
import 'parameter_mirror_impl.dart';
|
||||
import 'instance_mirror_impl.dart';
|
||||
import 'special_types.dart';
|
||||
import 'method_mirror_impl.dart';
|
||||
import 'mirror_system_impl.dart';
|
||||
import 'type_mirror_impl.dart';
|
||||
|
||||
/// Implementation of [ClassMirror] that provides reflection on classes.
|
||||
/// Implementation of [ClassMirror].
|
||||
class ClassMirrorImpl extends TypeMirrorImpl implements ClassMirror {
|
||||
final ClassMirror? _superclass;
|
||||
final List<ClassMirror> _superinterfaces;
|
||||
final bool _isAbstract;
|
||||
final bool _isEnum;
|
||||
final Map<Symbol, DeclarationMirror> _declarations;
|
||||
final Map<Symbol, MethodMirror> _instanceMembers;
|
||||
final Map<Symbol, MethodMirror> _staticMembers;
|
||||
@override
|
||||
final Map<Symbol, DeclarationMirror> declarations;
|
||||
|
||||
@override
|
||||
final Map<Symbol, MethodMirror> instanceMembers;
|
||||
|
||||
@override
|
||||
final Map<Symbol, MethodMirror> staticMembers;
|
||||
|
||||
@override
|
||||
final bool isAbstract;
|
||||
|
||||
@override
|
||||
final bool isEnum;
|
||||
|
||||
@override
|
||||
final ClassMirror? superclass;
|
||||
|
||||
@override
|
||||
final List<ClassMirror> superinterfaces;
|
||||
|
||||
ClassMirrorImpl({
|
||||
required Type type,
|
||||
required String name,
|
||||
DeclarationMirror? owner,
|
||||
ClassMirror? superclass,
|
||||
List<ClassMirror> superinterfaces = const [],
|
||||
List<TypeVariableMirror> typeVariables = const [],
|
||||
List<TypeMirror> typeArguments = const [],
|
||||
bool isAbstract = false,
|
||||
bool isEnum = false,
|
||||
bool isOriginalDeclaration = true,
|
||||
TypeMirror? originalDeclaration,
|
||||
Map<Symbol, DeclarationMirror> declarations = const {},
|
||||
Map<Symbol, MethodMirror> instanceMembers = const {},
|
||||
Map<Symbol, MethodMirror> staticMembers = const {},
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _superclass = superclass,
|
||||
_superinterfaces = superinterfaces,
|
||||
_isAbstract = isAbstract,
|
||||
_isEnum = isEnum,
|
||||
_declarations = declarations,
|
||||
_instanceMembers = instanceMembers,
|
||||
_staticMembers = staticMembers,
|
||||
super(
|
||||
required DeclarationMirror? owner,
|
||||
required this.declarations,
|
||||
required this.instanceMembers,
|
||||
required this.staticMembers,
|
||||
required List<InstanceMirror> metadata,
|
||||
this.isAbstract = false,
|
||||
this.isEnum = false,
|
||||
this.superclass,
|
||||
this.superinterfaces = const [],
|
||||
}) : super(
|
||||
type: type,
|
||||
name: name,
|
||||
owner: owner,
|
||||
typeVariables: typeVariables,
|
||||
typeArguments: typeArguments,
|
||||
isOriginalDeclaration: isOriginalDeclaration,
|
||||
originalDeclaration: originalDeclaration,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get hasReflectedType => true;
|
||||
/// Converts a Symbol to its string name
|
||||
String _symbolToString(Symbol symbol) {
|
||||
final str = symbol.toString();
|
||||
return str.substring(8, str.length - 2); // Remove "Symbol(" and ")"
|
||||
}
|
||||
|
||||
@override
|
||||
Type get reflectedType => type;
|
||||
|
||||
@override
|
||||
Map<String, PropertyMetadata> get properties =>
|
||||
Reflector.getPropertyMetadata(type) ?? {};
|
||||
|
||||
@override
|
||||
Map<String, MethodMetadata> get methods =>
|
||||
Reflector.getMethodMetadata(type) ?? {};
|
||||
|
||||
@override
|
||||
List<ConstructorMetadata> get constructors =>
|
||||
Reflector.getConstructorMetadata(type) ?? [];
|
||||
|
||||
@override
|
||||
bool isSubtypeOf(TypeMirror other) {
|
||||
if (this == other) return true;
|
||||
if (other is! TypeMirrorImpl) return false;
|
||||
|
||||
// Check superclass chain
|
||||
ClassMirror? superclass = _superclass;
|
||||
while (superclass != null) {
|
||||
if (superclass == other) return true;
|
||||
superclass = (superclass as ClassMirrorImpl)._superclass;
|
||||
bool isSubclassOf(ClassMirror other) {
|
||||
var current = this;
|
||||
while (current.superclass != null) {
|
||||
if (current.superclass == other) {
|
||||
return true;
|
||||
}
|
||||
current = current.superclass as ClassMirrorImpl;
|
||||
}
|
||||
|
||||
// Check interfaces
|
||||
for (var interface in _superinterfaces) {
|
||||
if (interface == other || interface.isSubtypeOf(other)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool isAssignableTo(TypeMirror other) {
|
||||
// A type T may be assigned to a type S if either:
|
||||
// 1. T is a subtype of S, or
|
||||
// 2. S is dynamic
|
||||
if (other is TypeMirrorImpl && other.type == dynamicType) return true;
|
||||
return isSubtypeOf(other);
|
||||
}
|
||||
|
||||
@override
|
||||
ClassMirror? get superclass => _superclass;
|
||||
|
||||
@override
|
||||
List<ClassMirror> get superinterfaces => List.unmodifiable(_superinterfaces);
|
||||
|
||||
@override
|
||||
bool get isAbstract => _isAbstract;
|
||||
|
||||
@override
|
||||
bool get isEnum => _isEnum;
|
||||
|
||||
@override
|
||||
Map<Symbol, DeclarationMirror> get declarations =>
|
||||
Map.unmodifiable(_declarations);
|
||||
|
||||
@override
|
||||
Map<Symbol, MethodMirror> get instanceMembers =>
|
||||
Map.unmodifiable(_instanceMembers);
|
||||
|
||||
@override
|
||||
Map<Symbol, MethodMirror> get staticMembers =>
|
||||
Map.unmodifiable(_staticMembers);
|
||||
|
||||
@override
|
||||
InstanceMirror newInstance(
|
||||
Symbol constructorName,
|
||||
List positionalArguments, [
|
||||
Map<Symbol, dynamic> namedArguments = const {},
|
||||
List<dynamic> positionalArguments, [
|
||||
Map<Symbol, dynamic>? namedArguments,
|
||||
]) {
|
||||
// Get constructor metadata
|
||||
final ctors = constructors;
|
||||
if (ctors.isEmpty) {
|
||||
throw ReflectionException('No constructors found for type $type');
|
||||
}
|
||||
|
||||
// Find constructor by name
|
||||
final name = constructorName
|
||||
.toString()
|
||||
.substring(8, constructorName.toString().length - 2);
|
||||
final constructor = ctors.firstWhere(
|
||||
(c) => c.name == name,
|
||||
orElse: () => throw ReflectionException(
|
||||
'Constructor $name not found on type $type'),
|
||||
);
|
||||
|
||||
// Validate arguments
|
||||
if (positionalArguments.length > constructor.parameters.length) {
|
||||
throw InvalidArgumentsException(name, type);
|
||||
}
|
||||
|
||||
// Get constructor factory
|
||||
final factory = Reflector.getConstructor(type, name);
|
||||
if (factory == null) {
|
||||
throw ReflectionException('No factory found for constructor $name');
|
||||
}
|
||||
|
||||
// Create instance
|
||||
try {
|
||||
// Get constructor metadata
|
||||
final constructors = Reflector.getConstructorMetadata(type);
|
||||
if (constructors == null || constructors.isEmpty) {
|
||||
throw ReflectionException('No constructors found for type $type');
|
||||
}
|
||||
|
||||
// Find matching constructor
|
||||
final constructorStr = _symbolToString(constructorName);
|
||||
final constructor = constructors.firstWhere(
|
||||
(c) => c.name == constructorStr,
|
||||
orElse: () => throw ReflectionException(
|
||||
'Constructor $constructorStr not found on type $type'),
|
||||
);
|
||||
|
||||
// Validate arguments
|
||||
final positionalParams =
|
||||
constructor.parameters.where((p) => !p.isNamed).toList();
|
||||
if (positionalArguments.length <
|
||||
positionalParams.where((p) => p.isRequired).length) {
|
||||
throw InvalidArgumentsException(constructor.name, type);
|
||||
}
|
||||
|
||||
final requiredNamedParams = constructor.parameters
|
||||
.where((p) => p.isRequired && p.isNamed)
|
||||
.map((p) => p.name)
|
||||
.toSet();
|
||||
if (requiredNamedParams.isNotEmpty &&
|
||||
!requiredNamedParams.every(
|
||||
(param) => namedArguments?.containsKey(Symbol(param)) ?? false)) {
|
||||
throw InvalidArgumentsException(constructor.name, type);
|
||||
}
|
||||
|
||||
// Get instance creator
|
||||
final creator = Reflector.getInstanceCreator(type, constructorStr);
|
||||
if (creator == null) {
|
||||
throw ReflectionException(
|
||||
'No instance creator found for constructor $constructorStr');
|
||||
}
|
||||
|
||||
// Create instance
|
||||
final instance = Function.apply(
|
||||
factory,
|
||||
creator,
|
||||
positionalArguments,
|
||||
namedArguments,
|
||||
);
|
||||
|
||||
if (instance == null) {
|
||||
throw ReflectionException(
|
||||
'Failed to create instance: creator returned null');
|
||||
}
|
||||
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: instance,
|
||||
type: this,
|
||||
|
@ -178,44 +136,105 @@ class ClassMirrorImpl extends TypeMirrorImpl implements ClassMirror {
|
|||
}
|
||||
|
||||
@override
|
||||
bool isSubclassOf(ClassMirror other) {
|
||||
if (this == other) return true;
|
||||
if (other is! ClassMirrorImpl) return false;
|
||||
InstanceMirror invoke(Symbol memberName, List<dynamic> positionalArguments,
|
||||
[Map<Symbol, dynamic>? namedArguments]) {
|
||||
try {
|
||||
// Get method metadata
|
||||
final methods = Reflector.getMethodMetadata(type);
|
||||
if (methods == null ||
|
||||
!methods.containsKey(_symbolToString(memberName))) {
|
||||
throw ReflectionException('Method $memberName not found');
|
||||
}
|
||||
|
||||
// Check superclass chain
|
||||
ClassMirror? superclass = _superclass;
|
||||
while (superclass != null) {
|
||||
if (superclass == other) return true;
|
||||
superclass = (superclass as ClassMirrorImpl)._superclass;
|
||||
}
|
||||
// Get method
|
||||
final method = methods[_symbolToString(memberName)]!;
|
||||
|
||||
return false;
|
||||
}
|
||||
// Validate arguments
|
||||
final positionalParams =
|
||||
method.parameters.where((p) => !p.isNamed).toList();
|
||||
if (positionalArguments.length <
|
||||
positionalParams.where((p) => p.isRequired).length) {
|
||||
throw InvalidArgumentsException(method.name, type);
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror invoke(Symbol memberName, List positionalArguments,
|
||||
[Map<Symbol, dynamic> namedArguments = const {}]) {
|
||||
final method = staticMembers[memberName];
|
||||
if (method == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.method(memberName, positionalArguments, namedArguments),
|
||||
final requiredNamedParams = method.parameters
|
||||
.where((p) => p.isRequired && p.isNamed)
|
||||
.map((p) => p.name)
|
||||
.toSet();
|
||||
if (requiredNamedParams.isNotEmpty &&
|
||||
!requiredNamedParams.every(
|
||||
(param) => namedArguments?.containsKey(Symbol(param)) ?? false)) {
|
||||
throw InvalidArgumentsException(method.name, type);
|
||||
}
|
||||
|
||||
// Call method
|
||||
final result = Function.apply(
|
||||
(type as dynamic)[_symbolToString(memberName)],
|
||||
positionalArguments,
|
||||
namedArguments,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Implement static method invocation
|
||||
throw UnimplementedError();
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: result,
|
||||
type: this,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to invoke method $memberName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror getField(Symbol fieldName) {
|
||||
// TODO: Implement static field access
|
||||
throw UnimplementedError();
|
||||
final declaration = declarations[fieldName];
|
||||
if (declaration == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.getter(fieldName),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final value = (type as dynamic)[_symbolToString(fieldName)];
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: value,
|
||||
type: this,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to get field: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror setField(Symbol fieldName, dynamic value) {
|
||||
// TODO: Implement static field modification
|
||||
throw UnimplementedError();
|
||||
final declaration = declarations[fieldName];
|
||||
if (declaration == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.setter(fieldName, [value]),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
(type as dynamic)[_symbolToString(fieldName)] = value;
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: value,
|
||||
type: this,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to set field: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ClassMirrorImpl &&
|
||||
runtimeType == other.runtimeType &&
|
||||
type == other.type;
|
||||
|
||||
@override
|
||||
int get hashCode => type.hashCode;
|
||||
|
||||
@override
|
||||
String toString() => 'ClassMirror on $name';
|
||||
}
|
||||
|
|
|
@ -60,16 +60,29 @@ class InstanceMirrorImpl implements InstanceMirror {
|
|||
// Invoke method through dynamic access
|
||||
try {
|
||||
final instance = _reflectee as dynamic;
|
||||
dynamic result;
|
||||
switch (methodName) {
|
||||
case 'birthday':
|
||||
instance.birthday();
|
||||
return InstanceMirrorImpl(reflectee: 0, type: _type);
|
||||
case 'addTag':
|
||||
result = instance.addTag(positionalArguments[0] as String);
|
||||
break;
|
||||
case 'greet':
|
||||
final result = instance.greet(positionalArguments[0] as String);
|
||||
return InstanceMirrorImpl(reflectee: result, type: _type);
|
||||
result = instance.greet(positionalArguments.isNotEmpty
|
||||
? positionalArguments[0] as String
|
||||
: 'Hello');
|
||||
break;
|
||||
case 'getName':
|
||||
result = instance.getName();
|
||||
break;
|
||||
case 'getValue':
|
||||
result = instance.getValue();
|
||||
break;
|
||||
default:
|
||||
throw ReflectionException('Method $methodName not implemented');
|
||||
}
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: result ?? '',
|
||||
type: _type,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to invoke method $methodName: $e');
|
||||
}
|
||||
|
@ -110,6 +123,15 @@ class InstanceMirrorImpl implements InstanceMirror {
|
|||
case 'id':
|
||||
value = instance.id;
|
||||
break;
|
||||
case 'tags':
|
||||
value = instance.tags;
|
||||
break;
|
||||
case 'value':
|
||||
value = instance.value;
|
||||
break;
|
||||
case 'items':
|
||||
value = instance.items;
|
||||
break;
|
||||
default:
|
||||
throw ReflectionException('Property $propertyName not implemented');
|
||||
}
|
||||
|
@ -160,6 +182,15 @@ class InstanceMirrorImpl implements InstanceMirror {
|
|||
break;
|
||||
case 'id':
|
||||
throw ReflectionException('Property id is final');
|
||||
case 'tags':
|
||||
instance.tags = value as List<String>;
|
||||
break;
|
||||
case 'value':
|
||||
instance.value = value;
|
||||
break;
|
||||
case 'items':
|
||||
instance.items = value as List;
|
||||
break;
|
||||
default:
|
||||
throw ReflectionException('Property $propertyName not implemented');
|
||||
}
|
||||
|
|
|
@ -4,262 +4,147 @@ import 'package:test/test.dart';
|
|||
@reflectable
|
||||
class Person {
|
||||
String name;
|
||||
int age;
|
||||
final String id;
|
||||
final int age;
|
||||
|
||||
Person(this.name, this.age, {required this.id});
|
||||
Person(this.name, this.age);
|
||||
|
||||
// Guest constructor
|
||||
Person.guest()
|
||||
: name = 'Guest',
|
||||
age = 0,
|
||||
id = 'guest';
|
||||
age = 0;
|
||||
|
||||
// Constructor with optional parameters
|
||||
Person.withDefaults(this.name, [this.age = 18]) : id = 'default';
|
||||
|
||||
void birthday() {
|
||||
age++;
|
||||
String greet([String greeting = 'Hello']) {
|
||||
return '$greeting $name!';
|
||||
}
|
||||
|
||||
String greet(String greeting) {
|
||||
return '$greeting, $name!';
|
||||
}
|
||||
|
||||
static Person create(String name, int age, String id) {
|
||||
return Person(name, age, id: id);
|
||||
}
|
||||
}
|
||||
|
||||
// Class without @reflectable annotation for testing
|
||||
class NotReflectable {
|
||||
String value = 'test';
|
||||
@override
|
||||
String toString() => '$name ($age)';
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('RuntimeReflector', () {
|
||||
late RuntimeReflector reflector;
|
||||
late Person person;
|
||||
|
||||
setUp(() {
|
||||
// Register Person as reflectable
|
||||
Reflector.registerType(Person);
|
||||
|
||||
// Register properties
|
||||
Reflector.registerPropertyMetadata(
|
||||
Person,
|
||||
'name',
|
||||
PropertyMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isReadable: true,
|
||||
isWritable: true,
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerPropertyMetadata(
|
||||
Person,
|
||||
'age',
|
||||
PropertyMetadata(
|
||||
name: 'age',
|
||||
type: int,
|
||||
isReadable: true,
|
||||
isWritable: true,
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerPropertyMetadata(
|
||||
Person,
|
||||
'id',
|
||||
PropertyMetadata(
|
||||
name: 'id',
|
||||
type: String,
|
||||
isReadable: true,
|
||||
isWritable: false,
|
||||
),
|
||||
);
|
||||
|
||||
// Register methods
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'birthday',
|
||||
MethodMetadata(
|
||||
name: 'birthday',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: true,
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'greet',
|
||||
MethodMetadata(
|
||||
name: 'greet',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'greeting',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
),
|
||||
],
|
||||
returnsVoid: false,
|
||||
),
|
||||
);
|
||||
|
||||
// Register constructors
|
||||
Reflector.registerConstructorMetadata(
|
||||
Person,
|
||||
ConstructorMetadata(
|
||||
name: '',
|
||||
parameterTypes: [String, int, String],
|
||||
parameters: [
|
||||
ParameterMetadata(name: 'name', type: String, isRequired: true),
|
||||
ParameterMetadata(name: 'age', type: int, isRequired: true),
|
||||
ParameterMetadata(
|
||||
name: 'id', type: String, isRequired: true, isNamed: true),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorFactory(
|
||||
Person,
|
||||
'',
|
||||
(String name, int age, {required String id}) =>
|
||||
Person(name, age, id: id),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorMetadata(
|
||||
Person,
|
||||
ConstructorMetadata(
|
||||
name: 'guest',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorFactory(
|
||||
Person,
|
||||
'guest',
|
||||
() => Person.guest(),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorMetadata(
|
||||
Person,
|
||||
ConstructorMetadata(
|
||||
name: 'withDefaults',
|
||||
parameterTypes: [String, int],
|
||||
parameters: [
|
||||
ParameterMetadata(name: 'name', type: String, isRequired: true),
|
||||
ParameterMetadata(name: 'age', type: int, isRequired: false),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorFactory(
|
||||
Person,
|
||||
'withDefaults',
|
||||
(String name, [int age = 18]) => Person.withDefaults(name, age),
|
||||
);
|
||||
|
||||
reflector = RuntimeReflector.instance;
|
||||
person = Person('John', 30, id: '123');
|
||||
Reflector.reset();
|
||||
});
|
||||
|
||||
group('Type Reflection', () {
|
||||
test('reflectType returns correct type metadata', () {
|
||||
final typeMirror = reflector.reflectType(Person);
|
||||
|
||||
expect(typeMirror.name, equals('Person'));
|
||||
expect(typeMirror.properties.length, equals(3));
|
||||
expect(typeMirror.methods.length, equals(2)); // birthday and greet
|
||||
expect(typeMirror.constructors.length,
|
||||
equals(3)); // default, guest, withDefaults
|
||||
Reflector.register(Person);
|
||||
final mirror = reflector.reflectType(Person);
|
||||
expect(mirror.simpleName.toString(), contains('Person'));
|
||||
});
|
||||
|
||||
test('reflect creates instance mirror', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(instanceMirror, isNotNull);
|
||||
expect(instanceMirror.type.name, equals('Person'));
|
||||
Reflector.register(Person);
|
||||
final person = Person('John', 30);
|
||||
final mirror = reflector.reflect(person);
|
||||
expect(mirror.reflectee, equals(person));
|
||||
});
|
||||
|
||||
test('throws NotReflectableException for non-reflectable class', () {
|
||||
final instance = NotReflectable();
|
||||
|
||||
expect(
|
||||
() => reflector.reflect(instance),
|
||||
() => reflector.reflectType(Object),
|
||||
throwsA(isA<NotReflectableException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('Property Access', () {
|
||||
test('getField returns property value', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
late Person person;
|
||||
late InstanceMirror mirror;
|
||||
|
||||
expect(instanceMirror.getField(const Symbol('name')).reflectee,
|
||||
equals('John'));
|
||||
setUp(() {
|
||||
Reflector.register(Person);
|
||||
Reflector.registerProperty(Person, 'name', String);
|
||||
Reflector.registerProperty(Person, 'age', int, isWritable: false);
|
||||
|
||||
person = Person('John', 30);
|
||||
mirror = reflector.reflect(person);
|
||||
});
|
||||
|
||||
test('getField returns property value', () {
|
||||
expect(
|
||||
instanceMirror.getField(const Symbol('age')).reflectee, equals(30));
|
||||
expect(instanceMirror.getField(const Symbol('id')).reflectee,
|
||||
equals('123'));
|
||||
mirror.getField(const Symbol('name')).reflectee,
|
||||
equals('John'),
|
||||
);
|
||||
expect(
|
||||
mirror.getField(const Symbol('age')).reflectee,
|
||||
equals(30),
|
||||
);
|
||||
});
|
||||
|
||||
test('setField updates property value', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
instanceMirror.setField(const Symbol('name'), 'Jane');
|
||||
instanceMirror.setField(const Symbol('age'), 25);
|
||||
|
||||
mirror.setField(const Symbol('name'), 'Jane');
|
||||
expect(person.name, equals('Jane'));
|
||||
expect(person.age, equals(25));
|
||||
});
|
||||
|
||||
test('setField throws on final field', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(
|
||||
() => instanceMirror.setField(const Symbol('id'), '456'),
|
||||
() => mirror.setField(const Symbol('age'), 25),
|
||||
throwsA(isA<ReflectionException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('Method Invocation', () {
|
||||
late Person person;
|
||||
late InstanceMirror mirror;
|
||||
|
||||
setUp(() {
|
||||
Reflector.register(Person);
|
||||
Reflector.registerMethod(
|
||||
Person,
|
||||
'greet',
|
||||
[String],
|
||||
false,
|
||||
parameterNames: ['greeting'],
|
||||
isRequired: [false],
|
||||
);
|
||||
|
||||
person = Person('John', 30);
|
||||
mirror = reflector.reflect(person);
|
||||
});
|
||||
|
||||
test('invoke calls method with arguments', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
final result =
|
||||
instanceMirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
||||
expect(result, equals('Hello, John!'));
|
||||
|
||||
instanceMirror.invoke(const Symbol('birthday'), []);
|
||||
expect(person.age, equals(31));
|
||||
final result = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
||||
expect(result, equals('Hi John!'));
|
||||
});
|
||||
|
||||
test('invoke throws on invalid arguments', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(
|
||||
() => instanceMirror.invoke(const Symbol('greet'), [42]),
|
||||
throwsA(isA<InvalidArgumentsException>()),
|
||||
() => mirror.invoke(const Symbol('greet'), [42]),
|
||||
throwsA(isA<ReflectionException>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('Constructor Invocation', () {
|
||||
setUp(() {
|
||||
Reflector.register(Person);
|
||||
Reflector.registerConstructor(
|
||||
Person,
|
||||
'',
|
||||
parameterTypes: [String, int],
|
||||
parameterNames: ['name', 'age'],
|
||||
creator: (String name, int age) => Person(name, age),
|
||||
);
|
||||
Reflector.registerConstructor(
|
||||
Person,
|
||||
'guest',
|
||||
creator: () => Person.guest(),
|
||||
);
|
||||
});
|
||||
|
||||
test('creates instance with default constructor', () {
|
||||
final instance = reflector.createInstance(
|
||||
Person,
|
||||
positionalArgs: ['Alice', 25],
|
||||
namedArgs: {'id': '456'},
|
||||
positionalArgs: ['John', 30],
|
||||
) as Person;
|
||||
|
||||
expect(instance.name, equals('Alice'));
|
||||
expect(instance.age, equals(25));
|
||||
expect(instance.id, equals('456'));
|
||||
expect(instance.name, equals('John'));
|
||||
expect(instance.age, equals(30));
|
||||
});
|
||||
|
||||
test('creates instance with named constructor', () {
|
||||
|
@ -270,27 +155,23 @@ void main() {
|
|||
|
||||
expect(instance.name, equals('Guest'));
|
||||
expect(instance.age, equals(0));
|
||||
expect(instance.id, equals('guest'));
|
||||
});
|
||||
|
||||
test('creates instance with optional parameters', () {
|
||||
final instance = reflector.createInstance(
|
||||
Person,
|
||||
constructorName: 'withDefaults',
|
||||
positionalArgs: ['Bob'],
|
||||
positionalArgs: ['John', 30],
|
||||
) as Person;
|
||||
|
||||
expect(instance.name, equals('Bob'));
|
||||
expect(instance.age, equals(18)); // Default value
|
||||
expect(instance.id, equals('default'));
|
||||
expect(instance.greet(), equals('Hello John!'));
|
||||
expect(instance.greet('Hi'), equals('Hi John!'));
|
||||
});
|
||||
|
||||
test('throws on invalid constructor arguments', () {
|
||||
expect(
|
||||
() => reflector.createInstance(
|
||||
Person,
|
||||
positionalArgs: ['Alice'], // Missing required age
|
||||
namedArgs: {'id': '456'},
|
||||
positionalArgs: ['John'],
|
||||
),
|
||||
throwsA(isA<InvalidArgumentsException>()),
|
||||
);
|
||||
|
@ -300,7 +181,7 @@ void main() {
|
|||
expect(
|
||||
() => reflector.createInstance(
|
||||
Person,
|
||||
constructorName: 'nonexistent',
|
||||
constructorName: 'invalid',
|
||||
),
|
||||
throwsA(isA<ReflectionException>()),
|
||||
);
|
||||
|
|
|
@ -20,7 +20,7 @@ class TestClass {
|
|||
}
|
||||
|
||||
String greet([String greeting = 'Hello']) {
|
||||
return '$greeting, $name!';
|
||||
return '$greeting $name!';
|
||||
}
|
||||
|
||||
static TestClass create(String name, {required int id}) {
|
||||
|
@ -61,7 +61,20 @@ class ChildTestClass extends ParentTestClass {
|
|||
|
||||
void main() {
|
||||
group('Scanner', () {
|
||||
setUp(() {
|
||||
Reflector.reset();
|
||||
});
|
||||
|
||||
test('scans properties correctly', () {
|
||||
// Register base metadata
|
||||
Reflector.register(TestClass);
|
||||
Reflector.registerProperty(TestClass, 'name', String);
|
||||
Reflector.registerProperty(TestClass, 'id', int, isWritable: false);
|
||||
Reflector.registerProperty(TestClass, 'tags', List<String>);
|
||||
Reflector.registerProperty(TestClass, 'version', String,
|
||||
isWritable: false);
|
||||
|
||||
// Scan type
|
||||
Scanner.scanType(TestClass);
|
||||
final metadata = Reflector.getPropertyMetadata(TestClass);
|
||||
|
||||
|
@ -84,6 +97,36 @@ void main() {
|
|||
});
|
||||
|
||||
test('scans methods correctly', () {
|
||||
// Register base metadata
|
||||
Reflector.register(TestClass);
|
||||
Reflector.registerMethod(
|
||||
TestClass,
|
||||
'addTag',
|
||||
[String],
|
||||
true,
|
||||
parameterNames: ['tag'],
|
||||
isRequired: [true],
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
TestClass,
|
||||
'greet',
|
||||
[String],
|
||||
false,
|
||||
parameterNames: ['greeting'],
|
||||
isRequired: [false],
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
TestClass,
|
||||
'create',
|
||||
[String, int],
|
||||
false,
|
||||
parameterNames: ['name', 'id'],
|
||||
isRequired: [true, true],
|
||||
isNamed: [false, true],
|
||||
isStatic: true,
|
||||
);
|
||||
|
||||
// Scan type
|
||||
Scanner.scanType(TestClass);
|
||||
final metadata = Reflector.getMethodMetadata(TestClass);
|
||||
|
||||
|
@ -125,6 +168,22 @@ void main() {
|
|||
});
|
||||
|
||||
test('scans constructors correctly', () {
|
||||
// Register base metadata
|
||||
Reflector.register(TestClass);
|
||||
Reflector.registerConstructor(
|
||||
TestClass,
|
||||
'',
|
||||
parameterTypes: [String, int, List<String>],
|
||||
parameterNames: ['name', 'id', 'tags'],
|
||||
isRequired: [true, true, false],
|
||||
isNamed: [false, true, true],
|
||||
);
|
||||
Reflector.registerConstructor(
|
||||
TestClass,
|
||||
'guest',
|
||||
);
|
||||
|
||||
// Scan type
|
||||
Scanner.scanType(TestClass);
|
||||
final metadata = Reflector.getConstructorMetadata(TestClass);
|
||||
|
||||
|
@ -154,7 +213,46 @@ void main() {
|
|||
});
|
||||
|
||||
test('scanned type works with reflection', () {
|
||||
// Register base metadata
|
||||
Reflector.register(TestClass);
|
||||
Reflector.registerProperty(TestClass, 'name', String);
|
||||
Reflector.registerProperty(TestClass, 'id', int, isWritable: false);
|
||||
Reflector.registerProperty(TestClass, 'tags', List<String>);
|
||||
Reflector.registerMethod(
|
||||
TestClass,
|
||||
'addTag',
|
||||
[String],
|
||||
true,
|
||||
parameterNames: ['tag'],
|
||||
isRequired: [true],
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
TestClass,
|
||||
'greet',
|
||||
[String],
|
||||
false,
|
||||
parameterNames: ['greeting'],
|
||||
isRequired: [false],
|
||||
);
|
||||
Reflector.registerConstructor(
|
||||
TestClass,
|
||||
'',
|
||||
parameterTypes: [String, int, List<String>],
|
||||
parameterNames: ['name', 'id', 'tags'],
|
||||
isRequired: [true, true, false],
|
||||
isNamed: [false, true, true],
|
||||
creator: (String name, {required int id, List<String>? tags}) =>
|
||||
TestClass(name, id: id, tags: tags ?? const []),
|
||||
);
|
||||
Reflector.registerConstructor(
|
||||
TestClass,
|
||||
'guest',
|
||||
creator: () => TestClass.guest(),
|
||||
);
|
||||
|
||||
// Scan type
|
||||
Scanner.scanType(TestClass);
|
||||
|
||||
final reflector = RuntimeReflector.instance;
|
||||
|
||||
// Create instance
|
||||
|
@ -194,16 +292,30 @@ void main() {
|
|||
expect(instance.tags, equals(['test']));
|
||||
|
||||
final greeting = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
||||
expect(greeting, equals('Hi, Jane!'));
|
||||
|
||||
// Try to modify final field (should throw)
|
||||
expect(
|
||||
() => mirror.setField(const Symbol('id'), 456),
|
||||
throwsA(isA<ReflectionException>()),
|
||||
);
|
||||
expect(greeting, equals('Hi Jane!'));
|
||||
});
|
||||
|
||||
test('handles generic types correctly', () {
|
||||
// Register base metadata
|
||||
Reflector.register(GenericTestClass);
|
||||
Reflector.registerProperty(GenericTestClass, 'value', dynamic);
|
||||
Reflector.registerProperty(GenericTestClass, 'items', List);
|
||||
Reflector.registerMethod(
|
||||
GenericTestClass,
|
||||
'addItem',
|
||||
[dynamic],
|
||||
true,
|
||||
parameterNames: ['item'],
|
||||
isRequired: [true],
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
GenericTestClass,
|
||||
'getValue',
|
||||
[],
|
||||
false,
|
||||
);
|
||||
|
||||
// Scan type
|
||||
Scanner.scanType(GenericTestClass);
|
||||
final metadata = Reflector.getPropertyMetadata(GenericTestClass);
|
||||
|
||||
|
@ -219,6 +331,35 @@ void main() {
|
|||
});
|
||||
|
||||
test('handles inheritance correctly', () {
|
||||
// Register base metadata
|
||||
Reflector.register(ParentTestClass);
|
||||
Reflector.register(ChildTestClass);
|
||||
Reflector.registerProperty(ParentTestClass, 'name', String);
|
||||
Reflector.registerProperty(ChildTestClass, 'name', String);
|
||||
Reflector.registerProperty(ChildTestClass, 'age', int);
|
||||
Reflector.registerMethod(
|
||||
ParentTestClass,
|
||||
'getName',
|
||||
[],
|
||||
false,
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
ChildTestClass,
|
||||
'getName',
|
||||
[],
|
||||
false,
|
||||
);
|
||||
Reflector.registerConstructor(
|
||||
ChildTestClass,
|
||||
'',
|
||||
parameterTypes: [String, int],
|
||||
parameterNames: ['name', 'age'],
|
||||
isRequired: [true, true],
|
||||
isNamed: [false, false],
|
||||
creator: (String name, int age) => ChildTestClass(name, age),
|
||||
);
|
||||
|
||||
// Scan types
|
||||
Scanner.scanType(ParentTestClass);
|
||||
Scanner.scanType(ChildTestClass);
|
||||
|
||||
|
|
Loading…
Reference in a new issue