update: update reflection package 36 pass 8 fail in testing
This commit is contained in:
parent
d5e6e4a19c
commit
8a932d8759
31 changed files with 4478 additions and 631 deletions
|
@ -1,16 +1,20 @@
|
|||
# Dart Pure Reflection
|
||||
# Platform Reflection
|
||||
|
||||
A lightweight, cross-platform reflection system for Dart that provides runtime type introspection and manipulation without using `dart:mirrors` or code generation.
|
||||
A lightweight, cross-platform reflection system for Dart that provides runtime type introspection and manipulation with an API similar to `dart:mirrors` but without its limitations.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Works on all platforms (Web, Mobile, Desktop)
|
||||
- ✅ No dependency on `dart:mirrors`
|
||||
- ✅ No code generation required
|
||||
- ✅ Pure runtime reflection
|
||||
- ✅ No code generation required
|
||||
- ✅ No manual registration needed
|
||||
- ✅ Complete mirror-based API
|
||||
- ✅ Type-safe property access
|
||||
- ✅ Method invocation with argument validation
|
||||
- ✅ Constructor invocation support
|
||||
- ✅ Library and isolate reflection
|
||||
- ✅ Full MirrorSystem implementation
|
||||
- ✅ Comprehensive error handling
|
||||
|
||||
## Installation
|
||||
|
@ -19,91 +23,136 @@ Add this to your package's `pubspec.yaml` file:
|
|||
|
||||
```yaml
|
||||
dependencies:
|
||||
reflection: ^1.0.0
|
||||
platform_reflection: ^0.1.0
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Setup
|
||||
### Basic Reflection
|
||||
|
||||
1. Add the `@reflectable` annotation and `Reflector` mixin to your class:
|
||||
Simply mark your class with `@reflectable`:
|
||||
|
||||
```dart
|
||||
import 'package:reflection/reflection.dart';
|
||||
import 'package:platform_reflection/reflection.dart';
|
||||
|
||||
@reflectable
|
||||
class User with Reflector {
|
||||
class User {
|
||||
String name;
|
||||
int age;
|
||||
final String id;
|
||||
|
||||
User(this.name, this.age, {required this.id});
|
||||
|
||||
void birthday() {
|
||||
age++;
|
||||
}
|
||||
|
||||
String greet(String greeting) {
|
||||
return '$greeting, $name!';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Register your class and its constructors:
|
||||
|
||||
```dart
|
||||
// Register the class
|
||||
Reflector.register(User);
|
||||
|
||||
// Register constructors
|
||||
Reflector.registerConstructor(
|
||||
User,
|
||||
'', // Default constructor
|
||||
(String name, int age, {String? id}) {
|
||||
if (id == null) throw ArgumentError.notNull('id');
|
||||
return User(name, age, id: id);
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
### Reflecting on Types
|
||||
Then use reflection directly:
|
||||
|
||||
```dart
|
||||
// Get the reflector instance
|
||||
final reflector = RuntimeReflector.instance;
|
||||
|
||||
// Get type metadata
|
||||
final userType = reflector.reflectType(User);
|
||||
print('Type name: ${userType.name}');
|
||||
print('Properties: ${userType.properties.keys.join(', ')}');
|
||||
print('Methods: ${userType.methods.keys.join(', ')}');
|
||||
```
|
||||
// Create instance using reflection
|
||||
final user = reflector.createInstance(
|
||||
User,
|
||||
positionalArgs: ['John', 30],
|
||||
namedArgs: {'id': '123'},
|
||||
) as User;
|
||||
|
||||
### Working with Instances
|
||||
// Get instance mirror
|
||||
final mirror = reflector.reflect(user);
|
||||
|
||||
```dart
|
||||
final user = User('john_doe', 30, id: 'usr_123');
|
||||
final userReflector = reflector.reflect(user);
|
||||
// Access properties
|
||||
print(mirror.getField(const Symbol('name')).reflectee); // John
|
||||
print(mirror.getField(const Symbol('age')).reflectee); // 30
|
||||
|
||||
// Read properties
|
||||
final name = userReflector.getField('name'); // john_doe
|
||||
final age = userReflector.getField('age'); // 30
|
||||
|
||||
// Write properties
|
||||
userReflector.setField('name', 'jane_doe');
|
||||
userReflector.setField('age', 25);
|
||||
// Modify properties
|
||||
mirror.setField(const Symbol('name'), 'Jane');
|
||||
mirror.setField(const Symbol('age'), 25);
|
||||
|
||||
// Invoke methods
|
||||
userReflector.invoke('someMethod', ['arg1', 'arg2']);
|
||||
mirror.invoke(const Symbol('birthday'), []);
|
||||
final greeting = mirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
||||
print(greeting); // Hello, Jane!
|
||||
```
|
||||
|
||||
### Creating Instances
|
||||
### Type Information
|
||||
|
||||
```dart
|
||||
// Using default constructor
|
||||
final newUser = reflector.createInstance(
|
||||
User,
|
||||
positionalArgs: ['alice', 28],
|
||||
namedArgs: {'id': 'usr_456'},
|
||||
) as User;
|
||||
// Get mirror system
|
||||
final mirrors = reflector.currentMirrorSystem;
|
||||
|
||||
// Using named constructor
|
||||
final specialUser = reflector.createInstance(
|
||||
User,
|
||||
constructorName: 'special',
|
||||
positionalArgs: ['bob'],
|
||||
) as User;
|
||||
// Get type mirror
|
||||
final typeMirror = mirrors.reflectType(User);
|
||||
|
||||
// Access type information
|
||||
print(typeMirror.name); // User
|
||||
print(typeMirror.properties); // {name: PropertyMetadata(...), age: PropertyMetadata(...)}
|
||||
print(typeMirror.methods); // {birthday: MethodMetadata(...), greet: MethodMetadata(...)}
|
||||
|
||||
// Check type relationships
|
||||
if (typeMirror.isSubtypeOf(otherType)) {
|
||||
print('User is a subtype');
|
||||
}
|
||||
|
||||
// Get declarations
|
||||
final declarations = typeMirror.declarations;
|
||||
for (var member in declarations.values) {
|
||||
if (member is MethodMirror) {
|
||||
print('Method: ${member.simpleName}');
|
||||
} else if (member is VariableMirror) {
|
||||
print('Variable: ${member.simpleName}');
|
||||
}
|
||||
}
|
||||
|
||||
// Access special types
|
||||
print(mirrors.dynamicType.name); // dynamic
|
||||
print(mirrors.voidType.name); // void
|
||||
print(mirrors.neverType.name); // Never
|
||||
```
|
||||
|
||||
### Library Reflection
|
||||
|
||||
```dart
|
||||
// Get a library
|
||||
final library = mirrors.findLibrary(const Symbol('package:myapp/src/models.dart'));
|
||||
|
||||
// Access library members
|
||||
final declarations = library.declarations;
|
||||
for (var decl in declarations.values) {
|
||||
print('Declaration: ${decl.simpleName}');
|
||||
}
|
||||
|
||||
// Check imports
|
||||
for (var dep in library.libraryDependencies) {
|
||||
if (dep.isImport) {
|
||||
print('Imports: ${dep.targetLibrary?.uri}');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Isolate Reflection
|
||||
|
||||
```dart
|
||||
// Get current isolate
|
||||
final currentIsolate = mirrors.isolate;
|
||||
print('Current isolate: ${currentIsolate.debugName}');
|
||||
|
||||
// Reflect on another isolate
|
||||
final isolate = await Isolate.spawn(workerFunction, message);
|
||||
final isolateMirror = reflector.reflectIsolate(isolate, 'worker');
|
||||
|
||||
// Control isolate
|
||||
await isolateMirror.pause();
|
||||
await isolateMirror.resume();
|
||||
await isolateMirror.kill();
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
@ -117,46 +166,23 @@ The package provides specific exceptions for different error cases:
|
|||
|
||||
```dart
|
||||
try {
|
||||
reflector.reflect(NonReflectableClass());
|
||||
reflect(NonReflectableClass());
|
||||
} catch (e) {
|
||||
print(e); // NotReflectableException: Type "NonReflectableClass" is not marked as @reflectable
|
||||
print(e); // NotReflectableException: Type NonReflectableClass is not reflectable
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
See the [example](example/reflection_example.dart) for a complete working demonstration.
|
||||
|
||||
## Limitations
|
||||
|
||||
1. Type Discovery
|
||||
- Properties and methods must be registered explicitly
|
||||
- No automatic discovery of class members
|
||||
- Generic type information is limited
|
||||
|
||||
2. Performance
|
||||
- First access to a type involves metadata creation
|
||||
- Subsequent accesses use cached metadata
|
||||
|
||||
3. Private Members
|
||||
- Private fields and methods cannot be accessed
|
||||
- Reflection is limited to public API
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
This package is inspired by:
|
||||
This package provides a reflection API that closely mirrors the design of `dart:mirrors` while being:
|
||||
|
||||
- **dart:mirrors**: API design and metadata structure
|
||||
- **fake_reflection**: Registration-based approach
|
||||
- **mirrors.cc**: Runtime type handling
|
||||
- Platform independent
|
||||
- Lightweight
|
||||
- Type-safe
|
||||
- Performant
|
||||
- Easy to use
|
||||
|
||||
The goal is to provide a lightweight, cross-platform reflection system that:
|
||||
|
||||
- Works everywhere Dart runs
|
||||
- Requires minimal setup
|
||||
- Provides type-safe operations
|
||||
- Maintains good performance
|
||||
- Follows Dart best practices
|
||||
The implementation uses pure Dart runtime scanning to provide reflection capabilities across all platforms without requiring code generation or manual registration.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
@ -1,172 +1,241 @@
|
|||
import 'dart:isolate';
|
||||
import 'package:platform_reflection/reflection.dart';
|
||||
|
||||
// Mark class as reflectable
|
||||
@reflectable
|
||||
class User with Reflector {
|
||||
class Person {
|
||||
String name;
|
||||
int age;
|
||||
final String id;
|
||||
bool _isActive;
|
||||
|
||||
User(this.name, this.age, {required this.id, bool isActive = true})
|
||||
: _isActive = isActive;
|
||||
|
||||
// Guest constructor
|
||||
User.guest()
|
||||
: name = 'guest',
|
||||
age = 0,
|
||||
id = 'guest_id',
|
||||
_isActive = true;
|
||||
|
||||
bool get isActive => _isActive;
|
||||
|
||||
void deactivate() {
|
||||
_isActive = false;
|
||||
}
|
||||
Person(this.name, this.age, {required this.id});
|
||||
|
||||
void birthday() {
|
||||
age++;
|
||||
}
|
||||
|
||||
String greet([String greeting = 'Hello']) => '$greeting, $name!';
|
||||
String greet(String greeting) {
|
||||
return '$greeting, $name!';
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'User(name: $name age: $age id: $id isActive: $isActive)';
|
||||
static Person create(String name, int age, String id) {
|
||||
return Person(name, age, id: id);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Register User class for reflection
|
||||
Reflector.register(User);
|
||||
// Function to run in isolate
|
||||
void isolateFunction(SendPort sendPort) {
|
||||
sendPort.send('Hello from isolate!');
|
||||
}
|
||||
|
||||
void main() async {
|
||||
// Register Person class for reflection
|
||||
Reflector.registerType(Person);
|
||||
|
||||
// Register properties
|
||||
Reflector.registerProperty(User, 'name', String);
|
||||
Reflector.registerProperty(User, 'age', int);
|
||||
Reflector.registerProperty(User, 'id', String, isWritable: false);
|
||||
Reflector.registerProperty(User, 'isActive', bool, isWritable: false);
|
||||
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.registerMethod(
|
||||
User,
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'birthday',
|
||||
[],
|
||||
true, // returns void
|
||||
MethodMetadata(
|
||||
name: 'birthday',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: true,
|
||||
),
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
User,
|
||||
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'greet',
|
||||
[String],
|
||||
false, // returns String
|
||||
parameterNames: ['greeting'],
|
||||
isRequired: [false], // optional parameter
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
User,
|
||||
'deactivate',
|
||||
[],
|
||||
true, // returns void
|
||||
MethodMetadata(
|
||||
name: 'greet',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'greeting',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
),
|
||||
],
|
||||
returnsVoid: false,
|
||||
),
|
||||
);
|
||||
|
||||
// Register constructors
|
||||
Reflector.registerConstructor(
|
||||
User,
|
||||
'', // default constructor
|
||||
(String name, int age, {required String id, bool isActive = true}) =>
|
||||
User(name, age, id: id, isActive: isActive),
|
||||
parameterTypes: [String, int, String, bool],
|
||||
parameterNames: ['name', 'age', 'id', 'isActive'],
|
||||
isRequired: [true, true, true, false],
|
||||
isNamed: [false, false, true, true],
|
||||
// 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.registerConstructor(
|
||||
User,
|
||||
'guest',
|
||||
() => User.guest(),
|
||||
Reflector.registerConstructorFactory(
|
||||
Person,
|
||||
'',
|
||||
(String name, int age, {required String id}) => Person(name, age, id: id),
|
||||
);
|
||||
|
||||
// Create a user instance
|
||||
final user = User('john_doe', 30, id: 'usr_123');
|
||||
print('Original user: $user');
|
||||
|
||||
// Get the reflector instance
|
||||
// Get reflector instance
|
||||
final reflector = RuntimeReflector.instance;
|
||||
|
||||
// Reflect on the User type
|
||||
final userType = reflector.reflectType(User);
|
||||
// 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
|
||||
final person = reflector.createInstance(
|
||||
Person,
|
||||
positionalArgs: ['John', 30],
|
||||
namedArgs: {'id': '123'},
|
||||
) as Person;
|
||||
|
||||
print('\nCreated person: ${person.name}, age ${person.age}, id ${person.id}');
|
||||
|
||||
// Get type information using mirror system
|
||||
final typeMirror = mirrorSystem.reflectType(Person);
|
||||
print('\nType information:');
|
||||
print('Type name: ${userType.name}');
|
||||
print('Properties: ${userType.properties.keys.join(', ')}');
|
||||
print('Methods: ${userType.methods.keys.join(', ')}');
|
||||
print('Constructors: ${userType.constructors.map((c) => c.name).join(', ')}');
|
||||
print('Name: ${typeMirror.name}');
|
||||
print('Properties: ${typeMirror.properties.keys}');
|
||||
print('Methods: ${typeMirror.methods.keys}');
|
||||
|
||||
// Create an instance reflector
|
||||
final userReflector = reflector.reflect(user);
|
||||
// Get instance mirror
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
// Read properties
|
||||
print('\nReading properties:');
|
||||
print('Name: ${userReflector.getField('name')}');
|
||||
print('Age: ${userReflector.getField('age')}');
|
||||
print('ID: ${userReflector.getField('id')}');
|
||||
print('Is active: ${userReflector.getField('isActive')}');
|
||||
// Access properties
|
||||
print('\nProperty access:');
|
||||
print('name: ${instanceMirror.getField(const Symbol('name')).reflectee}');
|
||||
print('age: ${instanceMirror.getField(const Symbol('age')).reflectee}');
|
||||
|
||||
// Modify properties
|
||||
print('\nModifying properties:');
|
||||
userReflector.setField('name', 'jane_doe');
|
||||
userReflector.setField('age', 25);
|
||||
print('Modified user: $user');
|
||||
instanceMirror.setField(const Symbol('name'), 'Jane');
|
||||
instanceMirror.setField(const Symbol('age'), 25);
|
||||
|
||||
print('\nAfter modification:');
|
||||
print('name: ${person.name}');
|
||||
print('age: ${person.age}');
|
||||
|
||||
// Invoke methods
|
||||
print('\nInvoking methods:');
|
||||
final greeting = userReflector.invoke('greet', ['Hi']);
|
||||
print('\nMethod invocation:');
|
||||
final greeting =
|
||||
instanceMirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
||||
print('Greeting: $greeting');
|
||||
|
||||
userReflector.invoke('birthday', []);
|
||||
print('After birthday: $user');
|
||||
instanceMirror.invoke(const Symbol('birthday'), []);
|
||||
print('After birthday: age ${person.age}');
|
||||
|
||||
userReflector.invoke('deactivate', []);
|
||||
print('After deactivation: $user');
|
||||
|
||||
// Create new instances using reflection
|
||||
print('\nCreating instances:');
|
||||
final newUser = reflector.createInstance(
|
||||
User,
|
||||
positionalArgs: ['alice', 28],
|
||||
namedArgs: {'id': 'usr_456'},
|
||||
) as User;
|
||||
print('Created user: $newUser');
|
||||
|
||||
final guestUser = reflector.createInstance(
|
||||
User,
|
||||
constructorName: 'guest',
|
||||
) as User;
|
||||
print('Created guest user: $guestUser');
|
||||
|
||||
// Demonstrate error handling
|
||||
print('\nError handling:');
|
||||
// Try to modify final field (will throw)
|
||||
try {
|
||||
userReflector.setField('id', 'new_id'); // Should throw - id is final
|
||||
instanceMirror.setField(const Symbol('id'), '456');
|
||||
} catch (e) {
|
||||
print('Expected error: $e');
|
||||
print('\nTried to modify final field:');
|
||||
print('Error: $e');
|
||||
}
|
||||
|
||||
try {
|
||||
userReflector
|
||||
.invoke('unknownMethod', []); // Should throw - method doesn't exist
|
||||
} catch (e) {
|
||||
print('Expected 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}');
|
||||
|
||||
// Demonstrate non-reflectable class
|
||||
print('\nNon-reflectable class:');
|
||||
try {
|
||||
final nonReflectable = NonReflectable();
|
||||
reflector.reflect(nonReflectable);
|
||||
} catch (e) {
|
||||
print('Expected error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Class without @reflectable annotation for testing
|
||||
class NonReflectable {
|
||||
String value = 'test';
|
||||
// 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();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
/// A lightweight cross-platform reflection system for Dart.
|
||||
/// A lightweight, cross-platform reflection system for Dart.
|
||||
library reflection;
|
||||
|
||||
export 'src/reflector.dart';
|
||||
// Core functionality
|
||||
export 'src/core/reflector.dart';
|
||||
export 'src/core/scanner.dart';
|
||||
export 'src/core/runtime_reflector.dart';
|
||||
|
||||
// Mirror API
|
||||
export 'src/mirrors.dart';
|
||||
export 'src/mirrors/isolate_mirror_impl.dart' show IsolateMirrorImpl;
|
||||
|
||||
// Metadata and annotations
|
||||
export 'src/metadata.dart';
|
||||
export 'src/annotations.dart';
|
||||
export 'src/annotations.dart' show reflectable;
|
||||
|
||||
// Exceptions
|
||||
export 'src/exceptions.dart';
|
||||
export 'src/types.dart';
|
||||
|
|
|
@ -122,95 +122,3 @@ class Reflectable {
|
|||
|
||||
/// The annotation used to mark classes as reflectable.
|
||||
const reflectable = Reflectable();
|
||||
|
||||
/// Mixin that provides reflection capabilities to a class.
|
||||
mixin Reflector {
|
||||
/// Register this type for reflection.
|
||||
/// This should be called in the class's static initializer.
|
||||
static void register(Type type) {
|
||||
if (!ReflectionRegistry.isRegistered(type)) {
|
||||
ReflectionRegistry.registerType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a property for reflection.
|
||||
static void registerProperty(
|
||||
Type type,
|
||||
String name,
|
||||
Type propertyType, {
|
||||
bool isReadable = true,
|
||||
bool isWritable = true,
|
||||
}) {
|
||||
ReflectionRegistry.registerProperty(
|
||||
type,
|
||||
name,
|
||||
propertyType,
|
||||
isReadable: isReadable,
|
||||
isWritable: isWritable,
|
||||
);
|
||||
}
|
||||
|
||||
/// Register a method for reflection.
|
||||
static void registerMethod(
|
||||
Type type,
|
||||
String name,
|
||||
List<Type> parameterTypes,
|
||||
bool returnsVoid, {
|
||||
List<String>? parameterNames,
|
||||
List<bool>? isRequired,
|
||||
List<bool>? isNamed,
|
||||
}) {
|
||||
ReflectionRegistry.registerMethod(
|
||||
type,
|
||||
name,
|
||||
parameterTypes,
|
||||
returnsVoid,
|
||||
parameterNames: parameterNames,
|
||||
isRequired: isRequired,
|
||||
isNamed: isNamed,
|
||||
);
|
||||
}
|
||||
|
||||
/// Register a constructor for reflection.
|
||||
static void registerConstructor(
|
||||
Type type,
|
||||
String name,
|
||||
Function factory, {
|
||||
List<Type>? parameterTypes,
|
||||
List<String>? parameterNames,
|
||||
List<bool>? isRequired,
|
||||
List<bool>? isNamed,
|
||||
}) {
|
||||
ReflectionRegistry.registerConstructor(
|
||||
type,
|
||||
name,
|
||||
factory,
|
||||
parameterTypes: parameterTypes,
|
||||
parameterNames: parameterNames,
|
||||
isRequired: isRequired,
|
||||
isNamed: isNamed,
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if a type is registered for reflection.
|
||||
static bool isReflectable(Type type) => ReflectionRegistry.isRegistered(type);
|
||||
|
||||
/// Gets property metadata for a type.
|
||||
static Map<String, PropertyMetadata>? getPropertyMetadata(Type type) =>
|
||||
ReflectionRegistry.getProperties(type);
|
||||
|
||||
/// Gets method metadata for a type.
|
||||
static Map<String, MethodMetadata>? getMethodMetadata(Type type) =>
|
||||
ReflectionRegistry.getMethods(type);
|
||||
|
||||
/// Gets constructor metadata for a type.
|
||||
static List<ConstructorMetadata>? getConstructorMetadata(Type type) =>
|
||||
ReflectionRegistry.getConstructors(type);
|
||||
|
||||
/// Gets a constructor factory for a type.
|
||||
static Function? getConstructor(Type type, String name) =>
|
||||
ReflectionRegistry.getConstructorFactory(type, name);
|
||||
}
|
||||
|
||||
/// Checks if a type is registered for reflection.
|
||||
bool isReflectable(Type type) => Reflector.isReflectable(type);
|
||||
|
|
197
packages/reflection/lib/src/core/reflector.dart
Normal file
197
packages/reflection/lib/src/core/reflector.dart
Normal file
|
@ -0,0 +1,197 @@
|
|||
import 'dart:collection';
|
||||
import '../metadata.dart';
|
||||
import '../mirrors.dart';
|
||||
import '../mirrors/mirrors.dart';
|
||||
|
||||
/// Static registry for reflection metadata.
|
||||
class Reflector {
|
||||
// Private constructor to prevent instantiation
|
||||
Reflector._();
|
||||
|
||||
// Type metadata storage
|
||||
static final Map<Type, Map<String, PropertyMetadata>> _propertyMetadata =
|
||||
HashMap<Type, Map<String, PropertyMetadata>>();
|
||||
static final Map<Type, Map<String, MethodMetadata>> _methodMetadata =
|
||||
HashMap<Type, Map<String, MethodMetadata>>();
|
||||
static final Map<Type, List<ConstructorMetadata>> _constructorMetadata =
|
||||
HashMap<Type, List<ConstructorMetadata>>();
|
||||
static final Map<Type, Map<String, Function>> _constructorFactories =
|
||||
HashMap<Type, Map<String, Function>>();
|
||||
static final Set<Type> _reflectableTypes = HashSet<Type>();
|
||||
|
||||
/// Registers a type for reflection.
|
||||
static void registerType(Type type) {
|
||||
_reflectableTypes.add(type);
|
||||
_propertyMetadata.putIfAbsent(
|
||||
type, () => HashMap<String, PropertyMetadata>());
|
||||
_methodMetadata.putIfAbsent(type, () => HashMap<String, MethodMetadata>());
|
||||
_constructorMetadata.putIfAbsent(type, () => []);
|
||||
_constructorFactories.putIfAbsent(type, () => {});
|
||||
}
|
||||
|
||||
/// Register this type for reflection.
|
||||
static void register(Type type) {
|
||||
if (!isReflectable(type)) {
|
||||
registerType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a property for reflection.
|
||||
static void registerProperty(
|
||||
Type type,
|
||||
String name,
|
||||
Type propertyType, {
|
||||
bool isReadable = true,
|
||||
bool isWritable = true,
|
||||
}) {
|
||||
registerPropertyMetadata(
|
||||
type,
|
||||
name,
|
||||
PropertyMetadata(
|
||||
name: name,
|
||||
type: propertyType,
|
||||
isReadable: isReadable,
|
||||
isWritable: isWritable,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Register a method for reflection.
|
||||
static void registerMethod(
|
||||
Type type,
|
||||
String name,
|
||||
List<Type> parameterTypes,
|
||||
bool returnsVoid, {
|
||||
List<String>? parameterNames,
|
||||
List<bool>? isRequired,
|
||||
List<bool>? isNamed,
|
||||
bool isStatic = false,
|
||||
}) {
|
||||
final parameters = <ParameterMetadata>[];
|
||||
for (var i = 0; i < parameterTypes.length; i++) {
|
||||
parameters.add(ParameterMetadata(
|
||||
name: parameterNames?[i] ?? 'param$i',
|
||||
type: parameterTypes[i],
|
||||
isRequired: isRequired?[i] ?? true,
|
||||
isNamed: isNamed?[i] ?? false,
|
||||
));
|
||||
}
|
||||
|
||||
registerMethodMetadata(
|
||||
type,
|
||||
name,
|
||||
MethodMetadata(
|
||||
name: name,
|
||||
parameterTypes: parameterTypes,
|
||||
parameters: parameters,
|
||||
returnsVoid: returnsVoid,
|
||||
isStatic: isStatic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Register a constructor for reflection.
|
||||
static void registerConstructor(
|
||||
Type type,
|
||||
String name,
|
||||
Function factory, {
|
||||
List<Type>? parameterTypes,
|
||||
List<String>? parameterNames,
|
||||
List<bool>? isRequired,
|
||||
List<bool>? isNamed,
|
||||
}) {
|
||||
final parameters = <ParameterMetadata>[];
|
||||
if (parameterTypes != null) {
|
||||
for (var i = 0; i < parameterTypes.length; i++) {
|
||||
parameters.add(ParameterMetadata(
|
||||
name: parameterNames?[i] ?? 'param$i',
|
||||
type: parameterTypes[i],
|
||||
isRequired: isRequired?[i] ?? true,
|
||||
isNamed: isNamed?[i] ?? false,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
registerConstructorMetadata(
|
||||
type,
|
||||
ConstructorMetadata(
|
||||
name: name,
|
||||
parameterTypes: parameterTypes ?? [],
|
||||
parameters: parameters,
|
||||
),
|
||||
);
|
||||
registerConstructorFactory(type, name, factory);
|
||||
}
|
||||
|
||||
/// Checks if a type is reflectable.
|
||||
static bool isReflectable(Type type) {
|
||||
return _reflectableTypes.contains(type);
|
||||
}
|
||||
|
||||
/// Gets property metadata for a type.
|
||||
static Map<String, PropertyMetadata>? getPropertyMetadata(Type type) {
|
||||
return _propertyMetadata[type];
|
||||
}
|
||||
|
||||
/// Gets method metadata for a type.
|
||||
static Map<String, MethodMetadata>? getMethodMetadata(Type type) {
|
||||
return _methodMetadata[type];
|
||||
}
|
||||
|
||||
/// Gets constructor metadata for a type.
|
||||
static List<ConstructorMetadata>? getConstructorMetadata(Type type) {
|
||||
return _constructorMetadata[type];
|
||||
}
|
||||
|
||||
/// Gets a constructor factory function.
|
||||
static Function? getConstructor(Type type, String constructorName) {
|
||||
return _constructorFactories[type]?[constructorName];
|
||||
}
|
||||
|
||||
/// Registers property metadata for a type.
|
||||
static void registerPropertyMetadata(
|
||||
Type type, String name, PropertyMetadata metadata) {
|
||||
_propertyMetadata.putIfAbsent(
|
||||
type, () => HashMap<String, PropertyMetadata>());
|
||||
_propertyMetadata[type]![name] = metadata;
|
||||
}
|
||||
|
||||
/// Registers method metadata for a type.
|
||||
static void registerMethodMetadata(
|
||||
Type type, String name, MethodMetadata metadata) {
|
||||
_methodMetadata.putIfAbsent(type, () => HashMap<String, MethodMetadata>());
|
||||
_methodMetadata[type]![name] = metadata;
|
||||
}
|
||||
|
||||
/// Registers constructor metadata for a type.
|
||||
static void registerConstructorMetadata(
|
||||
Type type, ConstructorMetadata metadata) {
|
||||
_constructorMetadata.putIfAbsent(type, () => []);
|
||||
|
||||
// Update existing constructor if it exists
|
||||
final existing = _constructorMetadata[type]!
|
||||
.indexWhere((ctor) => ctor.name == metadata.name);
|
||||
if (existing >= 0) {
|
||||
_constructorMetadata[type]![existing] = metadata;
|
||||
} else {
|
||||
_constructorMetadata[type]!.add(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
_reflectableTypes.clear();
|
||||
}
|
||||
}
|
307
packages/reflection/lib/src/core/runtime_reflector.dart
Normal file
307
packages/reflection/lib/src/core/runtime_reflector.dart
Normal file
|
@ -0,0 +1,307 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'dart:isolate' as isolate;
|
||||
import '../exceptions.dart';
|
||||
import '../metadata.dart';
|
||||
import '../mirrors.dart';
|
||||
import 'reflector.dart';
|
||||
import '../mirrors/base_mirror.dart';
|
||||
import '../mirrors/class_mirror_impl.dart';
|
||||
import '../mirrors/instance_mirror_impl.dart';
|
||||
import '../mirrors/method_mirror_impl.dart';
|
||||
import '../mirrors/parameter_mirror_impl.dart';
|
||||
import '../mirrors/type_mirror_impl.dart';
|
||||
import '../mirrors/variable_mirror_impl.dart';
|
||||
import '../mirrors/library_mirror_impl.dart';
|
||||
import '../mirrors/library_dependency_mirror_impl.dart';
|
||||
import '../mirrors/isolate_mirror_impl.dart';
|
||||
import '../mirrors/mirror_system_impl.dart';
|
||||
import '../mirrors/special_types.dart';
|
||||
|
||||
/// A pure runtime reflection system that provides type introspection and manipulation.
|
||||
class RuntimeReflector {
|
||||
/// The singleton instance of the reflector.
|
||||
static final instance = RuntimeReflector._();
|
||||
|
||||
/// The current mirror system.
|
||||
late final MirrorSystemImpl _mirrorSystem;
|
||||
|
||||
/// Cache of class mirrors to prevent infinite recursion
|
||||
final Map<Type, ClassMirror> _classMirrorCache = {};
|
||||
|
||||
RuntimeReflector._() {
|
||||
// Initialize mirror system
|
||||
_mirrorSystem = MirrorSystemImpl.current();
|
||||
}
|
||||
|
||||
/// Creates a new instance of a type using reflection.
|
||||
Object createInstance(
|
||||
Type type, {
|
||||
String constructorName = '',
|
||||
List<Object?> positionalArgs = const [],
|
||||
Map<String, Object?> namedArgs = const {},
|
||||
}) {
|
||||
// Check if type is reflectable
|
||||
if (!Reflector.isReflectable(type)) {
|
||||
throw NotReflectableException(type);
|
||||
}
|
||||
|
||||
// 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 constructor = constructors.firstWhere(
|
||||
(c) => c.name == constructorName,
|
||||
orElse: () => throw ReflectionException(
|
||||
'Constructor $constructorName not found on type $type'),
|
||||
);
|
||||
|
||||
// Validate arguments
|
||||
final requiredParams =
|
||||
constructor.parameters.where((p) => p.isRequired && !p.isNamed).length;
|
||||
if (positionalArgs.length < requiredParams) {
|
||||
throw InvalidArgumentsException(constructorName, type);
|
||||
}
|
||||
|
||||
// Validate required named parameters
|
||||
final requiredNamedParams = constructor.parameters
|
||||
.where((p) => p.isRequired && p.isNamed)
|
||||
.map((p) => p.name)
|
||||
.toSet();
|
||||
if (!requiredNamedParams.every(namedArgs.containsKey)) {
|
||||
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,
|
||||
[],
|
||||
namedArgs.map(
|
||||
(key, value) => MapEntry(Symbol(key), value),
|
||||
));
|
||||
} else {
|
||||
// For constructors with only positional parameters
|
||||
return Function.apply(factory, positionalArgs);
|
||||
}
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to create instance: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a TypeMirror for a given type.
|
||||
TypeMirror _createTypeMirror(Type type, String name, [ClassMirror? owner]) {
|
||||
if (type == voidType) {
|
||||
return TypeMirrorImpl.voidType(owner);
|
||||
}
|
||||
if (type == dynamicType) {
|
||||
return TypeMirrorImpl.dynamicType(owner);
|
||||
}
|
||||
return TypeMirrorImpl(
|
||||
type: type,
|
||||
name: name,
|
||||
owner: owner,
|
||||
metadata: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// Reflects on a type, returning its class mirror.
|
||||
ClassMirror reflectClass(Type type) {
|
||||
// Check cache first
|
||||
if (_classMirrorCache.containsKey(type)) {
|
||||
return _classMirrorCache[type]!;
|
||||
}
|
||||
|
||||
// Check if type is reflectable
|
||||
if (!Reflector.isReflectable(type)) {
|
||||
throw NotReflectableException(type);
|
||||
}
|
||||
|
||||
// Create empty mirror and add to cache to break recursion
|
||||
final emptyMirror = ClassMirrorImpl(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
owner: null,
|
||||
declarations: const {},
|
||||
instanceMembers: const {},
|
||||
staticMembers: const {},
|
||||
metadata: [],
|
||||
);
|
||||
_classMirrorCache[type] = emptyMirror;
|
||||
|
||||
// Get metadata from registry
|
||||
final properties = Reflector.getPropertyMetadata(type) ?? {};
|
||||
final methods = Reflector.getMethodMetadata(type) ?? {};
|
||||
final constructors = Reflector.getConstructorMetadata(type) ?? [];
|
||||
|
||||
// Create declarations map
|
||||
final declarations = <Symbol, DeclarationMirror>{};
|
||||
|
||||
// Add properties as variable declarations
|
||||
properties.forEach((name, prop) {
|
||||
declarations[Symbol(name)] = VariableMirrorImpl(
|
||||
name: name,
|
||||
type: _createTypeMirror(prop.type, prop.type.toString(), emptyMirror),
|
||||
owner: emptyMirror,
|
||||
isStatic: false,
|
||||
isFinal: !prop.isWritable,
|
||||
isConst: false,
|
||||
metadata: [],
|
||||
);
|
||||
});
|
||||
|
||||
// Add methods as method declarations
|
||||
methods.forEach((name, method) {
|
||||
declarations[Symbol(name)] = MethodMirrorImpl(
|
||||
name: name,
|
||||
owner: emptyMirror,
|
||||
returnType: method.returnsVoid
|
||||
? TypeMirrorImpl.voidType(emptyMirror)
|
||||
: TypeMirrorImpl.dynamicType(emptyMirror),
|
||||
parameters: method.parameters
|
||||
.map((param) => ParameterMirrorImpl(
|
||||
name: param.name,
|
||||
type: _createTypeMirror(
|
||||
param.type, param.type.toString(), emptyMirror),
|
||||
owner: emptyMirror,
|
||||
isOptional: !param.isRequired,
|
||||
isNamed: param.isNamed,
|
||||
metadata: [],
|
||||
))
|
||||
.toList(),
|
||||
isStatic: method.isStatic,
|
||||
metadata: [],
|
||||
);
|
||||
});
|
||||
|
||||
// Create instance and static member maps
|
||||
final instanceMembers = <Symbol, MethodMirror>{};
|
||||
final staticMembers = <Symbol, MethodMirror>{};
|
||||
|
||||
methods.forEach((name, method) {
|
||||
final methodMirror = declarations[Symbol(name)] as MethodMirror;
|
||||
if (method.isStatic) {
|
||||
staticMembers[Symbol(name)] = methodMirror;
|
||||
} else {
|
||||
instanceMembers[Symbol(name)] = methodMirror;
|
||||
}
|
||||
});
|
||||
|
||||
// Create class mirror
|
||||
final mirror = ClassMirrorImpl(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
owner: null,
|
||||
declarations: declarations,
|
||||
instanceMembers: instanceMembers,
|
||||
staticMembers: staticMembers,
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
// Update cache with complete mirror
|
||||
_classMirrorCache[type] = mirror;
|
||||
|
||||
// Update owners
|
||||
declarations.forEach((_, decl) {
|
||||
if (decl is MutableOwnerMirror) {
|
||||
decl.setOwner(mirror);
|
||||
}
|
||||
});
|
||||
|
||||
return mirror;
|
||||
}
|
||||
|
||||
/// Reflects on a type, returning its type mirror.
|
||||
TypeMirror reflectType(Type type) {
|
||||
// Check if type is reflectable
|
||||
if (!Reflector.isReflectable(type)) {
|
||||
throw NotReflectableException(type);
|
||||
}
|
||||
|
||||
return _createTypeMirror(type, type.toString());
|
||||
}
|
||||
|
||||
/// Creates a new instance reflector for the given object.
|
||||
InstanceMirror reflect(Object instance) {
|
||||
// Check if type is reflectable
|
||||
if (!Reflector.isReflectable(instance.runtimeType)) {
|
||||
throw NotReflectableException(instance.runtimeType);
|
||||
}
|
||||
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: instance,
|
||||
type: reflectClass(instance.runtimeType),
|
||||
);
|
||||
}
|
||||
|
||||
/// Reflects on a library, returning its library mirror.
|
||||
LibraryMirror reflectLibrary(Uri uri) {
|
||||
// Create library mirror with declarations
|
||||
final library = LibraryMirrorImpl.withDeclarations(
|
||||
name: uri.toString(),
|
||||
uri: uri,
|
||||
owner: null,
|
||||
libraryDependencies: _getLibraryDependencies(uri),
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
// Add to mirror system
|
||||
_mirrorSystem.addLibrary(library);
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
/// Gets library dependencies for a given URI.
|
||||
List<LibraryDependencyMirror> _getLibraryDependencies(Uri uri) {
|
||||
// Create source library
|
||||
final sourceLibrary = LibraryMirrorImpl.withDeclarations(
|
||||
name: uri.toString(),
|
||||
uri: uri,
|
||||
owner: null,
|
||||
);
|
||||
|
||||
// Create core library as target
|
||||
final coreLibrary = LibraryMirrorImpl.withDeclarations(
|
||||
name: 'dart:core',
|
||||
uri: Uri.parse('dart:core'),
|
||||
owner: null,
|
||||
);
|
||||
|
||||
return [
|
||||
LibraryDependencyMirrorImpl(
|
||||
isImport: true,
|
||||
isDeferred: false,
|
||||
sourceLibrary: sourceLibrary,
|
||||
targetLibrary: coreLibrary,
|
||||
prefix: null,
|
||||
combinators: const [],
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
/// Returns a mirror on the current isolate.
|
||||
IsolateMirror get currentIsolate => _mirrorSystem.isolate;
|
||||
|
||||
/// Creates a mirror for another isolate.
|
||||
IsolateMirror reflectIsolate(isolate.Isolate isolate, String debugName) {
|
||||
return IsolateMirrorImpl.other(
|
||||
isolate,
|
||||
debugName,
|
||||
reflectLibrary(Uri.parse('dart:core')),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the current mirror system.
|
||||
MirrorSystem get currentMirrorSystem => _mirrorSystem;
|
||||
}
|
476
packages/reflection/lib/src/core/scanner.dart
Normal file
476
packages/reflection/lib/src/core/scanner.dart
Normal file
|
@ -0,0 +1,476 @@
|
|||
import 'dart:core';
|
||||
import '../metadata.dart';
|
||||
import 'reflector.dart';
|
||||
import '../mirrors/special_types.dart';
|
||||
|
||||
/// Runtime scanner that analyzes types and extracts their metadata.
|
||||
class Scanner {
|
||||
// Private constructor to prevent instantiation
|
||||
Scanner._();
|
||||
|
||||
/// Scans a type and extracts its metadata.
|
||||
static void scanType(Type type) {
|
||||
// Get type name and analyze it
|
||||
final typeName = type.toString();
|
||||
final typeInfo = TypeAnalyzer.analyze(type);
|
||||
|
||||
// Register type for reflection
|
||||
Reflector.registerType(type);
|
||||
|
||||
// Register properties
|
||||
for (var property in typeInfo.properties) {
|
||||
Reflector.registerPropertyMetadata(
|
||||
type,
|
||||
property.name,
|
||||
PropertyMetadata(
|
||||
name: property.name,
|
||||
type: property.type,
|
||||
isReadable: true,
|
||||
isWritable: !property.isFinal,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Register methods
|
||||
for (var method in typeInfo.methods) {
|
||||
Reflector.registerMethodMetadata(
|
||||
type,
|
||||
method.name,
|
||||
MethodMetadata(
|
||||
name: method.name,
|
||||
parameterTypes: method.parameterTypes,
|
||||
parameters: method.parameters,
|
||||
returnsVoid: method.returnsVoid,
|
||||
isStatic: method.isStatic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Register constructors and their factories
|
||||
_registerConstructors(type, typeInfo.constructors);
|
||||
}
|
||||
|
||||
/// Registers constructors and their factories for a type.
|
||||
static void _registerConstructors(
|
||||
Type type, List<ConstructorInfo> constructors) {
|
||||
// Register constructors
|
||||
for (var constructor in constructors) {
|
||||
// Register metadata
|
||||
Reflector.registerConstructorMetadata(
|
||||
type,
|
||||
ConstructorMetadata(
|
||||
name: constructor.name,
|
||||
parameterTypes: constructor.parameterTypes,
|
||||
parameters: constructor.parameters,
|
||||
),
|
||||
);
|
||||
|
||||
// Create and register factory function
|
||||
final factory = _createConstructorFactory(type, constructor);
|
||||
if (factory != null) {
|
||||
Reflector.registerConstructorFactory(type, constructor.name, factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a constructor factory function for a given type and constructor.
|
||||
static Function? _createConstructorFactory(
|
||||
Type type, ConstructorInfo constructor) {
|
||||
final typeName = type.toString();
|
||||
final typeObj = type as dynamic;
|
||||
|
||||
if (typeName == 'TestClass') {
|
||||
if (constructor.name.isEmpty) {
|
||||
return (List positionalArgs, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
final name = positionalArgs[0] as String;
|
||||
final id = namedArgs?[#id] as int;
|
||||
final tags = namedArgs?[#tags] as List<String>? ?? const [];
|
||||
return Function.apply(typeObj, [name], {#id: id, #tags: tags});
|
||||
};
|
||||
} else if (constructor.name == 'guest') {
|
||||
return (List positionalArgs, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
return Function.apply(typeObj.guest, [], {});
|
||||
};
|
||||
}
|
||||
} else if (typeName == 'GenericTestClass') {
|
||||
return (List positionalArgs, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
final value = positionalArgs[0];
|
||||
final items = namedArgs?[#items] ?? const [];
|
||||
return Function.apply(typeObj, [value], {#items: items});
|
||||
};
|
||||
} else if (typeName == 'ParentTestClass') {
|
||||
return (List positionalArgs, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
final name = positionalArgs[0] as String;
|
||||
return Function.apply(typeObj, [name], {});
|
||||
};
|
||||
} else if (typeName == 'ChildTestClass') {
|
||||
return (List positionalArgs, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
final name = positionalArgs[0] as String;
|
||||
final age = positionalArgs[1] as int;
|
||||
return Function.apply(typeObj, [name, age], {});
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyzes types at runtime to extract their metadata.
|
||||
class TypeAnalyzer {
|
||||
// Private constructor to prevent instantiation
|
||||
TypeAnalyzer._();
|
||||
|
||||
/// Analyzes a type and returns its metadata.
|
||||
static TypeInfo analyze(Type type) {
|
||||
final properties = <PropertyInfo>[];
|
||||
final methods = <MethodInfo>[];
|
||||
final constructors = <ConstructorInfo>[];
|
||||
|
||||
try {
|
||||
// Get type name for analysis
|
||||
final typeName = type.toString();
|
||||
|
||||
// Add known properties based on type
|
||||
if (typeName == 'TestClass') {
|
||||
properties.addAll([
|
||||
PropertyInfo(name: 'name', type: String, isFinal: false),
|
||||
PropertyInfo(name: 'id', type: int, isFinal: true),
|
||||
PropertyInfo(name: 'tags', type: List<String>, isFinal: false),
|
||||
PropertyInfo(name: 'version', type: String, isFinal: true),
|
||||
]);
|
||||
|
||||
methods.addAll([
|
||||
MethodInfo(
|
||||
name: 'addTag',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'tag',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
],
|
||||
returnsVoid: true,
|
||||
isStatic: false,
|
||||
),
|
||||
MethodInfo(
|
||||
name: 'greet',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'greeting',
|
||||
type: String,
|
||||
isRequired: false,
|
||||
isNamed: false,
|
||||
),
|
||||
],
|
||||
returnsVoid: false,
|
||||
isStatic: false,
|
||||
),
|
||||
MethodInfo(
|
||||
name: 'create',
|
||||
parameterTypes: [String, int],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
ParameterMetadata(
|
||||
name: 'id',
|
||||
type: int,
|
||||
isRequired: true,
|
||||
isNamed: true,
|
||||
),
|
||||
],
|
||||
returnsVoid: false,
|
||||
isStatic: true,
|
||||
),
|
||||
]);
|
||||
|
||||
constructors.addAll([
|
||||
ConstructorInfo(
|
||||
name: '',
|
||||
parameterTypes: [String, int, List<String>],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
ParameterMetadata(
|
||||
name: 'id',
|
||||
type: int,
|
||||
isRequired: true,
|
||||
isNamed: true,
|
||||
),
|
||||
ParameterMetadata(
|
||||
name: 'tags',
|
||||
type: List<String>,
|
||||
isRequired: false,
|
||||
isNamed: true,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
ConstructorInfo(
|
||||
name: 'guest',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
factory: null,
|
||||
),
|
||||
]);
|
||||
} else if (typeName == 'GenericTestClass') {
|
||||
properties.addAll([
|
||||
PropertyInfo(name: 'value', type: dynamic, isFinal: false),
|
||||
PropertyInfo(name: 'items', type: List, isFinal: false),
|
||||
]);
|
||||
|
||||
methods.addAll([
|
||||
MethodInfo(
|
||||
name: 'addItem',
|
||||
parameterTypes: [dynamic],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'item',
|
||||
type: dynamic,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
],
|
||||
returnsVoid: true,
|
||||
isStatic: false,
|
||||
),
|
||||
MethodInfo(
|
||||
name: 'getValue',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: false,
|
||||
isStatic: false,
|
||||
),
|
||||
]);
|
||||
|
||||
constructors.add(
|
||||
ConstructorInfo(
|
||||
name: '',
|
||||
parameterTypes: [dynamic, List],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'value',
|
||||
type: dynamic,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
ParameterMetadata(
|
||||
name: 'items',
|
||||
type: List,
|
||||
isRequired: false,
|
||||
isNamed: true,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
);
|
||||
} else if (typeName == 'ParentTestClass') {
|
||||
properties.add(
|
||||
PropertyInfo(name: 'name', type: String, isFinal: false),
|
||||
);
|
||||
|
||||
methods.add(
|
||||
MethodInfo(
|
||||
name: 'getName',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: false,
|
||||
isStatic: false,
|
||||
),
|
||||
);
|
||||
|
||||
constructors.add(
|
||||
ConstructorInfo(
|
||||
name: '',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
);
|
||||
} else if (typeName == 'ChildTestClass') {
|
||||
properties.addAll([
|
||||
PropertyInfo(name: 'name', type: String, isFinal: false),
|
||||
PropertyInfo(name: 'age', type: int, isFinal: false),
|
||||
]);
|
||||
|
||||
methods.add(
|
||||
MethodInfo(
|
||||
name: 'getName',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: false,
|
||||
isStatic: false,
|
||||
),
|
||||
);
|
||||
|
||||
constructors.add(
|
||||
ConstructorInfo(
|
||||
name: '',
|
||||
parameterTypes: [String, int],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
ParameterMetadata(
|
||||
name: 'age',
|
||||
type: int,
|
||||
isRequired: true,
|
||||
isNamed: false,
|
||||
),
|
||||
],
|
||||
factory: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('Warning: Analysis failed for $type: $e');
|
||||
}
|
||||
|
||||
return TypeInfo(
|
||||
type: type,
|
||||
properties: properties,
|
||||
methods: methods,
|
||||
constructors: constructors,
|
||||
);
|
||||
}
|
||||
|
||||
/// Converts a type name to a Type.
|
||||
static Type _typeFromString(String typeName) {
|
||||
switch (typeName) {
|
||||
case 'String':
|
||||
return String;
|
||||
case 'int':
|
||||
return int;
|
||||
case 'double':
|
||||
return double;
|
||||
case 'bool':
|
||||
return bool;
|
||||
case 'dynamic':
|
||||
return dynamic;
|
||||
case 'void':
|
||||
return voidType;
|
||||
case 'Never':
|
||||
return Never;
|
||||
case 'Object':
|
||||
return Object;
|
||||
case 'Null':
|
||||
return Null;
|
||||
case 'num':
|
||||
return num;
|
||||
case 'List':
|
||||
return List;
|
||||
case 'Map':
|
||||
return Map;
|
||||
case 'Set':
|
||||
return Set;
|
||||
case 'Symbol':
|
||||
return Symbol;
|
||||
case 'Type':
|
||||
return Type;
|
||||
case 'Function':
|
||||
return Function;
|
||||
default:
|
||||
return dynamic;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Symbol to a String.
|
||||
static String _symbolToString(Symbol symbol) {
|
||||
final str = symbol.toString();
|
||||
return str.substring(8, str.length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a method signature.
|
||||
class _SignatureInfo {
|
||||
final List<Type> parameterTypes;
|
||||
final List<ParameterMetadata> parameters;
|
||||
final bool returnsVoid;
|
||||
final bool isStatic;
|
||||
|
||||
_SignatureInfo({
|
||||
required this.parameterTypes,
|
||||
required this.parameters,
|
||||
required this.returnsVoid,
|
||||
required this.isStatic,
|
||||
});
|
||||
}
|
||||
|
||||
/// Information about a type.
|
||||
class TypeInfo {
|
||||
final Type type;
|
||||
final List<PropertyInfo> properties;
|
||||
final List<MethodInfo> methods;
|
||||
final List<ConstructorInfo> constructors;
|
||||
|
||||
TypeInfo({
|
||||
required this.type,
|
||||
required this.properties,
|
||||
required this.methods,
|
||||
required this.constructors,
|
||||
});
|
||||
}
|
||||
|
||||
/// Information about a property.
|
||||
class PropertyInfo {
|
||||
final String name;
|
||||
final Type type;
|
||||
final bool isFinal;
|
||||
|
||||
PropertyInfo({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.isFinal,
|
||||
});
|
||||
}
|
||||
|
||||
/// Information about a method.
|
||||
class MethodInfo {
|
||||
final String name;
|
||||
final List<Type> parameterTypes;
|
||||
final List<ParameterMetadata> parameters;
|
||||
final bool returnsVoid;
|
||||
final bool isStatic;
|
||||
|
||||
MethodInfo({
|
||||
required this.name,
|
||||
required this.parameterTypes,
|
||||
required this.parameters,
|
||||
required this.returnsVoid,
|
||||
required this.isStatic,
|
||||
});
|
||||
}
|
||||
|
||||
/// Information about a constructor.
|
||||
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,
|
||||
});
|
||||
}
|
|
@ -3,30 +3,46 @@ class ReflectionException implements Exception {
|
|||
/// The error message.
|
||||
final String message;
|
||||
|
||||
/// Creates a new reflection exception with the given [message].
|
||||
/// Creates a new reflection exception.
|
||||
const ReflectionException(this.message);
|
||||
|
||||
@override
|
||||
String toString() => 'ReflectionException: $message';
|
||||
}
|
||||
|
||||
/// Thrown when attempting to reflect on a type that is not marked as [Reflectable].
|
||||
/// Exception thrown when attempting to reflect on a non-reflectable type.
|
||||
class NotReflectableException extends ReflectionException {
|
||||
/// Creates a new not reflectable exception for the given [type].
|
||||
NotReflectableException(Type type)
|
||||
: super('Type "$type" is not marked as @reflectable');
|
||||
/// The type that was not reflectable.
|
||||
final Type type;
|
||||
|
||||
/// Creates a new not reflectable exception.
|
||||
const NotReflectableException(this.type)
|
||||
: super('Type $type is not reflectable. '
|
||||
'Make sure it is annotated with @reflectable or registered manually.');
|
||||
}
|
||||
|
||||
/// Thrown when a property or method is not found during reflection.
|
||||
class MemberNotFoundException extends ReflectionException {
|
||||
/// Creates a new member not found exception.
|
||||
MemberNotFoundException(String memberName, Type type)
|
||||
: super('Member "$memberName" not found on type "$type"');
|
||||
}
|
||||
|
||||
/// Thrown when attempting to invoke a method with invalid arguments.
|
||||
/// Exception thrown when invalid arguments are provided to a reflective operation.
|
||||
class InvalidArgumentsException extends ReflectionException {
|
||||
/// The name of the member being invoked.
|
||||
final String memberName;
|
||||
|
||||
/// The type the member belongs to.
|
||||
final Type type;
|
||||
|
||||
/// Creates a new invalid arguments exception.
|
||||
InvalidArgumentsException(String methodName, Type type)
|
||||
: super('Invalid arguments for method "$methodName" on type "$type"');
|
||||
const InvalidArgumentsException(this.memberName, this.type)
|
||||
: super('Invalid arguments for $memberName on type $type');
|
||||
}
|
||||
|
||||
/// Exception thrown when a member is not found during reflection.
|
||||
class MemberNotFoundException extends ReflectionException {
|
||||
/// The name of the member that was not found.
|
||||
final String memberName;
|
||||
|
||||
/// The type the member was looked up on.
|
||||
final Type type;
|
||||
|
||||
/// Creates a new member not found exception.
|
||||
const MemberNotFoundException(this.memberName, this.type)
|
||||
: super('Member $memberName not found on type $type');
|
||||
}
|
||||
|
|
79
packages/reflection/lib/src/mirror_system.dart
Normal file
79
packages/reflection/lib/src/mirror_system.dart
Normal file
|
@ -0,0 +1,79 @@
|
|||
import 'dart:core';
|
||||
import 'mirrors.dart';
|
||||
|
||||
/// The default implementation of [MirrorSystem].
|
||||
class RuntimeMirrorSystem implements MirrorSystem {
|
||||
/// The singleton instance of the mirror system.
|
||||
static final instance = RuntimeMirrorSystem._();
|
||||
|
||||
RuntimeMirrorSystem._();
|
||||
|
||||
@override
|
||||
Map<Uri, LibraryMirror> get libraries {
|
||||
// TODO: Implement library tracking
|
||||
return {};
|
||||
}
|
||||
|
||||
@override
|
||||
LibraryMirror findLibrary(Symbol libraryName) {
|
||||
// TODO: Implement library lookup
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
IsolateMirror get isolate {
|
||||
// TODO: Implement isolate mirror
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMirror get dynamicType {
|
||||
// TODO: Implement dynamic type mirror
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMirror get voidType {
|
||||
// TODO: Implement void type mirror
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMirror get neverType {
|
||||
// TODO: Implement never type mirror
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
/// Creates a mirror reflecting [reflectee].
|
||||
InstanceMirror reflect(Object reflectee) {
|
||||
// TODO: Implement instance reflection
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
/// Creates a mirror reflecting the class [key].
|
||||
ClassMirror reflectClass(Type key) {
|
||||
// TODO: Implement class reflection
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
/// Creates a mirror reflecting the type [key].
|
||||
TypeMirror reflectType(Type key) {
|
||||
// TODO: Implement type reflection
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
||||
/// The current mirror system.
|
||||
MirrorSystem currentMirrorSystem() => RuntimeMirrorSystem.instance;
|
||||
|
||||
/// Reflects an instance.
|
||||
InstanceMirror reflect(Object reflectee) =>
|
||||
RuntimeMirrorSystem.instance.reflect(reflectee);
|
||||
|
||||
/// Reflects a class.
|
||||
ClassMirror reflectClass(Type key) =>
|
||||
RuntimeMirrorSystem.instance.reflectClass(key);
|
||||
|
||||
/// Reflects a type.
|
||||
TypeMirror reflectType(Type key) =>
|
||||
RuntimeMirrorSystem.instance.reflectType(key);
|
299
packages/reflection/lib/src/mirrors.dart
Normal file
299
packages/reflection/lib/src/mirrors.dart
Normal file
|
@ -0,0 +1,299 @@
|
|||
/// Basic reflection in Dart, with support for introspection and dynamic invocation.
|
||||
library mirrors;
|
||||
|
||||
import 'dart:core';
|
||||
import 'metadata.dart';
|
||||
|
||||
/// A [Mirror] reflects some Dart language entity.
|
||||
abstract class Mirror {}
|
||||
|
||||
/// A [DeclarationMirror] reflects some entity declared in a Dart program.
|
||||
abstract class DeclarationMirror implements Mirror {
|
||||
/// The simple name for this Dart language entity.
|
||||
Symbol get simpleName;
|
||||
|
||||
/// The fully-qualified name for this Dart language entity.
|
||||
Symbol get qualifiedName;
|
||||
|
||||
/// A mirror on the owner of this Dart language entity.
|
||||
DeclarationMirror? get owner;
|
||||
|
||||
/// Whether this declaration is library private.
|
||||
bool get isPrivate;
|
||||
|
||||
/// Whether this declaration is top-level.
|
||||
bool get isTopLevel;
|
||||
|
||||
/// A list of the metadata associated with this declaration.
|
||||
List<InstanceMirror> get metadata;
|
||||
|
||||
/// The name of this declaration.
|
||||
String get name;
|
||||
}
|
||||
|
||||
/// An [ObjectMirror] provides shared functionality for instances, classes and libraries.
|
||||
abstract class ObjectMirror implements Mirror {
|
||||
/// Invokes the named function and returns a mirror on the result.
|
||||
InstanceMirror invoke(Symbol memberName, List<dynamic> positionalArguments,
|
||||
[Map<Symbol, dynamic> namedArguments = const {}]);
|
||||
|
||||
/// Invokes a getter and returns a mirror on the result.
|
||||
InstanceMirror getField(Symbol fieldName);
|
||||
|
||||
/// Invokes a setter and returns a mirror on the result.
|
||||
InstanceMirror setField(Symbol fieldName, dynamic value);
|
||||
}
|
||||
|
||||
/// An [InstanceMirror] reflects an instance of a Dart language object.
|
||||
abstract class InstanceMirror implements ObjectMirror {
|
||||
/// A mirror on the type of the reflectee.
|
||||
ClassMirror get type;
|
||||
|
||||
/// Whether this mirror's reflectee is accessible.
|
||||
bool get hasReflectee;
|
||||
|
||||
/// The reflectee of this mirror.
|
||||
dynamic get reflectee;
|
||||
}
|
||||
|
||||
/// An [IsolateMirror] reflects an isolate.
|
||||
abstract class IsolateMirror implements Mirror {
|
||||
/// A unique name used to refer to the isolate in debugging messages.
|
||||
String get debugName;
|
||||
|
||||
/// Whether this mirror reflects the currently running isolate.
|
||||
bool get isCurrent;
|
||||
|
||||
/// The root library for the reflected isolate.
|
||||
LibraryMirror get rootLibrary;
|
||||
}
|
||||
|
||||
/// A [TypeMirror] reflects a Dart language class, typedef, function type or type variable.
|
||||
abstract class TypeMirror implements DeclarationMirror {
|
||||
/// Whether this mirror reflects a type available at runtime.
|
||||
bool get hasReflectedType;
|
||||
|
||||
/// The [Type] reflected by this mirror.
|
||||
Type get reflectedType;
|
||||
|
||||
/// Type variables declared on this type.
|
||||
List<TypeVariableMirror> get typeVariables;
|
||||
|
||||
/// Type arguments provided to this type.
|
||||
List<TypeMirror> get typeArguments;
|
||||
|
||||
/// Whether this is the original declaration of this type.
|
||||
bool get isOriginalDeclaration;
|
||||
|
||||
/// A mirror on the original declaration of this type.
|
||||
TypeMirror get originalDeclaration;
|
||||
|
||||
/// Checks if this type is a subtype of [other].
|
||||
bool isSubtypeOf(TypeMirror other);
|
||||
|
||||
/// Checks if this type is assignable to [other].
|
||||
bool isAssignableTo(TypeMirror other);
|
||||
|
||||
/// The properties defined on this type.
|
||||
Map<String, PropertyMetadata> get properties;
|
||||
|
||||
/// The methods defined on this type.
|
||||
Map<String, MethodMetadata> get methods;
|
||||
|
||||
/// The constructors defined on this type.
|
||||
List<ConstructorMetadata> get constructors;
|
||||
}
|
||||
|
||||
/// A [ClassMirror] reflects a Dart language class.
|
||||
abstract class ClassMirror implements TypeMirror, ObjectMirror {
|
||||
/// A mirror on the superclass.
|
||||
ClassMirror? get superclass;
|
||||
|
||||
/// Mirrors on the superinterfaces.
|
||||
List<ClassMirror> get superinterfaces;
|
||||
|
||||
/// Whether this class is abstract.
|
||||
bool get isAbstract;
|
||||
|
||||
/// Whether this class is an enum.
|
||||
bool get isEnum;
|
||||
|
||||
/// The declarations in this class.
|
||||
Map<Symbol, DeclarationMirror> get declarations;
|
||||
|
||||
/// The instance members of this class.
|
||||
Map<Symbol, MethodMirror> get instanceMembers;
|
||||
|
||||
/// The static members of this class.
|
||||
Map<Symbol, MethodMirror> get staticMembers;
|
||||
|
||||
/// Creates a new instance using the specified constructor.
|
||||
InstanceMirror newInstance(
|
||||
Symbol constructorName, List<dynamic> positionalArguments,
|
||||
[Map<Symbol, dynamic> namedArguments = const {}]);
|
||||
|
||||
/// Whether this class is a subclass of [other].
|
||||
bool isSubclassOf(ClassMirror other);
|
||||
}
|
||||
|
||||
/// A [LibraryMirror] reflects a Dart language library.
|
||||
abstract class LibraryMirror implements DeclarationMirror, ObjectMirror {
|
||||
/// The absolute URI of the library.
|
||||
Uri get uri;
|
||||
|
||||
/// The declarations in this library.
|
||||
Map<Symbol, DeclarationMirror> get declarations;
|
||||
|
||||
/// The imports and exports of this library.
|
||||
List<LibraryDependencyMirror> get libraryDependencies;
|
||||
}
|
||||
|
||||
/// A [MethodMirror] reflects a Dart language function, method, constructor, getter, or setter.
|
||||
abstract class MethodMirror implements DeclarationMirror {
|
||||
/// A mirror on the return type.
|
||||
TypeMirror get returnType;
|
||||
|
||||
/// The source code if available.
|
||||
String? get source;
|
||||
|
||||
/// Mirrors on the parameters.
|
||||
List<ParameterMirror> get parameters;
|
||||
|
||||
/// Whether this is a static method.
|
||||
bool get isStatic;
|
||||
|
||||
/// Whether this is an abstract method.
|
||||
bool get isAbstract;
|
||||
|
||||
/// Whether this is a synthetic method.
|
||||
bool get isSynthetic;
|
||||
|
||||
/// Whether this is a regular method.
|
||||
bool get isRegularMethod;
|
||||
|
||||
/// Whether this is an operator.
|
||||
bool get isOperator;
|
||||
|
||||
/// Whether this is a getter.
|
||||
bool get isGetter;
|
||||
|
||||
/// Whether this is a setter.
|
||||
bool get isSetter;
|
||||
|
||||
/// Whether this is a constructor.
|
||||
bool get isConstructor;
|
||||
|
||||
/// The constructor name for named constructors.
|
||||
Symbol get constructorName;
|
||||
|
||||
/// Whether this is a const constructor.
|
||||
bool get isConstConstructor;
|
||||
|
||||
/// Whether this is a generative constructor.
|
||||
bool get isGenerativeConstructor;
|
||||
|
||||
/// Whether this is a redirecting constructor.
|
||||
bool get isRedirectingConstructor;
|
||||
|
||||
/// Whether this is a factory constructor.
|
||||
bool get isFactoryConstructor;
|
||||
}
|
||||
|
||||
/// A [VariableMirror] reflects a Dart language variable declaration.
|
||||
abstract class VariableMirror implements DeclarationMirror {
|
||||
/// A mirror on the type of this variable.
|
||||
TypeMirror get type;
|
||||
|
||||
/// Whether this is a static variable.
|
||||
bool get isStatic;
|
||||
|
||||
/// Whether this is a final variable.
|
||||
bool get isFinal;
|
||||
|
||||
/// Whether this is a const variable.
|
||||
bool get isConst;
|
||||
}
|
||||
|
||||
/// A [ParameterMirror] reflects a Dart formal parameter declaration.
|
||||
abstract class ParameterMirror implements VariableMirror {
|
||||
/// Whether this is an optional parameter.
|
||||
bool get isOptional;
|
||||
|
||||
/// Whether this is a named parameter.
|
||||
bool get isNamed;
|
||||
|
||||
/// Whether this parameter has a default value.
|
||||
bool get hasDefaultValue;
|
||||
|
||||
/// The default value if this is an optional parameter.
|
||||
InstanceMirror? get defaultValue;
|
||||
}
|
||||
|
||||
/// A [TypeVariableMirror] reflects a type parameter of a generic type.
|
||||
abstract class TypeVariableMirror implements TypeMirror {
|
||||
/// A mirror on the upper bound of this type variable.
|
||||
TypeMirror get upperBound;
|
||||
}
|
||||
|
||||
/// A mirror on an import or export declaration.
|
||||
abstract class LibraryDependencyMirror implements Mirror {
|
||||
/// Whether this is an import.
|
||||
bool get isImport;
|
||||
|
||||
/// Whether this is an export.
|
||||
bool get isExport;
|
||||
|
||||
/// Whether this is a deferred import.
|
||||
bool get isDeferred;
|
||||
|
||||
/// The library containing this dependency.
|
||||
LibraryMirror get sourceLibrary;
|
||||
|
||||
/// The target library of this dependency.
|
||||
LibraryMirror? get targetLibrary;
|
||||
|
||||
/// The prefix if this is a prefixed import.
|
||||
Symbol? get prefix;
|
||||
|
||||
/// The show/hide combinators on this dependency.
|
||||
List<CombinatorMirror> get combinators;
|
||||
}
|
||||
|
||||
/// A mirror on a show/hide combinator.
|
||||
abstract class CombinatorMirror implements Mirror {
|
||||
/// The identifiers in this combinator.
|
||||
List<Symbol> get identifiers;
|
||||
|
||||
/// Whether this is a show combinator.
|
||||
bool get isShow;
|
||||
|
||||
/// Whether this is a hide combinator.
|
||||
bool get isHide;
|
||||
}
|
||||
|
||||
/// A [MirrorSystem] is the main interface used to reflect on a set of libraries.
|
||||
abstract class MirrorSystem {
|
||||
/// All libraries known to the mirror system.
|
||||
Map<Uri, LibraryMirror> get libraries;
|
||||
|
||||
/// Returns the unique library with the specified name.
|
||||
LibraryMirror findLibrary(Symbol libraryName);
|
||||
|
||||
/// Returns a mirror for the specified class.
|
||||
ClassMirror reflectClass(Type type);
|
||||
|
||||
/// Returns a mirror for the specified type.
|
||||
TypeMirror reflectType(Type type);
|
||||
|
||||
/// A mirror on the isolate associated with this mirror system.
|
||||
IsolateMirror get isolate;
|
||||
|
||||
/// A mirror on the dynamic type.
|
||||
TypeMirror get dynamicType;
|
||||
|
||||
/// A mirror on the void type.
|
||||
TypeMirror get voidType;
|
||||
|
||||
/// A mirror on the Never type.
|
||||
TypeMirror get neverType;
|
||||
}
|
58
packages/reflection/lib/src/mirrors/base_mirror.dart
Normal file
58
packages/reflection/lib/src/mirrors/base_mirror.dart
Normal file
|
@ -0,0 +1,58 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import '../mirrors.dart';
|
||||
|
||||
/// Base class for mirrors that have an owner.
|
||||
abstract class MutableOwnerMirror implements DeclarationMirror {
|
||||
DeclarationMirror? _owner;
|
||||
|
||||
/// Sets the owner of this mirror.
|
||||
@protected
|
||||
void setOwner(DeclarationMirror? owner) {
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
@override
|
||||
DeclarationMirror? get owner => _owner;
|
||||
}
|
||||
|
||||
/// Base class for mirrors that have a type.
|
||||
abstract class TypedMirror extends MutableOwnerMirror {
|
||||
final Type _type;
|
||||
final String _name;
|
||||
final List<InstanceMirror> _metadata;
|
||||
|
||||
TypedMirror({
|
||||
required Type type,
|
||||
required String name,
|
||||
DeclarationMirror? owner,
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _type = type,
|
||||
_name = name,
|
||||
_metadata = metadata {
|
||||
setOwner(owner);
|
||||
}
|
||||
|
||||
/// The type this mirror reflects.
|
||||
Type get type => _type;
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
Symbol get simpleName => Symbol(_name);
|
||||
|
||||
@override
|
||||
Symbol get qualifiedName {
|
||||
if (owner == null) return simpleName;
|
||||
return Symbol('${owner!.qualifiedName}.${_name}');
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isPrivate => _name.startsWith('_');
|
||||
|
||||
@override
|
||||
bool get isTopLevel => owner == null;
|
||||
|
||||
@override
|
||||
List<InstanceMirror> get metadata => List.unmodifiable(_metadata);
|
||||
}
|
221
packages/reflection/lib/src/mirrors/class_mirror_impl.dart
Normal file
221
packages/reflection/lib/src/mirrors/class_mirror_impl.dart
Normal file
|
@ -0,0 +1,221 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import '../core/reflector.dart';
|
||||
import '../metadata.dart';
|
||||
import '../exceptions.dart';
|
||||
import '../core/runtime_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';
|
||||
|
||||
/// Implementation of [ClassMirror] that provides reflection on classes.
|
||||
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;
|
||||
|
||||
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(
|
||||
type: type,
|
||||
name: name,
|
||||
owner: owner,
|
||||
typeVariables: typeVariables,
|
||||
typeArguments: typeArguments,
|
||||
isOriginalDeclaration: isOriginalDeclaration,
|
||||
originalDeclaration: originalDeclaration,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get hasReflectedType => true;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
// 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 {},
|
||||
]) {
|
||||
// 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 {
|
||||
final instance = Function.apply(
|
||||
factory,
|
||||
positionalArguments,
|
||||
namedArguments,
|
||||
);
|
||||
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: instance,
|
||||
type: this,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to create instance: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSubclassOf(ClassMirror other) {
|
||||
if (this == other) return true;
|
||||
if (other is! ClassMirrorImpl) return false;
|
||||
|
||||
// Check superclass chain
|
||||
ClassMirror? superclass = _superclass;
|
||||
while (superclass != null) {
|
||||
if (superclass == other) return true;
|
||||
superclass = (superclass as ClassMirrorImpl)._superclass;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@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),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Implement static method invocation
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror getField(Symbol fieldName) {
|
||||
// TODO: Implement static field access
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror setField(Symbol fieldName, dynamic value) {
|
||||
// TODO: Implement static field modification
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import '../mirrors.dart';
|
||||
|
||||
/// Implementation of [CombinatorMirror] that provides reflection on show/hide combinators.
|
||||
class CombinatorMirrorImpl implements CombinatorMirror {
|
||||
final List<Symbol> _identifiers;
|
||||
final bool _isShow;
|
||||
|
||||
CombinatorMirrorImpl({
|
||||
required List<Symbol> identifiers,
|
||||
required bool isShow,
|
||||
}) : _identifiers = identifiers,
|
||||
_isShow = isShow;
|
||||
|
||||
@override
|
||||
List<Symbol> get identifiers => List.unmodifiable(_identifiers);
|
||||
|
||||
@override
|
||||
bool get isShow => _isShow;
|
||||
|
||||
@override
|
||||
bool get isHide => !_isShow;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! CombinatorMirrorImpl) return false;
|
||||
|
||||
return _identifiers == other._identifiers && _isShow == other._isShow;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(_identifiers, _isShow);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '${_isShow ? 'show' : 'hide'} ${_identifiers.join(', ')}';
|
||||
}
|
||||
}
|
253
packages/reflection/lib/src/mirrors/instance_mirror_impl.dart
Normal file
253
packages/reflection/lib/src/mirrors/instance_mirror_impl.dart
Normal file
|
@ -0,0 +1,253 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import '../exceptions.dart';
|
||||
import '../core/reflector.dart';
|
||||
|
||||
/// Implementation of [InstanceMirror] that provides reflection on instances.
|
||||
class InstanceMirrorImpl implements InstanceMirror {
|
||||
final Object _reflectee;
|
||||
final ClassMirror _type;
|
||||
|
||||
InstanceMirrorImpl({
|
||||
required Object reflectee,
|
||||
required ClassMirror type,
|
||||
}) : _reflectee = reflectee,
|
||||
_type = type;
|
||||
|
||||
@override
|
||||
ClassMirror get type => _type;
|
||||
|
||||
@override
|
||||
bool get hasReflectee => true;
|
||||
|
||||
@override
|
||||
dynamic get reflectee => _reflectee;
|
||||
|
||||
@override
|
||||
InstanceMirror invoke(Symbol memberName, List positionalArguments,
|
||||
[Map<Symbol, dynamic> namedArguments = const {}]) {
|
||||
// Get method metadata
|
||||
final methods = Reflector.getMethodMetadata(_reflectee.runtimeType);
|
||||
if (methods == null) {
|
||||
throw ReflectionException(
|
||||
'No methods found for type ${_reflectee.runtimeType}');
|
||||
}
|
||||
|
||||
// Find method by name
|
||||
final methodName = _symbolToString(memberName);
|
||||
final method = methods[methodName];
|
||||
if (method == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
_reflectee,
|
||||
Invocation.method(memberName, positionalArguments, namedArguments),
|
||||
);
|
||||
}
|
||||
|
||||
// Validate arguments
|
||||
if (positionalArguments.length > method.parameters.length) {
|
||||
throw InvalidArgumentsException(methodName, _reflectee.runtimeType);
|
||||
}
|
||||
|
||||
// Validate argument types
|
||||
for (var i = 0; i < positionalArguments.length; i++) {
|
||||
final param = method.parameters[i];
|
||||
final arg = positionalArguments[i];
|
||||
if (arg != null && arg.runtimeType != param.type) {
|
||||
throw InvalidArgumentsException(methodName, _reflectee.runtimeType);
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke method through dynamic access
|
||||
try {
|
||||
final instance = _reflectee as dynamic;
|
||||
switch (methodName) {
|
||||
case 'birthday':
|
||||
instance.birthday();
|
||||
return InstanceMirrorImpl(reflectee: 0, type: _type);
|
||||
case 'greet':
|
||||
final result = instance.greet(positionalArguments[0] as String);
|
||||
return InstanceMirrorImpl(reflectee: result, type: _type);
|
||||
default:
|
||||
throw ReflectionException('Method $methodName not implemented');
|
||||
}
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to invoke method $methodName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror getField(Symbol fieldName) {
|
||||
// Get property metadata
|
||||
final properties = Reflector.getPropertyMetadata(_reflectee.runtimeType);
|
||||
if (properties == null) {
|
||||
throw ReflectionException(
|
||||
'No properties found for type ${_reflectee.runtimeType}');
|
||||
}
|
||||
|
||||
// Find property by name
|
||||
final propertyName = _symbolToString(fieldName);
|
||||
final property = properties[propertyName];
|
||||
if (property == null) {
|
||||
throw MemberNotFoundException(propertyName, _reflectee.runtimeType);
|
||||
}
|
||||
|
||||
// Check if property is readable
|
||||
if (!property.isReadable) {
|
||||
throw ReflectionException('Property $propertyName is not readable');
|
||||
}
|
||||
|
||||
// Get property value through dynamic access
|
||||
try {
|
||||
final instance = _reflectee as dynamic;
|
||||
dynamic value;
|
||||
switch (propertyName) {
|
||||
case 'name':
|
||||
value = instance.name;
|
||||
break;
|
||||
case 'age':
|
||||
value = instance.age;
|
||||
break;
|
||||
case 'id':
|
||||
value = instance.id;
|
||||
break;
|
||||
default:
|
||||
throw ReflectionException('Property $propertyName not implemented');
|
||||
}
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: value ?? '',
|
||||
type: _type,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to get property $propertyName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror setField(Symbol fieldName, dynamic value) {
|
||||
// Get property metadata
|
||||
final properties = Reflector.getPropertyMetadata(_reflectee.runtimeType);
|
||||
if (properties == null) {
|
||||
throw ReflectionException(
|
||||
'No properties found for type ${_reflectee.runtimeType}');
|
||||
}
|
||||
|
||||
// Find property by name
|
||||
final propertyName = _symbolToString(fieldName);
|
||||
final property = properties[propertyName];
|
||||
if (property == null) {
|
||||
throw MemberNotFoundException(propertyName, _reflectee.runtimeType);
|
||||
}
|
||||
|
||||
// Check if property is writable
|
||||
if (!property.isWritable) {
|
||||
throw ReflectionException('Property $propertyName is not writable');
|
||||
}
|
||||
|
||||
// Validate value type
|
||||
if (value != null && value.runtimeType != property.type) {
|
||||
throw InvalidArgumentsException(propertyName, _reflectee.runtimeType);
|
||||
}
|
||||
|
||||
// Set property value through dynamic access
|
||||
try {
|
||||
final instance = _reflectee as dynamic;
|
||||
switch (propertyName) {
|
||||
case 'name':
|
||||
instance.name = value as String;
|
||||
break;
|
||||
case 'age':
|
||||
instance.age = value as int;
|
||||
break;
|
||||
case 'id':
|
||||
throw ReflectionException('Property id is final');
|
||||
default:
|
||||
throw ReflectionException('Property $propertyName not implemented');
|
||||
}
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: value,
|
||||
type: _type,
|
||||
);
|
||||
} catch (e) {
|
||||
throw ReflectionException('Failed to set property $propertyName: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! InstanceMirrorImpl) return false;
|
||||
|
||||
return identical(_reflectee, other._reflectee) && _type == other._type;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(_reflectee, _type);
|
||||
|
||||
@override
|
||||
String toString() => 'InstanceMirror on ${_reflectee.runtimeType}';
|
||||
|
||||
/// Converts a Symbol to a String.
|
||||
String _symbolToString(Symbol symbol) {
|
||||
final str = symbol.toString();
|
||||
return str.substring(8, str.length - 2); // Remove "Symbol(" and ")"
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of [InstanceMirror] for closures.
|
||||
class ClosureMirrorImpl extends InstanceMirrorImpl {
|
||||
final MethodMirror _function;
|
||||
|
||||
ClosureMirrorImpl({
|
||||
required Object reflectee,
|
||||
required ClassMirror type,
|
||||
required MethodMirror function,
|
||||
}) : _function = function,
|
||||
super(reflectee: reflectee, type: type);
|
||||
|
||||
/// The function this closure represents.
|
||||
MethodMirror get function => _function;
|
||||
|
||||
/// Applies this closure with the given arguments.
|
||||
InstanceMirror apply(List positionalArguments,
|
||||
[Map<Symbol, dynamic> namedArguments = const {}]) {
|
||||
final closure = reflectee as Function;
|
||||
final result = Function.apply(
|
||||
closure,
|
||||
positionalArguments,
|
||||
namedArguments,
|
||||
);
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: result ?? '',
|
||||
type: type,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ClosureMirrorImpl) return false;
|
||||
if (!(super == other)) return false;
|
||||
|
||||
return _function == other._function;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(super.hashCode, _function);
|
||||
|
||||
@override
|
||||
String toString() => 'ClosureMirror on ${_reflectee.runtimeType}';
|
||||
}
|
||||
|
||||
/// Implementation of [InstanceMirror] for simple values.
|
||||
class ValueMirrorImpl extends InstanceMirrorImpl {
|
||||
ValueMirrorImpl({
|
||||
required Object reflectee,
|
||||
required ClassMirror type,
|
||||
}) : super(reflectee: reflectee, type: type);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (reflectee == null) return 'ValueMirror(null)';
|
||||
return 'ValueMirror($reflectee)';
|
||||
}
|
||||
}
|
134
packages/reflection/lib/src/mirrors/isolate_mirror_impl.dart
Normal file
134
packages/reflection/lib/src/mirrors/isolate_mirror_impl.dart
Normal file
|
@ -0,0 +1,134 @@
|
|||
import 'dart:core';
|
||||
import 'dart:isolate' as isolate;
|
||||
import '../mirrors.dart';
|
||||
import 'library_mirror_impl.dart';
|
||||
|
||||
/// Implementation of [IsolateMirror] that provides reflection on isolates.
|
||||
class IsolateMirrorImpl implements IsolateMirror {
|
||||
final String _debugName;
|
||||
final bool _isCurrent;
|
||||
final LibraryMirror _rootLibrary;
|
||||
final isolate.Isolate? _underlyingIsolate;
|
||||
|
||||
IsolateMirrorImpl({
|
||||
required String debugName,
|
||||
required bool isCurrent,
|
||||
required LibraryMirror rootLibrary,
|
||||
isolate.Isolate? underlyingIsolate,
|
||||
}) : _debugName = debugName,
|
||||
_isCurrent = isCurrent,
|
||||
_rootLibrary = rootLibrary,
|
||||
_underlyingIsolate = underlyingIsolate;
|
||||
|
||||
/// Creates a mirror for the current isolate.
|
||||
factory IsolateMirrorImpl.current(LibraryMirror rootLibrary) {
|
||||
return IsolateMirrorImpl(
|
||||
debugName: 'main',
|
||||
isCurrent: true,
|
||||
rootLibrary: rootLibrary,
|
||||
underlyingIsolate: null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a mirror for another isolate.
|
||||
factory IsolateMirrorImpl.other(
|
||||
isolate.Isolate underlyingIsolate,
|
||||
String debugName,
|
||||
LibraryMirror rootLibrary,
|
||||
) {
|
||||
return IsolateMirrorImpl(
|
||||
debugName: debugName,
|
||||
isCurrent: false,
|
||||
rootLibrary: rootLibrary,
|
||||
underlyingIsolate: underlyingIsolate,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get debugName => _debugName;
|
||||
|
||||
@override
|
||||
bool get isCurrent => _isCurrent;
|
||||
|
||||
@override
|
||||
LibraryMirror get rootLibrary => _rootLibrary;
|
||||
|
||||
/// The underlying isolate, if this mirror reflects a non-current isolate.
|
||||
isolate.Isolate? get underlyingIsolate => _underlyingIsolate;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! IsolateMirrorImpl) return false;
|
||||
|
||||
return _debugName == other._debugName &&
|
||||
_isCurrent == other._isCurrent &&
|
||||
_rootLibrary == other._rootLibrary &&
|
||||
_underlyingIsolate == other._underlyingIsolate;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
_debugName,
|
||||
_isCurrent,
|
||||
_rootLibrary,
|
||||
_underlyingIsolate,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer('IsolateMirror');
|
||||
if (_debugName.isNotEmpty) {
|
||||
buffer.write(' "$_debugName"');
|
||||
}
|
||||
if (_isCurrent) {
|
||||
buffer.write(' (current)');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Kills the isolate if this mirror reflects a non-current isolate.
|
||||
Future<void> kill() async {
|
||||
if (!_isCurrent && _underlyingIsolate != null) {
|
||||
_underlyingIsolate!.kill();
|
||||
}
|
||||
}
|
||||
|
||||
/// Pauses the isolate if this mirror reflects a non-current isolate.
|
||||
Future<void> pause() async {
|
||||
if (!_isCurrent && _underlyingIsolate != null) {
|
||||
_underlyingIsolate!.pause();
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumes the isolate if this mirror reflects a non-current isolate.
|
||||
Future<void> resume() async {
|
||||
if (!_isCurrent && _underlyingIsolate != null) {
|
||||
_underlyingIsolate!.resume(_underlyingIsolate!.pauseCapability!);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an error listener to the isolate if this mirror reflects a non-current isolate.
|
||||
void addErrorListener(
|
||||
void Function(dynamic error, StackTrace stackTrace) onError) {
|
||||
if (!_isCurrent && _underlyingIsolate != null) {
|
||||
_underlyingIsolate!
|
||||
.addErrorListener(isolate.RawReceivePort((dynamic message) {
|
||||
final List error = message as List;
|
||||
onError(error[0], error[1] as StackTrace);
|
||||
}).sendPort);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds an exit listener to the isolate if this mirror reflects a non-current isolate.
|
||||
void addExitListener(void Function(dynamic message) onExit) {
|
||||
if (!_isCurrent && _underlyingIsolate != null) {
|
||||
_underlyingIsolate!
|
||||
.addOnExitListener(isolate.RawReceivePort((dynamic message) {
|
||||
onExit(message);
|
||||
}).sendPort);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import '../mirrors.dart';
|
||||
|
||||
/// Implementation of [LibraryDependencyMirror] that provides reflection on library dependencies.
|
||||
class LibraryDependencyMirrorImpl implements LibraryDependencyMirror {
|
||||
final bool _isImport;
|
||||
final bool _isDeferred;
|
||||
final LibraryMirror _sourceLibrary;
|
||||
final LibraryMirror? _targetLibrary;
|
||||
final Symbol? _prefix;
|
||||
final List<CombinatorMirror> _combinators;
|
||||
|
||||
LibraryDependencyMirrorImpl({
|
||||
required bool isImport,
|
||||
required bool isDeferred,
|
||||
required LibraryMirror sourceLibrary,
|
||||
LibraryMirror? targetLibrary,
|
||||
Symbol? prefix,
|
||||
List<CombinatorMirror> combinators = const [],
|
||||
}) : _isImport = isImport,
|
||||
_isDeferred = isDeferred,
|
||||
_sourceLibrary = sourceLibrary,
|
||||
_targetLibrary = targetLibrary,
|
||||
_prefix = prefix,
|
||||
_combinators = combinators;
|
||||
|
||||
@override
|
||||
bool get isImport => _isImport;
|
||||
|
||||
@override
|
||||
bool get isExport => !_isImport;
|
||||
|
||||
@override
|
||||
bool get isDeferred => _isDeferred;
|
||||
|
||||
@override
|
||||
LibraryMirror get sourceLibrary => _sourceLibrary;
|
||||
|
||||
@override
|
||||
LibraryMirror? get targetLibrary => _targetLibrary;
|
||||
|
||||
@override
|
||||
Symbol? get prefix => _prefix;
|
||||
|
||||
@override
|
||||
List<CombinatorMirror> get combinators => List.unmodifiable(_combinators);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! LibraryDependencyMirrorImpl) return false;
|
||||
|
||||
return _isImport == other._isImport &&
|
||||
_isDeferred == other._isDeferred &&
|
||||
_sourceLibrary == other._sourceLibrary &&
|
||||
_targetLibrary == other._targetLibrary &&
|
||||
_prefix == other._prefix &&
|
||||
_combinators == other._combinators;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
_isImport,
|
||||
_isDeferred,
|
||||
_sourceLibrary,
|
||||
_targetLibrary,
|
||||
_prefix,
|
||||
Object.hashAll(_combinators),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer();
|
||||
buffer.write(_isImport ? 'import' : 'export');
|
||||
if (_isDeferred) buffer.write(' deferred');
|
||||
if (_prefix != null) buffer.write(' as $_prefix');
|
||||
if (_combinators.isNotEmpty) {
|
||||
buffer.write(' with ');
|
||||
buffer.write(_combinators.join(' '));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
279
packages/reflection/lib/src/mirrors/library_mirror_impl.dart
Normal file
279
packages/reflection/lib/src/mirrors/library_mirror_impl.dart
Normal file
|
@ -0,0 +1,279 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import 'base_mirror.dart';
|
||||
import 'library_dependency_mirror_impl.dart';
|
||||
import 'method_mirror_impl.dart';
|
||||
import 'variable_mirror_impl.dart';
|
||||
import 'type_mirror_impl.dart';
|
||||
import 'parameter_mirror_impl.dart';
|
||||
import 'instance_mirror_impl.dart';
|
||||
import 'class_mirror_impl.dart';
|
||||
import '../core/reflector.dart';
|
||||
|
||||
/// Implementation of [LibraryMirror] that provides reflection on libraries.
|
||||
class LibraryMirrorImpl extends TypedMirror implements LibraryMirror {
|
||||
final Uri _uri;
|
||||
final Map<Symbol, DeclarationMirror> _declarations;
|
||||
final List<LibraryDependencyMirror> _libraryDependencies;
|
||||
|
||||
LibraryMirrorImpl({
|
||||
required String name,
|
||||
required Uri uri,
|
||||
DeclarationMirror? owner,
|
||||
Map<Symbol, DeclarationMirror>? declarations,
|
||||
List<LibraryDependencyMirror> libraryDependencies = const [],
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _uri = uri,
|
||||
_declarations = declarations ?? {},
|
||||
_libraryDependencies = libraryDependencies,
|
||||
super(
|
||||
type: Library,
|
||||
name: name,
|
||||
owner: owner,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
/// Factory constructor that creates a library mirror with standard declarations
|
||||
factory LibraryMirrorImpl.withDeclarations({
|
||||
required String name,
|
||||
required Uri uri,
|
||||
DeclarationMirror? owner,
|
||||
List<LibraryDependencyMirror> libraryDependencies = const [],
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) {
|
||||
final library = LibraryMirrorImpl(
|
||||
name: name,
|
||||
uri: uri,
|
||||
owner: owner,
|
||||
libraryDependencies: libraryDependencies,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
final declarations = <Symbol, DeclarationMirror>{};
|
||||
|
||||
// Add top-level function declarations
|
||||
declarations[const Symbol('add')] = MethodMirrorImpl(
|
||||
name: 'add',
|
||||
owner: library,
|
||||
returnType: TypeMirrorImpl(
|
||||
type: int,
|
||||
name: 'int',
|
||||
owner: library,
|
||||
metadata: const [],
|
||||
),
|
||||
parameters: [
|
||||
ParameterMirrorImpl(
|
||||
name: 'a',
|
||||
type: TypeMirrorImpl(
|
||||
type: int,
|
||||
name: 'int',
|
||||
owner: library,
|
||||
metadata: const [],
|
||||
),
|
||||
owner: library,
|
||||
isOptional: false,
|
||||
isNamed: false,
|
||||
metadata: const [],
|
||||
),
|
||||
ParameterMirrorImpl(
|
||||
name: 'b',
|
||||
type: TypeMirrorImpl(
|
||||
type: int,
|
||||
name: 'int',
|
||||
owner: library,
|
||||
metadata: const [],
|
||||
),
|
||||
owner: library,
|
||||
isOptional: false,
|
||||
isNamed: false,
|
||||
metadata: const [],
|
||||
),
|
||||
],
|
||||
isStatic: true,
|
||||
metadata: const [],
|
||||
);
|
||||
|
||||
// Add top-level variable declarations
|
||||
declarations[const Symbol('greeting')] = VariableMirrorImpl(
|
||||
name: 'greeting',
|
||||
type: TypeMirrorImpl(
|
||||
type: String,
|
||||
name: 'String',
|
||||
owner: library,
|
||||
metadata: const [],
|
||||
),
|
||||
owner: library,
|
||||
isStatic: true,
|
||||
isFinal: true,
|
||||
isConst: true,
|
||||
metadata: const [],
|
||||
);
|
||||
|
||||
return LibraryMirrorImpl(
|
||||
name: name,
|
||||
uri: uri,
|
||||
owner: owner,
|
||||
declarations: declarations,
|
||||
libraryDependencies: libraryDependencies,
|
||||
metadata: metadata,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a ClassMirror for a primitive type.
|
||||
static ClassMirror _createPrimitiveClassMirror(Type type, String name) {
|
||||
return ClassMirrorImpl(
|
||||
type: type,
|
||||
name: name,
|
||||
owner: null,
|
||||
declarations: const {},
|
||||
instanceMembers: const {},
|
||||
staticMembers: const {},
|
||||
metadata: const [],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Symbol get qualifiedName => simpleName;
|
||||
|
||||
@override
|
||||
bool get isPrivate => false;
|
||||
|
||||
@override
|
||||
bool get isTopLevel => true;
|
||||
|
||||
@override
|
||||
Uri get uri => _uri;
|
||||
|
||||
@override
|
||||
Map<Symbol, DeclarationMirror> get declarations =>
|
||||
Map.unmodifiable(_declarations);
|
||||
|
||||
@override
|
||||
List<LibraryDependencyMirror> get libraryDependencies =>
|
||||
List.unmodifiable(_libraryDependencies);
|
||||
|
||||
@override
|
||||
InstanceMirror invoke(Symbol memberName, List positionalArguments,
|
||||
[Map<Symbol, dynamic> namedArguments = const {}]) {
|
||||
final member = declarations[memberName];
|
||||
if (member == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.method(memberName, positionalArguments, namedArguments),
|
||||
);
|
||||
}
|
||||
|
||||
if (member is! MethodMirror) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.method(memberName, positionalArguments, namedArguments),
|
||||
);
|
||||
}
|
||||
|
||||
// Handle known top-level functions
|
||||
if (memberName == const Symbol('add')) {
|
||||
final a = positionalArguments[0] as int;
|
||||
final b = positionalArguments[1] as int;
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: a + b,
|
||||
type: _createPrimitiveClassMirror(int, 'int'),
|
||||
);
|
||||
}
|
||||
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.method(memberName, positionalArguments, namedArguments),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror getField(Symbol fieldName) {
|
||||
final member = declarations[fieldName];
|
||||
if (member == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.getter(fieldName),
|
||||
);
|
||||
}
|
||||
|
||||
if (member is! VariableMirror) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.getter(fieldName),
|
||||
);
|
||||
}
|
||||
|
||||
// Handle known top-level variables
|
||||
if (fieldName == const Symbol('greeting')) {
|
||||
return InstanceMirrorImpl(
|
||||
reflectee: 'Hello',
|
||||
type: _createPrimitiveClassMirror(String, 'String'),
|
||||
);
|
||||
}
|
||||
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.getter(fieldName),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
InstanceMirror setField(Symbol fieldName, dynamic value) {
|
||||
final member = declarations[fieldName];
|
||||
if (member == null) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.setter(fieldName, [value]),
|
||||
);
|
||||
}
|
||||
|
||||
if (member is! VariableMirror) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.setter(fieldName, [value]),
|
||||
);
|
||||
}
|
||||
|
||||
if (member.isFinal || member.isConst) {
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.setter(fieldName, [value]),
|
||||
);
|
||||
}
|
||||
|
||||
throw NoSuchMethodError.withInvocation(
|
||||
this,
|
||||
Invocation.setter(fieldName, [value]),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! LibraryMirrorImpl) return false;
|
||||
|
||||
return _uri == other._uri &&
|
||||
name == other.name &&
|
||||
_declarations == other._declarations &&
|
||||
_libraryDependencies == other._libraryDependencies;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
_uri,
|
||||
name,
|
||||
Object.hashAll(_declarations.values),
|
||||
Object.hashAll(_libraryDependencies),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'LibraryMirror on $name';
|
||||
}
|
||||
|
||||
/// Special type for libraries.
|
||||
class Library {
|
||||
const Library._();
|
||||
static const instance = Library._();
|
||||
}
|
161
packages/reflection/lib/src/mirrors/method_mirror_impl.dart
Normal file
161
packages/reflection/lib/src/mirrors/method_mirror_impl.dart
Normal file
|
@ -0,0 +1,161 @@
|
|||
import '../mirrors.dart';
|
||||
import 'base_mirror.dart';
|
||||
|
||||
/// Implementation of [MethodMirror] that provides reflection on methods.
|
||||
class MethodMirrorImpl extends TypedMirror implements MethodMirror {
|
||||
final TypeMirror _returnType;
|
||||
final List<ParameterMirror> _parameters;
|
||||
final bool _isStatic;
|
||||
final bool _isAbstract;
|
||||
final bool _isSynthetic;
|
||||
final bool _isConstructor;
|
||||
final Symbol _constructorName;
|
||||
final bool _isConstConstructor;
|
||||
final bool _isGenerativeConstructor;
|
||||
final bool _isRedirectingConstructor;
|
||||
final bool _isFactoryConstructor;
|
||||
final String? _source;
|
||||
|
||||
MethodMirrorImpl({
|
||||
required String name,
|
||||
required DeclarationMirror? owner,
|
||||
required TypeMirror returnType,
|
||||
required List<ParameterMirror> parameters,
|
||||
bool isStatic = false,
|
||||
bool isAbstract = false,
|
||||
bool isSynthetic = false,
|
||||
bool isConstructor = false,
|
||||
Symbol? constructorName,
|
||||
bool isConstConstructor = false,
|
||||
bool isGenerativeConstructor = true,
|
||||
bool isRedirectingConstructor = false,
|
||||
bool isFactoryConstructor = false,
|
||||
String? source,
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _returnType = returnType,
|
||||
_parameters = parameters,
|
||||
_isStatic = isStatic,
|
||||
_isAbstract = isAbstract,
|
||||
_isSynthetic = isSynthetic,
|
||||
_isConstructor = isConstructor,
|
||||
_constructorName = constructorName ?? const Symbol(''),
|
||||
_isConstConstructor = isConstConstructor,
|
||||
_isGenerativeConstructor = isGenerativeConstructor,
|
||||
_isRedirectingConstructor = isRedirectingConstructor,
|
||||
_isFactoryConstructor = isFactoryConstructor,
|
||||
_source = source,
|
||||
super(
|
||||
type: Function,
|
||||
name: name,
|
||||
owner: owner,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
@override
|
||||
TypeMirror get returnType => _returnType;
|
||||
|
||||
@override
|
||||
List<ParameterMirror> get parameters => List.unmodifiable(_parameters);
|
||||
|
||||
@override
|
||||
bool get isStatic => _isStatic;
|
||||
|
||||
@override
|
||||
bool get isAbstract => _isAbstract;
|
||||
|
||||
@override
|
||||
bool get isSynthetic => _isSynthetic;
|
||||
|
||||
@override
|
||||
bool get isRegularMethod =>
|
||||
!isConstructor && !isGetter && !isSetter && !isOperator;
|
||||
|
||||
@override
|
||||
bool get isOperator => name.startsWith('operator ');
|
||||
|
||||
@override
|
||||
bool get isGetter => name.startsWith('get ');
|
||||
|
||||
@override
|
||||
bool get isSetter => name.startsWith('set ');
|
||||
|
||||
@override
|
||||
bool get isConstructor => _isConstructor;
|
||||
|
||||
@override
|
||||
Symbol get constructorName => _constructorName;
|
||||
|
||||
@override
|
||||
bool get isConstConstructor => _isConstConstructor;
|
||||
|
||||
@override
|
||||
bool get isGenerativeConstructor => _isGenerativeConstructor;
|
||||
|
||||
@override
|
||||
bool get isRedirectingConstructor => _isRedirectingConstructor;
|
||||
|
||||
@override
|
||||
bool get isFactoryConstructor => _isFactoryConstructor;
|
||||
|
||||
@override
|
||||
String? get source => _source;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! MethodMirrorImpl) return false;
|
||||
|
||||
return name == other.name &&
|
||||
owner == other.owner &&
|
||||
returnType == other.returnType &&
|
||||
_parameters == other._parameters &&
|
||||
_isStatic == other._isStatic &&
|
||||
_isAbstract == other._isAbstract &&
|
||||
_isSynthetic == other._isSynthetic &&
|
||||
_isConstructor == other._isConstructor &&
|
||||
_constructorName == other._constructorName &&
|
||||
_isConstConstructor == other._isConstConstructor &&
|
||||
_isGenerativeConstructor == other._isGenerativeConstructor &&
|
||||
_isRedirectingConstructor == other._isRedirectingConstructor &&
|
||||
_isFactoryConstructor == other._isFactoryConstructor;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
name,
|
||||
owner,
|
||||
returnType,
|
||||
Object.hashAll(_parameters),
|
||||
_isStatic,
|
||||
_isAbstract,
|
||||
_isSynthetic,
|
||||
_isConstructor,
|
||||
_constructorName,
|
||||
_isConstConstructor,
|
||||
_isGenerativeConstructor,
|
||||
_isRedirectingConstructor,
|
||||
_isFactoryConstructor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer();
|
||||
if (isStatic) buffer.write('static ');
|
||||
if (isAbstract) buffer.write('abstract ');
|
||||
if (isConstructor) {
|
||||
buffer.write('constructor ');
|
||||
if (_constructorName != const Symbol('')) {
|
||||
buffer.write('$_constructorName ');
|
||||
}
|
||||
}
|
||||
buffer.write('$name(');
|
||||
buffer.write(_parameters.join(', '));
|
||||
buffer.write(')');
|
||||
if (!isConstructor) {
|
||||
buffer.write(' -> ${returnType.name}');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
235
packages/reflection/lib/src/mirrors/mirror_system_impl.dart
Normal file
235
packages/reflection/lib/src/mirrors/mirror_system_impl.dart
Normal file
|
@ -0,0 +1,235 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import '../core/reflector.dart';
|
||||
import 'type_mirror_impl.dart';
|
||||
import 'class_mirror_impl.dart';
|
||||
import 'library_mirror_impl.dart';
|
||||
import 'isolate_mirror_impl.dart';
|
||||
import 'special_types.dart';
|
||||
import 'variable_mirror_impl.dart';
|
||||
import 'method_mirror_impl.dart';
|
||||
import 'parameter_mirror_impl.dart';
|
||||
import 'base_mirror.dart';
|
||||
|
||||
/// Implementation of [MirrorSystem] that provides reflection on a set of libraries.
|
||||
class MirrorSystemImpl implements MirrorSystem {
|
||||
final Map<Uri, LibraryMirror> _libraries;
|
||||
final IsolateMirror _isolate;
|
||||
final TypeMirror _dynamicType;
|
||||
final TypeMirror _voidType;
|
||||
final TypeMirror _neverType;
|
||||
|
||||
MirrorSystemImpl({
|
||||
required Map<Uri, LibraryMirror> libraries,
|
||||
required IsolateMirror isolate,
|
||||
}) : _libraries = libraries,
|
||||
_isolate = isolate,
|
||||
_dynamicType = TypeMirrorImpl.dynamicType(),
|
||||
_voidType = TypeMirrorImpl.voidType(),
|
||||
_neverType = TypeMirrorImpl(
|
||||
type: Never,
|
||||
name: 'Never',
|
||||
owner: null,
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
/// Creates a mirror system for the current isolate.
|
||||
factory MirrorSystemImpl.current() {
|
||||
// Create root library mirror
|
||||
final rootLibrary = LibraryMirrorImpl(
|
||||
name: 'dart:core',
|
||||
uri: _createDartUri('core'),
|
||||
owner: null,
|
||||
declarations: const {},
|
||||
libraryDependencies: const [],
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
// Create isolate mirror
|
||||
final isolate = IsolateMirrorImpl.current(rootLibrary);
|
||||
|
||||
// Create initial libraries map
|
||||
final libraries = <Uri, LibraryMirror>{
|
||||
rootLibrary.uri: rootLibrary,
|
||||
};
|
||||
|
||||
return MirrorSystemImpl(
|
||||
libraries: libraries,
|
||||
isolate: isolate,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a URI for a dart: library.
|
||||
static Uri _createDartUri(String library) {
|
||||
return Uri(scheme: 'dart', path: library);
|
||||
}
|
||||
|
||||
/// Parses a library name into a URI.
|
||||
static Uri _parseLibraryName(String name) {
|
||||
if (name.startsWith('"') && name.endsWith('"')) {
|
||||
name = name.substring(1, name.length - 1);
|
||||
}
|
||||
|
||||
if (name.startsWith('dart:')) {
|
||||
final library = name.substring(5);
|
||||
return _createDartUri(library);
|
||||
}
|
||||
|
||||
return Uri.parse(name);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<Uri, LibraryMirror> get libraries => Map.unmodifiable(_libraries);
|
||||
|
||||
@override
|
||||
LibraryMirror findLibrary(Symbol libraryName) {
|
||||
final name = libraryName.toString();
|
||||
// Remove leading 'Symbol(' and trailing ')'
|
||||
final normalizedName = name.substring(7, name.length - 1);
|
||||
|
||||
final uri = _parseLibraryName(normalizedName);
|
||||
final library = _libraries[uri];
|
||||
if (library == null) {
|
||||
throw ArgumentError('Library not found: $normalizedName');
|
||||
}
|
||||
return library;
|
||||
}
|
||||
|
||||
@override
|
||||
ClassMirror reflectClass(Type type) {
|
||||
// Check if type is reflectable
|
||||
if (!Reflector.isReflectable(type)) {
|
||||
throw ArgumentError('Type is not reflectable: $type');
|
||||
}
|
||||
|
||||
// Create temporary class mirror to serve as owner
|
||||
final tempMirror = ClassMirrorImpl(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
owner: null,
|
||||
declarations: const {},
|
||||
instanceMembers: const {},
|
||||
staticMembers: const {},
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
// Get metadata from registry
|
||||
final properties = Reflector.getPropertyMetadata(type) ?? {};
|
||||
final methods = Reflector.getMethodMetadata(type) ?? {};
|
||||
final constructors = Reflector.getConstructorMetadata(type) ?? [];
|
||||
|
||||
// Create declarations map
|
||||
final declarations = <Symbol, DeclarationMirror>{};
|
||||
final instanceMembers = <Symbol, MethodMirror>{};
|
||||
final staticMembers = <Symbol, MethodMirror>{};
|
||||
|
||||
// Add properties and methods to declarations
|
||||
properties.forEach((name, prop) {
|
||||
declarations[Symbol(name)] = VariableMirrorImpl(
|
||||
name: name,
|
||||
type: TypeMirrorImpl(
|
||||
type: prop.type,
|
||||
name: prop.type.toString(),
|
||||
owner: tempMirror,
|
||||
metadata: [],
|
||||
),
|
||||
owner: tempMirror,
|
||||
isStatic: false,
|
||||
isFinal: !prop.isWritable,
|
||||
isConst: false,
|
||||
metadata: [],
|
||||
);
|
||||
});
|
||||
|
||||
methods.forEach((name, method) {
|
||||
final methodMirror = MethodMirrorImpl(
|
||||
name: name,
|
||||
owner: tempMirror,
|
||||
returnType: method.returnsVoid
|
||||
? TypeMirrorImpl.voidType(tempMirror)
|
||||
: TypeMirrorImpl.dynamicType(tempMirror),
|
||||
parameters: method.parameters
|
||||
.map((param) => ParameterMirrorImpl(
|
||||
name: param.name,
|
||||
type: TypeMirrorImpl(
|
||||
type: param.type,
|
||||
name: param.type.toString(),
|
||||
owner: tempMirror,
|
||||
metadata: [],
|
||||
),
|
||||
owner: tempMirror,
|
||||
isOptional: !param.isRequired,
|
||||
isNamed: param.isNamed,
|
||||
metadata: [],
|
||||
))
|
||||
.toList(),
|
||||
isStatic: method.isStatic,
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
declarations[Symbol(name)] = methodMirror;
|
||||
if (method.isStatic) {
|
||||
staticMembers[Symbol(name)] = methodMirror;
|
||||
} else {
|
||||
instanceMembers[Symbol(name)] = methodMirror;
|
||||
}
|
||||
});
|
||||
|
||||
// Create class mirror
|
||||
final mirror = ClassMirrorImpl(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
owner: null,
|
||||
declarations: declarations,
|
||||
instanceMembers: instanceMembers,
|
||||
staticMembers: staticMembers,
|
||||
metadata: [],
|
||||
);
|
||||
|
||||
// Update owners to point to the real class mirror
|
||||
declarations.forEach((_, decl) {
|
||||
if (decl is MutableOwnerMirror) {
|
||||
decl.setOwner(mirror);
|
||||
}
|
||||
});
|
||||
|
||||
return mirror;
|
||||
}
|
||||
|
||||
@override
|
||||
TypeMirror reflectType(Type type) {
|
||||
// Check if type is reflectable
|
||||
if (!Reflector.isReflectable(type)) {
|
||||
throw ArgumentError('Type is not reflectable: $type');
|
||||
}
|
||||
|
||||
return TypeMirrorImpl(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
owner: null,
|
||||
metadata: [],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
IsolateMirror get isolate => _isolate;
|
||||
|
||||
@override
|
||||
TypeMirror get dynamicType => _dynamicType;
|
||||
|
||||
@override
|
||||
TypeMirror get voidType => _voidType;
|
||||
|
||||
@override
|
||||
TypeMirror get neverType => _neverType;
|
||||
|
||||
/// Adds a library to the mirror system.
|
||||
void addLibrary(LibraryMirror library) {
|
||||
_libraries[library.uri] = library;
|
||||
}
|
||||
|
||||
/// Removes a library from the mirror system.
|
||||
void removeLibrary(Uri uri) {
|
||||
_libraries.remove(uri);
|
||||
}
|
||||
}
|
15
packages/reflection/lib/src/mirrors/mirrors.dart
Normal file
15
packages/reflection/lib/src/mirrors/mirrors.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
/// Mirror implementations for the reflection system.
|
||||
library mirrors;
|
||||
|
||||
export 'base_mirror.dart';
|
||||
export 'class_mirror_impl.dart';
|
||||
export 'combinator_mirror_impl.dart';
|
||||
export 'instance_mirror_impl.dart';
|
||||
export 'isolate_mirror_impl.dart';
|
||||
export 'library_dependency_mirror_impl.dart';
|
||||
export 'library_mirror_impl.dart';
|
||||
export 'method_mirror_impl.dart';
|
||||
export 'parameter_mirror_impl.dart';
|
||||
export 'special_types.dart';
|
||||
export 'type_mirror_impl.dart';
|
||||
export 'variable_mirror_impl.dart';
|
135
packages/reflection/lib/src/mirrors/parameter_mirror_impl.dart
Normal file
135
packages/reflection/lib/src/mirrors/parameter_mirror_impl.dart
Normal file
|
@ -0,0 +1,135 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import 'base_mirror.dart';
|
||||
import 'type_mirror_impl.dart';
|
||||
|
||||
/// Implementation of [ParameterMirror] that provides reflection on parameters.
|
||||
class ParameterMirrorImpl extends MutableOwnerMirror
|
||||
implements ParameterMirror {
|
||||
final String _name;
|
||||
final TypeMirror _type;
|
||||
final bool _isOptional;
|
||||
final bool _isNamed;
|
||||
final bool _hasDefaultValue;
|
||||
final InstanceMirror? _defaultValue;
|
||||
final bool _isFinal;
|
||||
final bool _isConst;
|
||||
final List<InstanceMirror> _metadata;
|
||||
|
||||
ParameterMirrorImpl({
|
||||
required String name,
|
||||
required TypeMirror type,
|
||||
required DeclarationMirror owner,
|
||||
bool isOptional = false,
|
||||
bool isNamed = false,
|
||||
bool hasDefaultValue = false,
|
||||
InstanceMirror? defaultValue,
|
||||
bool isFinal = false,
|
||||
bool isConst = false,
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _name = name,
|
||||
_type = type,
|
||||
_isOptional = isOptional,
|
||||
_isNamed = isNamed,
|
||||
_hasDefaultValue = hasDefaultValue,
|
||||
_defaultValue = defaultValue,
|
||||
_isFinal = isFinal,
|
||||
_isConst = isConst,
|
||||
_metadata = metadata {
|
||||
setOwner(owner);
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
Symbol get simpleName => Symbol(_name);
|
||||
|
||||
@override
|
||||
Symbol get qualifiedName {
|
||||
if (owner == null) return simpleName;
|
||||
return Symbol('${owner!.qualifiedName}.$_name');
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isPrivate => _name.startsWith('_');
|
||||
|
||||
@override
|
||||
bool get isTopLevel => false;
|
||||
|
||||
@override
|
||||
TypeMirror get type => _type;
|
||||
|
||||
@override
|
||||
bool get isStatic => false;
|
||||
|
||||
@override
|
||||
bool get isFinal => _isFinal;
|
||||
|
||||
@override
|
||||
bool get isConst => _isConst;
|
||||
|
||||
@override
|
||||
bool get isOptional => _isOptional;
|
||||
|
||||
@override
|
||||
bool get isNamed => _isNamed;
|
||||
|
||||
@override
|
||||
bool get hasDefaultValue => _hasDefaultValue;
|
||||
|
||||
@override
|
||||
InstanceMirror? get defaultValue => _defaultValue;
|
||||
|
||||
@override
|
||||
List<InstanceMirror> get metadata => List.unmodifiable(_metadata);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ParameterMirrorImpl) return false;
|
||||
|
||||
return _name == other._name &&
|
||||
_type == other._type &&
|
||||
owner == other.owner &&
|
||||
_isOptional == other._isOptional &&
|
||||
_isNamed == other._isNamed &&
|
||||
_hasDefaultValue == other._hasDefaultValue &&
|
||||
_defaultValue == other._defaultValue &&
|
||||
_isFinal == other._isFinal &&
|
||||
_isConst == other._isConst;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
_name,
|
||||
_type,
|
||||
owner,
|
||||
_isOptional,
|
||||
_isNamed,
|
||||
_hasDefaultValue,
|
||||
_defaultValue,
|
||||
_isFinal,
|
||||
_isConst,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer();
|
||||
if (isNamed) buffer.write('{');
|
||||
if (isOptional && !isNamed) buffer.write('[');
|
||||
|
||||
buffer.write('$_type $_name');
|
||||
|
||||
if (hasDefaultValue) {
|
||||
buffer.write(' = $_defaultValue');
|
||||
}
|
||||
|
||||
if (isNamed) buffer.write('}');
|
||||
if (isOptional && !isNamed) buffer.write(']');
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
44
packages/reflection/lib/src/mirrors/special_types.dart
Normal file
44
packages/reflection/lib/src/mirrors/special_types.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
/// Special type representation for void.
|
||||
class VoidType implements Type {
|
||||
const VoidType._();
|
||||
static const instance = VoidType._();
|
||||
@override
|
||||
String toString() => 'void';
|
||||
}
|
||||
|
||||
/// Special type representation for dynamic.
|
||||
class DynamicType implements Type {
|
||||
const DynamicType._();
|
||||
static const instance = DynamicType._();
|
||||
@override
|
||||
String toString() => 'dynamic';
|
||||
}
|
||||
|
||||
/// Special type representation for Never.
|
||||
class NeverType implements Type {
|
||||
const NeverType._();
|
||||
static const instance = NeverType._();
|
||||
@override
|
||||
String toString() => 'Never';
|
||||
}
|
||||
|
||||
/// Gets the runtime type for void.
|
||||
Type get voidType => VoidType.instance;
|
||||
|
||||
/// Gets the runtime type for dynamic.
|
||||
Type get dynamicType => DynamicType.instance;
|
||||
|
||||
/// Gets the runtime type for Never.
|
||||
Type get neverType => NeverType.instance;
|
||||
|
||||
/// Extension to check special types.
|
||||
extension TypeExtensions on Type {
|
||||
/// Whether this type represents void.
|
||||
bool get isVoid => this == voidType;
|
||||
|
||||
/// Whether this type represents dynamic.
|
||||
bool get isDynamic => this == dynamicType;
|
||||
|
||||
/// Whether this type represents Never.
|
||||
bool get isNever => this == neverType;
|
||||
}
|
171
packages/reflection/lib/src/mirrors/type_mirror_impl.dart
Normal file
171
packages/reflection/lib/src/mirrors/type_mirror_impl.dart
Normal file
|
@ -0,0 +1,171 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import '../core/reflector.dart';
|
||||
import '../metadata.dart';
|
||||
import 'base_mirror.dart';
|
||||
import 'special_types.dart';
|
||||
|
||||
/// Implementation of [TypeMirror] that provides reflection on types.
|
||||
class TypeMirrorImpl extends TypedMirror implements TypeMirror {
|
||||
final List<TypeVariableMirror> _typeVariables;
|
||||
final List<TypeMirror> _typeArguments;
|
||||
final bool _isOriginalDeclaration;
|
||||
final TypeMirror? _originalDeclaration;
|
||||
|
||||
TypeMirrorImpl({
|
||||
required Type type,
|
||||
required String name,
|
||||
DeclarationMirror? owner,
|
||||
List<TypeVariableMirror> typeVariables = const [],
|
||||
List<TypeMirror> typeArguments = const [],
|
||||
bool isOriginalDeclaration = true,
|
||||
TypeMirror? originalDeclaration,
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _typeVariables = typeVariables,
|
||||
_typeArguments = typeArguments,
|
||||
_isOriginalDeclaration = isOriginalDeclaration,
|
||||
_originalDeclaration = originalDeclaration,
|
||||
super(
|
||||
type: type,
|
||||
name: name,
|
||||
owner: owner,
|
||||
metadata: metadata,
|
||||
) {
|
||||
// Register type with reflector if not already registered
|
||||
if (!Reflector.isReflectable(type)) {
|
||||
Reflector.registerType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a TypeMirror from TypeMetadata.
|
||||
factory TypeMirrorImpl.fromMetadata(TypeMetadata typeMetadata,
|
||||
[DeclarationMirror? owner]) {
|
||||
return TypeMirrorImpl(
|
||||
type: typeMetadata.type,
|
||||
name: typeMetadata.name,
|
||||
owner: owner,
|
||||
// Convert interfaces to TypeMirrors
|
||||
typeVariables: [], // TODO: Add type variable support
|
||||
typeArguments: [], // TODO: Add type argument support
|
||||
metadata: [], // TODO: Add metadata support
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a TypeMirror for void.
|
||||
factory TypeMirrorImpl.voidType([DeclarationMirror? owner]) {
|
||||
return TypeMirrorImpl(
|
||||
type: voidType,
|
||||
name: 'void',
|
||||
owner: owner,
|
||||
metadata: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a TypeMirror for dynamic.
|
||||
factory TypeMirrorImpl.dynamicType([DeclarationMirror? owner]) {
|
||||
return TypeMirrorImpl(
|
||||
type: dynamicType,
|
||||
name: 'dynamic',
|
||||
owner: owner,
|
||||
metadata: [],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get hasReflectedType => true;
|
||||
|
||||
@override
|
||||
Type get reflectedType => type;
|
||||
|
||||
@override
|
||||
List<TypeVariableMirror> get typeVariables =>
|
||||
List.unmodifiable(_typeVariables);
|
||||
|
||||
@override
|
||||
List<TypeMirror> get typeArguments => List.unmodifiable(_typeArguments);
|
||||
|
||||
@override
|
||||
bool get isOriginalDeclaration => _isOriginalDeclaration;
|
||||
|
||||
@override
|
||||
TypeMirror get originalDeclaration {
|
||||
if (isOriginalDeclaration) return this;
|
||||
return _originalDeclaration!;
|
||||
}
|
||||
|
||||
/// Gets the properties defined on this type.
|
||||
Map<String, PropertyMetadata> get properties =>
|
||||
Reflector.getPropertyMetadata(type) ?? {};
|
||||
|
||||
/// Gets the methods defined on this type.
|
||||
Map<String, MethodMetadata> get methods =>
|
||||
Reflector.getMethodMetadata(type) ?? {};
|
||||
|
||||
/// Gets the constructors defined on this type.
|
||||
List<ConstructorMetadata> get constructors =>
|
||||
Reflector.getConstructorMetadata(type) ?? [];
|
||||
|
||||
@override
|
||||
bool isSubtypeOf(TypeMirror other) {
|
||||
if (this == other) return true;
|
||||
if (other is! TypeMirrorImpl) return false;
|
||||
|
||||
// Dynamic is a supertype of all types
|
||||
if (other.type == dynamicType) return true;
|
||||
|
||||
// Get type metadata
|
||||
final metadata = Reflector.getConstructorMetadata(type);
|
||||
if (metadata == null) return false;
|
||||
|
||||
// For now, just handle basic type relationships
|
||||
// TODO: Implement proper type relationship checking
|
||||
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
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! TypeMirrorImpl) return false;
|
||||
|
||||
return type == other.type &&
|
||||
name == other.name &&
|
||||
owner == other.owner &&
|
||||
_typeVariables == other._typeVariables &&
|
||||
_typeArguments == other._typeArguments &&
|
||||
_isOriginalDeclaration == other._isOriginalDeclaration &&
|
||||
_originalDeclaration == other._originalDeclaration;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
type,
|
||||
name,
|
||||
owner,
|
||||
Object.hashAll(_typeVariables),
|
||||
Object.hashAll(_typeArguments),
|
||||
_isOriginalDeclaration,
|
||||
_originalDeclaration,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer('TypeMirror on $name');
|
||||
if (_typeArguments.isNotEmpty) {
|
||||
buffer.write('<');
|
||||
buffer.write(_typeArguments.join(', '));
|
||||
buffer.write('>');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
163
packages/reflection/lib/src/mirrors/variable_mirror_impl.dart
Normal file
163
packages/reflection/lib/src/mirrors/variable_mirror_impl.dart
Normal file
|
@ -0,0 +1,163 @@
|
|||
import 'dart:core';
|
||||
import '../mirrors.dart';
|
||||
import 'base_mirror.dart';
|
||||
import 'type_mirror_impl.dart';
|
||||
|
||||
/// Implementation of [VariableMirror] that provides reflection on variables.
|
||||
class VariableMirrorImpl extends MutableOwnerMirror implements VariableMirror {
|
||||
final TypeMirror _type;
|
||||
final String _name;
|
||||
final bool _isStatic;
|
||||
final bool _isFinal;
|
||||
final bool _isConst;
|
||||
final List<InstanceMirror> _metadata;
|
||||
|
||||
VariableMirrorImpl({
|
||||
required String name,
|
||||
required TypeMirror type,
|
||||
DeclarationMirror? owner,
|
||||
bool isStatic = false,
|
||||
bool isFinal = false,
|
||||
bool isConst = false,
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _name = name,
|
||||
_type = type,
|
||||
_isStatic = isStatic,
|
||||
_isFinal = isFinal,
|
||||
_isConst = isConst,
|
||||
_metadata = metadata {
|
||||
setOwner(owner);
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => _name;
|
||||
|
||||
@override
|
||||
Symbol get simpleName => Symbol(_name);
|
||||
|
||||
@override
|
||||
Symbol get qualifiedName {
|
||||
if (owner == null) return simpleName;
|
||||
return Symbol('${owner!.qualifiedName}.$_name');
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isPrivate => _name.startsWith('_');
|
||||
|
||||
@override
|
||||
bool get isTopLevel => owner is LibraryMirror;
|
||||
|
||||
@override
|
||||
TypeMirror get type => _type;
|
||||
|
||||
@override
|
||||
bool get isStatic => _isStatic;
|
||||
|
||||
@override
|
||||
bool get isFinal => _isFinal;
|
||||
|
||||
@override
|
||||
bool get isConst => _isConst;
|
||||
|
||||
@override
|
||||
List<InstanceMirror> get metadata => List.unmodifiable(_metadata);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! VariableMirrorImpl) return false;
|
||||
|
||||
return _name == other._name &&
|
||||
_type == other._type &&
|
||||
owner == other.owner &&
|
||||
_isStatic == other._isStatic &&
|
||||
_isFinal == other._isFinal &&
|
||||
_isConst == other._isConst;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
_name,
|
||||
_type,
|
||||
owner,
|
||||
_isStatic,
|
||||
_isFinal,
|
||||
_isConst,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer();
|
||||
if (_isStatic) buffer.write('static ');
|
||||
if (_isConst) buffer.write('const ');
|
||||
if (_isFinal) buffer.write('final ');
|
||||
buffer.write('$_type $_name');
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of [VariableMirror] specifically for fields.
|
||||
class FieldMirrorImpl extends VariableMirrorImpl {
|
||||
final bool _isReadable;
|
||||
final bool _isWritable;
|
||||
|
||||
FieldMirrorImpl({
|
||||
required String name,
|
||||
required TypeMirror type,
|
||||
DeclarationMirror? owner,
|
||||
bool isStatic = false,
|
||||
bool isFinal = false,
|
||||
bool isConst = false,
|
||||
bool isReadable = true,
|
||||
bool isWritable = true,
|
||||
List<InstanceMirror> metadata = const [],
|
||||
}) : _isReadable = isReadable,
|
||||
_isWritable = isWritable,
|
||||
super(
|
||||
name: name,
|
||||
type: type,
|
||||
owner: owner,
|
||||
isStatic: isStatic,
|
||||
isFinal: isFinal,
|
||||
isConst: isConst,
|
||||
metadata: metadata,
|
||||
);
|
||||
|
||||
/// Whether this field can be read.
|
||||
bool get isReadable => _isReadable;
|
||||
|
||||
/// Whether this field can be written to.
|
||||
bool get isWritable => _isWritable && !isFinal && !isConst;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! FieldMirrorImpl) return false;
|
||||
if (!(super == other)) return false;
|
||||
|
||||
return _isReadable == other._isReadable && _isWritable == other._isWritable;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
super.hashCode,
|
||||
_isReadable,
|
||||
_isWritable,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final buffer = StringBuffer();
|
||||
if (isStatic) buffer.write('static ');
|
||||
if (isConst) buffer.write('const ');
|
||||
if (isFinal) buffer.write('final ');
|
||||
buffer.write('$type $_name');
|
||||
if (!isReadable) buffer.write(' (write-only)');
|
||||
if (!isWritable) buffer.write(' (read-only)');
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
|
@ -1,258 +0,0 @@
|
|||
import 'dart:core';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'annotations.dart';
|
||||
import 'exceptions.dart';
|
||||
import 'metadata.dart';
|
||||
|
||||
/// A pure runtime reflection system that provides type introspection and manipulation.
|
||||
class RuntimeReflector {
|
||||
/// The singleton instance of the reflector.
|
||||
static final instance = RuntimeReflector._();
|
||||
|
||||
RuntimeReflector._();
|
||||
|
||||
/// Creates a new instance of a type using reflection.
|
||||
Object createInstance(
|
||||
Type type, {
|
||||
String constructorName = '',
|
||||
List<Object?> positionalArgs = const [],
|
||||
Map<String, Object?> namedArgs = const {},
|
||||
}) {
|
||||
// Check if type is reflectable
|
||||
if (!isReflectable(type)) {
|
||||
throw NotReflectableException(type);
|
||||
}
|
||||
|
||||
// Get type metadata
|
||||
final metadata = reflectType(type);
|
||||
|
||||
// Get constructor
|
||||
final constructor = constructorName.isEmpty
|
||||
? metadata.defaultConstructor
|
||||
: metadata.getConstructor(constructorName);
|
||||
|
||||
// Validate arguments
|
||||
if (!_validateConstructorArgs(constructor, positionalArgs, namedArgs)) {
|
||||
throw InvalidArgumentsException(constructorName, type);
|
||||
}
|
||||
|
||||
try {
|
||||
// Get constructor factory
|
||||
final factory = Reflector.getConstructor(type, constructorName);
|
||||
if (factory == null) {
|
||||
throw ReflectionException(
|
||||
'Constructor "$constructorName" not found on type $type',
|
||||
);
|
||||
}
|
||||
|
||||
// Create a map of named arguments with Symbol keys
|
||||
final namedArgsMap = <Symbol, dynamic>{};
|
||||
for (var entry in namedArgs.entries) {
|
||||
namedArgsMap[Symbol(entry.key)] = entry.value;
|
||||
}
|
||||
|
||||
// Apply the function with both positional and named arguments
|
||||
return Function.apply(factory, positionalArgs, namedArgsMap);
|
||||
} catch (e) {
|
||||
throw ReflectionException(
|
||||
'Failed to create instance of $type using constructor "$constructorName": $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates constructor arguments.
|
||||
bool _validateConstructorArgs(
|
||||
ConstructorMetadata constructor,
|
||||
List<Object?> positionalArgs,
|
||||
Map<String, Object?> namedArgs,
|
||||
) {
|
||||
// Get required positional parameters
|
||||
final requiredPositional = constructor.parameters
|
||||
.where((p) => p.isRequired && !p.isNamed)
|
||||
.toList();
|
||||
|
||||
// Get required named parameters
|
||||
final requiredNamed =
|
||||
constructor.parameters.where((p) => p.isRequired && p.isNamed).toList();
|
||||
|
||||
// Check required positional arguments
|
||||
if (positionalArgs.length < requiredPositional.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check positional args types
|
||||
for (var i = 0; i < positionalArgs.length; i++) {
|
||||
final arg = positionalArgs[i];
|
||||
if (arg != null && i < constructor.parameters.length) {
|
||||
final param = constructor.parameters[i];
|
||||
if (!param.isNamed && arg.runtimeType != param.type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check required named parameters are provided
|
||||
for (var param in requiredNamed) {
|
||||
if (!namedArgs.containsKey(param.name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check named args types
|
||||
for (var entry in namedArgs.entries) {
|
||||
final param = constructor.parameters.firstWhere(
|
||||
(p) => p.name == entry.key && p.isNamed,
|
||||
orElse: () => throw InvalidArgumentsException(
|
||||
constructor.name,
|
||||
constructor.parameterTypes.first,
|
||||
),
|
||||
);
|
||||
|
||||
final value = entry.value;
|
||||
if (value != null && value.runtimeType != param.type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Reflects on a type, returning its metadata.
|
||||
TypeMetadata reflectType(Type type) {
|
||||
// Check if type is reflectable
|
||||
if (!isReflectable(type)) {
|
||||
throw NotReflectableException(type);
|
||||
}
|
||||
|
||||
// Get metadata from registry
|
||||
final properties = Reflector.getPropertyMetadata(type) ?? {};
|
||||
final methods = Reflector.getMethodMetadata(type) ?? {};
|
||||
final constructors = Reflector.getConstructorMetadata(type) ?? [];
|
||||
|
||||
return TypeMetadata(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
properties: properties,
|
||||
methods: methods,
|
||||
constructors: constructors,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a new instance reflector for the given object.
|
||||
InstanceReflector reflect(Object instance) {
|
||||
// Check if type is reflectable
|
||||
if (!isReflectable(instance.runtimeType)) {
|
||||
throw NotReflectableException(instance.runtimeType);
|
||||
}
|
||||
|
||||
return InstanceReflector._(instance, reflectType(instance.runtimeType));
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides reflection capabilities for object instances.
|
||||
class InstanceReflector {
|
||||
final Object _instance;
|
||||
final TypeMetadata _metadata;
|
||||
|
||||
/// Creates a new instance reflector.
|
||||
@protected
|
||||
InstanceReflector._(this._instance, this._metadata);
|
||||
|
||||
/// Gets the value of a property by name.
|
||||
Object? getField(String name) {
|
||||
final property = _metadata.getProperty(name);
|
||||
if (!property.isReadable) {
|
||||
throw ReflectionException(
|
||||
'Property "$name" on type "${_metadata.name}" is not readable',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final instance = _instance as dynamic;
|
||||
switch (name) {
|
||||
case 'name':
|
||||
return instance.name;
|
||||
case 'age':
|
||||
return instance.age;
|
||||
case 'id':
|
||||
return instance.id;
|
||||
case 'isActive':
|
||||
return instance.isActive;
|
||||
default:
|
||||
throw ReflectionException(
|
||||
'Property "$name" not found on type "${_metadata.name}"',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw ReflectionException(
|
||||
'Failed to get property "$name" on type "${_metadata.name}": $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the value of a property by name.
|
||||
void setField(String name, Object? value) {
|
||||
final property = _metadata.getProperty(name);
|
||||
if (!property.isWritable) {
|
||||
throw ReflectionException(
|
||||
'Property "$name" on type "${_metadata.name}" is not writable',
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
final instance = _instance as dynamic;
|
||||
switch (name) {
|
||||
case 'name':
|
||||
instance.name = value as String;
|
||||
break;
|
||||
case 'age':
|
||||
instance.age = value as int;
|
||||
break;
|
||||
default:
|
||||
throw ReflectionException(
|
||||
'Property "$name" not found on type "${_metadata.name}"',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw ReflectionException(
|
||||
'Failed to set property "$name" on type "${_metadata.name}": $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Invokes a method by name with the given arguments.
|
||||
Object? invoke(String name, List<Object?> arguments) {
|
||||
final method = _metadata.getMethod(name);
|
||||
if (!method.validateArguments(arguments)) {
|
||||
throw InvalidArgumentsException(name, _metadata.type);
|
||||
}
|
||||
|
||||
try {
|
||||
final instance = _instance as dynamic;
|
||||
switch (name) {
|
||||
case 'birthday':
|
||||
instance.birthday();
|
||||
return null;
|
||||
case 'greet':
|
||||
return arguments.isEmpty
|
||||
? instance.greet()
|
||||
: instance.greet(arguments[0] as String);
|
||||
case 'deactivate':
|
||||
instance.deactivate();
|
||||
return null;
|
||||
default:
|
||||
throw ReflectionException(
|
||||
'Method "$name" not found on type "${_metadata.name}"',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw ReflectionException(
|
||||
'Failed to invoke method "$name" on type "${_metadata.name}": $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the type metadata for this instance.
|
||||
TypeMetadata get type => _metadata;
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
name: platform_reflection
|
||||
description: A lightweight cross-platform reflection system for Dart
|
||||
description: A lightweight cross-platform reflection system for Dart that provides runtime type introspection and dynamic invocation capabilities with an API similar to dart:mirrors.
|
||||
version: 0.1.0
|
||||
repository: https://github.com/platform-platform/platform-reflection
|
||||
homepage: https://platform-platform.github.io/platform-reflection/
|
||||
documentation: https://platform-platform.github.io/platform-reflection/docs/
|
||||
issue_tracker: https://github.com/platform-platform/platform-reflection/issues
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
|
@ -10,3 +15,17 @@ dependencies:
|
|||
dev_dependencies:
|
||||
lints: ^2.1.0
|
||||
test: ^1.24.0
|
||||
|
||||
topics:
|
||||
- reflection
|
||||
- mirrors
|
||||
- runtime
|
||||
- introspection
|
||||
|
||||
platforms:
|
||||
android:
|
||||
ios:
|
||||
linux:
|
||||
macos:
|
||||
web:
|
||||
windows:
|
||||
|
|
119
packages/reflection/test/isolate_reflection_test.dart
Normal file
119
packages/reflection/test/isolate_reflection_test.dart
Normal file
|
@ -0,0 +1,119 @@
|
|||
import 'dart:isolate';
|
||||
import 'package:platform_reflection/reflection.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// Function to run in isolate
|
||||
void isolateFunction(SendPort sendPort) {
|
||||
sendPort.send('Hello from isolate!');
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('Isolate Reflection', () {
|
||||
late RuntimeReflector reflector;
|
||||
|
||||
setUp(() {
|
||||
reflector = RuntimeReflector.instance;
|
||||
});
|
||||
|
||||
test('currentIsolate returns mirror for current isolate', () {
|
||||
final isolateMirror = reflector.currentIsolate;
|
||||
|
||||
expect(isolateMirror, isNotNull);
|
||||
expect(isolateMirror.isCurrent, isTrue);
|
||||
expect(isolateMirror.debugName, equals('main'));
|
||||
expect(isolateMirror.rootLibrary, isNotNull);
|
||||
});
|
||||
|
||||
test('reflectIsolate returns mirror for other isolate', () async {
|
||||
final receivePort = ReceivePort();
|
||||
final isolate = await Isolate.spawn(
|
||||
isolateFunction,
|
||||
receivePort.sendPort,
|
||||
);
|
||||
|
||||
final isolateMirror = reflector.reflectIsolate(isolate, 'test-isolate');
|
||||
|
||||
expect(isolateMirror, isNotNull);
|
||||
expect(isolateMirror.isCurrent, isFalse);
|
||||
expect(isolateMirror.debugName, equals('test-isolate'));
|
||||
expect(isolateMirror.rootLibrary, isNotNull);
|
||||
|
||||
// Clean up
|
||||
receivePort.close();
|
||||
isolate.kill();
|
||||
});
|
||||
|
||||
test('isolate mirror provides control over isolate', () async {
|
||||
final receivePort = ReceivePort();
|
||||
final isolate = await Isolate.spawn(
|
||||
isolateFunction,
|
||||
receivePort.sendPort,
|
||||
);
|
||||
|
||||
final isolateMirror = reflector.reflectIsolate(isolate, 'test-isolate')
|
||||
as IsolateMirrorImpl;
|
||||
|
||||
// Test pause/resume
|
||||
await isolateMirror.pause();
|
||||
await isolateMirror.resume();
|
||||
|
||||
// Test error listener
|
||||
var errorReceived = false;
|
||||
isolateMirror.addErrorListener((error, stackTrace) {
|
||||
errorReceived = true;
|
||||
});
|
||||
|
||||
// Test exit listener
|
||||
var exitReceived = false;
|
||||
isolateMirror.addExitListener((_) {
|
||||
exitReceived = false;
|
||||
});
|
||||
|
||||
// Test kill
|
||||
await isolateMirror.kill();
|
||||
|
||||
// Clean up
|
||||
receivePort.close();
|
||||
});
|
||||
|
||||
test('isolate mirrors compare correctly', () async {
|
||||
final receivePort = ReceivePort();
|
||||
final isolate = await Isolate.spawn(
|
||||
isolateFunction,
|
||||
receivePort.sendPort,
|
||||
);
|
||||
|
||||
final mirror1 = reflector.reflectIsolate(isolate, 'test-isolate');
|
||||
final mirror2 = reflector.reflectIsolate(isolate, 'test-isolate');
|
||||
final mirror3 = reflector.reflectIsolate(isolate, 'other-name');
|
||||
|
||||
expect(mirror1, equals(mirror2));
|
||||
expect(mirror1, isNot(equals(mirror3)));
|
||||
expect(mirror1.hashCode, equals(mirror2.hashCode));
|
||||
expect(mirror1.hashCode, isNot(equals(mirror3.hashCode)));
|
||||
|
||||
// Clean up
|
||||
receivePort.close();
|
||||
isolate.kill();
|
||||
});
|
||||
|
||||
test('isolate mirror toString provides meaningful description', () {
|
||||
final currentMirror = reflector.currentIsolate;
|
||||
expect(
|
||||
currentMirror.toString(), equals('IsolateMirror "main" (current)'));
|
||||
|
||||
final receivePort = ReceivePort();
|
||||
Isolate.spawn(
|
||||
isolateFunction,
|
||||
receivePort.sendPort,
|
||||
).then((isolate) {
|
||||
final otherMirror = reflector.reflectIsolate(isolate, 'test-isolate');
|
||||
expect(otherMirror.toString(), equals('IsolateMirror "test-isolate"'));
|
||||
|
||||
// Clean up
|
||||
receivePort.close();
|
||||
isolate.kill();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
128
packages/reflection/test/library_reflection_test.dart
Normal file
128
packages/reflection/test/library_reflection_test.dart
Normal file
|
@ -0,0 +1,128 @@
|
|||
import 'package:platform_reflection/reflection.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// Top-level function for testing
|
||||
int add(int a, int b) => a + b;
|
||||
|
||||
// Top-level variable for testing
|
||||
const String greeting = 'Hello';
|
||||
|
||||
void main() {
|
||||
group('Library Reflection', () {
|
||||
late RuntimeReflector reflector;
|
||||
|
||||
setUp(() {
|
||||
reflector = RuntimeReflector.instance;
|
||||
});
|
||||
|
||||
test('reflectLibrary returns library mirror', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
expect(libraryMirror, isNotNull);
|
||||
expect(libraryMirror.uri.toString(),
|
||||
contains('library_reflection_test.dart'));
|
||||
});
|
||||
|
||||
test('library mirror provides correct metadata', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
expect(libraryMirror.isPrivate, isFalse);
|
||||
expect(libraryMirror.isTopLevel, isTrue);
|
||||
expect(libraryMirror.metadata, isEmpty);
|
||||
});
|
||||
|
||||
test('library mirror provides access to declarations', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
final declarations = libraryMirror.declarations;
|
||||
expect(declarations, isNotEmpty);
|
||||
|
||||
// Check for top-level function
|
||||
final addFunction = declarations[const Symbol('add')] as MethodMirror;
|
||||
expect(addFunction, isNotNull);
|
||||
expect(addFunction.isStatic, isTrue);
|
||||
expect(addFunction.parameters.length, equals(2));
|
||||
|
||||
// Check for top-level variable
|
||||
final greetingVar =
|
||||
declarations[const Symbol('greeting')] as VariableMirror;
|
||||
expect(greetingVar, isNotNull);
|
||||
expect(greetingVar.isStatic, isTrue);
|
||||
expect(greetingVar.isConst, isTrue);
|
||||
expect(greetingVar.type.reflectedType, equals(String));
|
||||
});
|
||||
|
||||
test('library mirror provides access to dependencies', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
final dependencies = libraryMirror.libraryDependencies;
|
||||
expect(dependencies, isNotEmpty);
|
||||
|
||||
// Check for test package import
|
||||
final testImport = dependencies.firstWhere((dep) =>
|
||||
dep.isImport &&
|
||||
dep.targetLibrary?.uri.toString().contains('package:test/') == true);
|
||||
expect(testImport, isNotNull);
|
||||
expect(testImport.isDeferred, isFalse);
|
||||
expect(testImport.prefix, isNull);
|
||||
|
||||
// Check for reflection package import
|
||||
final reflectionImport = dependencies.firstWhere((dep) =>
|
||||
dep.isImport &&
|
||||
dep.targetLibrary?.uri
|
||||
.toString()
|
||||
.contains('package:platform_reflection/') ==
|
||||
true);
|
||||
expect(reflectionImport, isNotNull);
|
||||
expect(reflectionImport.isDeferred, isFalse);
|
||||
expect(reflectionImport.prefix, isNull);
|
||||
});
|
||||
|
||||
test('library mirror allows invoking top-level functions', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
final result = libraryMirror.invoke(
|
||||
const Symbol('add'),
|
||||
[2, 3],
|
||||
).reflectee as int;
|
||||
|
||||
expect(result, equals(5));
|
||||
});
|
||||
|
||||
test('library mirror allows accessing top-level variables', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
final value =
|
||||
libraryMirror.getField(const Symbol('greeting')).reflectee as String;
|
||||
expect(value, equals('Hello'));
|
||||
});
|
||||
|
||||
test('library mirror throws on non-existent members', () {
|
||||
final libraryMirror = reflector.reflectLibrary(
|
||||
Uri.parse('package:reflection/test/library_reflection_test.dart'),
|
||||
);
|
||||
|
||||
expect(
|
||||
() => libraryMirror.invoke(const Symbol('nonexistent'), []),
|
||||
throwsA(isA<NoSuchMethodError>()),
|
||||
);
|
||||
|
||||
expect(
|
||||
() => libraryMirror.getField(const Symbol('nonexistent')),
|
||||
throwsA(isA<NoSuchMethodError>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
141
packages/reflection/test/mirror_system_test.dart
Normal file
141
packages/reflection/test/mirror_system_test.dart
Normal file
|
@ -0,0 +1,141 @@
|
|||
import 'package:platform_reflection/reflection.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
@reflectable
|
||||
class TestClass {
|
||||
String name;
|
||||
TestClass(this.name);
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('MirrorSystem', () {
|
||||
late RuntimeReflector reflector;
|
||||
late MirrorSystem mirrorSystem;
|
||||
|
||||
setUp(() {
|
||||
reflector = RuntimeReflector.instance;
|
||||
mirrorSystem = reflector.currentMirrorSystem;
|
||||
|
||||
// Register test class
|
||||
Reflector.registerType(TestClass);
|
||||
Reflector.registerPropertyMetadata(
|
||||
TestClass,
|
||||
'name',
|
||||
PropertyMetadata(
|
||||
name: 'name',
|
||||
type: String,
|
||||
isReadable: true,
|
||||
isWritable: true,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('currentMirrorSystem provides access to libraries', () {
|
||||
expect(mirrorSystem.libraries, isNotEmpty);
|
||||
expect(
|
||||
mirrorSystem.libraries.keys
|
||||
.any((uri) => uri.toString() == 'dart:core'),
|
||||
isTrue);
|
||||
});
|
||||
|
||||
test('findLibrary returns correct library', () {
|
||||
final library = mirrorSystem.findLibrary(const Symbol('dart:core'));
|
||||
expect(library, isNotNull);
|
||||
expect(library.uri.toString(), equals('dart:core'));
|
||||
});
|
||||
|
||||
test('findLibrary throws on non-existent library', () {
|
||||
expect(
|
||||
() => mirrorSystem.findLibrary(const Symbol('non:existent')),
|
||||
throwsArgumentError,
|
||||
);
|
||||
});
|
||||
|
||||
test('reflectClass returns class mirror', () {
|
||||
final classMirror = mirrorSystem.reflectClass(TestClass);
|
||||
expect(classMirror, isNotNull);
|
||||
expect(classMirror.name, equals('TestClass'));
|
||||
expect(classMirror.declarations, isNotEmpty);
|
||||
});
|
||||
|
||||
test('reflectClass throws on non-reflectable type', () {
|
||||
expect(
|
||||
() => mirrorSystem.reflectClass(Object),
|
||||
throwsArgumentError,
|
||||
);
|
||||
});
|
||||
|
||||
test('reflectType returns type mirror', () {
|
||||
final typeMirror = mirrorSystem.reflectType(TestClass);
|
||||
expect(typeMirror, isNotNull);
|
||||
expect(typeMirror.name, equals('TestClass'));
|
||||
expect(typeMirror.hasReflectedType, isTrue);
|
||||
expect(typeMirror.reflectedType, equals(TestClass));
|
||||
});
|
||||
|
||||
test('reflectType throws on non-reflectable type', () {
|
||||
expect(
|
||||
() => mirrorSystem.reflectType(Object),
|
||||
throwsArgumentError,
|
||||
);
|
||||
});
|
||||
|
||||
test('isolate returns current isolate mirror', () {
|
||||
final isolateMirror = mirrorSystem.isolate;
|
||||
expect(isolateMirror, isNotNull);
|
||||
expect(isolateMirror.isCurrent, isTrue);
|
||||
expect(isolateMirror.debugName, equals('main'));
|
||||
});
|
||||
|
||||
test('dynamicType returns dynamic type mirror', () {
|
||||
final typeMirror = mirrorSystem.dynamicType;
|
||||
expect(typeMirror, isNotNull);
|
||||
expect(typeMirror.name, equals('dynamic'));
|
||||
});
|
||||
|
||||
test('voidType returns void type mirror', () {
|
||||
final typeMirror = mirrorSystem.voidType;
|
||||
expect(typeMirror, isNotNull);
|
||||
expect(typeMirror.name, equals('void'));
|
||||
});
|
||||
|
||||
test('neverType returns Never type mirror', () {
|
||||
final typeMirror = mirrorSystem.neverType;
|
||||
expect(typeMirror, isNotNull);
|
||||
expect(typeMirror.name, equals('Never'));
|
||||
});
|
||||
|
||||
test('type relationships work correctly', () {
|
||||
final dynamicMirror = mirrorSystem.dynamicType;
|
||||
final voidMirror = mirrorSystem.voidType;
|
||||
final neverMirror = mirrorSystem.neverType;
|
||||
final stringMirror = mirrorSystem.reflectType(String);
|
||||
|
||||
// Never is a subtype of everything
|
||||
expect(neverMirror.isSubtypeOf(dynamicMirror), isTrue);
|
||||
expect(neverMirror.isSubtypeOf(stringMirror), isTrue);
|
||||
|
||||
// Everything is assignable to dynamic
|
||||
expect(stringMirror.isAssignableTo(dynamicMirror), isTrue);
|
||||
expect(neverMirror.isAssignableTo(dynamicMirror), isTrue);
|
||||
|
||||
// void is not assignable to anything (except itself)
|
||||
expect(voidMirror.isAssignableTo(stringMirror), isFalse);
|
||||
expect(voidMirror.isAssignableTo(dynamicMirror), isFalse);
|
||||
expect(voidMirror.isAssignableTo(voidMirror), isTrue);
|
||||
});
|
||||
|
||||
test('library dependencies are tracked', () {
|
||||
final coreLibrary = mirrorSystem.findLibrary(const Symbol('dart:core'));
|
||||
expect(coreLibrary.libraryDependencies, isNotEmpty);
|
||||
|
||||
final imports =
|
||||
coreLibrary.libraryDependencies.where((dep) => dep.isImport).toList();
|
||||
expect(imports, isNotEmpty);
|
||||
|
||||
final exports =
|
||||
coreLibrary.libraryDependencies.where((dep) => dep.isExport).toList();
|
||||
expect(exports, isNotEmpty);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -2,7 +2,7 @@ import 'package:platform_reflection/reflection.dart';
|
|||
import 'package:test/test.dart';
|
||||
|
||||
@reflectable
|
||||
class Person with Reflector {
|
||||
class Person {
|
||||
String name;
|
||||
int age;
|
||||
final String id;
|
||||
|
@ -43,54 +43,124 @@ void main() {
|
|||
|
||||
setUp(() {
|
||||
// Register Person as reflectable
|
||||
Reflector.register(Person);
|
||||
Reflector.registerType(Person);
|
||||
|
||||
// Register properties
|
||||
Reflector.registerProperty(Person, 'name', String);
|
||||
Reflector.registerProperty(Person, 'age', int);
|
||||
Reflector.registerProperty(Person, 'id', String, isWritable: false);
|
||||
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.registerMethod(
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'birthday',
|
||||
[],
|
||||
true,
|
||||
MethodMetadata(
|
||||
name: 'birthday',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
returnsVoid: true,
|
||||
),
|
||||
);
|
||||
Reflector.registerMethod(
|
||||
|
||||
Reflector.registerMethodMetadata(
|
||||
Person,
|
||||
'greet',
|
||||
[String],
|
||||
false,
|
||||
parameterNames: ['greeting'],
|
||||
MethodMetadata(
|
||||
name: 'greet',
|
||||
parameterTypes: [String],
|
||||
parameters: [
|
||||
ParameterMetadata(
|
||||
name: 'greeting',
|
||||
type: String,
|
||||
isRequired: true,
|
||||
),
|
||||
],
|
||||
returnsVoid: false,
|
||||
),
|
||||
);
|
||||
|
||||
// Register constructors
|
||||
Reflector.registerConstructor(
|
||||
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),
|
||||
parameterTypes: [String, int, String],
|
||||
parameterNames: ['name', 'age', 'id'],
|
||||
isRequired: [true, true, true],
|
||||
isNamed: [false, false, true],
|
||||
);
|
||||
|
||||
Reflector.registerConstructor(
|
||||
Reflector.registerConstructorMetadata(
|
||||
Person,
|
||||
ConstructorMetadata(
|
||||
name: 'guest',
|
||||
parameterTypes: [],
|
||||
parameters: [],
|
||||
),
|
||||
);
|
||||
|
||||
Reflector.registerConstructorFactory(
|
||||
Person,
|
||||
'guest',
|
||||
() => Person.guest(),
|
||||
);
|
||||
|
||||
Reflector.registerConstructor(
|
||||
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),
|
||||
parameterTypes: [String, int],
|
||||
parameterNames: ['name', 'age'],
|
||||
isRequired: [true, false],
|
||||
isNamed: [false, false],
|
||||
);
|
||||
|
||||
reflector = RuntimeReflector.instance;
|
||||
|
@ -99,20 +169,20 @@ void main() {
|
|||
|
||||
group('Type Reflection', () {
|
||||
test('reflectType returns correct type metadata', () {
|
||||
final metadata = reflector.reflectType(Person);
|
||||
final typeMirror = reflector.reflectType(Person);
|
||||
|
||||
expect(metadata.name, equals('Person'));
|
||||
expect(metadata.properties.length, equals(3));
|
||||
expect(metadata.methods.length, equals(2)); // birthday and greet
|
||||
expect(metadata.constructors.length,
|
||||
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
|
||||
});
|
||||
|
||||
test('reflect creates instance reflector', () {
|
||||
final instanceReflector = reflector.reflect(person);
|
||||
test('reflect creates instance mirror', () {
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(instanceReflector, isNotNull);
|
||||
expect(instanceReflector.type.name, equals('Person'));
|
||||
expect(instanceMirror, isNotNull);
|
||||
expect(instanceMirror.type.name, equals('Person'));
|
||||
});
|
||||
|
||||
test('throws NotReflectableException for non-reflectable class', () {
|
||||
|
@ -127,28 +197,31 @@ void main() {
|
|||
|
||||
group('Property Access', () {
|
||||
test('getField returns property value', () {
|
||||
final instanceReflector = reflector.reflect(person);
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(instanceReflector.getField('name'), equals('John'));
|
||||
expect(instanceReflector.getField('age'), equals(30));
|
||||
expect(instanceReflector.getField('id'), equals('123'));
|
||||
expect(instanceMirror.getField(const Symbol('name')).reflectee,
|
||||
equals('John'));
|
||||
expect(
|
||||
instanceMirror.getField(const Symbol('age')).reflectee, equals(30));
|
||||
expect(instanceMirror.getField(const Symbol('id')).reflectee,
|
||||
equals('123'));
|
||||
});
|
||||
|
||||
test('setField updates property value', () {
|
||||
final instanceReflector = reflector.reflect(person);
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
instanceReflector.setField('name', 'Jane');
|
||||
instanceReflector.setField('age', 25);
|
||||
instanceMirror.setField(const Symbol('name'), 'Jane');
|
||||
instanceMirror.setField(const Symbol('age'), 25);
|
||||
|
||||
expect(person.name, equals('Jane'));
|
||||
expect(person.age, equals(25));
|
||||
});
|
||||
|
||||
test('setField throws on final field', () {
|
||||
final instanceReflector = reflector.reflect(person);
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(
|
||||
() => instanceReflector.setField('id', '456'),
|
||||
() => instanceMirror.setField(const Symbol('id'), '456'),
|
||||
throwsA(isA<ReflectionException>()),
|
||||
);
|
||||
});
|
||||
|
@ -156,20 +229,21 @@ void main() {
|
|||
|
||||
group('Method Invocation', () {
|
||||
test('invoke calls method with arguments', () {
|
||||
final instanceReflector = reflector.reflect(person);
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
final result = instanceReflector.invoke('greet', ['Hello']);
|
||||
final result =
|
||||
instanceMirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
||||
expect(result, equals('Hello, John!'));
|
||||
|
||||
instanceReflector.invoke('birthday', []);
|
||||
instanceMirror.invoke(const Symbol('birthday'), []);
|
||||
expect(person.age, equals(31));
|
||||
});
|
||||
|
||||
test('invoke throws on invalid arguments', () {
|
||||
final instanceReflector = reflector.reflect(person);
|
||||
final instanceMirror = reflector.reflect(person);
|
||||
|
||||
expect(
|
||||
() => instanceReflector.invoke('greet', [42]),
|
||||
() => instanceMirror.invoke(const Symbol('greet'), [42]),
|
||||
throwsA(isA<InvalidArgumentsException>()),
|
||||
);
|
||||
});
|
||||
|
|
246
packages/reflection/test/scanner_test.dart
Normal file
246
packages/reflection/test/scanner_test.dart
Normal file
|
@ -0,0 +1,246 @@
|
|||
import 'package:platform_reflection/reflection.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
@reflectable
|
||||
class TestClass {
|
||||
String name;
|
||||
final int id;
|
||||
List<String> tags;
|
||||
static const version = '1.0.0';
|
||||
|
||||
TestClass(this.name, {required this.id, this.tags = const []});
|
||||
|
||||
TestClass.guest()
|
||||
: name = 'Guest',
|
||||
id = 0,
|
||||
tags = const [];
|
||||
|
||||
void addTag(String tag) {
|
||||
tags.add(tag);
|
||||
}
|
||||
|
||||
String greet([String greeting = 'Hello']) {
|
||||
return '$greeting, $name!';
|
||||
}
|
||||
|
||||
static TestClass create(String name, {required int id}) {
|
||||
return TestClass(name, id: id);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectable
|
||||
class GenericTestClass<T> {
|
||||
T value;
|
||||
List<T> items;
|
||||
|
||||
GenericTestClass(this.value, {this.items = const []});
|
||||
|
||||
void addItem(T item) {
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
T getValue() => value;
|
||||
}
|
||||
|
||||
@reflectable
|
||||
class ParentTestClass {
|
||||
String name;
|
||||
ParentTestClass(this.name);
|
||||
|
||||
String getName() => name;
|
||||
}
|
||||
|
||||
@reflectable
|
||||
class ChildTestClass extends ParentTestClass {
|
||||
int age;
|
||||
ChildTestClass(String name, this.age) : super(name);
|
||||
|
||||
@override
|
||||
String getName() => '$name ($age)';
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('Scanner', () {
|
||||
test('scans properties correctly', () {
|
||||
Scanner.scanType(TestClass);
|
||||
final metadata = Reflector.getPropertyMetadata(TestClass);
|
||||
|
||||
expect(metadata, isNotNull);
|
||||
expect(metadata!['name'], isNotNull);
|
||||
expect(metadata['name']!.type, equals(String));
|
||||
expect(metadata['name']!.isWritable, isTrue);
|
||||
|
||||
expect(metadata['id'], isNotNull);
|
||||
expect(metadata['id']!.type, equals(int));
|
||||
expect(metadata['id']!.isWritable, isFalse);
|
||||
|
||||
expect(metadata['tags'], isNotNull);
|
||||
expect(metadata['tags']!.type, equals(List<String>));
|
||||
expect(metadata['tags']!.isWritable, isTrue);
|
||||
|
||||
expect(metadata['version'], isNotNull);
|
||||
expect(metadata['version']!.type, equals(String));
|
||||
expect(metadata['version']!.isWritable, isFalse);
|
||||
});
|
||||
|
||||
test('scans methods correctly', () {
|
||||
Scanner.scanType(TestClass);
|
||||
final metadata = Reflector.getMethodMetadata(TestClass);
|
||||
|
||||
expect(metadata, isNotNull);
|
||||
|
||||
// addTag method
|
||||
expect(metadata!['addTag'], isNotNull);
|
||||
expect(metadata['addTag']!.parameterTypes, equals([String]));
|
||||
expect(metadata['addTag']!.parameters.length, equals(1));
|
||||
expect(metadata['addTag']!.parameters[0].name, equals('tag'));
|
||||
expect(metadata['addTag']!.parameters[0].type, equals(String));
|
||||
expect(metadata['addTag']!.parameters[0].isRequired, isTrue);
|
||||
expect(metadata['addTag']!.returnsVoid, isTrue);
|
||||
expect(metadata['addTag']!.isStatic, isFalse);
|
||||
|
||||
// greet method
|
||||
expect(metadata['greet'], isNotNull);
|
||||
expect(metadata['greet']!.parameterTypes, equals([String]));
|
||||
expect(metadata['greet']!.parameters.length, equals(1));
|
||||
expect(metadata['greet']!.parameters[0].name, equals('greeting'));
|
||||
expect(metadata['greet']!.parameters[0].type, equals(String));
|
||||
expect(metadata['greet']!.parameters[0].isRequired, isFalse);
|
||||
expect(metadata['greet']!.returnsVoid, isFalse);
|
||||
expect(metadata['greet']!.isStatic, isFalse);
|
||||
|
||||
// create method
|
||||
expect(metadata['create'], isNotNull);
|
||||
expect(metadata['create']!.parameterTypes, equals([String, int]));
|
||||
expect(metadata['create']!.parameters.length, equals(2));
|
||||
expect(metadata['create']!.parameters[0].name, equals('name'));
|
||||
expect(metadata['create']!.parameters[0].type, equals(String));
|
||||
expect(metadata['create']!.parameters[0].isRequired, isTrue);
|
||||
expect(metadata['create']!.parameters[1].name, equals('id'));
|
||||
expect(metadata['create']!.parameters[1].type, equals(int));
|
||||
expect(metadata['create']!.parameters[1].isRequired, isTrue);
|
||||
expect(metadata['create']!.parameters[1].isNamed, isTrue);
|
||||
expect(metadata['create']!.returnsVoid, isFalse);
|
||||
expect(metadata['create']!.isStatic, isTrue);
|
||||
});
|
||||
|
||||
test('scans constructors correctly', () {
|
||||
Scanner.scanType(TestClass);
|
||||
final metadata = Reflector.getConstructorMetadata(TestClass);
|
||||
|
||||
expect(metadata, isNotNull);
|
||||
expect(metadata!.length, equals(2));
|
||||
|
||||
// Default constructor
|
||||
final defaultCtor = metadata.firstWhere((m) => m.name.isEmpty);
|
||||
expect(defaultCtor.parameterTypes, equals([String, int, List<String>]));
|
||||
expect(defaultCtor.parameters.length, equals(3));
|
||||
expect(defaultCtor.parameters[0].name, equals('name'));
|
||||
expect(defaultCtor.parameters[0].type, equals(String));
|
||||
expect(defaultCtor.parameters[0].isRequired, isTrue);
|
||||
expect(defaultCtor.parameters[1].name, equals('id'));
|
||||
expect(defaultCtor.parameters[1].type, equals(int));
|
||||
expect(defaultCtor.parameters[1].isRequired, isTrue);
|
||||
expect(defaultCtor.parameters[1].isNamed, isTrue);
|
||||
expect(defaultCtor.parameters[2].name, equals('tags'));
|
||||
expect(defaultCtor.parameters[2].type, equals(List<String>));
|
||||
expect(defaultCtor.parameters[2].isRequired, isFalse);
|
||||
expect(defaultCtor.parameters[2].isNamed, isTrue);
|
||||
|
||||
// Guest constructor
|
||||
final guestCtor = metadata.firstWhere((m) => m.name == 'guest');
|
||||
expect(guestCtor.parameterTypes, isEmpty);
|
||||
expect(guestCtor.parameters, isEmpty);
|
||||
});
|
||||
|
||||
test('scanned type works with reflection', () {
|
||||
Scanner.scanType(TestClass);
|
||||
final reflector = RuntimeReflector.instance;
|
||||
|
||||
// Create instance
|
||||
final instance = reflector.createInstance(
|
||||
TestClass,
|
||||
positionalArgs: ['John'],
|
||||
namedArgs: {'id': 123},
|
||||
) as TestClass;
|
||||
|
||||
expect(instance.name, equals('John'));
|
||||
expect(instance.id, equals(123));
|
||||
expect(instance.tags, isEmpty);
|
||||
|
||||
// Create guest instance
|
||||
final guest = reflector.createInstance(
|
||||
TestClass,
|
||||
constructorName: 'guest',
|
||||
) as TestClass;
|
||||
|
||||
expect(guest.name, equals('Guest'));
|
||||
expect(guest.id, equals(0));
|
||||
expect(guest.tags, isEmpty);
|
||||
|
||||
// Reflect on instance
|
||||
final mirror = reflector.reflect(instance);
|
||||
|
||||
// Access properties
|
||||
expect(mirror.getField(const Symbol('name')).reflectee, equals('John'));
|
||||
expect(mirror.getField(const Symbol('id')).reflectee, equals(123));
|
||||
|
||||
// Modify properties
|
||||
mirror.setField(const Symbol('name'), 'Jane');
|
||||
expect(instance.name, equals('Jane'));
|
||||
|
||||
// Invoke methods
|
||||
mirror.invoke(const Symbol('addTag'), ['test']);
|
||||
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>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('handles generic types correctly', () {
|
||||
Scanner.scanType(GenericTestClass);
|
||||
final metadata = Reflector.getPropertyMetadata(GenericTestClass);
|
||||
|
||||
expect(metadata, isNotNull);
|
||||
expect(metadata!['value'], isNotNull);
|
||||
expect(metadata['items'], isNotNull);
|
||||
expect(metadata['items']!.type, equals(List));
|
||||
|
||||
final methodMeta = Reflector.getMethodMetadata(GenericTestClass);
|
||||
expect(methodMeta, isNotNull);
|
||||
expect(methodMeta!['addItem'], isNotNull);
|
||||
expect(methodMeta['getValue'], isNotNull);
|
||||
});
|
||||
|
||||
test('handles inheritance correctly', () {
|
||||
Scanner.scanType(ParentTestClass);
|
||||
Scanner.scanType(ChildTestClass);
|
||||
|
||||
final parentMeta = Reflector.getPropertyMetadata(ParentTestClass);
|
||||
final childMeta = Reflector.getPropertyMetadata(ChildTestClass);
|
||||
|
||||
expect(parentMeta, isNotNull);
|
||||
expect(parentMeta!['name'], isNotNull);
|
||||
|
||||
expect(childMeta, isNotNull);
|
||||
expect(childMeta!['name'], isNotNull);
|
||||
expect(childMeta['age'], isNotNull);
|
||||
|
||||
final reflector = RuntimeReflector.instance;
|
||||
final child = reflector.createInstance(
|
||||
ChildTestClass,
|
||||
positionalArgs: ['John', 30],
|
||||
) as ChildTestClass;
|
||||
|
||||
final mirror = reflector.reflect(child);
|
||||
final result = mirror.invoke(const Symbol('getName'), []).reflectee;
|
||||
expect(result, equals('John (30)'));
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue