OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library kernel.transformations.reify.transformation.builder; |
| 6 |
| 7 import '../asts.dart'; |
| 8 import 'package:kernel/ast.dart'; |
| 9 import 'dart:collection' show LinkedHashMap; |
| 10 import 'binding.dart' show RuntimeLibrary; |
| 11 import 'package:kernel/core_types.dart' show CoreTypes; |
| 12 |
| 13 class Scope { |
| 14 final Map<String, TreeNode> names = <String, TreeNode>{}; |
| 15 |
| 16 bool nameAlreadyTaken(String name, TreeNode node) { |
| 17 TreeNode existing = names[name]; |
| 18 return existing != null && existing == node; |
| 19 } |
| 20 |
| 21 void add(String name, TreeNode node) { |
| 22 assert(!nameAlreadyTaken(name, node)); |
| 23 names[name] = node; |
| 24 } |
| 25 } |
| 26 |
| 27 class Namer { |
| 28 final Scope scope; |
| 29 Namer([Scope scope]) : this.scope = scope ?? new Scope(); |
| 30 |
| 31 String _getProposal(TreeNode node) { |
| 32 if (node is Class) { |
| 33 return node.name; |
| 34 } |
| 35 throw 'unsupported node: $node'; |
| 36 } |
| 37 |
| 38 String getNameFor(TreeNode node) { |
| 39 String base = _getProposal(node); |
| 40 int id = 0; |
| 41 String proposal = base; |
| 42 while (scope.nameAlreadyTaken(proposal, node)) { |
| 43 proposal = "$base${++id}"; |
| 44 } |
| 45 scope.add(proposal, node); |
| 46 return proposal; |
| 47 } |
| 48 } |
| 49 |
| 50 class RuntimeTypeSupportBuilder { |
| 51 // TODO(karlklose): group this together with other information about what |
| 52 // needs to be built. |
| 53 final LinkedHashMap<Class, int> reifiedClassIds = |
| 54 new LinkedHashMap<Class, int>(); |
| 55 |
| 56 int currentDeclarationId = 0; |
| 57 |
| 58 final Field declarations; |
| 59 |
| 60 final RuntimeLibrary rtiLibrary; |
| 61 |
| 62 final CoreTypes coreTypes; |
| 63 |
| 64 final DartType declarationType; |
| 65 |
| 66 RuntimeTypeSupportBuilder( |
| 67 RuntimeLibrary rtiLibrary, CoreTypes coreTypes, Library mainLibrary) |
| 68 : declarations = new Field(new Name(r"$declarations"), |
| 69 isFinal: true, isStatic: true, fileUri: mainLibrary.fileUri), |
| 70 declarationType = new InterfaceType(coreTypes.listClass, |
| 71 <DartType>[rtiLibrary.declarationClass.rawType]), |
| 72 rtiLibrary = rtiLibrary, |
| 73 coreTypes = coreTypes { |
| 74 mainLibrary.addMember(declarations); |
| 75 } |
| 76 |
| 77 int addDeclaration(Class cls) { |
| 78 return reifiedClassIds.putIfAbsent(cls, () { |
| 79 return currentDeclarationId++; |
| 80 }); |
| 81 } |
| 82 |
| 83 final Name indexOperatorName = new Name("[]"); |
| 84 |
| 85 MethodInvocation createArrayAccess(Expression target, int index) { |
| 86 return new MethodInvocation(target, indexOperatorName, |
| 87 new Arguments(<Expression>[new IntLiteral(index)])); |
| 88 } |
| 89 |
| 90 Expression createAccessDeclaration(Class cls) { |
| 91 return createArrayAccess(new StaticGet(declarations), addDeclaration(cls)); |
| 92 } |
| 93 |
| 94 Name getTypeTestTagName(Class cls) { |
| 95 return new Name('\$is\$${cls.name}'); |
| 96 } |
| 97 |
| 98 Name typeVariableGetterName(TypeParameter parameter) { |
| 99 Class cls = getEnclosingClass(parameter); |
| 100 return new Name("\$${cls.name}\$${parameter.name}"); |
| 101 } |
| 102 |
| 103 // A call to a constructor or factory of a class that we have not transformed |
| 104 // is wrapped in a call to `attachType`. |
| 105 Expression attachTypeToConstructorInvocation( |
| 106 InvocationExpression invocation, Member member) { |
| 107 assert(member is Procedure && member.kind == ProcedureKind.Factory || |
| 108 member is Constructor); |
| 109 Class targetClass = member.parent; |
| 110 assert(targetClass != null); |
| 111 DartType type = new InterfaceType(targetClass, invocation.arguments.types); |
| 112 return callAttachType(invocation, type); |
| 113 } |
| 114 |
| 115 Expression callAttachType(Expression expression, DartType type) { |
| 116 return new StaticInvocation(rtiLibrary.attachTypeFunction, |
| 117 new Arguments(<Expression>[expression, createRuntimeType(type)])); |
| 118 } |
| 119 |
| 120 Expression createGetType(Expression receiver, {needsInterceptor: true}) { |
| 121 if (receiver is ThisExpression || !needsInterceptor) { |
| 122 return new PropertyGet(receiver, rtiLibrary.runtimeTypeName); |
| 123 } |
| 124 return new StaticInvocation( |
| 125 rtiLibrary.interceptorFunction, new Arguments(<Expression>[receiver])); |
| 126 } |
| 127 |
| 128 Expression createGetTypeArguments(Expression typeObject) { |
| 129 return new StaticInvocation(rtiLibrary.typeArgumentsFunction, |
| 130 new Arguments(<Expression>[typeObject])); |
| 131 } |
| 132 |
| 133 // TODO(karlklose): consider adding a unique identifier for each test site. |
| 134 /// `receiver.[subtypeTestName]([type])` |
| 135 StaticInvocation createIsSubtypeOf( |
| 136 Expression receiver, Expression typeExpression, |
| 137 {targetHasTypeProperty: false}) { |
| 138 Expression receiverType = |
| 139 createGetType(receiver, needsInterceptor: !targetHasTypeProperty); |
| 140 return new StaticInvocation(rtiLibrary.isSubtypeOfFunction, |
| 141 new Arguments(<Expression>[receiverType, typeExpression])); |
| 142 } |
| 143 |
| 144 int getTypeVariableIndex(TypeParameter variable) { |
| 145 Class c = getEnclosingClass(variable); |
| 146 List<TypeParameter> variables = c.typeParameters; |
| 147 for (int i = 0; i < variables.length; ++i) { |
| 148 if (variables[i].name == variable.name) { |
| 149 return i; |
| 150 } |
| 151 } |
| 152 throw new Exception( |
| 153 "Type variable $variable not found in enclosing class $c"); |
| 154 } |
| 155 |
| 156 Expression createNewInterface( |
| 157 Expression declaration, Expression typeArgumentList) { |
| 158 List<Expression> arguments = <Expression>[declaration]; |
| 159 if (typeArgumentList != null) { |
| 160 arguments.add(typeArgumentList); |
| 161 } |
| 162 return new ConstructorInvocation( |
| 163 rtiLibrary.interfaceTypeConstructor, new Arguments(arguments)); |
| 164 } |
| 165 |
| 166 /// Returns `true` if [types] is a list of [TypeParameterType]s that exactly |
| 167 /// match the [TypeParameters] of the class they are defined in, i.e., |
| 168 /// for all 0 <= i < cls.typeParameters.length. |
| 169 /// types[i].parameter == cls.typeParameters[i]. |
| 170 bool matchesTypeParameters(List<DartType> types) { |
| 171 List<TypeParameter> parameters; |
| 172 for (int i = 0; i < types.length; ++i) { |
| 173 var type = types[i]; |
| 174 if (type is TypeParameterType) { |
| 175 if (parameters == null) { |
| 176 Class cls = getEnclosingClass(type.parameter); |
| 177 parameters = cls.typeParameters; |
| 178 if (parameters.length != types.length) return false; |
| 179 } |
| 180 if (type.parameter != parameters[i]) { |
| 181 return false; |
| 182 } |
| 183 } else { |
| 184 return false; |
| 185 } |
| 186 } |
| 187 return true; |
| 188 } |
| 189 |
| 190 // TODO(karlklose): Refactor into visitor. |
| 191 // TODO(karlklose): split this method in different strategies. |
| 192 /// Creates an expression to represent a runtime type instance of type [type]. |
| 193 Expression createRuntimeType(DartType type, |
| 194 {reifyTypeVariable: false, |
| 195 Expression createReference(Class cls), |
| 196 VariableDeclaration typeContext}) { |
| 197 Expression buildReifiedTypeVariable(TypeParameterType type) { |
| 198 Expression typeVariables = new PropertyGet( |
| 199 createReference(type.parameter.parent), |
| 200 rtiLibrary.variablesFieldName); |
| 201 return createArrayAccess( |
| 202 typeVariables, getTypeVariableIndex(type.parameter)); |
| 203 } |
| 204 |
| 205 Expression buildDirectTypeVariableAccess(TypeParameterType variable) { |
| 206 Class cls = getEnclosingClass(variable.parameter); |
| 207 return extractTypeVariable( |
| 208 cls, |
| 209 variable.parameter, |
| 210 getTypeVariableIndex(variable.parameter), |
| 211 new VariableGet(typeContext)); |
| 212 } |
| 213 |
| 214 Expression buildGetterTypeVariableAccess(TypeParameterType type) { |
| 215 return new PropertyGet( |
| 216 new ThisExpression(), typeVariableGetterName(type.parameter)); |
| 217 } |
| 218 |
| 219 Expression buildTypeVariable(TypeParameterType type) { |
| 220 if (reifyTypeVariable) { |
| 221 assert(typeContext == null); |
| 222 return buildReifiedTypeVariable(type); |
| 223 } else if (typeContext != null) { |
| 224 return buildDirectTypeVariableAccess(type); |
| 225 } else { |
| 226 return buildGetterTypeVariableAccess(type); |
| 227 } |
| 228 } |
| 229 |
| 230 createReference ??= createAccessDeclaration; |
| 231 |
| 232 /// Helper to make recursive invocation more readable. |
| 233 Expression createPart(DartType type) { |
| 234 return createRuntimeType(type, |
| 235 reifyTypeVariable: reifyTypeVariable, |
| 236 createReference: createReference, |
| 237 typeContext: typeContext); |
| 238 } |
| 239 |
| 240 if (type is InterfaceType || type is Supertype) { |
| 241 InterfaceType interfaceType = null; |
| 242 if (type is InterfaceType) { |
| 243 interfaceType = type; |
| 244 } else { |
| 245 interfaceType = (type as Supertype).asInterfaceType; |
| 246 } |
| 247 Class cls = interfaceType.classNode; |
| 248 Expression declaration = createReference(cls); |
| 249 List<DartType> typeArguments = interfaceType.typeArguments; |
| 250 Expression typeArgumentList; |
| 251 if (typeArguments.isNotEmpty) { |
| 252 if (!reifyTypeVariable && matchesTypeParameters(typeArguments)) { |
| 253 // The type argument list corresponds to the list of type parameters |
| 254 // and we are not in "declaration emitter" mode, we can reuse the |
| 255 // type argument vector. |
| 256 TypeParameterType parameterType = typeArguments[0]; |
| 257 Class cls = parameterType.parameter.parent; |
| 258 Expression typeObject = typeContext != null |
| 259 ? new VariableGet(typeContext) |
| 260 : createGetType(new ThisExpression()); |
| 261 typeArgumentList = |
| 262 createGetTypeArguments(createCallAsInstanceOf(typeObject, cls)); |
| 263 } else { |
| 264 typeArgumentList = |
| 265 new ListLiteral(typeArguments.map(createPart).toList()); |
| 266 } |
| 267 } |
| 268 return createNewInterface(declaration, typeArgumentList); |
| 269 } else if (type is DynamicType) { |
| 270 return new ConstructorInvocation( |
| 271 rtiLibrary.dynamicTypeConstructor, new Arguments([]), |
| 272 isConst: true); |
| 273 } else if (type is TypeParameterType) { |
| 274 return buildTypeVariable(type); |
| 275 } else if (type is FunctionType) { |
| 276 FunctionType functionType = type; |
| 277 Expression returnType = createPart(functionType.returnType); |
| 278 List<Expression> encodedParameterTypes = |
| 279 functionType.positionalParameters.map(createPart).toList(); |
| 280 List<NamedType> namedParameters = functionType.namedParameters; |
| 281 int data; |
| 282 if (namedParameters.isNotEmpty) { |
| 283 for (NamedType param in namedParameters) { |
| 284 encodedParameterTypes.add(new StringLiteral(param.name)); |
| 285 encodedParameterTypes.add(createPart(param.type)); |
| 286 } |
| 287 data = functionType.namedParameters.length << 1 | 1; |
| 288 } else { |
| 289 data = (functionType.positionalParameters.length - |
| 290 functionType.requiredParameterCount) << |
| 291 1; |
| 292 } |
| 293 Expression functionTypeExpression = new ConstructorInvocation( |
| 294 rtiLibrary.interfaceTypeConstructor, |
| 295 new Arguments( |
| 296 <Expression>[createReference(coreTypes.functionClass)])); |
| 297 Arguments arguments = new Arguments(<Expression>[ |
| 298 functionTypeExpression, |
| 299 returnType, |
| 300 new IntLiteral(data), |
| 301 new ListLiteral(encodedParameterTypes) |
| 302 ]); |
| 303 return new ConstructorInvocation( |
| 304 rtiLibrary.functionTypeConstructor, arguments); |
| 305 } else if (type is VoidType) { |
| 306 return new ConstructorInvocation( |
| 307 rtiLibrary.voidTypeConstructor, new Arguments(<Expression>[])); |
| 308 } |
| 309 return new InvalidExpression(); |
| 310 } |
| 311 |
| 312 Expression createCallAsInstanceOf(Expression receiver, Class cls) { |
| 313 return new StaticInvocation(rtiLibrary.asInstanceOfFunction, |
| 314 new Arguments(<Expression>[receiver, createAccessDeclaration(cls)])); |
| 315 } |
| 316 |
| 317 /// `get get$<variable-name> => <get-type>.arguments[<variable-index>]` |
| 318 Member createTypeVariableGetter( |
| 319 Class cls, TypeParameter variable, int index) { |
| 320 Expression type = createGetType(new ThisExpression()); |
| 321 Expression argument = extractTypeVariable(cls, variable, index, type); |
| 322 return new Procedure( |
| 323 typeVariableGetterName(variable), |
| 324 ProcedureKind.Getter, |
| 325 new FunctionNode(new ReturnStatement(argument), |
| 326 returnType: rtiLibrary.typeType), |
| 327 fileUri: cls.fileUri); |
| 328 } |
| 329 |
| 330 Expression extractTypeVariable( |
| 331 Class cls, TypeParameter variable, int index, Expression typeObject) { |
| 332 Expression type = createCallAsInstanceOf(typeObject, cls); |
| 333 Expression arguments = new StaticInvocation( |
| 334 rtiLibrary.typeArgumentsFunction, new Arguments(<Expression>[type])); |
| 335 // TODO(karlklose): use the global index instead of the local one. |
| 336 return createArrayAccess(arguments, index); |
| 337 } |
| 338 |
| 339 void insertAsFirstArgument(Arguments arguments, Expression expression) { |
| 340 expression.parent = arguments; |
| 341 arguments.positional.insert(0, expression); |
| 342 } |
| 343 |
| 344 /// Creates a call to the `init` function that completes the definition of a |
| 345 /// class by setting its (direct) supertypes. |
| 346 Expression createCallInit( |
| 347 VariableDeclaration declarations, |
| 348 int index, |
| 349 InterfaceType supertype, |
| 350 List<InterfaceType> interfaces, |
| 351 FunctionType callableType) { |
| 352 /// Helper to create a reference to the declaration in the declaration |
| 353 /// list instead of the field to avoid cycles if that field's |
| 354 /// initialization depends on the class we are currently initializing. |
| 355 Expression createReference(Class declaration) { |
| 356 int id = reifiedClassIds[declaration]; |
| 357 return createArrayAccess(new VariableGet(declarations), id); |
| 358 } |
| 359 |
| 360 bool isNotMarkerInterface(InterfaceType interface) { |
| 361 return interface.classNode != rtiLibrary.markerClass; |
| 362 } |
| 363 |
| 364 Expression createLocalType(DartType type) { |
| 365 if (type == null) return null; |
| 366 return createRuntimeType(type, |
| 367 reifyTypeVariable: true, createReference: createReference); |
| 368 } |
| 369 |
| 370 Expression supertypeExpression = |
| 371 supertype == null ? new NullLiteral() : createLocalType(supertype); |
| 372 |
| 373 List<Expression> interfaceTypes = interfaces |
| 374 .where(isNotMarkerInterface) |
| 375 .map(createLocalType) |
| 376 .toList(growable: false); |
| 377 |
| 378 Expression callableTypeExpression = createLocalType(callableType); |
| 379 |
| 380 List<Expression> arguments = <Expression>[ |
| 381 new VariableGet(declarations), |
| 382 new IntLiteral(index), |
| 383 supertypeExpression, |
| 384 ]; |
| 385 |
| 386 if (interfaceTypes.isNotEmpty || callableTypeExpression != null) { |
| 387 arguments.add(new ListLiteral(interfaceTypes)); |
| 388 if (callableTypeExpression != null) { |
| 389 arguments.add(callableTypeExpression); |
| 390 } |
| 391 } |
| 392 |
| 393 return new StaticInvocation( |
| 394 rtiLibrary.initFunction, new Arguments(arguments)); |
| 395 } |
| 396 |
| 397 Expression createDeclarationsInitializer() { |
| 398 List<Statement> statements = <Statement>[]; |
| 399 // Call function to allocate the class declarations given the names and |
| 400 // number of type variables of the classes. |
| 401 Namer classNamer = new Namer(); |
| 402 List<Expression> names = <Expression>[]; |
| 403 List<Expression> parameterCount = <Expression>[]; |
| 404 reifiedClassIds.keys.forEach((Class c) { |
| 405 names.add(new StringLiteral(classNamer.getNameFor(c))); |
| 406 parameterCount.add(new IntLiteral(c.typeParameters.length)); |
| 407 }); |
| 408 Expression namesList = new ListLiteral(names); |
| 409 Expression parameterCountList = new ListLiteral(parameterCount); |
| 410 StaticInvocation callAllocate = new StaticInvocation( |
| 411 rtiLibrary.allocateDeclarationsFunction, |
| 412 new Arguments(<Expression>[namesList, parameterCountList])); |
| 413 |
| 414 VariableDeclaration parameter = |
| 415 new VariableDeclaration("d", type: declarationType); |
| 416 |
| 417 reifiedClassIds.forEach((Class cls, int id) { |
| 418 if (cls == rtiLibrary.markerClass) return; |
| 419 |
| 420 // If the class declares a `call` method, translate the signature to a |
| 421 // reified type. |
| 422 FunctionType callableType; |
| 423 Procedure call = cls.procedures.firstWhere( |
| 424 (Procedure p) => p.name.name == "call", |
| 425 orElse: () => null); |
| 426 if (call != null) { |
| 427 FunctionNode function = call.function; |
| 428 |
| 429 final namedParameters = new List<NamedType>(); |
| 430 for (VariableDeclaration v in function.namedParameters) { |
| 431 namedParameters.add(new NamedType(v.name, v.type)); |
| 432 } |
| 433 |
| 434 List<DartType> positionalArguments = function.positionalParameters |
| 435 .map((VariableDeclaration v) => v.type) |
| 436 .toList(); |
| 437 callableType = new FunctionType( |
| 438 positionalArguments, function.returnType, |
| 439 namedParameters: namedParameters, |
| 440 requiredParameterCount: function.requiredParameterCount); |
| 441 } |
| 442 statements.add(new ExpressionStatement(createCallInit( |
| 443 parameter, |
| 444 id, |
| 445 cls.supertype?.asInterfaceType, |
| 446 cls.implementedTypes.map((Supertype type) => type?.asInterfaceType), |
| 447 callableType))); |
| 448 }); |
| 449 |
| 450 statements.add(new ReturnStatement(new VariableGet(parameter))); |
| 451 |
| 452 Expression function = new FunctionExpression(new FunctionNode( |
| 453 new Block(statements), |
| 454 positionalParameters: <VariableDeclaration>[parameter], |
| 455 returnType: declarationType)); |
| 456 |
| 457 return new MethodInvocation( |
| 458 function, new Name("call"), new Arguments(<Expression>[callAllocate])); |
| 459 } |
| 460 |
| 461 void createDeclarations() { |
| 462 /// Recursively find all referenced classes in [type]. |
| 463 void collectNewReferencedClasses(DartType type, Set<Class> newClasses) { |
| 464 if (type is InterfaceType || type is Supertype) { |
| 465 InterfaceType interfaceType = null; |
| 466 if (type is InterfaceType) { |
| 467 interfaceType = type; |
| 468 } else { |
| 469 interfaceType = (type as Supertype).asInterfaceType; |
| 470 } |
| 471 Class cls = interfaceType.classNode; |
| 472 if (!reifiedClassIds.containsKey(cls) && !newClasses.contains(cls)) { |
| 473 newClasses.add(cls); |
| 474 } |
| 475 |
| 476 interfaceType.typeArguments.forEach((DartType argument) { |
| 477 collectNewReferencedClasses(argument, newClasses); |
| 478 }); |
| 479 } |
| 480 // TODO(karlklose): function types |
| 481 } |
| 482 |
| 483 Iterable<Class> classes = reifiedClassIds.keys; |
| 484 while (classes.isNotEmpty) { |
| 485 Set<Class> newClasses = new Set<Class>(); |
| 486 for (Class c in classes) { |
| 487 collectNewReferencedClasses(c.supertype?.asInterfaceType, newClasses); |
| 488 c.implementedTypes.forEach((Supertype supertype) { |
| 489 collectNewReferencedClasses(supertype?.asInterfaceType, newClasses); |
| 490 }); |
| 491 } |
| 492 for (Class newClass in newClasses) { |
| 493 // Make sure that there is a declaration field for the class and its |
| 494 // library's declaration list is setup. |
| 495 addDeclaration(newClass); |
| 496 } |
| 497 classes = newClasses; |
| 498 } |
| 499 Expression initializer = createDeclarationsInitializer(); |
| 500 initializer.parent = declarations; |
| 501 declarations.initializer = initializer; |
| 502 declarations.type = declarationType; |
| 503 } |
| 504 |
| 505 Procedure createGetter( |
| 506 Name name, Expression expression, Class cls, DartType type) { |
| 507 return new Procedure(name, ProcedureKind.Getter, |
| 508 new FunctionNode(new ReturnStatement(expression), returnType: type), |
| 509 fileUri: cls.fileUri); |
| 510 } |
| 511 } |
OLD | NEW |