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