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,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');
}
}

View file

@ -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),