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.
|
/// 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,
|
||||||
}) {
|
}) {
|
||||||
// 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 {
|
try {
|
||||||
// Convert named args to symbols
|
// Check if type is reflectable
|
||||||
final namedArgSymbols = namedArgs.map(
|
if (!Reflector.isReflectable(type)) {
|
||||||
(key, value) => MapEntry(Symbol(key), value),
|
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()
|
// Convert positional args to List if single value provided
|
||||||
final factoryStr = factory.toString();
|
final args = positionalArgs is List
|
||||||
final isScannerStyle = factoryStr.contains('List<dynamic>') &&
|
? positionalArgs
|
||||||
factoryStr.contains('Map<Symbol');
|
: positionalArgs != null
|
||||||
|
? [positionalArgs]
|
||||||
|
: [];
|
||||||
|
|
||||||
if (isScannerStyle) {
|
// Convert string keys to symbols for named args
|
||||||
// For scanner-style factory, pass args as two positional parameters
|
final symbolNamedArgs =
|
||||||
return Function.apply(factory, [positionalArgs, namedArgSymbols]);
|
namedArgs?.map((key, value) => MapEntry(Symbol(key), value)) ?? {};
|
||||||
} else {
|
|
||||||
// For direct-style factory, pass args directly
|
// Validate arguments
|
||||||
if (namedArgs.isEmpty) {
|
final requiredParams = constructor.parameters
|
||||||
return Function.apply(factory, positionalArgs);
|
.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 {
|
} 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) {
|
} catch (e) {
|
||||||
|
if (e is InvalidArgumentsException) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
throw ReflectionException('Failed to create instance: $e');
|
throw ReflectionException('Failed to create instance: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
name: property.name,
|
||||||
property.name,
|
type: property.type,
|
||||||
PropertyMetadata(
|
isReadable: true,
|
||||||
name: property.name,
|
isWritable: !property.isFinal,
|
||||||
type: property.type,
|
|
||||||
isReadable: true,
|
|
||||||
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,
|
name: method.name,
|
||||||
method.name,
|
parameterTypes: method.parameterTypes,
|
||||||
MethodMetadata(
|
parameters: method.parameters,
|
||||||
name: method.name,
|
returnsVoid: method.returnsVoid,
|
||||||
parameterTypes: method.parameterTypes,
|
isStatic: method.isStatic,
|
||||||
parameters: method.parameters,
|
|
||||||
returnsVoid: method.returnsVoid,
|
|
||||||
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(
|
||||||
|
name: constructor.name,
|
||||||
/// Registers constructors and their factories for a type.
|
parameterTypes: constructor.parameterTypes,
|
||||||
static void _registerConstructors(
|
parameters: constructor.parameters,
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
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,15 +118,62 @@ 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);
|
||||||
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(
|
return wrapper.noSuchMethod(
|
||||||
Invocation.method(#call, args, namedArgs ?? {}),
|
Invocation.method(#call, args, const {}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
|
Loading…
Reference in a new issue