| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino 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.md file. | |
| 4 | |
| 5 library fletchc.fletch_backend; | |
| 6 | |
| 7 import 'dart:async' show | |
| 8 Future; | |
| 9 | |
| 10 import 'dart:collection' show | |
| 11 Queue; | |
| 12 | |
| 13 import 'package:compiler/src/common/backend_api.dart' show | |
| 14 Backend, | |
| 15 ImpactTransformer; | |
| 16 | |
| 17 import 'package:compiler/src/common/tasks.dart' show | |
| 18 CompilerTask; | |
| 19 | |
| 20 import 'package:compiler/src/enqueue.dart' show | |
| 21 Enqueuer, | |
| 22 ResolutionEnqueuer; | |
| 23 | |
| 24 import 'package:compiler/src/diagnostics/messages.dart' show | |
| 25 MessageKind; | |
| 26 | |
| 27 import 'package:compiler/src/diagnostics/diagnostic_listener.dart' show | |
| 28 DiagnosticMessage; | |
| 29 | |
| 30 import 'package:compiler/src/common/registry.dart' show | |
| 31 Registry; | |
| 32 | |
| 33 import 'package:compiler/src/dart_types.dart' show | |
| 34 DartType, | |
| 35 InterfaceType; | |
| 36 | |
| 37 import 'package:compiler/src/tree/tree.dart' show | |
| 38 DartString, | |
| 39 EmptyStatement, | |
| 40 Expression; | |
| 41 | |
| 42 import 'package:compiler/src/elements/elements.dart' show | |
| 43 AbstractFieldElement, | |
| 44 AstElement, | |
| 45 ClassElement, | |
| 46 ConstructorElement, | |
| 47 Element, | |
| 48 ExecutableElement, | |
| 49 FieldElement, | |
| 50 FormalElement, | |
| 51 FunctionElement, | |
| 52 FunctionSignature, | |
| 53 FunctionTypedElement, | |
| 54 LibraryElement, | |
| 55 MemberElement, | |
| 56 Name, | |
| 57 ParameterElement, | |
| 58 PublicName; | |
| 59 | |
| 60 import 'package:compiler/src/universe/selector.dart' show | |
| 61 Selector; | |
| 62 | |
| 63 import 'package:compiler/src/universe/use.dart' show | |
| 64 DynamicUse, | |
| 65 StaticUse, | |
| 66 TypeUse, | |
| 67 TypeUseKind; | |
| 68 | |
| 69 import 'package:compiler/src/universe/call_structure.dart' show | |
| 70 CallStructure; | |
| 71 | |
| 72 import 'package:compiler/src/common.dart' show | |
| 73 Spannable; | |
| 74 | |
| 75 import 'package:compiler/src/elements/modelx.dart' show | |
| 76 FunctionElementX; | |
| 77 | |
| 78 import 'package:compiler/src/dart_backend/dart_backend.dart' show | |
| 79 DartConstantTask; | |
| 80 | |
| 81 import 'package:compiler/src/constants/constant_system.dart' show | |
| 82 ConstantSystem; | |
| 83 | |
| 84 import 'package:compiler/src/compile_time_constants.dart' show | |
| 85 BackendConstantEnvironment; | |
| 86 | |
| 87 import 'package:compiler/src/constants/values.dart' show | |
| 88 ConstantValue, | |
| 89 ConstructedConstantValue, | |
| 90 FunctionConstantValue, | |
| 91 ListConstantValue, | |
| 92 MapConstantValue, | |
| 93 StringConstantValue; | |
| 94 | |
| 95 import 'package:compiler/src/constants/expressions.dart' show | |
| 96 ConstantExpression; | |
| 97 | |
| 98 import 'package:compiler/src/resolution/tree_elements.dart' show | |
| 99 TreeElements; | |
| 100 | |
| 101 import 'package:compiler/src/library_loader.dart' show | |
| 102 LibraryLoader; | |
| 103 | |
| 104 import 'package:persistent/persistent.dart' show | |
| 105 PersistentMap; | |
| 106 | |
| 107 import 'fletch_function_builder.dart' show | |
| 108 FletchFunctionBuilder; | |
| 109 | |
| 110 import 'fletch_class_builder.dart' show | |
| 111 FletchClassBuilder; | |
| 112 | |
| 113 import 'fletch_system_builder.dart' show | |
| 114 FletchSystemBuilder; | |
| 115 | |
| 116 import '../incremental_backend.dart' show | |
| 117 IncrementalFletchBackend; | |
| 118 | |
| 119 import 'fletch_enqueuer.dart' show | |
| 120 FletchEnqueueTask, | |
| 121 shouldReportEnqueuingOfElement; | |
| 122 | |
| 123 import 'fletch_registry.dart' show | |
| 124 ClosureKind, | |
| 125 FletchRegistry; | |
| 126 | |
| 127 import 'diagnostic.dart' show | |
| 128 throwInternalError; | |
| 129 | |
| 130 import 'package:compiler/src/common/names.dart' show | |
| 131 Identifiers, | |
| 132 Names; | |
| 133 | |
| 134 import 'package:compiler/src/universe/world_impact.dart' show | |
| 135 TransformedWorldImpact, | |
| 136 WorldImpact, | |
| 137 WorldImpactBuilder; | |
| 138 | |
| 139 import 'class_debug_info.dart'; | |
| 140 import 'codegen_visitor.dart'; | |
| 141 import 'debug_info.dart'; | |
| 142 import 'debug_info_constructor_codegen.dart'; | |
| 143 import 'debug_info_function_codegen.dart'; | |
| 144 import 'debug_info_lazy_field_initializer_codegen.dart'; | |
| 145 import 'fletch_context.dart'; | |
| 146 import 'fletch_selector.dart'; | |
| 147 import 'function_codegen.dart'; | |
| 148 import 'lazy_field_initializer_codegen.dart'; | |
| 149 import 'constructor_codegen.dart'; | |
| 150 import 'closure_environment.dart'; | |
| 151 | |
| 152 import '../bytecodes.dart'; | |
| 153 import '../vm_commands.dart'; | |
| 154 import '../fletch_system.dart'; | |
| 155 import 'package:compiler/src/common/resolution.dart'; | |
| 156 | |
| 157 const FletchSystem BASE_FLETCH_SYSTEM = const FletchSystem( | |
| 158 const PersistentMap<int, FletchFunction>(), | |
| 159 const PersistentMap<Element, FletchFunction>(), | |
| 160 const PersistentMap<ConstructorElement, FletchFunction>(), | |
| 161 const PersistentMap<int, int>(), | |
| 162 const PersistentMap<int, FletchClass>(), | |
| 163 const PersistentMap<ClassElement, FletchClass>(), | |
| 164 const PersistentMap<int, FletchConstant>(), | |
| 165 const PersistentMap<ConstantValue, FletchConstant>(), | |
| 166 const PersistentMap<int, String>(), | |
| 167 const PersistentMap<int, int>(), | |
| 168 const PersistentMap<int, int>(), | |
| 169 const PersistentMap<ParameterStubSignature, FletchFunction>()); | |
| 170 | |
| 171 class FletchBackend extends Backend | |
| 172 implements IncrementalFletchBackend { | |
| 173 static const String growableListName = '_GrowableList'; | |
| 174 static const String constantListName = '_ConstantList'; | |
| 175 static const String constantByteListName = '_ConstantByteList'; | |
| 176 static const String constantMapName = '_ConstantMap'; | |
| 177 static const String fletchNoSuchMethodErrorName = 'FletchNoSuchMethodError'; | |
| 178 static const String noSuchMethodName = '_noSuchMethod'; | |
| 179 static const String noSuchMethodTrampolineName = '_noSuchMethodTrampoline'; | |
| 180 | |
| 181 final FletchContext context; | |
| 182 | |
| 183 final DartConstantTask constantCompilerTask; | |
| 184 | |
| 185 /// Constructors that need to have an initilizer compiled. See | |
| 186 /// [compilePendingConstructorInitializers]. | |
| 187 final Queue<FletchFunctionBuilder> pendingConstructorInitializers = | |
| 188 new Queue<FletchFunctionBuilder>(); | |
| 189 | |
| 190 final Set<FunctionElement> externals = new Set<FunctionElement>(); | |
| 191 | |
| 192 // TODO(ahe): This should be queried from World. | |
| 193 final Map<ClassElement, Set<ClassElement>> directSubclasses = | |
| 194 <ClassElement, Set<ClassElement>>{}; | |
| 195 | |
| 196 /// Set of classes that have special meaning to the Fletch VM. They're | |
| 197 /// created using [PushBuiltinClass] instead of [PushNewClass]. | |
| 198 // TODO(ahe): Move this to FletchSystem? | |
| 199 final Set<ClassElement> builtinClasses = new Set<ClassElement>(); | |
| 200 | |
| 201 // TODO(ahe): This should be invalidated by a new [FletchSystem]. | |
| 202 final Map<MemberElement, ClosureEnvironment> closureEnvironments = | |
| 203 <MemberElement, ClosureEnvironment>{}; | |
| 204 | |
| 205 // TODO(ahe): This should be moved to [FletchSystem]. | |
| 206 final Map<FunctionElement, FletchClassBuilder> closureClasses = | |
| 207 <FunctionElement, FletchClassBuilder>{}; | |
| 208 | |
| 209 // TODO(ahe): This should be moved to [FletchSystem]. | |
| 210 final Map<FieldElement, FletchFunctionBuilder> lazyFieldInitializers = | |
| 211 <FieldElement, FletchFunctionBuilder>{}; | |
| 212 | |
| 213 // TODO(ahe): This should be moved to [FletchSystem]. | |
| 214 Map<FletchClassBuilder, FletchFunctionBuilder> tearoffFunctions; | |
| 215 | |
| 216 FletchCompilerImplementation get compiler => super.compiler; | |
| 217 | |
| 218 LibraryElement fletchSystemLibrary; | |
| 219 LibraryElement fletchFFILibrary; | |
| 220 LibraryElement collectionLibrary; | |
| 221 LibraryElement mathLibrary; | |
| 222 LibraryElement get asyncLibrary => compiler.asyncLibrary; | |
| 223 LibraryElement fletchLibrary; | |
| 224 | |
| 225 FunctionElement fletchSystemEntry; | |
| 226 | |
| 227 FunctionElement fletchExternalInvokeMain; | |
| 228 | |
| 229 FunctionElement fletchExternalYield; | |
| 230 | |
| 231 FunctionElement fletchExternalNativeError; | |
| 232 | |
| 233 FunctionElement fletchExternalCoroutineChange; | |
| 234 | |
| 235 FunctionElement fletchUnresolved; | |
| 236 FunctionElement fletchCompileError; | |
| 237 | |
| 238 FletchClassBuilder compiledObjectClass; | |
| 239 | |
| 240 ClassElement smiClass; | |
| 241 ClassElement mintClass; | |
| 242 ClassElement growableListClass; | |
| 243 ClassElement fletchNoSuchMethodErrorClass; | |
| 244 ClassElement bigintClass; | |
| 245 ClassElement uint32DigitsClass; | |
| 246 | |
| 247 FletchClassBuilder compiledClosureClass; | |
| 248 | |
| 249 /// Holds a reference to the class Coroutine if it exists. | |
| 250 ClassElement coroutineClass; | |
| 251 | |
| 252 FletchSystemBuilder systemBuilder; | |
| 253 | |
| 254 final Set<FunctionElement> alwaysEnqueue = new Set<FunctionElement>(); | |
| 255 | |
| 256 FletchImpactTransformer impactTransformer; | |
| 257 | |
| 258 FletchBackend(FletchCompilerImplementation compiler) | |
| 259 : this.context = compiler.context, | |
| 260 this.constantCompilerTask = new DartConstantTask(compiler), | |
| 261 this.systemBuilder = new FletchSystemBuilder(BASE_FLETCH_SYSTEM), | |
| 262 super(compiler) { | |
| 263 this.impactTransformer = new FletchImpactTransformer(this); | |
| 264 } | |
| 265 | |
| 266 void newSystemBuilder(FletchSystem predecessorSystem) { | |
| 267 systemBuilder = new FletchSystemBuilder(predecessorSystem); | |
| 268 } | |
| 269 | |
| 270 // TODO(zarah): Move to FletchSystemBuilder. | |
| 271 FletchClassBuilder getClassBuilderOfExistingClass(int id) { | |
| 272 FletchClassBuilder classBuilder = systemBuilder.lookupClassBuilder(id); | |
| 273 if (classBuilder != null) return classBuilder; | |
| 274 FletchClass klass = systemBuilder.lookupClass(id); | |
| 275 if (klass.element != null) return registerClassElement(klass.element); | |
| 276 // [klass] is a tearoff class | |
| 277 return systemBuilder.newPatchClassBuilder(id, compiledClosureClass); | |
| 278 } | |
| 279 | |
| 280 FletchClassBuilder registerClassElement(ClassElement element) { | |
| 281 if (element == null) return null; | |
| 282 assert(element.isDeclaration); | |
| 283 | |
| 284 FletchClassBuilder classBuilder = | |
| 285 systemBuilder.lookupClassBuilderByElement(element); | |
| 286 if (classBuilder != null) return classBuilder; | |
| 287 | |
| 288 directSubclasses[element] = new Set<ClassElement>(); | |
| 289 FletchClassBuilder superclass = registerClassElement(element.superclass); | |
| 290 if (superclass != null) { | |
| 291 Set<ClassElement> subclasses = directSubclasses[element.superclass]; | |
| 292 subclasses.add(element); | |
| 293 } | |
| 294 classBuilder = systemBuilder.newClassBuilder( | |
| 295 element, superclass, builtinClasses.contains(element)); | |
| 296 | |
| 297 // TODO(ajohnsen): Currently, the FletchRegistry does not enqueue fields. | |
| 298 // This is a workaround, where we basically add getters for all fields. | |
| 299 classBuilder.updateImplicitAccessors(this); | |
| 300 | |
| 301 Element callMember = element.lookupLocalMember(Identifiers.call); | |
| 302 if (callMember != null && callMember.isFunction) { | |
| 303 FunctionElement function = callMember; | |
| 304 classBuilder.createIsFunctionEntry( | |
| 305 this, function.functionSignature.parameterCount); | |
| 306 } | |
| 307 return classBuilder; | |
| 308 } | |
| 309 | |
| 310 FletchClassBuilder createCallableStubClass( | |
| 311 int fields, int arity, FletchClassBuilder superclass) { | |
| 312 FletchClassBuilder classBuilder = systemBuilder.newClassBuilder( | |
| 313 null, superclass, false, extraFields: fields); | |
| 314 classBuilder.createIsFunctionEntry(this, arity); | |
| 315 return classBuilder; | |
| 316 } | |
| 317 | |
| 318 List<CompilerTask> get tasks => <CompilerTask>[]; | |
| 319 | |
| 320 ConstantSystem get constantSystem { | |
| 321 return constantCompilerTask.constantCompiler.constantSystem; | |
| 322 } | |
| 323 | |
| 324 BackendConstantEnvironment get constants => constantCompilerTask; | |
| 325 | |
| 326 bool classNeedsRti(ClassElement cls) => false; | |
| 327 | |
| 328 bool methodNeedsRti(FunctionElement function) => false; | |
| 329 | |
| 330 void enqueueHelpers(ResolutionEnqueuer world, Registry incomingRegistry) { | |
| 331 FletchRegistry registry = new FletchRegistry(compiler); | |
| 332 compiler.patchAnnotationClass = patchAnnotationClass; | |
| 333 | |
| 334 bool hasMissingHelpers = false; | |
| 335 loadHelperMethods((String name) { | |
| 336 LibraryElement library = fletchSystemLibrary; | |
| 337 Element helper = library.findLocal(name); | |
| 338 // TODO(ahe): Make it cleaner. | |
| 339 if (helper != null && helper.isAbstractField) { | |
| 340 AbstractFieldElement abstractField = helper; | |
| 341 helper = abstractField.getter; | |
| 342 } | |
| 343 if (helper == null) { | |
| 344 hasMissingHelpers = true; | |
| 345 compiler.reporter.reportErrorMessage( | |
| 346 library, MessageKind.GENERIC, | |
| 347 {'text': "Required implementation method '$name' not found."}); | |
| 348 } | |
| 349 return helper; | |
| 350 }); | |
| 351 if (hasMissingHelpers) { | |
| 352 throwInternalError( | |
| 353 "Some implementation methods are missing, see details above"); | |
| 354 } | |
| 355 world.registerStaticUse( | |
| 356 new StaticUse.staticInvoke(fletchCompileError, CallStructure.ONE_ARG)); | |
| 357 world.registerStaticUse( | |
| 358 new StaticUse.staticInvoke(fletchSystemEntry, CallStructure.ONE_ARG)); | |
| 359 world.registerStaticUse( | |
| 360 new StaticUse.staticInvoke(fletchUnresolved, CallStructure.ONE_ARG)); | |
| 361 | |
| 362 loadHelperClasses(( | |
| 363 String name, | |
| 364 LibraryElement library, | |
| 365 {bool builtin: false}) { | |
| 366 var classImpl = library.findLocal(name); | |
| 367 if (classImpl == null) classImpl = library.implementation.find(name); | |
| 368 if (classImpl == null) { | |
| 369 compiler.reporter.reportErrorMessage( | |
| 370 library, MessageKind.GENERIC, | |
| 371 {'text': "Required implementation class '$name' not found."}); | |
| 372 hasMissingHelpers = true; | |
| 373 return null; | |
| 374 } | |
| 375 if (hasMissingHelpers) return null; | |
| 376 if (builtin) builtinClasses.add(classImpl); | |
| 377 { | |
| 378 // TODO(ahe): Register in ResolutionCallbacks. The lines in this block | |
| 379 // should not happen at this point in time. | |
| 380 classImpl.ensureResolved(compiler.resolution); | |
| 381 world.registerInstantiatedType(classImpl.rawType); | |
| 382 // TODO(ahe): This is a hack to let both the world and the codegen know | |
| 383 // about the instantiated type. | |
| 384 registry.registerInstantiatedType(classImpl.rawType); | |
| 385 } | |
| 386 return registerClassElement(classImpl); | |
| 387 }); | |
| 388 if (hasMissingHelpers) { | |
| 389 throwInternalError( | |
| 390 "Some implementation classes are missing, see details above"); | |
| 391 } | |
| 392 | |
| 393 // Register list constructors to world. | |
| 394 // TODO(ahe): Register growableListClass through ResolutionCallbacks. | |
| 395 growableListClass.constructors.forEach((Element element) { | |
| 396 world.registerStaticUse(new StaticUse.constructorInvoke(element, null)); | |
| 397 }); | |
| 398 | |
| 399 // TODO(ajohnsen): Remove? String interpolation does not enqueue '+'. | |
| 400 // Investigate what else it may enqueue, could be StringBuilder, and then | |
| 401 // consider using that instead. | |
| 402 world.registerDynamicUse( | |
| 403 new DynamicUse(new Selector.binaryOperator('+'), null)); | |
| 404 | |
| 405 world.registerDynamicUse(new DynamicUse( | |
| 406 new Selector.call(new PublicName('add'), CallStructure.ONE_ARG), null)); | |
| 407 | |
| 408 alwaysEnqueue.add( | |
| 409 compiler.coreClasses.objectClass.implementation.lookupLocalMember( | |
| 410 noSuchMethodTrampolineName)); | |
| 411 alwaysEnqueue.add( | |
| 412 compiler.coreClasses.objectClass.implementation.lookupLocalMember( | |
| 413 noSuchMethodName)); | |
| 414 | |
| 415 if (coroutineClass != null) { | |
| 416 builtinClasses.add(coroutineClass); | |
| 417 alwaysEnqueue.add(coroutineClass.lookupLocalMember("_coroutineStart")); | |
| 418 } | |
| 419 | |
| 420 for (FunctionElement element in alwaysEnqueue) { | |
| 421 world.registerStaticUse(new StaticUse.foreignUse(element)); | |
| 422 } | |
| 423 } | |
| 424 | |
| 425 void loadHelperMethods( | |
| 426 FunctionElement findHelper(String name)) { | |
| 427 | |
| 428 FunctionElement findExternal(String name) { | |
| 429 FunctionElement helper = findHelper(name); | |
| 430 if (helper != null) externals.add(helper); | |
| 431 return helper; | |
| 432 } | |
| 433 | |
| 434 fletchSystemEntry = findHelper('entry'); | |
| 435 fletchExternalInvokeMain = findExternal('invokeMain'); | |
| 436 fletchExternalYield = findExternal('yield'); | |
| 437 fletchExternalCoroutineChange = findExternal('coroutineChange'); | |
| 438 fletchExternalNativeError = findExternal('nativeError'); | |
| 439 fletchUnresolved = findExternal('unresolved'); | |
| 440 fletchCompileError = findExternal('compileError'); | |
| 441 } | |
| 442 | |
| 443 void loadHelperClasses( | |
| 444 FletchClassBuilder loadClass( | |
| 445 String name, | |
| 446 LibraryElement library, | |
| 447 {bool builtin})) { | |
| 448 compiledObjectClass = | |
| 449 loadClass("Object", compiler.coreLibrary, builtin: true); | |
| 450 compiledClosureClass = | |
| 451 loadClass("_TearOffClosure", compiler.coreLibrary, builtin: true); | |
| 452 smiClass = loadClass("_Smi", compiler.coreLibrary, builtin: true)?.element; | |
| 453 mintClass = | |
| 454 loadClass("_Mint", compiler.coreLibrary, builtin: true)?.element; | |
| 455 loadClass("_OneByteString", compiler.coreLibrary, builtin: true); | |
| 456 loadClass("_TwoByteString", compiler.coreLibrary, builtin: true); | |
| 457 // TODO(ahe): Register _ConstantList through ResolutionCallbacks. | |
| 458 loadClass(constantListName, fletchSystemLibrary, builtin: true); | |
| 459 loadClass(constantByteListName, fletchSystemLibrary, builtin: true); | |
| 460 loadClass(constantMapName, fletchSystemLibrary, builtin: true); | |
| 461 loadClass("_DoubleImpl", compiler.coreLibrary, builtin: true); | |
| 462 loadClass("Null", compiler.coreLibrary, builtin: true); | |
| 463 loadClass("bool", compiler.coreLibrary, builtin: true); | |
| 464 loadClass("StackOverflowError", compiler.coreLibrary, builtin: true); | |
| 465 loadClass("Port", fletchLibrary, builtin: true); | |
| 466 loadClass("Process", fletchLibrary, builtin: true); | |
| 467 loadClass("ProcessDeath", fletchLibrary, builtin: true); | |
| 468 loadClass("ForeignMemory", fletchFFILibrary, builtin: true); | |
| 469 if (context.enableBigint) { | |
| 470 bigintClass = loadClass("_Bigint", compiler.coreLibrary)?.element; | |
| 471 uint32DigitsClass = | |
| 472 loadClass("_Uint32Digits", compiler.coreLibrary)?.element; | |
| 473 } | |
| 474 growableListClass = | |
| 475 loadClass(growableListName, fletchSystemLibrary)?.element; | |
| 476 fletchNoSuchMethodErrorClass = | |
| 477 loadClass(fletchNoSuchMethodErrorName, | |
| 478 fletchSystemLibrary, | |
| 479 builtin: true)?.element; | |
| 480 | |
| 481 // This class is optional. | |
| 482 coroutineClass = fletchSystemLibrary.implementation.find("Coroutine"); | |
| 483 if (coroutineClass != null) { | |
| 484 coroutineClass.ensureResolved(compiler.resolution); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 void onElementResolved(Element element, TreeElements elements) { | |
| 489 if (alwaysEnqueue.contains(element)) { | |
| 490 var registry = new FletchRegistry(compiler); | |
| 491 if (element.isStatic || element.isTopLevel) { | |
| 492 registry.registerStaticUse(new StaticUse.foreignUse(element)); | |
| 493 } else { | |
| 494 registry.registerDynamicUse(new Selector.fromElement(element)); | |
| 495 } | |
| 496 } | |
| 497 } | |
| 498 | |
| 499 ClassElement get intImplementation => smiClass; | |
| 500 | |
| 501 /// Class of annotations to mark patches in patch files. | |
| 502 /// | |
| 503 /// The patch parser (pkg/compiler/lib/src/patch_parser.dart). The patch | |
| 504 /// parser looks for an annotation on the form "@patch", where "patch" is | |
| 505 /// compile-time constant instance of [patchAnnotationClass]. | |
| 506 ClassElement get patchAnnotationClass { | |
| 507 // TODO(ahe): Introduce a proper constant class to identify constants. For | |
| 508 // now, we simply put "const patch = "patch";" in fletch._system. | |
| 509 return super.stringImplementation; | |
| 510 } | |
| 511 | |
| 512 FletchClassBuilder createClosureClass( | |
| 513 FunctionElement closure, | |
| 514 ClosureEnvironment closureEnvironment) { | |
| 515 return closureClasses.putIfAbsent(closure, () { | |
| 516 ClosureInfo info = closureEnvironment.closures[closure]; | |
| 517 int fields = info.free.length; | |
| 518 if (info.isThisFree) fields++; | |
| 519 return createCallableStubClass( | |
| 520 fields, | |
| 521 closure.functionSignature.parameterCount, | |
| 522 compiledClosureClass); | |
| 523 }); | |
| 524 } | |
| 525 | |
| 526 /** | |
| 527 * Create a tearoff class for function [function]. | |
| 528 * | |
| 529 * The class will have one method named 'call', accepting the same arguments | |
| 530 * as [function]. The method will load the arguments received and statically | |
| 531 * call [function] (essential a tail-call). | |
| 532 * | |
| 533 * If [function] is an instance member, the class will have one field, the | |
| 534 * instance. | |
| 535 */ | |
| 536 FletchClassBuilder createTearoffClass(FletchFunctionBase function) { | |
| 537 FletchClassBuilder tearoffClass = | |
| 538 systemBuilder.getTearoffClassBuilder(function, compiledClosureClass); | |
| 539 if (tearoffClass != null) return tearoffClass; | |
| 540 FunctionSignature signature = function.signature; | |
| 541 bool hasThis = function.isInstanceMember; | |
| 542 tearoffClass = createCallableStubClass( | |
| 543 hasThis ? 1 : 0, | |
| 544 signature.parameterCount, | |
| 545 compiledClosureClass); | |
| 546 | |
| 547 FletchFunctionBuilder functionBuilder = | |
| 548 systemBuilder.newTearOff(function, tearoffClass.classId); | |
| 549 | |
| 550 BytecodeAssembler assembler = functionBuilder.assembler; | |
| 551 int argumentCount = signature.parameterCount; | |
| 552 if (hasThis) { | |
| 553 argumentCount++; | |
| 554 // If the tearoff has a 'this' value, load it. It's the only field | |
| 555 // in the tearoff class. | |
| 556 assembler | |
| 557 ..loadParameter(0) | |
| 558 ..loadField(0); | |
| 559 } | |
| 560 for (int i = 0; i < signature.parameterCount; i++) { | |
| 561 // The closure-class is at parameter index 0, so argument i is at | |
| 562 // i + 1. | |
| 563 assembler.loadParameter(i + 1); | |
| 564 } | |
| 565 int constId = functionBuilder.allocateConstantFromFunction( | |
| 566 function.functionId); | |
| 567 // TODO(ajohnsen): Create a tail-call bytecode, so we don't have to | |
| 568 // load all the arguments. | |
| 569 assembler | |
| 570 ..invokeStatic(constId, argumentCount) | |
| 571 ..ret() | |
| 572 ..methodEnd(); | |
| 573 | |
| 574 String symbol = context.getCallSymbol(signature); | |
| 575 int id = context.getSymbolId(symbol); | |
| 576 int fletchSelector = FletchSelector.encodeMethod( | |
| 577 id, | |
| 578 signature.parameterCount); | |
| 579 tearoffClass.addToMethodTable(fletchSelector, functionBuilder); | |
| 580 | |
| 581 if (!function.isInstanceMember) return tearoffClass; | |
| 582 | |
| 583 ClassElement classElement = | |
| 584 systemBuilder.lookupClassBuilder(function.memberOf).element; | |
| 585 if (classElement == null) return tearoffClass; | |
| 586 | |
| 587 // Create == function that tests for equality. | |
| 588 int isSelector = context.toFletchTearoffIsSelector( | |
| 589 function.name, | |
| 590 classElement); | |
| 591 tearoffClass.addIsSelector(isSelector); | |
| 592 | |
| 593 FletchFunctionBuilder equal = systemBuilder.newFunctionBuilder( | |
| 594 FletchFunctionKind.NORMAL, | |
| 595 2); | |
| 596 | |
| 597 BytecodeLabel isFalse = new BytecodeLabel(); | |
| 598 equal.assembler | |
| 599 // First test for class. This ensures it's the exact function that | |
| 600 // we expect. | |
| 601 ..loadParameter(1) | |
| 602 ..invokeTest(isSelector, 0) | |
| 603 ..branchIfFalse(isFalse) | |
| 604 // Then test that the receiver is identical. | |
| 605 ..loadParameter(0) | |
| 606 ..loadField(0) | |
| 607 ..loadParameter(1) | |
| 608 ..loadField(0) | |
| 609 ..identicalNonNumeric() | |
| 610 ..branchIfFalse(isFalse) | |
| 611 ..loadLiteralTrue() | |
| 612 ..ret() | |
| 613 ..bind(isFalse) | |
| 614 ..loadLiteralFalse() | |
| 615 ..ret() | |
| 616 ..methodEnd(); | |
| 617 | |
| 618 id = context.getSymbolId("=="); | |
| 619 int equalsSelector = FletchSelector.encodeMethod(id, 1); | |
| 620 tearoffClass.addToMethodTable(equalsSelector, equal); | |
| 621 | |
| 622 // Create hashCode getter. We simply add the object hashCode and the | |
| 623 // method id of the tearoff'ed function. | |
| 624 FletchFunctionBuilder hashCode = systemBuilder.newFunctionBuilder( | |
| 625 FletchFunctionKind.ACCESSOR, | |
| 626 1); | |
| 627 | |
| 628 int hashCodeSelector = FletchSelector.encodeGetter( | |
| 629 context.getSymbolId("hashCode")); | |
| 630 | |
| 631 // TODO(ajohnsen): Use plus, we plus is always enqueued. Consider using | |
| 632 // xor when we have a way to enqueue it from here. | |
| 633 int plusSelector = FletchSelector.encodeMethod( | |
| 634 context.getSymbolId("+"), 1); | |
| 635 | |
| 636 hashCode.assembler | |
| 637 ..loadParameter(0) | |
| 638 ..loadField(0) | |
| 639 ..invokeMethod(hashCodeSelector, 0) | |
| 640 ..loadLiteral(function.functionId) | |
| 641 ..invokeMethod(plusSelector, 1) | |
| 642 ..ret() | |
| 643 ..methodEnd(); | |
| 644 | |
| 645 tearoffClass.addToMethodTable(hashCodeSelector, hashCode); | |
| 646 | |
| 647 return tearoffClass; | |
| 648 } | |
| 649 | |
| 650 FletchFunctionBase getFunctionForElement(FunctionElement element) { | |
| 651 assert(element.memberContext == element); | |
| 652 | |
| 653 FletchFunctionBase function = | |
| 654 systemBuilder.lookupFunctionByElement(element); | |
| 655 if (function != null) return function; | |
| 656 | |
| 657 return createFletchFunctionBuilder(element); | |
| 658 } | |
| 659 | |
| 660 /// Get the constructor initializer function for [constructor]. The function | |
| 661 /// will be created the first time it's called for [constructor]. | |
| 662 /// | |
| 663 /// See [compilePendingConstructorInitializers] for an overview of | |
| 664 /// constructor intializers and constructor bodies. | |
| 665 FletchFunctionBase getConstructorInitializerFunction( | |
| 666 ConstructorElement constructor) { | |
| 667 assert(constructor.isDeclaration); | |
| 668 constructor = constructor.implementation; | |
| 669 FletchFunctionBase base = | |
| 670 systemBuilder.lookupConstructorInitializerByElement(constructor); | |
| 671 if (base != null) return base; | |
| 672 | |
| 673 FletchFunctionBuilder builder = systemBuilder.newConstructorInitializer( | |
| 674 constructor); | |
| 675 pendingConstructorInitializers.addFirst(builder); | |
| 676 | |
| 677 return builder; | |
| 678 } | |
| 679 | |
| 680 FletchFunctionBuilder createFletchFunctionBuilder(FunctionElement function) { | |
| 681 assert(function.memberContext == function); | |
| 682 | |
| 683 FletchClassBuilder holderClass; | |
| 684 if (function.isInstanceMember || function.isGenerativeConstructor) { | |
| 685 ClassElement enclosingClass = function.enclosingClass.declaration; | |
| 686 holderClass = registerClassElement(enclosingClass); | |
| 687 } | |
| 688 return internalCreateFletchFunctionBuilder( | |
| 689 function, | |
| 690 function.name, | |
| 691 holderClass); | |
| 692 } | |
| 693 | |
| 694 FletchFunctionBuilder internalCreateFletchFunctionBuilder( | |
| 695 FunctionElement function, | |
| 696 String name, | |
| 697 FletchClassBuilder holderClass) { | |
| 698 FletchFunctionBuilder functionBuilder = | |
| 699 systemBuilder.lookupFunctionBuilderByElement(function.declaration); | |
| 700 if (functionBuilder != null) return functionBuilder; | |
| 701 | |
| 702 FunctionTypedElement implementation = function.implementation; | |
| 703 int memberOf = holderClass != null ? holderClass.classId : null; | |
| 704 return systemBuilder.newFunctionBuilderWithSignature( | |
| 705 name, | |
| 706 function, | |
| 707 // Parameter initializers are expressed in the potential | |
| 708 // implementation. | |
| 709 implementation.functionSignature, | |
| 710 memberOf, | |
| 711 kind: function.isAccessor | |
| 712 ? FletchFunctionKind.ACCESSOR | |
| 713 : FletchFunctionKind.NORMAL, | |
| 714 mapByElement: function.declaration); | |
| 715 } | |
| 716 | |
| 717 ClassDebugInfo createClassDebugInfo(FletchClass klass) { | |
| 718 return new ClassDebugInfo(klass); | |
| 719 } | |
| 720 | |
| 721 DebugInfo createDebugInfo( | |
| 722 FletchFunction function, | |
| 723 FletchSystem currentSystem) { | |
| 724 DebugInfo debugInfo = new DebugInfo(function); | |
| 725 AstElement element = function.element; | |
| 726 if (element == null) return debugInfo; | |
| 727 List<Bytecode> expectedBytecodes = function.bytecodes; | |
| 728 element = element.implementation; | |
| 729 TreeElements elements = element.resolvedAst.elements; | |
| 730 ClosureEnvironment closureEnvironment = createClosureEnvironment( | |
| 731 element, | |
| 732 elements); | |
| 733 CodegenVisitor codegen; | |
| 734 FletchFunctionBuilder builder = | |
| 735 new FletchFunctionBuilder.fromFletchFunction(function); | |
| 736 if (function.isLazyFieldInitializer) { | |
| 737 codegen = new DebugInfoLazyFieldInitializerCodegen( | |
| 738 debugInfo, | |
| 739 builder, | |
| 740 context, | |
| 741 elements, | |
| 742 closureEnvironment, | |
| 743 element, | |
| 744 compiler); | |
| 745 } else if (function.isInitializerList) { | |
| 746 ClassElement enclosingClass = element.enclosingClass; | |
| 747 // TODO(ajohnsen): Don't depend on the class builder. | |
| 748 FletchClassBuilder classBuilder = | |
| 749 systemBuilder.lookupClassBuilderByElement(enclosingClass.declaration); | |
| 750 codegen = new DebugInfoConstructorCodegen( | |
| 751 debugInfo, | |
| 752 builder, | |
| 753 context, | |
| 754 elements, | |
| 755 closureEnvironment, | |
| 756 element, | |
| 757 classBuilder, | |
| 758 compiler); | |
| 759 } else { | |
| 760 codegen = new DebugInfoFunctionCodegen( | |
| 761 debugInfo, | |
| 762 builder, | |
| 763 context, | |
| 764 elements, | |
| 765 closureEnvironment, | |
| 766 element, | |
| 767 compiler); | |
| 768 } | |
| 769 if (isNative(element)) { | |
| 770 compiler.reporter.withCurrentElement(element, () { | |
| 771 codegenNativeFunction(element, codegen); | |
| 772 }); | |
| 773 } else if (isExternal(element)) { | |
| 774 compiler.reporter.withCurrentElement(element, () { | |
| 775 codegenExternalFunction(element, codegen); | |
| 776 }); | |
| 777 } else { | |
| 778 compiler.reporter.withCurrentElement(element, () { codegen.compile(); }); | |
| 779 } | |
| 780 // The debug codegen should generate the same bytecodes as the original | |
| 781 // codegen. If that is not the case debug information will be useless. | |
| 782 if (!Bytecode.identicalBytecodes(expectedBytecodes, | |
| 783 codegen.assembler.bytecodes)) { | |
| 784 throw 'Debug info code different from running code.'; | |
| 785 } | |
| 786 return debugInfo; | |
| 787 } | |
| 788 | |
| 789 codegen(_) { | |
| 790 new UnsupportedError( | |
| 791 "Method [codegen] not supported, use [compileElement] instead"); | |
| 792 } | |
| 793 | |
| 794 /// Invoked by [FletchEnqueuer] once per element that needs to be compiled. | |
| 795 /// | |
| 796 /// This is used to generate the bytecodes for [declaration]. | |
| 797 void compileElement( | |
| 798 AstElement declaration, | |
| 799 TreeElements treeElements, | |
| 800 FletchRegistry registry) { | |
| 801 AstElement element = declaration.implementation; | |
| 802 compiler.reporter.withCurrentElement(element, () { | |
| 803 assert(declaration.isDeclaration); | |
| 804 context.compiler.reportVerboseInfo(element, 'Compiling $element'); | |
| 805 if (element.isFunction || | |
| 806 element.isGetter || | |
| 807 element.isSetter || | |
| 808 element.isGenerativeConstructor || | |
| 809 element.isFactoryConstructor) { | |
| 810 // For a generative constructor, this means compile the constructor | |
| 811 // body. See [compilePendingConstructorInitializers] for an overview of | |
| 812 // how constructor initializers and constructor bodies are compiled. | |
| 813 codegenFunction(element, treeElements, registry); | |
| 814 } else if (element.isField) { | |
| 815 context.compiler.reportVerboseInfo( | |
| 816 element, "Asked to compile a field, but don't know how"); | |
| 817 } else { | |
| 818 compiler.reporter.internalError( | |
| 819 element, "Uninimplemented element kind: ${element.kind}"); | |
| 820 } | |
| 821 }); | |
| 822 } | |
| 823 | |
| 824 /// Invoked by [FletchEnqueuer] once per [selector] that may invoke | |
| 825 /// [declaration]. | |
| 826 /// | |
| 827 /// This is used to generate stubs for [declaration]. | |
| 828 void compileElementUsage( | |
| 829 AstElement declaration, | |
| 830 Selector selector, | |
| 831 TreeElements treeElements, | |
| 832 FletchRegistry registry) { | |
| 833 AstElement element = declaration.implementation; | |
| 834 compiler.reporter.withCurrentElement(element, () { | |
| 835 assert(declaration.isDeclaration); | |
| 836 context.compiler.reportVerboseInfo(element, 'Compiling $element'); | |
| 837 if (!element.isInstanceMember && !isLocalFunction(element)) { | |
| 838 // No stub needed. Optional arguments are handled at call-site. | |
| 839 } else if (element.isFunction) { | |
| 840 FletchFunctionBase function = | |
| 841 systemBuilder.lookupFunctionByElement(element.declaration); | |
| 842 CallStructure callStructure = selector.callStructure; | |
| 843 FunctionSignature signature = function.signature; | |
| 844 if (selector.isGetter) { | |
| 845 if (shouldReportEnqueuingOfElement(compiler, element)) { | |
| 846 context.compiler.reportVerboseInfo( | |
| 847 element, 'Adding tear-off stub'); | |
| 848 } | |
| 849 createTearoffGetterForFunction( | |
| 850 function, isSpecialCallMethod: element.name == "call"); | |
| 851 } else if (selector.isCall && | |
| 852 callStructure.signatureApplies(signature) && | |
| 853 !isExactParameterMatch(signature, callStructure)) { | |
| 854 if (shouldReportEnqueuingOfElement(compiler, element)) { | |
| 855 context.compiler.reportVerboseInfo( | |
| 856 element, 'Adding stub for $selector'); | |
| 857 } | |
| 858 createParameterStub(function, selector); | |
| 859 } | |
| 860 } else if (element.isGetter || element.isSetter) { | |
| 861 // No stub needed. If a getter returns a closure, the VM's | |
| 862 // no-such-method handling will do the right thing. | |
| 863 } else { | |
| 864 context.compiler.reportVerboseInfo( | |
| 865 element, "Asked to compile this, but don't know how"); | |
| 866 } | |
| 867 }); | |
| 868 } | |
| 869 | |
| 870 /// Invoked by [FletchEnqueuer] once per `call` [selector] that may invoke | |
| 871 /// [declaration] as an implicit closure (for example, a tear-off). | |
| 872 /// | |
| 873 /// This is used to generate parameter stubs for the closures. | |
| 874 void compileClosurizationUsage( | |
| 875 AstElement declaration, | |
| 876 Selector selector, | |
| 877 TreeElements treeElements, | |
| 878 FletchRegistry registry, | |
| 879 ClosureKind kind) { | |
| 880 AstElement element = declaration.implementation; | |
| 881 compiler.reporter.withCurrentElement(element, () { | |
| 882 assert(declaration.isDeclaration); | |
| 883 if (shouldReportEnqueuingOfElement(compiler, element)) { | |
| 884 context.compiler.reportVerboseInfo( | |
| 885 element, 'Need tear-off parameter stub $selector'); | |
| 886 } | |
| 887 FletchFunctionBase function = | |
| 888 systemBuilder.lookupFunctionByElement(element.declaration); | |
| 889 if (function == null) { | |
| 890 compiler.reporter.internalError( | |
| 891 element, "Has no fletch function, but used as tear-off"); | |
| 892 } | |
| 893 if (selector.isGetter) { | |
| 894 // This is a special tear-off getter. | |
| 895 | |
| 896 // TODO(ahe): This code should probably use [kind] to detect the | |
| 897 // various cases instead of [isLocalFunction] and looking at names. | |
| 898 | |
| 899 assert(selector.memberName == Names.CALL_NAME); | |
| 900 if (isLocalFunction(element) || | |
| 901 memberName(element) == Names.CALL_NAME) { | |
| 902 createTearoffGetterForFunction( | |
| 903 function, isSpecialCallMethod: true); | |
| 904 return; | |
| 905 } | |
| 906 int stub = systemBuilder.lookupTearOffById(function.functionId); | |
| 907 if (stub == null) { | |
| 908 compiler.reporter.internalError( | |
| 909 element, "No tear-off stub to compile `call` tear-off"); | |
| 910 } else { | |
| 911 function = systemBuilder.lookupFunction(stub); | |
| 912 createTearoffGetterForFunction(function, isSpecialCallMethod: true); | |
| 913 return; | |
| 914 } | |
| 915 } | |
| 916 switch (kind) { | |
| 917 case ClosureKind.tearOff: | |
| 918 case ClosureKind.superTearOff: | |
| 919 if (memberName(element) == Names.CALL_NAME) { | |
| 920 // This is really a functionLikeTearOff. | |
| 921 break; | |
| 922 } | |
| 923 // A tear-off has a corresponding stub in a closure class. Look up | |
| 924 // that stub: | |
| 925 int stub = systemBuilder.lookupTearOffById(function.functionId); | |
| 926 if (stub == null) { | |
| 927 compiler.reporter | |
| 928 .internalError(element, "Couldn't find tear-off stub"); | |
| 929 } else { | |
| 930 function = systemBuilder.lookupFunction(stub); | |
| 931 } | |
| 932 break; | |
| 933 | |
| 934 case ClosureKind.localFunction: | |
| 935 // A local function already is a member of its closure class, and | |
| 936 // doesn't have a stub. | |
| 937 break; | |
| 938 | |
| 939 case ClosureKind.functionLike: | |
| 940 case ClosureKind.functionLikeTearOff: | |
| 941 compiler.reporter.internalError(element, "Unimplemented: $kind"); | |
| 942 break; | |
| 943 } | |
| 944 | |
| 945 if (!isExactParameterMatch(function.signature, selector.callStructure)) { | |
| 946 createParameterStub(function, selector); | |
| 947 } | |
| 948 }); | |
| 949 } | |
| 950 | |
| 951 void codegenFunction( | |
| 952 FunctionElement function, | |
| 953 TreeElements elements, | |
| 954 FletchRegistry registry) { | |
| 955 registry.registerStaticUse(new StaticUse.foreignUse(fletchSystemEntry)); | |
| 956 | |
| 957 ClosureEnvironment closureEnvironment = createClosureEnvironment( | |
| 958 function, | |
| 959 elements); | |
| 960 | |
| 961 FletchFunctionBuilder functionBuilder; | |
| 962 | |
| 963 if (function.memberContext != function) { | |
| 964 functionBuilder = internalCreateFletchFunctionBuilder( | |
| 965 function, | |
| 966 Identifiers.call, | |
| 967 createClosureClass(function, closureEnvironment)); | |
| 968 } else { | |
| 969 functionBuilder = createFletchFunctionBuilder(function); | |
| 970 } | |
| 971 | |
| 972 FunctionCodegen codegen = new FunctionCodegen( | |
| 973 functionBuilder, | |
| 974 context, | |
| 975 elements, | |
| 976 registry, | |
| 977 closureEnvironment, | |
| 978 function); | |
| 979 | |
| 980 if (isNative(function)) { | |
| 981 codegenNativeFunction(function, codegen); | |
| 982 } else if (isExternal(function)) { | |
| 983 codegenExternalFunction(function, codegen); | |
| 984 } else { | |
| 985 codegen.compile(); | |
| 986 } | |
| 987 | |
| 988 if (functionBuilder.isInstanceMember && !function.isGenerativeConstructor) { | |
| 989 // Inject the function into the method table of the 'holderClass' class. | |
| 990 // Note that while constructor bodies has a this argument, we don't inject | |
| 991 // them into the method table. | |
| 992 String symbol = context.getSymbolForFunction( | |
| 993 functionBuilder.name, | |
| 994 function.functionSignature, | |
| 995 function.library); | |
| 996 int id = context.getSymbolId(symbol); | |
| 997 int arity = function.functionSignature.parameterCount; | |
| 998 SelectorKind kind = SelectorKind.Method; | |
| 999 if (function.isGetter) kind = SelectorKind.Getter; | |
| 1000 if (function.isSetter) kind = SelectorKind.Setter; | |
| 1001 int fletchSelector = FletchSelector.encode(id, kind, arity); | |
| 1002 FletchClassBuilder classBuilder = | |
| 1003 systemBuilder.lookupClassBuilder(functionBuilder.memberOf); | |
| 1004 classBuilder.addToMethodTable(fletchSelector, functionBuilder); | |
| 1005 // Inject method into all mixin usages. | |
| 1006 getMixinApplicationsOfClass(classBuilder).forEach((ClassElement usage) { | |
| 1007 FletchClassBuilder compiledUsage = registerClassElement(usage); | |
| 1008 compiledUsage.addToMethodTable(fletchSelector, functionBuilder); | |
| 1009 }); | |
| 1010 } | |
| 1011 | |
| 1012 if (compiler.verbose) { | |
| 1013 context.compiler.reportVerboseInfo( | |
| 1014 function, functionBuilder.verboseToString()); | |
| 1015 } | |
| 1016 } | |
| 1017 | |
| 1018 List<ClassElement> getMixinApplicationsOfClass(FletchClassBuilder builder) { | |
| 1019 ClassElement element = builder.element; | |
| 1020 if (element == null) return []; | |
| 1021 List<ClassElement> mixinUsage = | |
| 1022 compiler.world.mixinUsesOf(element).toList(); | |
| 1023 for (int i = 0; i < mixinUsage.length; i++) { | |
| 1024 ClassElement usage = mixinUsage[i]; | |
| 1025 // Recursively add mixin-usage of the current 'usage'. | |
| 1026 assert(!compiler.world.mixinUsesOf(usage).any(mixinUsage.contains)); | |
| 1027 mixinUsage.addAll(compiler.world.mixinUsesOf(usage)); | |
| 1028 } | |
| 1029 return mixinUsage; | |
| 1030 } | |
| 1031 | |
| 1032 void codegenNativeFunction( | |
| 1033 FunctionElement function, | |
| 1034 FunctionCodegen codegen) { | |
| 1035 String name = '.${function.name}'; | |
| 1036 | |
| 1037 ClassElement enclosingClass = function.enclosingClass; | |
| 1038 if (enclosingClass != null) name = '${enclosingClass.name}$name'; | |
| 1039 | |
| 1040 FletchNativeDescriptor descriptor = context.nativeDescriptors[name]; | |
| 1041 if (descriptor == null) { | |
| 1042 throw "Unsupported native function: $name"; | |
| 1043 } | |
| 1044 | |
| 1045 if (name == "Coroutine._coroutineNewStack") { | |
| 1046 // The static native method `Coroutine._coroutineNewStack` will invoke | |
| 1047 // the instance method `Coroutine._coroutineStart`. | |
| 1048 if (coroutineClass == null) { | |
| 1049 compiler.reporter.internalError( | |
| 1050 function, "required class [Coroutine] not found"); | |
| 1051 } | |
| 1052 FunctionElement coroutineStart = | |
| 1053 coroutineClass.lookupLocalMember("_coroutineStart"); | |
| 1054 Selector selector = new Selector.fromElement(coroutineStart); | |
| 1055 new FletchRegistry(compiler) | |
| 1056 ..registerDynamicUse(selector); | |
| 1057 } else if (name == "Process._spawn") { | |
| 1058 // The native method `Process._spawn` will do a closure invoke with 0, 1, | |
| 1059 // or 2 arguments. | |
| 1060 new FletchRegistry(compiler) | |
| 1061 ..registerDynamicUse(new Selector.callClosure(0)) | |
| 1062 ..registerDynamicUse(new Selector.callClosure(1)) | |
| 1063 ..registerDynamicUse(new Selector.callClosure(2)); | |
| 1064 } | |
| 1065 | |
| 1066 int arity = codegen.assembler.functionArity; | |
| 1067 if (name == "Port.send" || | |
| 1068 name == "Port._sendList" || | |
| 1069 name == "Port._sendExit") { | |
| 1070 codegen.assembler.invokeNativeYield(arity, descriptor.index); | |
| 1071 } else { | |
| 1072 codegen.assembler.invokeNative(arity, descriptor.index); | |
| 1073 } | |
| 1074 | |
| 1075 EmptyStatement empty = function.node.body.asEmptyStatement(); | |
| 1076 if (empty != null) { | |
| 1077 // A native method without a body. | |
| 1078 codegen.assembler | |
| 1079 ..emitThrow() | |
| 1080 ..methodEnd(); | |
| 1081 } else { | |
| 1082 codegen.compile(); | |
| 1083 } | |
| 1084 } | |
| 1085 | |
| 1086 void codegenExternalFunction( | |
| 1087 FunctionElement function, | |
| 1088 FunctionCodegen codegen) { | |
| 1089 if (function == fletchExternalYield) { | |
| 1090 codegenExternalYield(function, codegen); | |
| 1091 } else if (function == context.compiler.identicalFunction.implementation) { | |
| 1092 codegenIdentical(function, codegen); | |
| 1093 } else if (function == fletchExternalInvokeMain) { | |
| 1094 codegenExternalInvokeMain(function, codegen); | |
| 1095 } else if (function.name == noSuchMethodTrampolineName && | |
| 1096 function.library == compiler.coreLibrary) { | |
| 1097 codegenExternalNoSuchMethodTrampoline(function, codegen); | |
| 1098 } else { | |
| 1099 DiagnosticMessage message = context.compiler.reporter | |
| 1100 .createMessage(function.node, | |
| 1101 MessageKind.GENERIC, | |
| 1102 {'text': | |
| 1103 'External function "${function.name}" is not supported'}); | |
| 1104 compiler.reporter.reportError(message); | |
| 1105 codegen | |
| 1106 ..doCompileError(message) | |
| 1107 ..assembler.ret() | |
| 1108 ..assembler.methodEnd(); | |
| 1109 } | |
| 1110 } | |
| 1111 | |
| 1112 void codegenIdentical( | |
| 1113 FunctionElement function, | |
| 1114 FunctionCodegen codegen) { | |
| 1115 codegen.assembler | |
| 1116 ..loadParameter(0) | |
| 1117 ..loadParameter(1) | |
| 1118 ..identical() | |
| 1119 ..ret() | |
| 1120 ..methodEnd(); | |
| 1121 } | |
| 1122 | |
| 1123 void codegenExternalYield( | |
| 1124 FunctionElement function, | |
| 1125 FunctionCodegen codegen) { | |
| 1126 codegen.assembler | |
| 1127 ..loadParameter(0) | |
| 1128 ..processYield() | |
| 1129 ..ret() | |
| 1130 ..methodEnd(); | |
| 1131 } | |
| 1132 | |
| 1133 void codegenExternalInvokeMain( | |
| 1134 FunctionElement function, | |
| 1135 FunctionCodegen codegen) { | |
| 1136 compiler.reporter.internalError( | |
| 1137 function, "[codegenExternalInvokeMain] not implemented."); | |
| 1138 // TODO(ahe): This code shouldn't normally be called, only if invokeMain is | |
| 1139 // torn off. Perhaps we should just say we don't support that. | |
| 1140 } | |
| 1141 | |
| 1142 void codegenExternalNoSuchMethodTrampoline( | |
| 1143 FunctionElement function, | |
| 1144 FunctionCodegen codegen) { | |
| 1145 // NOTE: The number of arguments to the [noSuchMethodName] function must be | |
| 1146 // kept in sync with: | |
| 1147 // src/vm/interpreter.cc:HandleEnterNoSuchMethod | |
| 1148 int id = context.getSymbolId( | |
| 1149 context.mangleName(new Name(noSuchMethodName, compiler.coreLibrary))); | |
| 1150 int fletchSelector = FletchSelector.encodeMethod(id, 3); | |
| 1151 BytecodeLabel skipGetter = new BytecodeLabel(); | |
| 1152 codegen.assembler | |
| 1153 ..enterNoSuchMethod(skipGetter) | |
| 1154 // First invoke the getter. | |
| 1155 ..invokeSelector(2) | |
| 1156 // Then invoke 'call', with the receiver being the result of the | |
| 1157 // previous invokeSelector. | |
| 1158 ..invokeSelector(1) | |
| 1159 ..exitNoSuchMethod() | |
| 1160 ..bind(skipGetter) | |
| 1161 ..invokeMethod(fletchSelector, 1) | |
| 1162 ..exitNoSuchMethod() | |
| 1163 ..methodEnd(); | |
| 1164 } | |
| 1165 | |
| 1166 bool isNative(Element element) { | |
| 1167 if (element is FunctionElement) { | |
| 1168 for (var metadata in element.metadata) { | |
| 1169 // TODO(ahe): This code should ensure that @native resolves to precisely | |
| 1170 // the native variable in dart:fletch._system. | |
| 1171 if (metadata.constant == null) continue; | |
| 1172 ConstantValue value = context.getConstantValue(metadata.constant); | |
| 1173 if (!value.isString) continue; | |
| 1174 StringConstantValue stringValue = value; | |
| 1175 if (stringValue.toDartString().slowToString() != 'native') continue; | |
| 1176 return true; | |
| 1177 } | |
| 1178 } | |
| 1179 return false; | |
| 1180 } | |
| 1181 | |
| 1182 bool isExternal(Element element) { | |
| 1183 if (element is FunctionElement) return element.isExternal; | |
| 1184 return false; | |
| 1185 } | |
| 1186 | |
| 1187 bool get canHandleCompilationFailed => true; | |
| 1188 | |
| 1189 ClosureEnvironment createClosureEnvironment( | |
| 1190 ExecutableElement element, | |
| 1191 TreeElements elements) { | |
| 1192 MemberElement member = element.memberContext; | |
| 1193 return closureEnvironments.putIfAbsent(member, () { | |
| 1194 ClosureVisitor environment = new ClosureVisitor(member, elements); | |
| 1195 return environment.compute(); | |
| 1196 }); | |
| 1197 } | |
| 1198 | |
| 1199 void markFunctionConstantAsUsed(FunctionConstantValue value) { | |
| 1200 // TODO(ajohnsen): Use registry in CodegenVisitor to register the used | |
| 1201 // constants. | |
| 1202 FunctionElement function = value.element; | |
| 1203 createTearoffClass(createFletchFunctionBuilder(function)); | |
| 1204 // Be sure to actually enqueue the function for compilation. | |
| 1205 FletchRegistry registry = new FletchRegistry(compiler); | |
| 1206 registry.registerStaticUse(new StaticUse.foreignUse(function)); | |
| 1207 } | |
| 1208 | |
| 1209 FletchFunctionBase createParameterStub( | |
| 1210 FletchFunctionBase function, | |
| 1211 Selector selector) { | |
| 1212 CallStructure callStructure = selector.callStructure; | |
| 1213 assert(callStructure.signatureApplies(function.signature)); | |
| 1214 ParameterStubSignature signature = new ParameterStubSignature( | |
| 1215 function.functionId, callStructure); | |
| 1216 FletchFunctionBase stub = systemBuilder.lookupParameterStub(signature); | |
| 1217 if (stub != null) return stub; | |
| 1218 | |
| 1219 int arity = selector.argumentCount; | |
| 1220 if (function.isInstanceMember) arity++; | |
| 1221 | |
| 1222 FletchFunctionBuilder builder = systemBuilder.newFunctionBuilder( | |
| 1223 FletchFunctionKind.PARAMETER_STUB, | |
| 1224 arity); | |
| 1225 | |
| 1226 BytecodeAssembler assembler = builder.assembler; | |
| 1227 | |
| 1228 void loadInitializerOrNull(ParameterElement parameter) { | |
| 1229 Expression initializer = parameter.initializer; | |
| 1230 if (initializer != null) { | |
| 1231 ConstantExpression expression = context.compileConstant( | |
| 1232 initializer, | |
| 1233 parameter.memberContext.resolvedAst.elements, | |
| 1234 isConst: true); | |
| 1235 int constId = builder.allocateConstant( | |
| 1236 context.getConstantValue(expression)); | |
| 1237 assembler.loadConst(constId); | |
| 1238 } else { | |
| 1239 assembler.loadLiteralNull(); | |
| 1240 } | |
| 1241 } | |
| 1242 | |
| 1243 // Load this. | |
| 1244 if (function.isInstanceMember) assembler.loadParameter(0); | |
| 1245 | |
| 1246 int index = function.isInstanceMember ? 1 : 0; | |
| 1247 function.signature.orderedForEachParameter((ParameterElement parameter) { | |
| 1248 if (!parameter.isOptional) { | |
| 1249 assembler.loadParameter(index); | |
| 1250 } else if (parameter.isNamed) { | |
| 1251 int parameterIndex = selector.namedArguments.indexOf(parameter.name); | |
| 1252 if (parameterIndex >= 0) { | |
| 1253 if (function.isInstanceMember) parameterIndex++; | |
| 1254 int position = selector.positionalArgumentCount + parameterIndex; | |
| 1255 assembler.loadParameter(position); | |
| 1256 } else { | |
| 1257 loadInitializerOrNull(parameter); | |
| 1258 } | |
| 1259 } else { | |
| 1260 if (index < arity) { | |
| 1261 assembler.loadParameter(index); | |
| 1262 } else { | |
| 1263 loadInitializerOrNull(parameter); | |
| 1264 } | |
| 1265 } | |
| 1266 index++; | |
| 1267 }); | |
| 1268 | |
| 1269 // TODO(ajohnsen): We have to be extra careful when overriding a | |
| 1270 // method that takes optional arguments. We really should | |
| 1271 // enumerate all the stubs in the superclasses and make sure | |
| 1272 // they're overridden. | |
| 1273 int constId = builder.allocateConstantFromFunction(function.functionId); | |
| 1274 assembler | |
| 1275 ..invokeStatic(constId, index) | |
| 1276 ..ret() | |
| 1277 ..methodEnd(); | |
| 1278 | |
| 1279 if (function.isInstanceMember) { | |
| 1280 int fletchSelector = context.toFletchSelector(selector); | |
| 1281 FletchClassBuilder classBuilder = getClassBuilderOfExistingClass(function.
memberOf); | |
| 1282 classBuilder.addToMethodTable(fletchSelector, builder); | |
| 1283 | |
| 1284 // Inject parameter stub into all mixin usages. | |
| 1285 getMixinApplicationsOfClass(classBuilder).forEach((ClassElement usage) { | |
| 1286 FletchClassBuilder classBuilder = | |
| 1287 systemBuilder.lookupClassBuilderByElement(usage); | |
| 1288 classBuilder.addToMethodTable(fletchSelector, builder); | |
| 1289 }); | |
| 1290 } | |
| 1291 | |
| 1292 systemBuilder.registerParameterStub(signature, builder); | |
| 1293 | |
| 1294 return builder; | |
| 1295 } | |
| 1296 | |
| 1297 /// Create a tear-off getter for [function]. If [isSpecialCallMethod] is | |
| 1298 /// `true`, this is the special case for `someClosure.call` which should | |
| 1299 /// always return `someClosure`. This implies that when [isSpecialCallMethod] | |
| 1300 /// is true, we assume [function] is already a member of a closure class (or | |
| 1301 /// a class with a `call` method [ClosureKind.functionLike]) and that the | |
| 1302 /// getter should be added to that class. | |
| 1303 void createTearoffGetterForFunction( | |
| 1304 FletchFunctionBuilder function, | |
| 1305 {bool isSpecialCallMethod}) { | |
| 1306 if (isSpecialCallMethod == null) { | |
| 1307 throw new ArgumentError("isSpecialCallMethod"); | |
| 1308 } | |
| 1309 FletchFunctionBuilder getter = systemBuilder.newFunctionBuilder( | |
| 1310 FletchFunctionKind.ACCESSOR, | |
| 1311 1); | |
| 1312 // If the getter is of 'call', return the instance instead. | |
| 1313 if (isSpecialCallMethod) { | |
| 1314 getter.assembler | |
| 1315 ..loadParameter(0) | |
| 1316 ..ret() | |
| 1317 ..methodEnd(); | |
| 1318 } else { | |
| 1319 FletchClassBuilder tearoffClass = createTearoffClass(function); | |
| 1320 int constId = getter.allocateConstantFromClass(tearoffClass.classId); | |
| 1321 getter.assembler | |
| 1322 ..loadParameter(0) | |
| 1323 ..allocate(constId, tearoffClass.fields) | |
| 1324 ..ret() | |
| 1325 ..methodEnd(); | |
| 1326 } | |
| 1327 // If the name is private, we need the library. | |
| 1328 // Invariant: We only generate public stubs, e.g. 'call'. | |
| 1329 LibraryElement library; | |
| 1330 if (function.element != null) { | |
| 1331 library = function.element.library; | |
| 1332 } | |
| 1333 // TODO(sigurdm): Avoid allocating new name here. | |
| 1334 Name name = new Name(function.name, library); | |
| 1335 int fletchSelector = context.toFletchSelector( | |
| 1336 new Selector.getter(name)); | |
| 1337 FletchClassBuilder classBuilder = systemBuilder.lookupClassBuilder( | |
| 1338 function.memberOf); | |
| 1339 classBuilder.addToMethodTable(fletchSelector, getter); | |
| 1340 | |
| 1341 // Inject getter into all mixin usages. | |
| 1342 getMixinApplicationsOfClass(classBuilder).forEach((ClassElement usage) { | |
| 1343 FletchClassBuilder classBuilder = | |
| 1344 systemBuilder.lookupClassBuilderByElement(usage); | |
| 1345 classBuilder.addToMethodTable(fletchSelector, getter); | |
| 1346 }); | |
| 1347 } | |
| 1348 | |
| 1349 void compileTypeTest(ClassElement element, InterfaceType type) { | |
| 1350 assert(element.isDeclaration); | |
| 1351 int fletchSelector = context.toFletchIsSelector(type.element); | |
| 1352 FletchClassBuilder builder = | |
| 1353 systemBuilder.lookupClassBuilderByElement(element); | |
| 1354 if (builder != null) { | |
| 1355 context.compiler.reportVerboseInfo( | |
| 1356 element, 'Adding is-selector for $type'); | |
| 1357 builder.addIsSelector(fletchSelector); | |
| 1358 } | |
| 1359 } | |
| 1360 | |
| 1361 int assembleProgram() => 0; | |
| 1362 | |
| 1363 FletchDelta computeDelta() { | |
| 1364 | |
| 1365 if (fletchSystemLibrary == null && compiler.compilationFailed) { | |
| 1366 // TODO(ahe): Ensure fletchSystemLibrary is not null. | |
| 1367 return null; | |
| 1368 } | |
| 1369 | |
| 1370 List<VmCommand> commands = <VmCommand>[ | |
| 1371 const NewMap(MapId.methods), | |
| 1372 const NewMap(MapId.classes), | |
| 1373 const NewMap(MapId.constants), | |
| 1374 ]; | |
| 1375 | |
| 1376 FletchSystem system = systemBuilder.computeSystem(context, commands); | |
| 1377 | |
| 1378 commands.add(const PushNewInteger(0)); | |
| 1379 commands.add(new PushFromMap( | |
| 1380 MapId.methods, | |
| 1381 system.lookupFunctionByElement(fletchSystemEntry).functionId)); | |
| 1382 | |
| 1383 return new FletchDelta(system, systemBuilder.predecessorSystem, commands); | |
| 1384 } | |
| 1385 | |
| 1386 bool enableCodegenWithErrorsIfSupported(Spannable spannable) { | |
| 1387 return true; | |
| 1388 } | |
| 1389 | |
| 1390 bool enableDeferredLoadingIfSupported(Spannable spannable, Registry registry)
{ | |
| 1391 return false; | |
| 1392 } | |
| 1393 | |
| 1394 bool registerDeferredLoading(Spannable node, Registry registry) { | |
| 1395 compiler.reporter.reportWarningMessage( | |
| 1396 node, | |
| 1397 MessageKind.GENERIC, | |
| 1398 {'text': "Deferred loading is not supported."}); | |
| 1399 return false; | |
| 1400 } | |
| 1401 | |
| 1402 bool get supportsReflection => false; | |
| 1403 | |
| 1404 // TODO(sigurdm): Support async/await on the mobile platform. | |
| 1405 bool get supportsAsyncAwait { | |
| 1406 return !compiler.platformConfigUri.path.contains("embedded"); | |
| 1407 } | |
| 1408 | |
| 1409 Future onLibraryScanned(LibraryElement library, LibraryLoader loader) { | |
| 1410 if (library.isPlatformLibrary) { | |
| 1411 String path = library.canonicalUri.path; | |
| 1412 switch(path) { | |
| 1413 case 'fletch._system': | |
| 1414 fletchSystemLibrary = library; | |
| 1415 break; | |
| 1416 case 'fletch.ffi': | |
| 1417 fletchFFILibrary = library; | |
| 1418 break; | |
| 1419 case 'fletch.collection': | |
| 1420 collectionLibrary = library; | |
| 1421 break; | |
| 1422 case 'math': | |
| 1423 mathLibrary = library; | |
| 1424 break; | |
| 1425 case 'fletch': | |
| 1426 fletchLibrary = library; | |
| 1427 break; | |
| 1428 } | |
| 1429 | |
| 1430 if (!library.isPatched) { | |
| 1431 // Apply patch, if any. | |
| 1432 Uri patchUri = compiler.resolvePatchUri(library.canonicalUri.path); | |
| 1433 if (patchUri != null) { | |
| 1434 return compiler.patchParser.patchLibrary(loader, patchUri, library); | |
| 1435 } | |
| 1436 } | |
| 1437 } | |
| 1438 return null; | |
| 1439 } | |
| 1440 | |
| 1441 bool isBackendLibrary(LibraryElement library) { | |
| 1442 return library == fletchSystemLibrary; | |
| 1443 } | |
| 1444 | |
| 1445 /// Return non-null to enable patching. Possible return values are 'new' and | |
| 1446 /// 'old'. Referring to old and new emitter. Since the new emitter is the | |
| 1447 /// future, we assume 'old' will go away. So it seems the best option for | |
| 1448 /// Fletch is 'new'. | |
| 1449 String get patchVersion => 'new'; | |
| 1450 | |
| 1451 FunctionElement resolveExternalFunction(FunctionElement element) { | |
| 1452 if (element.isPatched) { | |
| 1453 FunctionElementX patch = element.patch; | |
| 1454 compiler.reporter.withCurrentElement(patch, () { | |
| 1455 patch.parseNode(compiler.parsing); | |
| 1456 patch.computeType(compiler.resolution); | |
| 1457 }); | |
| 1458 element = patch; | |
| 1459 // TODO(ahe): Don't use ensureResolved (fix TODO in isNative instead). | |
| 1460 element.metadata.forEach((m) => m.ensureResolved(compiler.resolution)); | |
| 1461 } else if (element.library == fletchSystemLibrary) { | |
| 1462 // Nothing needed for now. | |
| 1463 } else if (element.library == compiler.coreLibrary) { | |
| 1464 // Nothing needed for now. | |
| 1465 } else if (element.library == mathLibrary) { | |
| 1466 // Nothing needed for now. | |
| 1467 } else if (element.library == asyncLibrary) { | |
| 1468 // Nothing needed for now. | |
| 1469 } else if (element.library == fletchLibrary) { | |
| 1470 // Nothing needed for now. | |
| 1471 } else if (externals.contains(element)) { | |
| 1472 // Nothing needed for now. | |
| 1473 } else { | |
| 1474 compiler.reporter.reportErrorMessage( | |
| 1475 element, MessageKind.PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION); | |
| 1476 } | |
| 1477 return element; | |
| 1478 } | |
| 1479 | |
| 1480 int compileLazyFieldInitializer( | |
| 1481 FieldElement field, | |
| 1482 FletchRegistry registry) { | |
| 1483 int index = context.getStaticFieldIndex(field, null); | |
| 1484 | |
| 1485 if (field.initializer == null) return index; | |
| 1486 | |
| 1487 if (lazyFieldInitializers.containsKey(field)) return index; | |
| 1488 | |
| 1489 FletchFunctionBuilder functionBuilder = systemBuilder.newFunctionBuilder( | |
| 1490 FletchFunctionKind.LAZY_FIELD_INITIALIZER, | |
| 1491 0, | |
| 1492 name: "${field.name} lazy initializer", | |
| 1493 element: field); | |
| 1494 lazyFieldInitializers[field] = functionBuilder; | |
| 1495 | |
| 1496 TreeElements elements = field.resolvedAst.elements; | |
| 1497 | |
| 1498 ClosureEnvironment closureEnvironment = createClosureEnvironment( | |
| 1499 field, | |
| 1500 elements); | |
| 1501 | |
| 1502 LazyFieldInitializerCodegen codegen = new LazyFieldInitializerCodegen( | |
| 1503 functionBuilder, | |
| 1504 context, | |
| 1505 elements, | |
| 1506 registry, | |
| 1507 closureEnvironment, | |
| 1508 field); | |
| 1509 | |
| 1510 codegen.compile(); | |
| 1511 | |
| 1512 return index; | |
| 1513 } | |
| 1514 | |
| 1515 /// Compiles the initializer part of a constructor. | |
| 1516 /// | |
| 1517 /// See [compilePendingConstructorInitializers] for an overview of how | |
| 1518 /// constructor initializer and bodies are compiled. | |
| 1519 void compileConstructorInitializer(FletchFunctionBuilder functionBuilder) { | |
| 1520 ConstructorElement constructor = functionBuilder.element; | |
| 1521 assert(constructor.isImplementation); | |
| 1522 compiler.reporter.withCurrentElement(constructor, () { | |
| 1523 assert(functionBuilder == | |
| 1524 systemBuilder.lookupConstructorInitializerByElement(constructor)); | |
| 1525 context.compiler.reportVerboseInfo( | |
| 1526 constructor, 'Compiling constructor initializer $constructor'); | |
| 1527 | |
| 1528 TreeElements elements = constructor.resolvedAst.elements; | |
| 1529 | |
| 1530 // TODO(ahe): We shouldn't create a registry, but we have to as long as | |
| 1531 // the enqueuer doesn't support elements with more than one compilation | |
| 1532 // artifact. | |
| 1533 FletchRegistry registry = new FletchRegistry(compiler); | |
| 1534 | |
| 1535 FletchClassBuilder classBuilder = | |
| 1536 registerClassElement(constructor.enclosingClass.declaration); | |
| 1537 | |
| 1538 ClosureEnvironment closureEnvironment = | |
| 1539 createClosureEnvironment(constructor, elements); | |
| 1540 | |
| 1541 ConstructorCodegen codegen = new ConstructorCodegen( | |
| 1542 functionBuilder, | |
| 1543 context, | |
| 1544 elements, | |
| 1545 registry, | |
| 1546 closureEnvironment, | |
| 1547 constructor, | |
| 1548 classBuilder); | |
| 1549 | |
| 1550 codegen.compile(); | |
| 1551 | |
| 1552 if (compiler.verbose) { | |
| 1553 context.compiler.reportVerboseInfo( | |
| 1554 constructor, functionBuilder.verboseToString()); | |
| 1555 } | |
| 1556 }); | |
| 1557 } | |
| 1558 | |
| 1559 /** | |
| 1560 * Generate a getter for field [fieldIndex]. | |
| 1561 */ | |
| 1562 int makeGetter(int fieldIndex) { | |
| 1563 return systemBuilder.getGetterByFieldIndex(fieldIndex); | |
| 1564 } | |
| 1565 | |
| 1566 /** | |
| 1567 * Generate a setter for field [fieldIndex]. | |
| 1568 */ | |
| 1569 int makeSetter(int fieldIndex) { | |
| 1570 return systemBuilder.getSetterByFieldIndex(fieldIndex); | |
| 1571 } | |
| 1572 | |
| 1573 void generateUnimplementedError( | |
| 1574 Spannable spannable, | |
| 1575 String reason, | |
| 1576 FletchFunctionBuilder function, | |
| 1577 {bool suppressHint: false}) { | |
| 1578 if (!suppressHint) { | |
| 1579 compiler.reporter.reportHintMessage( | |
| 1580 spannable, MessageKind.GENERIC, {'text': reason}); | |
| 1581 } | |
| 1582 var constString = constantSystem.createString( | |
| 1583 new DartString.literal(reason)); | |
| 1584 context.markConstantUsed(constString); | |
| 1585 function | |
| 1586 ..assembler.loadConst(function.allocateConstant(constString)) | |
| 1587 ..assembler.emitThrow(); | |
| 1588 } | |
| 1589 | |
| 1590 void forEachSubclassOf(ClassElement cls, void f(ClassElement cls)) { | |
| 1591 Queue<ClassElement> queue = new Queue<ClassElement>(); | |
| 1592 queue.add(cls); | |
| 1593 while (queue.isNotEmpty) { | |
| 1594 ClassElement cls = queue.removeFirst(); | |
| 1595 if (compiler.world.isInstantiated(cls.declaration)) { | |
| 1596 queue.addAll(compiler.world.strictSubclassesOf(cls)); | |
| 1597 } | |
| 1598 f(cls); | |
| 1599 } | |
| 1600 } | |
| 1601 | |
| 1602 void newElement(Element element) { | |
| 1603 if (element.isField && element.isInstanceMember) { | |
| 1604 forEachSubclassOf(element.enclosingClass, (ClassElement cls) { | |
| 1605 FletchClassBuilder builder = registerClassElement(cls); | |
| 1606 builder.addField(element); | |
| 1607 }); | |
| 1608 } | |
| 1609 } | |
| 1610 | |
| 1611 void replaceFunctionUsageElement(Element element, List<Element> users) { | |
| 1612 for (Element user in users) { | |
| 1613 systemBuilder.replaceUsage(user, element); | |
| 1614 } | |
| 1615 } | |
| 1616 | |
| 1617 void forgetElement(Element element) { | |
| 1618 // TODO(ahe): The front-end should remove the element from | |
| 1619 // elementsWithCompileTimeErrors. | |
| 1620 compiler.elementsWithCompileTimeErrors.remove(element); | |
| 1621 FletchFunctionBase function = | |
| 1622 systemBuilder.lookupFunctionByElement(element); | |
| 1623 if (function == null) return; | |
| 1624 systemBuilder.forgetFunction(function); | |
| 1625 } | |
| 1626 | |
| 1627 void removeField(FieldElement element) { | |
| 1628 if (!element.isInstanceMember) return; | |
| 1629 ClassElement enclosingClass = element.enclosingClass; | |
| 1630 forEachSubclassOf(enclosingClass, (ClassElement cls) { | |
| 1631 FletchClassBuilder builder = registerClassElement(cls); | |
| 1632 builder.removeField(element); | |
| 1633 }); | |
| 1634 } | |
| 1635 | |
| 1636 void removeFunction(FunctionElement element) { | |
| 1637 FletchFunctionBase function = | |
| 1638 systemBuilder.lookupFunctionByElement(element); | |
| 1639 if (function == null) return; | |
| 1640 if (element.isInstanceMember) { | |
| 1641 ClassElement enclosingClass = element.enclosingClass; | |
| 1642 FletchClassBuilder builder = registerClassElement(enclosingClass); | |
| 1643 builder.removeFromMethodTable(function); | |
| 1644 } | |
| 1645 } | |
| 1646 | |
| 1647 /// Invoked during codegen enqueuing to compile constructor initializers. | |
| 1648 /// | |
| 1649 /// There's only one [Element] representing a constructor, but Fletch uses | |
| 1650 /// two different functions for implementing a constructor. | |
| 1651 /// | |
| 1652 /// The first function takes care of allocating the instance and initializing | |
| 1653 /// fields (called the constructor initializer), the other function | |
| 1654 /// implements the body of the constructor (what is between the curly | |
| 1655 /// braces). A constructor initializer never calls constructor initializers | |
| 1656 /// of a superclass. Instead field initializers from the superclass are | |
| 1657 /// inlined in the constructor initializer. The constructor initializer will | |
| 1658 /// call all the constructor bodies from superclasses in the correct order. | |
| 1659 /// | |
| 1660 /// The constructor bodies are basically special instance methods that can | |
| 1661 /// only be called from constructor initializers. Unlike constructor bodies, | |
| 1662 /// we only need constructor initializer for classes that are directly | |
| 1663 /// instantiated (excluding, for example, abstract classes). | |
| 1664 /// | |
| 1665 /// Given this, we compile constructor bodies when the normal enqueuer tells | |
| 1666 /// us to compile a generative constructor (see [codegen]), and track | |
| 1667 /// constructor initializers in a separate queue. | |
| 1668 void compilePendingConstructorInitializers() { | |
| 1669 while (pendingConstructorInitializers.isNotEmpty) { | |
| 1670 compileConstructorInitializer( | |
| 1671 pendingConstructorInitializers.removeLast()); | |
| 1672 } | |
| 1673 } | |
| 1674 | |
| 1675 bool onQueueEmpty(Enqueuer enqueuer, Iterable<ClassElement> recentClasses) { | |
| 1676 if (enqueuer is! ResolutionEnqueuer) { | |
| 1677 compilePendingConstructorInitializers(); | |
| 1678 } | |
| 1679 return true; | |
| 1680 } | |
| 1681 | |
| 1682 FletchEnqueueTask makeEnqueuer() => new FletchEnqueueTask(compiler); | |
| 1683 | |
| 1684 static bool isExactParameterMatch( | |
| 1685 FunctionSignature signature, | |
| 1686 CallStructure callStructure) { | |
| 1687 if (!callStructure.signatureApplies(signature)) { | |
| 1688 return false; | |
| 1689 } | |
| 1690 if (!signature.hasOptionalParameters) { | |
| 1691 // There are no optional parameters, and the signature applies, so this | |
| 1692 // is an exact match. | |
| 1693 return true; | |
| 1694 } | |
| 1695 if (!signature.optionalParametersAreNamed) { | |
| 1696 // The optional parameters aren't named which means that they are | |
| 1697 // optional positional parameters. So we have an exact match if the | |
| 1698 // number of parameters matches the number of arguments. | |
| 1699 return callStructure.argumentCount == signature.parameterCount; | |
| 1700 } | |
| 1701 // Otherwise, the optional parameters are named, and we have an exact match | |
| 1702 // if the named arguments in the call occur in the same order as the | |
| 1703 // parameters in the signature. | |
| 1704 if (callStructure.namedArguments.length != | |
| 1705 signature.optionalParameterCount) { | |
| 1706 return false; | |
| 1707 } | |
| 1708 int index = 0; | |
| 1709 for (var parameter in signature.orderedOptionalParameters) { | |
| 1710 if (parameter.name != callStructure.namedArguments[index++]) return false; | |
| 1711 } | |
| 1712 return true; | |
| 1713 } | |
| 1714 | |
| 1715 static FletchBackend createInstance(FletchCompilerImplementation compiler) { | |
| 1716 return new FletchBackend(compiler); | |
| 1717 } | |
| 1718 | |
| 1719 Uri resolvePatchUri(String libraryName, Uri libraryRoot) { | |
| 1720 throw "Not implemented"; | |
| 1721 } | |
| 1722 | |
| 1723 } | |
| 1724 | |
| 1725 class FletchImpactTransformer extends ImpactTransformer { | |
| 1726 final FletchBackend backend; | |
| 1727 | |
| 1728 FletchImpactTransformer(this.backend); | |
| 1729 | |
| 1730 @override | |
| 1731 WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) { | |
| 1732 TransformedWorldImpact transformed = | |
| 1733 new TransformedWorldImpact(worldImpact); | |
| 1734 | |
| 1735 bool anyChange = false; | |
| 1736 | |
| 1737 if (worldImpact.constSymbolNames.isNotEmpty) { | |
| 1738 ClassElement symbolClass = | |
| 1739 backend.compiler.coreClasses.symbolClass.declaration; | |
| 1740 transformed.registerTypeUse( | |
| 1741 new TypeUse.instantiation(symbolClass.rawType)); | |
| 1742 transformed.registerStaticUse( | |
| 1743 new StaticUse.foreignUse( | |
| 1744 symbolClass.lookupConstructor(""))); | |
| 1745 anyChange = true; | |
| 1746 } | |
| 1747 | |
| 1748 for (MapLiteralUse mapLiteralUse in worldImpact.mapLiterals) { | |
| 1749 if (mapLiteralUse.isConstant) continue; | |
| 1750 transformed.registerTypeUse( | |
| 1751 new TypeUse.instantiation(backend.mapImplementation.rawType)); | |
| 1752 transformed.registerStaticUse( | |
| 1753 new StaticUse.constructorInvoke( | |
| 1754 backend.mapImplementation.lookupConstructor(""), | |
| 1755 CallStructure.NO_ARGS)); | |
| 1756 anyChange = true; | |
| 1757 } | |
| 1758 return anyChange ? transformed : worldImpact; | |
| 1759 } | |
| 1760 | |
| 1761 @override | |
| 1762 transformCodegenImpact(impact) => throw "unimplemented"; | |
| 1763 } | |
| 1764 | |
| 1765 bool isLocalFunction(Element element) { | |
| 1766 if (!element.isFunction) return false; | |
| 1767 if (element is ExecutableElement) { | |
| 1768 return element.memberContext != element; | |
| 1769 } | |
| 1770 return false; | |
| 1771 } | |
| 1772 | |
| 1773 Name memberName(AstElement element) { | |
| 1774 if (isLocalFunction(element)) return null; | |
| 1775 MemberElement member = element; | |
| 1776 return member.memberName; | |
| 1777 } | |
| OLD | NEW |