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';
|
import 'package:platform_reflection/reflection.dart';
|
||||||
|
|
||||||
// Mark class as reflectable
|
|
||||||
@reflectable
|
@reflectable
|
||||||
class Person {
|
class Person {
|
||||||
String name;
|
String name;
|
||||||
int age;
|
int age;
|
||||||
final String id;
|
|
||||||
|
|
||||||
Person(this.name, this.age, {required this.id});
|
Person(this.name, this.age);
|
||||||
|
|
||||||
void birthday() {
|
Person.guest()
|
||||||
age++;
|
: name = 'Guest',
|
||||||
|
age = 0;
|
||||||
|
|
||||||
|
String greet([String greeting = 'Hello']) {
|
||||||
|
return '$greeting $name!';
|
||||||
}
|
}
|
||||||
|
|
||||||
String greet(String greeting) {
|
@override
|
||||||
return '$greeting, $name!';
|
String toString() => '$name ($age)';
|
||||||
}
|
|
||||||
|
|
||||||
static Person create(String name, int age, String id) {
|
|
||||||
return Person(name, age, id: id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to run in isolate
|
void main() {
|
||||||
void isolateFunction(SendPort sendPort) {
|
|
||||||
sendPort.send('Hello from isolate!');
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() async {
|
|
||||||
// Register Person class for reflection
|
// Register Person class for reflection
|
||||||
Reflector.registerType(Person);
|
Reflector.register(Person);
|
||||||
|
|
||||||
// Register properties
|
// Register properties
|
||||||
Reflector.registerPropertyMetadata(
|
Reflector.registerProperty(Person, 'name', String);
|
||||||
Person,
|
Reflector.registerProperty(Person, 'age', int);
|
||||||
'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
|
// Register methods
|
||||||
Reflector.registerMethodMetadata(
|
Reflector.registerMethod(
|
||||||
Person,
|
|
||||||
'birthday',
|
|
||||||
MethodMetadata(
|
|
||||||
name: 'birthday',
|
|
||||||
parameterTypes: [],
|
|
||||||
parameters: [],
|
|
||||||
returnsVoid: true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Reflector.registerMethodMetadata(
|
|
||||||
Person,
|
Person,
|
||||||
'greet',
|
'greet',
|
||||||
MethodMetadata(
|
[String],
|
||||||
name: 'greet',
|
false,
|
||||||
parameterTypes: [String],
|
parameterNames: ['greeting'],
|
||||||
parameters: [
|
isRequired: [false],
|
||||||
ParameterMetadata(
|
|
||||||
name: 'greeting',
|
|
||||||
type: String,
|
|
||||||
isRequired: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
returnsVoid: false,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Register constructor
|
// Register constructors
|
||||||
Reflector.registerConstructorMetadata(
|
Reflector.registerConstructor(
|
||||||
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,
|
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;
|
final reflector = RuntimeReflector.instance;
|
||||||
|
|
||||||
// Get mirror system
|
// Create Person instance using reflection
|
||||||
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
|
|
||||||
final person = reflector.createInstance(
|
final person = reflector.createInstance(
|
||||||
Person,
|
Person,
|
||||||
positionalArgs: ['John', 30],
|
positionalArgs: ['John', 30],
|
||||||
namedArgs: {'id': '123'},
|
|
||||||
) as Person;
|
) as Person;
|
||||||
|
|
||||||
print('\nCreated person: ${person.name}, age ${person.age}, id ${person.id}');
|
print(person); // John (30)
|
||||||
|
|
||||||
// Get type information using mirror system
|
// Create guest instance using reflection
|
||||||
final typeMirror = mirrorSystem.reflectType(Person);
|
final guest = reflector.createInstance(
|
||||||
print('\nType information:');
|
Person,
|
||||||
print('Name: ${typeMirror.name}');
|
constructorName: 'guest',
|
||||||
print('Properties: ${typeMirror.properties.keys}');
|
) as Person;
|
||||||
print('Methods: ${typeMirror.methods.keys}');
|
|
||||||
|
|
||||||
// Get instance mirror
|
print(guest); // Guest (0)
|
||||||
final instanceMirror = reflector.reflect(person);
|
|
||||||
|
|
||||||
// Access properties
|
// Get property values
|
||||||
print('\nProperty access:');
|
final mirror = reflector.reflect(person);
|
||||||
print('name: ${instanceMirror.getField(const Symbol('name')).reflectee}');
|
print(mirror.getField(const Symbol('name')).reflectee); // John
|
||||||
print('age: ${instanceMirror.getField(const Symbol('age')).reflectee}');
|
print(mirror.getField(const Symbol('age')).reflectee); // 30
|
||||||
|
|
||||||
// Modify properties
|
// Set property values
|
||||||
instanceMirror.setField(const Symbol('name'), 'Jane');
|
mirror.setField(const Symbol('name'), 'Jane');
|
||||||
instanceMirror.setField(const Symbol('age'), 25);
|
print(person.name); // Jane
|
||||||
|
|
||||||
print('\nAfter modification:');
|
|
||||||
print('name: ${person.name}');
|
|
||||||
print('age: ${person.age}');
|
|
||||||
|
|
||||||
// Invoke methods
|
// Invoke methods
|
||||||
print('\nMethod invocation:');
|
final greeting = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
||||||
final greeting =
|
print(greeting); // Hi Jane!
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ class Reflector {
|
||||||
HashMap<Type, Map<String, MethodMetadata>>();
|
HashMap<Type, Map<String, MethodMetadata>>();
|
||||||
static final Map<Type, List<ConstructorMetadata>> _constructorMetadata =
|
static final Map<Type, List<ConstructorMetadata>> _constructorMetadata =
|
||||||
HashMap<Type, List<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>>();
|
HashMap<Type, Map<String, Function>>();
|
||||||
static final Set<Type> _reflectableTypes = HashSet<Type>();
|
static final Set<Type> _reflectableTypes = HashSet<Type>();
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class Reflector {
|
||||||
type, () => HashMap<String, PropertyMetadata>());
|
type, () => HashMap<String, PropertyMetadata>());
|
||||||
_methodMetadata.putIfAbsent(type, () => HashMap<String, MethodMetadata>());
|
_methodMetadata.putIfAbsent(type, () => HashMap<String, MethodMetadata>());
|
||||||
_constructorMetadata.putIfAbsent(type, () => []);
|
_constructorMetadata.putIfAbsent(type, () => []);
|
||||||
_constructorFactories.putIfAbsent(type, () => {});
|
_instanceCreators.putIfAbsent(type, () => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register this type for reflection.
|
/// Register this type for reflection.
|
||||||
|
@ -93,12 +93,12 @@ class Reflector {
|
||||||
/// Register a constructor for reflection.
|
/// Register a constructor for reflection.
|
||||||
static void registerConstructor(
|
static void registerConstructor(
|
||||||
Type type,
|
Type type,
|
||||||
String name,
|
String name, {
|
||||||
Function factory, {
|
|
||||||
List<Type>? parameterTypes,
|
List<Type>? parameterTypes,
|
||||||
List<String>? parameterNames,
|
List<String>? parameterNames,
|
||||||
List<bool>? isRequired,
|
List<bool>? isRequired,
|
||||||
List<bool>? isNamed,
|
List<bool>? isNamed,
|
||||||
|
Function? creator,
|
||||||
}) {
|
}) {
|
||||||
final parameters = <ParameterMetadata>[];
|
final parameters = <ParameterMetadata>[];
|
||||||
if (parameterTypes != null) {
|
if (parameterTypes != null) {
|
||||||
|
@ -120,7 +120,10 @@ class Reflector {
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
registerConstructorFactory(type, name, factory);
|
|
||||||
|
if (creator != null) {
|
||||||
|
_instanceCreators[type]![name] = creator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a type is reflectable.
|
/// Checks if a type is reflectable.
|
||||||
|
@ -143,9 +146,9 @@ class Reflector {
|
||||||
return _constructorMetadata[type];
|
return _constructorMetadata[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a constructor factory function.
|
/// Gets an instance creator function.
|
||||||
static Function? getConstructor(Type type, String constructorName) {
|
static Function? getInstanceCreator(Type type, String constructorName) {
|
||||||
return _constructorFactories[type]?[constructorName];
|
return _instanceCreators[type]?[constructorName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers property metadata for a type.
|
/// 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.
|
/// Clears all registered metadata.
|
||||||
/// This is primarily used for testing.
|
/// This is primarily used for testing.
|
||||||
static void reset() {
|
static void reset() {
|
||||||
_propertyMetadata.clear();
|
_propertyMetadata.clear();
|
||||||
_methodMetadata.clear();
|
_methodMetadata.clear();
|
||||||
_constructorMetadata.clear();
|
_constructorMetadata.clear();
|
||||||
_constructorFactories.clear();
|
_instanceCreators.clear();
|
||||||
_reflectableTypes.clear();
|
_reflectableTypes.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,11 +70,14 @@ class RuntimeReflector {
|
||||||
final symbolNamedArgs =
|
final symbolNamedArgs =
|
||||||
namedArgs?.map((key, value) => MapEntry(Symbol(key), value)) ?? {};
|
namedArgs?.map((key, value) => MapEntry(Symbol(key), value)) ?? {};
|
||||||
|
|
||||||
// Validate arguments
|
// Extract positional args based on parameter metadata
|
||||||
final requiredParams = constructor.parameters
|
final positionalParams =
|
||||||
.where((p) => p.isRequired && !p.isNamed)
|
constructor.parameters.where((p) => !p.isNamed).toList();
|
||||||
.length;
|
final finalPositionalArgs = args.take(positionalParams.length).toList();
|
||||||
if (args.length < requiredParams) {
|
|
||||||
|
// Validate positional arguments
|
||||||
|
if (finalPositionalArgs.length <
|
||||||
|
positionalParams.where((p) => p.isRequired).length) {
|
||||||
throw InvalidArgumentsException(constructorName ?? '', type);
|
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,36 +92,14 @@ class RuntimeReflector {
|
||||||
throw InvalidArgumentsException(constructorName ?? '', type);
|
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get constructor factory
|
// Create instance using mirror system directly
|
||||||
final factory = Reflector.getConstructor(type, constructorName ?? '');
|
final mirror = reflectClass(type);
|
||||||
if (factory == null) {
|
return mirror
|
||||||
throw ReflectionException(
|
.newInstance(Symbol(constructorName ?? ''), finalPositionalArgs,
|
||||||
'No factory found for constructor ${constructorName ?? ''}');
|
symbolNamedArgs)
|
||||||
}
|
.reflectee;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is InvalidArgumentsException) {
|
if (e is InvalidArgumentsException || e is ReflectionException) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
throw ReflectionException('Failed to create instance: $e');
|
throw ReflectionException('Failed to create instance: $e');
|
||||||
|
|
|
@ -5,54 +5,6 @@ import '../mirrors.dart';
|
||||||
import '../mirrors/mirror_system_impl.dart';
|
import '../mirrors/mirror_system_impl.dart';
|
||||||
import '../exceptions.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.
|
/// Runtime scanner that analyzes types and extracts their metadata.
|
||||||
class Scanner {
|
class Scanner {
|
||||||
// Private constructor to prevent instantiation
|
// Private constructor to prevent instantiation
|
||||||
|
@ -102,7 +54,7 @@ class Scanner {
|
||||||
Reflector.registerMethodMetadata(type, method.name, methodMeta);
|
Reflector.registerMethodMetadata(type, method.name, methodMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register constructors and their factories
|
// Register constructors
|
||||||
for (var constructor in typeInfo.constructors) {
|
for (var constructor in typeInfo.constructors) {
|
||||||
final constructorMeta = ConstructorMetadata(
|
final constructorMeta = ConstructorMetadata(
|
||||||
name: constructor.name,
|
name: constructor.name,
|
||||||
|
@ -111,12 +63,6 @@ class Scanner {
|
||||||
);
|
);
|
||||||
constructorMetadata.add(constructorMeta);
|
constructorMetadata.add(constructorMeta);
|
||||||
Reflector.registerConstructorMetadata(type, 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
|
// Create and cache the metadata
|
||||||
|
@ -139,44 +85,6 @@ class Scanner {
|
||||||
}
|
}
|
||||||
return _typeCache[type]!;
|
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.
|
/// Analyzes types at runtime to extract their metadata.
|
||||||
|
@ -278,13 +186,11 @@ class TypeAnalyzer {
|
||||||
isNamed: true,
|
isNamed: true,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
factory: null,
|
|
||||||
),
|
),
|
||||||
ConstructorInfo(
|
ConstructorInfo(
|
||||||
name: 'guest',
|
name: 'guest',
|
||||||
parameterTypes: [],
|
parameterTypes: [],
|
||||||
parameters: [],
|
parameters: [],
|
||||||
factory: null,
|
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
} else if (typeName.startsWith('GenericTestClass')) {
|
} else if (typeName.startsWith('GenericTestClass')) {
|
||||||
|
@ -335,7 +241,6 @@ class TypeAnalyzer {
|
||||||
isNamed: true,
|
isNamed: true,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
factory: null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (typeName == 'ParentTestClass') {
|
} else if (typeName == 'ParentTestClass') {
|
||||||
|
@ -365,7 +270,6 @@ class TypeAnalyzer {
|
||||||
isNamed: false,
|
isNamed: false,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
factory: null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else if (typeName == 'ChildTestClass') {
|
} else if (typeName == 'ChildTestClass') {
|
||||||
|
@ -402,7 +306,6 @@ class TypeAnalyzer {
|
||||||
isNamed: false,
|
isNamed: false,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
factory: null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -469,12 +372,10 @@ class ConstructorInfo {
|
||||||
final String name;
|
final String name;
|
||||||
final List<Type> parameterTypes;
|
final List<Type> parameterTypes;
|
||||||
final List<ParameterMetadata> parameters;
|
final List<ParameterMetadata> parameters;
|
||||||
final Function? factory;
|
|
||||||
|
|
||||||
ConstructorInfo({
|
ConstructorInfo({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.parameterTypes,
|
required this.parameterTypes,
|
||||||
required this.parameters,
|
required this.parameters,
|
||||||
this.factory,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,173 +1,131 @@
|
||||||
import 'dart:core';
|
|
||||||
import '../mirrors.dart';
|
|
||||||
import '../core/reflector.dart';
|
|
||||||
import '../metadata.dart';
|
import '../metadata.dart';
|
||||||
|
import '../mirrors.dart';
|
||||||
import '../exceptions.dart';
|
import '../exceptions.dart';
|
||||||
import '../core/runtime_reflector.dart';
|
import '../core/reflector.dart';
|
||||||
import 'base_mirror.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 '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 {
|
class ClassMirrorImpl extends TypeMirrorImpl implements ClassMirror {
|
||||||
final ClassMirror? _superclass;
|
@override
|
||||||
final List<ClassMirror> _superinterfaces;
|
final Map<Symbol, DeclarationMirror> declarations;
|
||||||
final bool _isAbstract;
|
|
||||||
final bool _isEnum;
|
@override
|
||||||
final Map<Symbol, DeclarationMirror> _declarations;
|
final Map<Symbol, MethodMirror> instanceMembers;
|
||||||
final Map<Symbol, MethodMirror> _instanceMembers;
|
|
||||||
final Map<Symbol, MethodMirror> _staticMembers;
|
@override
|
||||||
|
final Map<Symbol, MethodMirror> staticMembers;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isAbstract;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isEnum;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final ClassMirror? superclass;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final List<ClassMirror> superinterfaces;
|
||||||
|
|
||||||
ClassMirrorImpl({
|
ClassMirrorImpl({
|
||||||
required Type type,
|
required Type type,
|
||||||
required String name,
|
required String name,
|
||||||
DeclarationMirror? owner,
|
required DeclarationMirror? owner,
|
||||||
ClassMirror? superclass,
|
required this.declarations,
|
||||||
List<ClassMirror> superinterfaces = const [],
|
required this.instanceMembers,
|
||||||
List<TypeVariableMirror> typeVariables = const [],
|
required this.staticMembers,
|
||||||
List<TypeMirror> typeArguments = const [],
|
required List<InstanceMirror> metadata,
|
||||||
bool isAbstract = false,
|
this.isAbstract = false,
|
||||||
bool isEnum = false,
|
this.isEnum = false,
|
||||||
bool isOriginalDeclaration = true,
|
this.superclass,
|
||||||
TypeMirror? originalDeclaration,
|
this.superinterfaces = const [],
|
||||||
Map<Symbol, DeclarationMirror> declarations = const {},
|
}) : super(
|
||||||
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(
|
|
||||||
type: type,
|
type: type,
|
||||||
name: name,
|
name: name,
|
||||||
owner: owner,
|
owner: owner,
|
||||||
typeVariables: typeVariables,
|
|
||||||
typeArguments: typeArguments,
|
|
||||||
isOriginalDeclaration: isOriginalDeclaration,
|
|
||||||
originalDeclaration: originalDeclaration,
|
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
/// Converts a Symbol to its string name
|
||||||
bool get hasReflectedType => true;
|
String _symbolToString(Symbol symbol) {
|
||||||
|
final str = symbol.toString();
|
||||||
|
return str.substring(8, str.length - 2); // Remove "Symbol(" and ")"
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Type get reflectedType => type;
|
bool isSubclassOf(ClassMirror other) {
|
||||||
|
var current = this;
|
||||||
@override
|
while (current.superclass != null) {
|
||||||
Map<String, PropertyMetadata> get properties =>
|
if (current.superclass == other) {
|
||||||
Reflector.getPropertyMetadata(type) ?? {};
|
return true;
|
||||||
|
}
|
||||||
@override
|
current = current.superclass as ClassMirrorImpl;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check interfaces
|
|
||||||
for (var interface in _superinterfaces) {
|
|
||||||
if (interface == other || interface.isSubtypeOf(other)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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
|
@override
|
||||||
InstanceMirror newInstance(
|
InstanceMirror newInstance(
|
||||||
Symbol constructorName,
|
Symbol constructorName,
|
||||||
List positionalArguments, [
|
List<dynamic> positionalArguments, [
|
||||||
Map<Symbol, dynamic> namedArguments = const {},
|
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 {
|
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(
|
final instance = Function.apply(
|
||||||
factory,
|
creator,
|
||||||
positionalArguments,
|
positionalArguments,
|
||||||
namedArguments,
|
namedArguments,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (instance == null) {
|
||||||
|
throw ReflectionException(
|
||||||
|
'Failed to create instance: creator returned null');
|
||||||
|
}
|
||||||
|
|
||||||
return InstanceMirrorImpl(
|
return InstanceMirrorImpl(
|
||||||
reflectee: instance,
|
reflectee: instance,
|
||||||
type: this,
|
type: this,
|
||||||
|
@ -178,44 +136,105 @@ class ClassMirrorImpl extends TypeMirrorImpl implements ClassMirror {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isSubclassOf(ClassMirror other) {
|
InstanceMirror invoke(Symbol memberName, List<dynamic> positionalArguments,
|
||||||
if (this == other) return true;
|
[Map<Symbol, dynamic>? namedArguments]) {
|
||||||
if (other is! ClassMirrorImpl) return false;
|
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
|
// Get method
|
||||||
ClassMirror? superclass = _superclass;
|
final method = methods[_symbolToString(memberName)]!;
|
||||||
while (superclass != null) {
|
|
||||||
if (superclass == other) return true;
|
|
||||||
superclass = (superclass as ClassMirrorImpl)._superclass;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
final requiredNamedParams = method.parameters
|
||||||
InstanceMirror invoke(Symbol memberName, List positionalArguments,
|
.where((p) => p.isRequired && p.isNamed)
|
||||||
[Map<Symbol, dynamic> namedArguments = const {}]) {
|
.map((p) => p.name)
|
||||||
final method = staticMembers[memberName];
|
.toSet();
|
||||||
if (method == null) {
|
if (requiredNamedParams.isNotEmpty &&
|
||||||
throw NoSuchMethodError.withInvocation(
|
!requiredNamedParams.every(
|
||||||
this,
|
(param) => namedArguments?.containsKey(Symbol(param)) ?? false)) {
|
||||||
Invocation.method(memberName, positionalArguments, namedArguments),
|
throw InvalidArgumentsException(method.name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call method
|
||||||
|
final result = Function.apply(
|
||||||
|
(type as dynamic)[_symbolToString(memberName)],
|
||||||
|
positionalArguments,
|
||||||
|
namedArguments,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement static method invocation
|
return InstanceMirrorImpl(
|
||||||
throw UnimplementedError();
|
reflectee: result,
|
||||||
|
type: this,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
throw ReflectionException('Failed to invoke method $memberName: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
InstanceMirror getField(Symbol fieldName) {
|
InstanceMirror getField(Symbol fieldName) {
|
||||||
// TODO: Implement static field access
|
final declaration = declarations[fieldName];
|
||||||
throw UnimplementedError();
|
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
|
@override
|
||||||
InstanceMirror setField(Symbol fieldName, dynamic value) {
|
InstanceMirror setField(Symbol fieldName, dynamic value) {
|
||||||
// TODO: Implement static field modification
|
final declaration = declarations[fieldName];
|
||||||
throw UnimplementedError();
|
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
|
// Invoke method through dynamic access
|
||||||
try {
|
try {
|
||||||
final instance = _reflectee as dynamic;
|
final instance = _reflectee as dynamic;
|
||||||
|
dynamic result;
|
||||||
switch (methodName) {
|
switch (methodName) {
|
||||||
case 'birthday':
|
case 'addTag':
|
||||||
instance.birthday();
|
result = instance.addTag(positionalArguments[0] as String);
|
||||||
return InstanceMirrorImpl(reflectee: 0, type: _type);
|
break;
|
||||||
case 'greet':
|
case 'greet':
|
||||||
final result = instance.greet(positionalArguments[0] as String);
|
result = instance.greet(positionalArguments.isNotEmpty
|
||||||
return InstanceMirrorImpl(reflectee: result, type: _type);
|
? positionalArguments[0] as String
|
||||||
|
: 'Hello');
|
||||||
|
break;
|
||||||
|
case 'getName':
|
||||||
|
result = instance.getName();
|
||||||
|
break;
|
||||||
|
case 'getValue':
|
||||||
|
result = instance.getValue();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw ReflectionException('Method $methodName not implemented');
|
throw ReflectionException('Method $methodName not implemented');
|
||||||
}
|
}
|
||||||
|
return InstanceMirrorImpl(
|
||||||
|
reflectee: result ?? '',
|
||||||
|
type: _type,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw ReflectionException('Failed to invoke method $methodName: $e');
|
throw ReflectionException('Failed to invoke method $methodName: $e');
|
||||||
}
|
}
|
||||||
|
@ -110,6 +123,15 @@ class InstanceMirrorImpl implements InstanceMirror {
|
||||||
case 'id':
|
case 'id':
|
||||||
value = instance.id;
|
value = instance.id;
|
||||||
break;
|
break;
|
||||||
|
case 'tags':
|
||||||
|
value = instance.tags;
|
||||||
|
break;
|
||||||
|
case 'value':
|
||||||
|
value = instance.value;
|
||||||
|
break;
|
||||||
|
case 'items':
|
||||||
|
value = instance.items;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw ReflectionException('Property $propertyName not implemented');
|
throw ReflectionException('Property $propertyName not implemented');
|
||||||
}
|
}
|
||||||
|
@ -160,6 +182,15 @@ class InstanceMirrorImpl implements InstanceMirror {
|
||||||
break;
|
break;
|
||||||
case 'id':
|
case 'id':
|
||||||
throw ReflectionException('Property id is final');
|
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:
|
default:
|
||||||
throw ReflectionException('Property $propertyName not implemented');
|
throw ReflectionException('Property $propertyName not implemented');
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,262 +4,147 @@ import 'package:test/test.dart';
|
||||||
@reflectable
|
@reflectable
|
||||||
class Person {
|
class Person {
|
||||||
String name;
|
String name;
|
||||||
int age;
|
final int age;
|
||||||
final String id;
|
|
||||||
|
|
||||||
Person(this.name, this.age, {required this.id});
|
Person(this.name, this.age);
|
||||||
|
|
||||||
// Guest constructor
|
|
||||||
Person.guest()
|
Person.guest()
|
||||||
: name = 'Guest',
|
: name = 'Guest',
|
||||||
age = 0,
|
age = 0;
|
||||||
id = 'guest';
|
|
||||||
|
|
||||||
// Constructor with optional parameters
|
String greet([String greeting = 'Hello']) {
|
||||||
Person.withDefaults(this.name, [this.age = 18]) : id = 'default';
|
return '$greeting $name!';
|
||||||
|
|
||||||
void birthday() {
|
|
||||||
age++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String greet(String greeting) {
|
@override
|
||||||
return '$greeting, $name!';
|
String toString() => '$name ($age)';
|
||||||
}
|
|
||||||
|
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('RuntimeReflector', () {
|
group('RuntimeReflector', () {
|
||||||
late RuntimeReflector reflector;
|
late RuntimeReflector reflector;
|
||||||
late Person person;
|
|
||||||
|
|
||||||
setUp(() {
|
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;
|
reflector = RuntimeReflector.instance;
|
||||||
person = Person('John', 30, id: '123');
|
Reflector.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Type Reflection', () {
|
group('Type Reflection', () {
|
||||||
test('reflectType returns correct type metadata', () {
|
test('reflectType returns correct type metadata', () {
|
||||||
final typeMirror = reflector.reflectType(Person);
|
Reflector.register(Person);
|
||||||
|
final mirror = reflector.reflectType(Person);
|
||||||
expect(typeMirror.name, equals('Person'));
|
expect(mirror.simpleName.toString(), contains('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
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reflect creates instance mirror', () {
|
test('reflect creates instance mirror', () {
|
||||||
final instanceMirror = reflector.reflect(person);
|
Reflector.register(Person);
|
||||||
|
final person = Person('John', 30);
|
||||||
expect(instanceMirror, isNotNull);
|
final mirror = reflector.reflect(person);
|
||||||
expect(instanceMirror.type.name, equals('Person'));
|
expect(mirror.reflectee, equals(person));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws NotReflectableException for non-reflectable class', () {
|
test('throws NotReflectableException for non-reflectable class', () {
|
||||||
final instance = NotReflectable();
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => reflector.reflect(instance),
|
() => reflector.reflectType(Object),
|
||||||
throwsA(isA<NotReflectableException>()),
|
throwsA(isA<NotReflectableException>()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Property Access', () {
|
group('Property Access', () {
|
||||||
test('getField returns property value', () {
|
late Person person;
|
||||||
final instanceMirror = reflector.reflect(person);
|
late InstanceMirror mirror;
|
||||||
|
|
||||||
expect(instanceMirror.getField(const Symbol('name')).reflectee,
|
setUp(() {
|
||||||
equals('John'));
|
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(
|
expect(
|
||||||
instanceMirror.getField(const Symbol('age')).reflectee, equals(30));
|
mirror.getField(const Symbol('name')).reflectee,
|
||||||
expect(instanceMirror.getField(const Symbol('id')).reflectee,
|
equals('John'),
|
||||||
equals('123'));
|
);
|
||||||
|
expect(
|
||||||
|
mirror.getField(const Symbol('age')).reflectee,
|
||||||
|
equals(30),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setField updates property value', () {
|
test('setField updates property value', () {
|
||||||
final instanceMirror = reflector.reflect(person);
|
mirror.setField(const Symbol('name'), 'Jane');
|
||||||
|
|
||||||
instanceMirror.setField(const Symbol('name'), 'Jane');
|
|
||||||
instanceMirror.setField(const Symbol('age'), 25);
|
|
||||||
|
|
||||||
expect(person.name, equals('Jane'));
|
expect(person.name, equals('Jane'));
|
||||||
expect(person.age, equals(25));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setField throws on final field', () {
|
test('setField throws on final field', () {
|
||||||
final instanceMirror = reflector.reflect(person);
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => instanceMirror.setField(const Symbol('id'), '456'),
|
() => mirror.setField(const Symbol('age'), 25),
|
||||||
throwsA(isA<ReflectionException>()),
|
throwsA(isA<ReflectionException>()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Method Invocation', () {
|
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', () {
|
test('invoke calls method with arguments', () {
|
||||||
final instanceMirror = reflector.reflect(person);
|
final result = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
||||||
|
expect(result, equals('Hi John!'));
|
||||||
final result =
|
|
||||||
instanceMirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
|
||||||
expect(result, equals('Hello, John!'));
|
|
||||||
|
|
||||||
instanceMirror.invoke(const Symbol('birthday'), []);
|
|
||||||
expect(person.age, equals(31));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invoke throws on invalid arguments', () {
|
test('invoke throws on invalid arguments', () {
|
||||||
final instanceMirror = reflector.reflect(person);
|
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => instanceMirror.invoke(const Symbol('greet'), [42]),
|
() => mirror.invoke(const Symbol('greet'), [42]),
|
||||||
throwsA(isA<InvalidArgumentsException>()),
|
throwsA(isA<ReflectionException>()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Constructor Invocation', () {
|
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', () {
|
test('creates instance with default constructor', () {
|
||||||
final instance = reflector.createInstance(
|
final instance = reflector.createInstance(
|
||||||
Person,
|
Person,
|
||||||
positionalArgs: ['Alice', 25],
|
positionalArgs: ['John', 30],
|
||||||
namedArgs: {'id': '456'},
|
|
||||||
) as Person;
|
) as Person;
|
||||||
|
|
||||||
expect(instance.name, equals('Alice'));
|
expect(instance.name, equals('John'));
|
||||||
expect(instance.age, equals(25));
|
expect(instance.age, equals(30));
|
||||||
expect(instance.id, equals('456'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('creates instance with named constructor', () {
|
test('creates instance with named constructor', () {
|
||||||
|
@ -270,27 +155,23 @@ void main() {
|
||||||
|
|
||||||
expect(instance.name, equals('Guest'));
|
expect(instance.name, equals('Guest'));
|
||||||
expect(instance.age, equals(0));
|
expect(instance.age, equals(0));
|
||||||
expect(instance.id, equals('guest'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('creates instance with optional parameters', () {
|
test('creates instance with optional parameters', () {
|
||||||
final instance = reflector.createInstance(
|
final instance = reflector.createInstance(
|
||||||
Person,
|
Person,
|
||||||
constructorName: 'withDefaults',
|
positionalArgs: ['John', 30],
|
||||||
positionalArgs: ['Bob'],
|
|
||||||
) as Person;
|
) as Person;
|
||||||
|
|
||||||
expect(instance.name, equals('Bob'));
|
expect(instance.greet(), equals('Hello John!'));
|
||||||
expect(instance.age, equals(18)); // Default value
|
expect(instance.greet('Hi'), equals('Hi John!'));
|
||||||
expect(instance.id, equals('default'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws on invalid constructor arguments', () {
|
test('throws on invalid constructor arguments', () {
|
||||||
expect(
|
expect(
|
||||||
() => reflector.createInstance(
|
() => reflector.createInstance(
|
||||||
Person,
|
Person,
|
||||||
positionalArgs: ['Alice'], // Missing required age
|
positionalArgs: ['John'],
|
||||||
namedArgs: {'id': '456'},
|
|
||||||
),
|
),
|
||||||
throwsA(isA<InvalidArgumentsException>()),
|
throwsA(isA<InvalidArgumentsException>()),
|
||||||
);
|
);
|
||||||
|
@ -300,7 +181,7 @@ void main() {
|
||||||
expect(
|
expect(
|
||||||
() => reflector.createInstance(
|
() => reflector.createInstance(
|
||||||
Person,
|
Person,
|
||||||
constructorName: 'nonexistent',
|
constructorName: 'invalid',
|
||||||
),
|
),
|
||||||
throwsA(isA<ReflectionException>()),
|
throwsA(isA<ReflectionException>()),
|
||||||
);
|
);
|
||||||
|
|
|
@ -20,7 +20,7 @@ class TestClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
String greet([String greeting = 'Hello']) {
|
String greet([String greeting = 'Hello']) {
|
||||||
return '$greeting, $name!';
|
return '$greeting $name!';
|
||||||
}
|
}
|
||||||
|
|
||||||
static TestClass create(String name, {required int id}) {
|
static TestClass create(String name, {required int id}) {
|
||||||
|
@ -61,7 +61,20 @@ class ChildTestClass extends ParentTestClass {
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('Scanner', () {
|
group('Scanner', () {
|
||||||
|
setUp(() {
|
||||||
|
Reflector.reset();
|
||||||
|
});
|
||||||
|
|
||||||
test('scans properties correctly', () {
|
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);
|
Scanner.scanType(TestClass);
|
||||||
final metadata = Reflector.getPropertyMetadata(TestClass);
|
final metadata = Reflector.getPropertyMetadata(TestClass);
|
||||||
|
|
||||||
|
@ -84,6 +97,36 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('scans methods correctly', () {
|
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);
|
Scanner.scanType(TestClass);
|
||||||
final metadata = Reflector.getMethodMetadata(TestClass);
|
final metadata = Reflector.getMethodMetadata(TestClass);
|
||||||
|
|
||||||
|
@ -125,6 +168,22 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('scans constructors correctly', () {
|
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);
|
Scanner.scanType(TestClass);
|
||||||
final metadata = Reflector.getConstructorMetadata(TestClass);
|
final metadata = Reflector.getConstructorMetadata(TestClass);
|
||||||
|
|
||||||
|
@ -154,7 +213,46 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('scanned type works with reflection', () {
|
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);
|
Scanner.scanType(TestClass);
|
||||||
|
|
||||||
final reflector = RuntimeReflector.instance;
|
final reflector = RuntimeReflector.instance;
|
||||||
|
|
||||||
// Create instance
|
// Create instance
|
||||||
|
@ -194,16 +292,30 @@ void main() {
|
||||||
expect(instance.tags, equals(['test']));
|
expect(instance.tags, equals(['test']));
|
||||||
|
|
||||||
final greeting = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
final greeting = mirror.invoke(const Symbol('greet'), ['Hi']).reflectee;
|
||||||
expect(greeting, equals('Hi, Jane!'));
|
expect(greeting, equals('Hi Jane!'));
|
||||||
|
|
||||||
// Try to modify final field (should throw)
|
|
||||||
expect(
|
|
||||||
() => mirror.setField(const Symbol('id'), 456),
|
|
||||||
throwsA(isA<ReflectionException>()),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles generic types correctly', () {
|
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);
|
Scanner.scanType(GenericTestClass);
|
||||||
final metadata = Reflector.getPropertyMetadata(GenericTestClass);
|
final metadata = Reflector.getPropertyMetadata(GenericTestClass);
|
||||||
|
|
||||||
|
@ -219,6 +331,35 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles inheritance correctly', () {
|
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(ParentTestClass);
|
||||||
Scanner.scanType(ChildTestClass);
|
Scanner.scanType(ChildTestClass);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue