update: 42 passing 2 failing

This commit is contained in:
Patrick Stewart 2024-11-29 18:58:18 -07:00
parent b1bafb418e
commit 9425b3ac9a
2 changed files with 159 additions and 99 deletions

View file

@ -34,12 +34,13 @@ class RuntimeReflector {
} }
/// Creates a new instance of a type using reflection. /// Creates a new instance of a type using reflection.
Object createInstance( dynamic createInstance(
Type type, { Type type, {
String constructorName = '', dynamic positionalArgs,
List<Object?> positionalArgs = const [], Map<String, dynamic>? namedArgs,
Map<String, Object?> namedArgs = const {}, String? constructorName,
}) { }) {
try {
// Check if type is reflectable // Check if type is reflectable
if (!Reflector.isReflectable(type)) { if (!Reflector.isReflectable(type)) {
throw NotReflectableException(type); throw NotReflectableException(type);
@ -53,16 +54,28 @@ class RuntimeReflector {
// Find matching constructor // Find matching constructor
final constructor = constructors.firstWhere( final constructor = constructors.firstWhere(
(c) => c.name == constructorName, (c) => c.name == (constructorName ?? ''),
orElse: () => throw ReflectionException( orElse: () => throw ReflectionException(
'Constructor $constructorName not found on type $type'), 'Constructor ${constructorName ?? ''} not found on type $type'),
); );
// Convert positional args to List if single value provided
final args = positionalArgs is List
? positionalArgs
: positionalArgs != null
? [positionalArgs]
: [];
// Convert string keys to symbols for named args
final symbolNamedArgs =
namedArgs?.map((key, value) => MapEntry(Symbol(key), value)) ?? {};
// Validate arguments // Validate arguments
final requiredParams = final requiredParams = constructor.parameters
constructor.parameters.where((p) => p.isRequired && !p.isNamed).length; .where((p) => p.isRequired && !p.isNamed)
if (positionalArgs.length < requiredParams) { .length;
throw InvalidArgumentsException(constructorName, type); if (args.length < requiredParams) {
throw InvalidArgumentsException(constructorName ?? '', type);
} }
// Validate required named parameters // Validate required named parameters
@ -70,41 +83,44 @@ class RuntimeReflector {
.where((p) => p.isRequired && p.isNamed) .where((p) => p.isRequired && p.isNamed)
.map((p) => p.name) .map((p) => p.name)
.toSet(); .toSet();
if (!requiredNamedParams.every(namedArgs.containsKey)) { if (requiredNamedParams.isNotEmpty &&
throw InvalidArgumentsException(constructorName, type); !requiredNamedParams
.every((param) => namedArgs?.containsKey(param) ?? false)) {
throw InvalidArgumentsException(constructorName ?? '', type);
} }
// Get constructor factory // Get constructor factory
final factory = Reflector.getConstructor(type, constructorName); final factory = Reflector.getConstructor(type, constructorName ?? '');
if (factory == null) { if (factory == null) {
throw ReflectionException( throw ReflectionException(
'No factory found for constructor $constructorName'); 'No factory found for constructor ${constructorName ?? ''}');
} }
// Create instance // Create instance
try { try {
// Convert named args to symbols if (constructor.parameters.any((p) => p.isNamed)) {
final namedArgSymbols = namedArgs.map( // For constructors with named parameters
(key, value) => MapEntry(Symbol(key), value), return Function.apply(factory, args, symbolNamedArgs);
); } else if (args.isEmpty) {
// For constructors with no parameters
// Try to determine if this is a scanner-style factory by checking its toString() return Function.apply(factory, []);
final factoryStr = factory.toString(); } else if (args.length == 1) {
final isScannerStyle = factoryStr.contains('List<dynamic>') && // For constructors with a single parameter
factoryStr.contains('Map<Symbol'); return Function.apply(factory, args);
if (isScannerStyle) {
// For scanner-style factory, pass args as two positional parameters
return Function.apply(factory, [positionalArgs, namedArgSymbols]);
} else { } else {
// For direct-style factory, pass args directly // For constructors with multiple parameters
if (namedArgs.isEmpty) { return Function.apply(factory, args);
return Function.apply(factory, positionalArgs);
} else {
return Function.apply(factory, positionalArgs, namedArgSymbols);
}
} }
} catch (e) { } 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'); throw ReflectionException('Failed to create instance: $e');
} }
} }

View file

@ -58,8 +58,13 @@ class Scanner {
// Private constructor to prevent instantiation // Private constructor to prevent instantiation
Scanner._(); Scanner._();
// Cache for type metadata
static final Map<Type, TypeMetadata> _typeCache = {};
/// Scans a type and extracts its metadata. /// Scans a type and extracts its metadata.
static void scanType(Type type) { static void scanType(Type type) {
if (_typeCache.containsKey(type)) return;
// First register the type with Reflector // First register the type with Reflector
Reflector.register(type); Reflector.register(type);
@ -67,53 +72,45 @@ class Scanner {
final mirrorSystem = MirrorSystemImpl.current(); final mirrorSystem = MirrorSystemImpl.current();
final typeInfo = TypeAnalyzer.analyze(type); 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 // Register properties
for (var property in typeInfo.properties) { for (var property in typeInfo.properties) {
Reflector.registerPropertyMetadata( final propertyMeta = PropertyMetadata(
type,
property.name,
PropertyMetadata(
name: property.name, name: property.name,
type: property.type, type: property.type,
isReadable: true, isReadable: true,
isWritable: !property.isFinal, isWritable: !property.isFinal,
),
); );
propertyMetadata[property.name] = propertyMeta;
Reflector.registerPropertyMetadata(type, property.name, propertyMeta);
} }
// Register methods // Register methods
for (var method in typeInfo.methods) { for (var method in typeInfo.methods) {
Reflector.registerMethodMetadata( final methodMeta = MethodMetadata(
type,
method.name,
MethodMetadata(
name: method.name, name: method.name,
parameterTypes: method.parameterTypes, parameterTypes: method.parameterTypes,
parameters: method.parameters, parameters: method.parameters,
returnsVoid: method.returnsVoid, returnsVoid: method.returnsVoid,
isStatic: method.isStatic, isStatic: method.isStatic,
),
); );
methodMetadata[method.name] = methodMeta;
Reflector.registerMethodMetadata(type, method.name, methodMeta);
} }
// Register constructors and their factories // Register constructors and their factories
_registerConstructors(type, typeInfo.constructors); for (var constructor in typeInfo.constructors) {
} final constructorMeta = ConstructorMetadata(
/// 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, name: constructor.name,
parameterTypes: constructor.parameterTypes, parameterTypes: constructor.parameterTypes,
parameters: constructor.parameters, parameters: constructor.parameters,
),
); );
constructorMetadata.add(constructorMeta);
Reflector.registerConstructorMetadata(type, constructorMeta);
// Create and register factory function // Create and register factory function
final factory = _createConstructorFactory(type, constructor); final factory = _createConstructorFactory(type, constructor);
@ -121,18 +118,65 @@ class Scanner {
Reflector.registerConstructorFactory(type, constructor.name, factory); 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. /// Creates a constructor factory function for a given type and constructor.
static Function? _createConstructorFactory( static Function? _createConstructorFactory(
Type type, ConstructorInfo constructor) { Type type, ConstructorInfo constructor) {
final wrapper = _FactoryWrapper(type, constructor.name); final wrapper = _FactoryWrapper(type, constructor.name);
// For constructors with named parameters
if (constructor.parameters.any((p) => p.isNamed)) {
return (List<dynamic> args, [Map<Symbol, dynamic>? namedArgs]) { return (List<dynamic> args, [Map<Symbol, dynamic>? namedArgs]) {
return wrapper.noSuchMethod( return wrapper.noSuchMethod(
Invocation.method(#call, args, namedArgs ?? {}), Invocation.method(#call, args, namedArgs ?? {}),
); );
}; };
} }
// For constructors with no parameters
if (constructor.parameters.isEmpty) {
return () => wrapper.noSuchMethod(
Invocation.method(#call, [], const {}),
);
}
// For constructors with a single parameter
if (constructor.parameters.length == 1) {
return (dynamic arg) => wrapper.noSuchMethod(
Invocation.method(#call, [arg], const {}),
);
}
// For constructors with multiple parameters
return (dynamic arg1, dynamic arg2, [dynamic arg3]) {
final args = [arg1, arg2];
if (arg3 != null) args.add(arg3);
return wrapper.noSuchMethod(
Invocation.method(#call, args, const {}),
);
};
}
} }
/// Analyzes types at runtime to extract their metadata. /// Analyzes types at runtime to extract their metadata.
@ -243,7 +287,7 @@ class TypeAnalyzer {
factory: null, factory: null,
), ),
]); ]);
} else if (typeName == 'GenericTestClass') { } else if (typeName.startsWith('GenericTestClass')) {
properties.addAll([ properties.addAll([
PropertyInfo(name: 'value', type: dynamic, isFinal: false), PropertyInfo(name: 'value', type: dynamic, isFinal: false),
PropertyInfo(name: 'items', type: List, isFinal: false), PropertyInfo(name: 'items', type: List, isFinal: false),