Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(638)

Side by Side Diff: pkg/fletchc/lib/src/fletch_backend.dart

Issue 1659163007: Rename fletch -> dartino (Closed) Base URL: https://github.com/dartino/sdk.git@master
Patch Set: address comments Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « pkg/fletchc/lib/src/find_position_visitor.dart ('k') | pkg/fletchc/lib/src/fletch_class_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698