Skip to content

Commit ea0ad27

Browse files
committed
adds cast method
1 parent 7569e10 commit ea0ad27

File tree

9 files changed

+457
-8
lines changed

9 files changed

+457
-8
lines changed

lib/runtime.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ import 'package:runtime/src/compiler.dart';
66
import 'package:runtime/src/mirror_context.dart';
77

88
export 'src/analyzer.dart';
9-
export 'package:runtime/src/context.dart';
9+
export 'src/context.dart';
1010
export 'src/build.dart';
1111
export 'src/compiler.dart';
1212
export 'src/file_system.dart';
1313
export 'src/generator.dart';
1414
export 'src/build_context.dart';
1515
export 'src/build_manager.dart';
1616
export 'src/mirror_context.dart';
17+
export 'src/exceptions.dart';
1718

1819
/// Compiler for the runtime package itself.
1920
///

lib/src/analyzer.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import 'dart:io';
2-
3-
import 'package:analyzer/dart/analysis/analysis_context.dart';
41
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
52
import 'package:analyzer/dart/analysis/results.dart';
63
import 'package:analyzer/dart/ast/ast.dart';
@@ -43,7 +40,8 @@ class CodeAnalyzer {
4340
}
4441
}
4542

46-
throw ArgumentError("'uri' could not be resolved");
43+
throw ArgumentError("'uri' could not be resolved (contexts: "
44+
"${contexts.contexts.map((c) => c.contextRoot.root.toUri()).join(", ")})");
4745
}
4846

