blob: 4c9ecde11f7962ca7488d9a8d84f525d69ede7a5 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:core';
import 'dart:core' as core;
import 'dart:mirrors';
import 'queries/index.dart';
bool _parseBool(String val) {
if (val == 'true') return true;
if (val == 'false') return false;
throw Exception('$val is not a valid value for boolean');
}
class ReflectQuery {
static String nameType(Type type) {
final classMirror = reflectClass(type);
return Query.stripQuerySuffix(classMirror.simpleName.toString());
}
static Query instantiate(QueryFactory f, Map<String, String> arguments) {
final classMirror = reflectClass(f.type);
final constructors = List<MethodMirror>.from(classMirror.declarations.values
.whereType<MethodMirror>()
.where((decl) => decl.isConstructor));
if (arguments.isNotEmpty && constructors.isEmpty) {
throw Exception(
'$f has no constructors, but specified arguments $arguments');
}
for (final constructor in constructors) {
if (constructor is MethodMirror) {
final List<ParameterMirror> parameters = constructor.parameters;
if (parameters.any((param) => !param.isNamed)) {
throw Exception('$constructor must only use named parameters');
}
final parameterNames =
parameters.where((param) => param.isNamed).map((e) => e.simpleName);
if (arguments.keys.any((arg) => !parameterNames.contains(Symbol(arg))))
continue;
final resolvedArgs =
Map<Symbol, dynamic>.fromEntries(arguments.entries.map((entry) {
final key = entry.key, value = entry.value;
final param =
parameters.firstWhere((param) => param.simpleName == Symbol(key));
final name = param.simpleName;
switch (param.type.simpleName) {
case #int:
return MapEntry<Symbol, dynamic>(name, int.parse(value));
case #bool:
return MapEntry<Symbol, dynamic>(name, _parseBool(value));
case #String:
return MapEntry<Symbol, dynamic>(name, value);
default:
throw Exception('Unexpected type ${param.type.simpleName}');
}
}));
return classMirror
.newInstance(core.Symbol.empty, [], resolvedArgs)
.reflectee;
}
}
final constructorDesc = constructors
.map((c) => describeConstructor(c, f))
.map((s) => ' - tried $s')
.join('\n');
throw Exception(
'Did not find suitable constructor for ${f.type} with $arguments.\n\n'
'$constructorDesc\n');
}
/// Prints the method signature for `constructor`.
static String describeConstructor(MethodMirror constructor, QueryFactory f) {
final params = describeArgDeclarations(constructor);
if (params.isEmpty) {
return '${f.name}()';
} else {
return '${f.name}({${params.join(", ")}})';
}
}
static Iterable<String> describeArgDeclarations(MethodMirror constructor) =>
constructor.parameters.map((param) {
final typeName = MirrorSystem.getName(param.type.simpleName);
final paramName = MirrorSystem.getName(param.simpleName);
var str = '$typeName $paramName';
if (param.hasDefaultValue) {
str += ' = ${param.defaultValue.reflectee}';
}
return str;
});
static Iterable<String> describeQueryConstructors(QueryFactory f) {
final classMirror = reflectClass(f.type);
final constructors = List<MethodMirror>.from(classMirror.declarations.values
.whereType<MethodMirror>()
.where((decl) => decl.isConstructor));
return constructors.map((c) => describeConstructor(c, f));
}
static bool hasCustomArguments(QueryFactory f) {
final classMirror = reflectClass(f.type);
final constructors = List<MethodMirror>.from(classMirror.declarations.values
.whereType<MethodMirror>()
.where((decl) => decl.isConstructor));
return constructors.any((c) => c.parameters.isNotEmpty);
}
}