From 9425b3ac9a36476f54b5beda8239c09b69102d51 Mon Sep 17 00:00:00 2001 From: Patrick Stewart Date: Fri, 29 Nov 2024 18:58:18 -0700 Subject: [PATCH] update: 42 passing 2 failing --- .../lib/src/core/runtime_reflector.dart | 138 ++++++++++-------- packages/reflection/lib/src/core/scanner.dart | 120 ++++++++++----- 2 files changed, 159 insertions(+), 99 deletions(-) diff --git a/packages/reflection/lib/src/core/runtime_reflector.dart b/packages/reflection/lib/src/core/runtime_reflector.dart index 696056a..0f9d434 100644 --- a/packages/reflection/lib/src/core/runtime_reflector.dart +++ b/packages/reflection/lib/src/core/runtime_reflector.dart @@ -34,77 +34,93 @@ class RuntimeReflector { } /// Creates a new instance of a type using reflection. - Object createInstance( + dynamic createInstance( Type type, { - String constructorName = '', - List positionalArgs = const [], - Map namedArgs = const {}, + dynamic positionalArgs, + Map? 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') && - factoryStr.contains('Map 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'); } } diff --git a/packages/reflection/lib/src/core/scanner.dart b/packages/reflection/lib/src/core/scanner.dart index 57641cc..3b41176 100644 --- a/packages/reflection/lib/src/core/scanner.dart +++ b/packages/reflection/lib/src/core/scanner.dart @@ -58,8 +58,13 @@ class Scanner { // Private constructor to prevent instantiation Scanner._(); + // Cache for type metadata + static final Map _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 = {}; + final methodMetadata = {}; + final 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 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 args, [Map? namedArgs]) { + + // For constructors with named parameters + if (constructor.parameters.any((p) => p.isNamed)) { + return (List args, [Map? 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),