4947
ClassDeclaration getClassFromFile(String className, Uri fileUri) {
@@ -70,8 +68,8 @@ class CodeAnalyzer {
7068

7169
final unit = contexts.contextFor(path).currentSession.getParsedUnit(path);
7270
if (unit.errors.isNotEmpty) {
73-
throw StateError(
74-
"Project file '${path}' could not be analysed for the following reasons:\n\t${unit.errors.join("\n\t")}");
71+
throw StateError("Project file '${path}' could not be analysed for the "
72+
"following reasons:\n\t${unit.errors.join("\n\t")}");
7573
}
7674

7775
return unit.unit;

lib/src/context.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ abstract class RuntimeContext {
3131
/// `Subclass` must also have a runtime. The runtime objects for both `Subclass` and `Base`
3232
/// must be the same type.
3333
dynamic operator [](Type type) => runtimes[type];
34+
35+
T coerce<T>(dynamic input);
3436
}
3537

3638
class RuntimeCollection {

lib/src/exceptions.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class TypeCoercionException implements Exception {
2+
TypeCoercionException(this.expectedType, this.actualType);
3+
4+
final Type expectedType;
5+
final Type actualType;
6+
7+
@override
8+
String toString({bool includeActualType = false}) {
9+
final trailingString = includeActualType ? " (input is '$actualType')" : "";
10+
return "input is not expected type '$expectedType'$trailingString";
11+
}
12+
}

lib/src/mirror_coerce.dart

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'dart:mirrors';
2+
3+
import 'package:runtime/src/exceptions.dart';
4+
5+
dynamic runtimeCast(dynamic object, TypeMirror intoType) {
6+
final exceptionToThrow =
7+
TypeCoercionException(intoType.reflectedType, object.runtimeType as Type);
8+
9+
try {
10+
final objectType = reflect(object).type;
11+
if (objectType.isAssignableTo(intoType)) {
12+
return object;
13+
}
14+
15+
if (intoType.isSubtypeOf(reflectType(List))) {
16+
if (object is! List) {
17+
throw exceptionToThrow;
18+
}
19+
20+
final elementType = intoType.typeArguments.first;
21+
final elements = (object as List).map((e) => runtimeCast(e, elementType));
22+
return (intoType as ClassMirror).newInstance(#from, [elements]).reflectee;
23+
} else if (intoType.isSubtypeOf(reflectType(Map, [String, dynamic]))) {
24+
if (object is! Map<String, dynamic>) {
25+
throw exceptionToThrow;
26+
}
27+
28+
final output = (intoType as ClassMirror)
29+
.newInstance(const Symbol(""), []).reflectee as Map<String, dynamic>;
30+
final valueType = intoType.typeArguments.last;
31+
(object as Map<String, dynamic>).forEach((key, val) {
32+
output[key] = runtimeCast(val, valueType);
33+
});
34+
return output;
35+
}
36+
} on TypeError {
37+
throw exceptionToThrow;
38+
} on CastError {
39+
throw exceptionToThrow;
40+
} on TypeCoercionException {
41+
throw exceptionToThrow;
42+
}
43+
44+
throw exceptionToThrow;
45+
}
46+
47+
bool isTypeFullyPrimitive(TypeMirror type) {
48+
if (type == reflectType(dynamic)) {
49+
return true;
50+
}
51+
52+
if (type.isSubtypeOf(reflectType(List))) {
53+
return isTypeFullyPrimitive(type.typeArguments.first);
54+
} else if (type.isSubtypeOf(reflectType(Map))) {
55+
return isTypeFullyPrimitive(type.typeArguments.first) &&
56+
isTypeFullyPrimitive(type.typeArguments.last);
57+
}
58+
59+
if (type.isSubtypeOf(reflectType(num))) {
60+
return true;
61+
}
62+
63+
if (type.isSubtypeOf(reflectType(String))) {
64+
return true;
65+
}
66+
67+
if (type.isSubtypeOf(reflectType(bool))) {
68+
return true;
69+
}
70+
71+
return false;
72+
}

lib/src/mirror_context.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:mirrors';
22

33
import 'package:runtime/src/context.dart';
44
import 'package:runtime/src/compiler.dart';
5+
import 'package:runtime/src/mirror_coerce.dart';
56

67
RuntimeContext instance = MirrorContext._();
78

@@ -57,6 +58,11 @@ class MirrorContext extends RuntimeContext {
5758
return true;
5859
}).toList();
5960
}
61+
62+
@override
63+
T coerce<T>(dynamic input) {
64+
return runtimeCast(input, reflectType(T));
65+
}
6066
}
6167

6268
T firstMetadataOfType<T>(DeclarationMirror dm, {TypeMirror dynamicType}) {

lib/src/slow_coerce.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:runtime/src/exceptions.dart';
2+
3+
const String _listPrefix = "List<";
4+
const String _mapPrefix = "Map<String,";
5+
6+
T cast<T>(dynamic input) {
7+
if (input == null) {
8+
return null;
9+
}
10+
11+
try {
12+
final typeString = T.toString();
13+
if (typeString.startsWith(_listPrefix)) {
14+
if (input is! List) {
15+
throw TypeError();
16+
}
17+
18+
if (typeString == "List<int>") {
19+
return List<int>.from(input) as T;
20+
} else if (typeString == "List<num>") {
21+
return List<num>.from(input) as T;
22+
} else if (typeString == "List<double>") {
23+
return List<double>.from(input) as T;
24+
} else if (typeString == "List<String>") {
25+
return List<String>.from(input) as T;
26+
} else if (typeString == "List<bool>") {
27+
return List<bool>.from(input) as T;
28+
} else if (typeString == "List<Map<String, dynamic>>") {
29+
final objects = <Map<String, dynamic>>[];
30+
(input as List).forEach((o) {
31+
if (o == null) {
32+
objects.add(null);
33+
} else {
34+
if (o is! Map<String, dynamic>) {
35+
throw TypeError();
36+
}
37+
objects.add(o);
38+
}
39+
});
40+
return objects as T;
41+
}
42+
} else if (typeString.startsWith(_mapPrefix)) {
43+
if (input is! Map) {
44+
throw TypeError();
45+
}
46+
47+
final inputMap = input as Map<String, dynamic>;
48+
if (typeString == "Map<String, int>") {
49+
return Map<String, int>.from(inputMap) as T;
50+
} else if (typeString == "Map<String, num>") {
51+
return Map<String, num>.from(inputMap) as T;
52+
} else if (typeString == "Map<String, double>") {
53+
return Map<String, double>.from(inputMap) as T;
54+
} else if (typeString == "Map<String, String>") {
55+
return Map<String, String>.from(inputMap) as T;
56+
} else if (typeString == "Map<String, bool>") {
57+
return Map<String, bool>.from(inputMap) as T;
58+
}
59+
}
60+
61+
return input as T;
62+
} on CastError {
63+
throw TypeCoercionException(T, input.runtimeType as Type);
64+
} on TypeError {
65+
throw TypeCoercionException(T, input.runtimeType as Type);
66+
}
67+
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: runtime
22
description: Provides behaviors and base types for packages that can use mirrors and be AOT compiled.
3-
version: 1.0.0-7
3+
version: 1.0.0-8
44
author: Joe Conway <[email protected]>
55
homepage: https://stablekernel.com
66

0 commit comments

Comments
 (0)