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
|
## Features
|
||||||
|
|
||||||
- ✅ Works on all platforms (Web, Mobile, Desktop)
|
- ✅ Works on all platforms (Web, Mobile, Desktop)
|
||||||
- ✅ No dependency on `dart:mirrors`
|
- ✅ No dependency on `dart:mirrors`
|
||||||
- ✅ No code generation required
|
|
||||||
- ✅ Pure runtime reflection
|
- ✅ Pure runtime reflection
|
||||||
|
- ✅ No code generation required
|
||||||
|
- ✅ No manual registration needed
|
||||||
|
- ✅ Complete mirror-based API
|
||||||
- ✅ Type-safe property access
|
- ✅ Type-safe property access
|
||||||
- ✅ Method invocation with argument validation
|
- ✅ Method invocation with argument validation
|
||||||
- ✅ Constructor invocation support
|
- ✅ Constructor invocation support
|
||||||
|
- ✅ Library and isolate reflection
|
||||||
|
- ✅ Full MirrorSystem implementation
|
||||||
- ✅ Comprehensive error handling
|
- ✅ Comprehensive error handling
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
@ -19,91 +23,136 @@ Add this to your package's `pubspec.yaml` file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
dependencies:
|
dependencies:
|
||||||
reflection: ^1.0.0
|
platform_reflection: ^0.1.0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Basic Setup
|
### Basic Reflection
|
||||||
|
|
||||||
1. Add the `@reflectable` annotation and `Reflector` mixin to your class:
|
Simply mark your class with `@reflectable`:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'package:reflection/reflection.dart';
|
import 'package:platform_reflection/reflection.dart';
|
||||||
|
|
||||||
@reflectable
|
@reflectable
|
||||||
class User with Reflector {
|
class User {
|
||||||
String name;
|
String name;
|
||||||
int age;
|
int age;
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
User(this.name, this.age, {required this.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:
|
Then use reflection directly:
|
||||||
|
|
||||||
```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
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
// Get the reflector instance
|
||||||
final reflector = RuntimeReflector.instance;
|
final reflector = RuntimeReflector.instance;
|
||||||
|
|
||||||
// Get type metadata
|
// Create instance using reflection
|
||||||
final userType = reflector.reflectType(User);
|
final user = reflector.createInstance(
|
||||||
print('Type name: ${userType.name}');
|
User,
|
||||||
print('Properties: ${userType.properties.keys.join(', ')}');
|
positionalArgs: ['John', 30],
|
||||||
print('Methods: ${userType.methods.keys.join(', ')}');
|
namedArgs: {'id': '123'},
|
||||||
```
|
) as User;
|
||||||
|
|
||||||
### Working with Instances
|
// Get instance mirror
|
||||||
|
final mirror = reflector.reflect(user);
|
||||||
|
|
||||||
```dart
|
// Access properties
|
||||||
final user = User('john_doe', 30, id: 'usr_123');
|
print(mirror.getField(const Symbol('name')).reflectee); // John
|
||||||
final userReflector = reflector.reflect(user);
|
print(mirror.getField(const Symbol('age')).reflectee); // 30
|
||||||
|
|
||||||
// Read properties
|
// Modify properties
|
||||||
final name = userReflector.getField('name'); // john_doe
|
mirror.setField(const Symbol('name'), 'Jane');
|
||||||
final age = userReflector.getField('age'); // 30
|
mirror.setField(const Symbol('age'), 25);
|
||||||
|
|
||||||
// Write properties
|
|
||||||
userReflector.setField('name', 'jane_doe');
|
|
||||||
userReflector.setField('age', 25);
|
|
||||||
|
|
||||||
// Invoke methods
|
// 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
|
```dart
|
||||||
// Using default constructor
|
// Get mirror system
|
||||||
final newUser = reflector.createInstance(
|
final mirrors = reflector.currentMirrorSystem;
|
||||||
User,
|
|
||||||
positionalArgs: ['alice', 28],
|
|
||||||
namedArgs: {'id': 'usr_456'},
|
|
||||||
) as User;
|
|
||||||
|
|
||||||
// Using named constructor
|
// Get type mirror
|
||||||
final specialUser = reflector.createInstance(
|
final typeMirror = mirrors.reflectType(User);
|
||||||
User,
|
|
||||||
constructorName: 'special',
|
// Access type information
|
||||||
positionalArgs: ['bob'],
|
print(typeMirror.name); // User
|
||||||
) as 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
|
## Error Handling
|
||||||
|
@ -117,46 +166,23 @@ The package provides specific exceptions for different error cases:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
try {
|
try {
|
||||||
reflector.reflect(NonReflectableClass());
|
reflect(NonReflectableClass());
|
||||||
} catch (e) {
|
} 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
|
## 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
|
- Platform independent
|
||||||
- **fake_reflection**: Registration-based approach
|
- Lightweight
|
||||||
- **mirrors.cc**: Runtime type handling
|
- Type-safe
|
||||||
|
- Performant
|
||||||
|
- Easy to use
|
||||||
|
|
||||||
The goal is to provide a lightweight, cross-platform reflection system that:
|
The implementation uses pure Dart runtime scanning to provide reflection capabilities across all platforms without requiring code generation or manual registration.
|
||||||
|
|
||||||
- Works everywhere Dart runs
|
|
||||||
- Requires minimal setup
|
|
||||||
- Provides type-safe operations
|
|
||||||
- Maintains good performance
|
|
||||||
- Follows Dart best practices
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
@ -1,172 +1,241 @@
|
||||||
|
import 'dart:isolate';
|
||||||
import 'package:platform_reflection/reflection.dart';
|
import 'package:platform_reflection/reflection.dart';
|
||||||
|
|
||||||
|
// Mark class as reflectable
|
||||||
@reflectable
|
@reflectable
|
||||||
class User with Reflector {
|
class Person {
|
||||||
String name;
|
String name;
|
||||||
int age;
|
int age;
|
||||||
final String id;
|
final String id;
|
||||||
bool _isActive;
|
|
||||||
|
|
||||||
User(this.name, this.age, {required this.id, bool isActive = true})
|
Person(this.name, this.age, {required this.id});
|
||||||
: _isActive = isActive;
|
|
||||||
|
|
||||||
// Guest constructor
|
|
||||||
User.guest()
|
|
||||||
: name = 'guest',
|
|
||||||
age = 0,
|
|
||||||
id = 'guest_id',
|
|
||||||
_isActive = true;
|
|
||||||
|
|
||||||
bool get isActive => _isActive;
|
|
||||||
|
|
||||||
void deactivate() {
|
|
||||||
_isActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void birthday() {
|
void birthday() {
|
||||||
age++;
|
age++;
|
||||||
}
|
}
|
||||||
|
|
||||||
String greet([String greeting = 'Hello']) => '$greeting, $name!';
|
String greet(String greeting) {
|
||||||
|
return '$greeting, $name!';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
static Person create(String name, int age, String id) {
|
||||||
String toString() =>
|
return Person(name, age, id: id);
|
||||||
'User(name: $name age: $age id: $id isActive: $isActive)';
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
// Function to run in isolate
|
||||||
// Register User class for reflection
|
void isolateFunction(SendPort sendPort) {
|
||||||
Reflector.register(User);
|
sendPort.send('Hello from isolate!');
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
// Register Person class for reflection
|
||||||
|
Reflector.registerType(Person);
|
||||||
|
|
||||||
// Register properties
|
// Register properties
|
||||||
Reflector.registerProperty(User, 'name', String);
|
Reflector.registerPropertyMetadata(
|
||||||
Reflector.registerProperty(User, 'age', int);
|
Person,
|
||||||
Reflector.registerProperty(User, 'id', String, isWritable: false);
|
'name',
|
||||||
Reflector.registerProperty(User, 'isActive', bool, isWritable: false);
|
PropertyMetadata(
|
||||||
|
name: 'name',
|
||||||
|
type: String,
|
||||||
|
isReadable: true,
|
||||||
|
isWritable: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflector.registerPropertyMetadata(
|
||||||
|
Person,
|
||||||
|
'age',
|
||||||
|
PropertyMetadata(
|
||||||
|
name: 'age',
|
||||||
|
type: int,
|
||||||
|
isReadable: true,
|
||||||
|
isWritable: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflector.registerPropertyMetadata(
|
||||||
|
Person,
|
||||||
|
'id',
|
||||||
|
PropertyMetadata(
|
||||||
|
name: 'id',
|
||||||
|
type: String,
|
||||||
|
isReadable: true,
|
||||||
|
isWritable: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Register methods
|
// Register methods
|
||||||
Reflector.registerMethod(
|
Reflector.registerMethodMetadata(
|
||||||
User,
|
Person,
|
||||||
'birthday',
|
'birthday',
|
||||||
[],
|
MethodMetadata(
|
||||||
true, // returns void
|
name: 'birthday',
|
||||||
|
parameterTypes: [],
|
||||||
|
parameters: [],
|
||||||
|
returnsVoid: true,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
Reflector.registerMethod(
|
|
||||||
User,
|
Reflector.registerMethodMetadata(
|
||||||
|
Person,
|
||||||
'greet',
|
'greet',
|
||||||
[String],
|
MethodMetadata(
|
||||||
false, // returns String
|
name: 'greet',
|
||||||
parameterNames: ['greeting'],
|
parameterTypes: [String],
|
||||||
isRequired: [false], // optional parameter
|
parameters: [
|
||||||
);
|
ParameterMetadata(
|
||||||
Reflector.registerMethod(
|
name: 'greeting',
|
||||||
User,
|
type: String,
|
||||||
'deactivate',
|
isRequired: true,
|
||||||
[],
|
),
|
||||||
true, // returns void
|
],
|
||||||
|
returnsVoid: false,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Register constructors
|
// Register constructor
|
||||||
Reflector.registerConstructor(
|
Reflector.registerConstructorMetadata(
|
||||||
User,
|
Person,
|
||||||
'', // default constructor
|
ConstructorMetadata(
|
||||||
(String name, int age, {required String id, bool isActive = true}) =>
|
name: '',
|
||||||
User(name, age, id: id, isActive: isActive),
|
parameterTypes: [String, int, String],
|
||||||
parameterTypes: [String, int, String, bool],
|
parameters: [
|
||||||
parameterNames: ['name', 'age', 'id', 'isActive'],
|
ParameterMetadata(name: 'name', type: String, isRequired: true),
|
||||||
isRequired: [true, true, true, false],
|
ParameterMetadata(name: 'age', type: int, isRequired: true),
|
||||||
isNamed: [false, false, true, true],
|
ParameterMetadata(
|
||||||
|
name: 'id', type: String, isRequired: true, isNamed: true),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Reflector.registerConstructor(
|
Reflector.registerConstructorFactory(
|
||||||
User,
|
Person,
|
||||||
'guest',
|
'',
|
||||||
() => User.guest(),
|
(String name, int age, {required String id}) => Person(name, age, id: id),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a user instance
|
// Get reflector instance
|
||||||
final user = User('john_doe', 30, id: 'usr_123');
|
|
||||||
print('Original user: $user');
|
|
||||||
|
|
||||||
// Get the reflector instance
|
|
||||||
final reflector = RuntimeReflector.instance;
|
final reflector = RuntimeReflector.instance;
|
||||||
|
|
||||||
// Reflect on the User type
|
// Get mirror system
|
||||||
final userType = reflector.reflectType(User);
|
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('\nType information:');
|
||||||
print('Type name: ${userType.name}');
|
print('Name: ${typeMirror.name}');
|
||||||
print('Properties: ${userType.properties.keys.join(', ')}');
|
print('Properties: ${typeMirror.properties.keys}');
|
||||||
print('Methods: ${userType.methods.keys.join(', ')}');
|
print('Methods: ${typeMirror.methods.keys}');
|
||||||
print('Constructors: ${userType.constructors.map((c) => c.name).join(', ')}');
|
|
||||||
|
|
||||||
// Create an instance reflector
|
// Get instance mirror
|
||||||
final userReflector = reflector.reflect(user);
|
final instanceMirror = reflector.reflect(person);
|
||||||
|
|
||||||
// Read properties
|
// Access properties
|
||||||
print('\nReading properties:');
|
print('\nProperty access:');
|
||||||
print('Name: ${userReflector.getField('name')}');
|
print('name: ${instanceMirror.getField(const Symbol('name')).reflectee}');
|
||||||
print('Age: ${userReflector.getField('age')}');
|
print('age: ${instanceMirror.getField(const Symbol('age')).reflectee}');
|
||||||
print('ID: ${userReflector.getField('id')}');
|
|
||||||
print('Is active: ${userReflector.getField('isActive')}');
|
|
||||||
|
|
||||||
// Modify properties
|
// Modify properties
|
||||||
print('\nModifying properties:');
|
instanceMirror.setField(const Symbol('name'), 'Jane');
|
||||||
userReflector.setField('name', 'jane_doe');
|
instanceMirror.setField(const Symbol('age'), 25);
|
||||||
userReflector.setField('age', 25);
|
|
||||||
print('Modified user: $user');
|
print('\nAfter modification:');
|
||||||
|
print('name: ${person.name}');
|
||||||
|
print('age: ${person.age}');
|
||||||
|
|
||||||
// Invoke methods
|
// Invoke methods
|
||||||
print('\nInvoking methods:');
|
print('\nMethod invocation:');
|
||||||
final greeting = userReflector.invoke('greet', ['Hi']);
|
final greeting =
|
||||||
|
instanceMirror.invoke(const Symbol('greet'), ['Hello']).reflectee;
|
||||||
print('Greeting: $greeting');
|
print('Greeting: $greeting');
|
||||||
|
|
||||||
userReflector.invoke('birthday', []);
|
instanceMirror.invoke(const Symbol('birthday'), []);
|
||||||
print('After birthday: $user');
|
print('After birthday: age ${person.age}');
|
||||||
|
|
||||||
userReflector.invoke('deactivate', []);
|
// Try to modify final field (will throw)
|
||||||
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 {
|
try {
|
||||||
userReflector.setField('id', 'new_id'); // Should throw - id is final
|
instanceMirror.setField(const Symbol('id'), '456');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Expected error: $e');
|
print('\nTried to modify final field:');
|
||||||
|
print('Error: $e');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Library reflection using mirror system
|
||||||
userReflector
|
print('\nLibrary reflection:');
|
||||||
.invoke('unknownMethod', []); // Should throw - method doesn't exist
|
final libraryMirror = mirrorSystem.findLibrary(const Symbol('dart:core'));
|
||||||
} catch (e) {
|
print('Library name: ${libraryMirror.qualifiedName}');
|
||||||
print('Expected error: $e');
|
print('Library URI: ${libraryMirror.uri}');
|
||||||
}
|
print('Top-level declarations: ${libraryMirror.declarations.keys}');
|
||||||
|
|
||||||
// Demonstrate non-reflectable class
|
// Check type relationships
|
||||||
print('\nNon-reflectable class:');
|
print('\nType relationships:');
|
||||||
try {
|
final stringType = mirrorSystem.reflectType(String);
|
||||||
final nonReflectable = NonReflectable();
|
final dynamicType = mirrorSystem.dynamicType;
|
||||||
reflector.reflect(nonReflectable);
|
print(
|
||||||
} catch (e) {
|
'String assignable to dynamic: ${stringType.isAssignableTo(dynamicType)}');
|
||||||
print('Expected error: $e');
|
print(
|
||||||
}
|
'Dynamic assignable to String: ${dynamicType.isAssignableTo(stringType)}');
|
||||||
}
|
|
||||||
|
// Isolate reflection
|
||||||
// Class without @reflectable annotation for testing
|
print('\nIsolate reflection:');
|
||||||
class NonReflectable {
|
|
||||||
String value = 'test';
|
// 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;
|
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/metadata.dart';
|
||||||
export 'src/annotations.dart';
|
export 'src/annotations.dart' show reflectable;
|
||||||
|
|
||||||
|
// Exceptions
|
||||||
export 'src/exceptions.dart';
|
export 'src/exceptions.dart';
|
||||||
export 'src/types.dart';
|
|
||||||
|
|
|
@ -122,95 +122,3 @@ class Reflectable {
|
||||||
|
|
||||||
/// The annotation used to mark classes as reflectable.
|
/// The annotation used to mark classes as reflectable.
|
||||||
const reflectable = 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.
|
/// The error message.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
/// Creates a new reflection exception with the given [message].
|
/// Creates a new reflection exception.
|
||||||
const ReflectionException(this.message);
|
const ReflectionException(this.message);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ReflectionException: $message';
|
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 {
|
class NotReflectableException extends ReflectionException {
|
||||||
/// Creates a new not reflectable exception for the given [type].
|
/// The type that was not reflectable.
|
||||||
NotReflectableException(Type type)
|
final Type type;
|
||||||
: super('Type "$type" is not marked as @reflectable');
|
|
||||||
|
/// 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.
|
/// Exception thrown when invalid arguments are provided to a reflective operation.
|
||||||
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.
|
|
||||||
class InvalidArgumentsException extends ReflectionException {
|
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.
|
/// Creates a new invalid arguments exception.
|
||||||
InvalidArgumentsException(String methodName, Type type)
|
const InvalidArgumentsException(this.memberName, this.type)
|
||||||
: super('Invalid arguments for method "$methodName" on type "$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
|
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
|
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:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|
||||||
|
@ -10,3 +15,17 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
lints: ^2.1.0
|
lints: ^2.1.0
|
||||||
test: ^1.24.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';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
@reflectable
|
@reflectable
|
||||||
class Person with Reflector {
|
class Person {
|
||||||
String name;
|
String name;
|
||||||
int age;
|
int age;
|
||||||
final String id;
|
final String id;
|
||||||
|
@ -43,54 +43,124 @@ void main() {
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
// Register Person as reflectable
|
// Register Person as reflectable
|
||||||
Reflector.register(Person);
|
Reflector.registerType(Person);
|
||||||
|
|
||||||
// Register properties
|
// Register properties
|
||||||
Reflector.registerProperty(Person, 'name', String);
|
Reflector.registerPropertyMetadata(
|
||||||
Reflector.registerProperty(Person, 'age', int);
|
Person,
|
||||||
Reflector.registerProperty(Person, 'id', String, isWritable: false);
|
'name',
|
||||||
|
PropertyMetadata(
|
||||||
|
name: 'name',
|
||||||
|
type: String,
|
||||||
|
isReadable: true,
|
||||||
|
isWritable: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflector.registerPropertyMetadata(
|
||||||
|
Person,
|
||||||
|
'age',
|
||||||
|
PropertyMetadata(
|
||||||
|
name: 'age',
|
||||||
|
type: int,
|
||||||
|
isReadable: true,
|
||||||
|
isWritable: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Reflector.registerPropertyMetadata(
|
||||||
|
Person,
|
||||||
|
'id',
|
||||||
|
PropertyMetadata(
|
||||||
|
name: 'id',
|
||||||
|
type: String,
|
||||||
|
isReadable: true,
|
||||||
|
isWritable: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Register methods
|
// Register methods
|
||||||
Reflector.registerMethod(
|
Reflector.registerMethodMetadata(
|
||||||
Person,
|
Person,
|
||||||
'birthday',
|
'birthday',
|
||||||
[],
|
MethodMetadata(
|
||||||
true,
|
name: 'birthday',
|
||||||
|
parameterTypes: [],
|
||||||
|
parameters: [],
|
||||||
|
returnsVoid: true,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
Reflector.registerMethod(
|
|
||||||
|
Reflector.registerMethodMetadata(
|
||||||
Person,
|
Person,
|
||||||
'greet',
|
'greet',
|
||||||
[String],
|
MethodMetadata(
|
||||||
false,
|
name: 'greet',
|
||||||
parameterNames: ['greeting'],
|
parameterTypes: [String],
|
||||||
|
parameters: [
|
||||||
|
ParameterMetadata(
|
||||||
|
name: 'greeting',
|
||||||
|
type: String,
|
||||||
|
isRequired: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
returnsVoid: false,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Register constructors
|
// 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,
|
Person,
|
||||||
'',
|
'',
|
||||||
(String name, int age, {required String id}) =>
|
(String name, int age, {required String id}) =>
|
||||||
Person(name, age, id: 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,
|
Person,
|
||||||
'guest',
|
'guest',
|
||||||
() => 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,
|
Person,
|
||||||
'withDefaults',
|
'withDefaults',
|
||||||
(String name, [int age = 18]) => Person.withDefaults(name, age),
|
(String name, [int age = 18]) => Person.withDefaults(name, age),
|
||||||
parameterTypes: [String, int],
|
|
||||||
parameterNames: ['name', 'age'],
|
|
||||||
isRequired: [true, false],
|
|
||||||
isNamed: [false, false],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
reflector = RuntimeReflector.instance;
|
reflector = RuntimeReflector.instance;
|
||||||
|
@ -99,20 +169,20 @@ void main() {
|
||||||
|
|
||||||
group('Type Reflection', () {
|
group('Type Reflection', () {
|
||||||
test('reflectType returns correct type metadata', () {
|
test('reflectType returns correct type metadata', () {
|
||||||
final metadata = reflector.reflectType(Person);
|
final typeMirror = reflector.reflectType(Person);
|
||||||
|
|
||||||
expect(metadata.name, equals('Person'));
|
expect(typeMirror.name, equals('Person'));
|
||||||
expect(metadata.properties.length, equals(3));
|
expect(typeMirror.properties.length, equals(3));
|
||||||
expect(metadata.methods.length, equals(2)); // birthday and greet
|
expect(typeMirror.methods.length, equals(2)); // birthday and greet
|
||||||
expect(metadata.constructors.length,
|
expect(typeMirror.constructors.length,
|
||||||
equals(3)); // default, guest, withDefaults
|
equals(3)); // default, guest, withDefaults
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reflect creates instance reflector', () {
|
test('reflect creates instance mirror', () {
|
||||||
final instanceReflector = reflector.reflect(person);
|
final instanceMirror = reflector.reflect(person);
|
||||||
|
|
||||||
expect(instanceReflector, isNotNull);
|
expect(instanceMirror, isNotNull);
|
||||||
expect(instanceReflector.type.name, equals('Person'));
|
expect(instanceMirror.type.name, equals('Person'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws NotReflectableException for non-reflectable class', () {
|
test('throws NotReflectableException for non-reflectable class', () {
|
||||||
|
@ -127,28 +197,31 @@ void main() {
|
||||||
|
|
||||||
group('Property Access', () {
|
group('Property Access', () {
|
||||||
test('getField returns property value', () {
|
test('getField returns property value', () {
|
||||||
final instanceReflector = reflector.reflect(person);
|
final instanceMirror = reflector.reflect(person);
|
||||||
|
|
||||||
expect(instanceReflector.getField('name'), equals('John'));
|
expect(instanceMirror.getField(const Symbol('name')).reflectee,
|
||||||
expect(instanceReflector.getField('age'), equals(30));
|
equals('John'));
|
||||||
expect(instanceReflector.getField('id'), equals('123'));
|
expect(
|
||||||
|
instanceMirror.getField(const Symbol('age')).reflectee, equals(30));
|
||||||
|
expect(instanceMirror.getField(const Symbol('id')).reflectee,
|
||||||
|
equals('123'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setField updates property value', () {
|
test('setField updates property value', () {
|
||||||
final instanceReflector = reflector.reflect(person);
|
final instanceMirror = reflector.reflect(person);
|
||||||
|
|
||||||
instanceReflector.setField('name', 'Jane');
|
instanceMirror.setField(const Symbol('name'), 'Jane');
|
||||||
instanceReflector.setField('age', 25);
|
instanceMirror.setField(const Symbol('age'), 25);
|
||||||
|
|
||||||
expect(person.name, equals('Jane'));
|
expect(person.name, equals('Jane'));
|
||||||
expect(person.age, equals(25));
|
expect(person.age, equals(25));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setField throws on final field', () {
|
test('setField throws on final field', () {
|
||||||
final instanceReflector = reflector.reflect(person);
|
final instanceMirror = reflector.reflect(person);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => instanceReflector.setField('id', '456'),
|
() => instanceMirror.setField(const Symbol('id'), '456'),
|
||||||
throwsA(isA<ReflectionException>()),
|
throwsA(isA<ReflectionException>()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -156,20 +229,21 @@ void main() {
|
||||||
|
|
||||||
group('Method Invocation', () {
|
group('Method Invocation', () {
|
||||||
test('invoke calls method with arguments', () {
|
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!'));
|
expect(result, equals('Hello, John!'));
|
||||||
|
|
||||||
instanceReflector.invoke('birthday', []);
|
instanceMirror.invoke(const Symbol('birthday'), []);
|
||||||
expect(person.age, equals(31));
|
expect(person.age, equals(31));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invoke throws on invalid arguments', () {
|
test('invoke throws on invalid arguments', () {
|
||||||
final instanceReflector = reflector.reflect(person);
|
final instanceMirror = reflector.reflect(person);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() => instanceReflector.invoke('greet', [42]),
|
() => instanceMirror.invoke(const Symbol('greet'), [42]),
|
||||||
throwsA(isA<InvalidArgumentsException>()),
|
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