| Index: pkg/fletchc/lib/src/fletch_backend.dart
|
| diff --git a/pkg/fletchc/lib/src/fletch_backend.dart b/pkg/fletchc/lib/src/fletch_backend.dart
|
| deleted file mode 100644
|
| index 9470a09cfa631f8c34db352e197cfcc1fdad0399..0000000000000000000000000000000000000000
|
| --- a/pkg/fletchc/lib/src/fletch_backend.dart
|
| +++ /dev/null
|
| @@ -1,1788 +0,0 @@
|
| -// Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE.md file.
|
| -
|
| -library fletchc.fletch_backend;
|
| -
|
| -import 'dart:async' show
|
| - Future;
|
| -
|
| -import 'dart:collection' show
|
| - Queue;
|
| -
|
| -import 'package:compiler/src/common/backend_api.dart' show
|
| - Backend,
|
| - ImpactTransformer;
|
| -
|
| -import 'package:compiler/src/common/tasks.dart' show
|
| - CompilerTask;
|
| -
|
| -import 'package:compiler/src/enqueue.dart' show
|
| - Enqueuer,
|
| - ResolutionEnqueuer;
|
| -
|
| -import 'package:compiler/src/diagnostics/messages.dart' show
|
| - MessageKind;
|
| -
|
| -import 'package:compiler/src/diagnostics/diagnostic_listener.dart' show
|
| - DiagnosticMessage;
|
| -
|
| -import 'package:compiler/src/common/registry.dart' show
|
| - Registry;
|
| -
|
| -import 'package:compiler/src/dart_types.dart' show
|
| - DartType,
|
| - InterfaceType;
|
| -
|
| -import 'package:compiler/src/tree/tree.dart' show
|
| - DartString,
|
| - EmptyStatement,
|
| - Expression;
|
| -
|
| -import 'package:compiler/src/elements/elements.dart' show
|
| - AbstractFieldElement,
|
| - AstElement,
|
| - ClassElement,
|
| - ConstructorElement,
|
| - Element,
|
| - ExecutableElement,
|
| - FieldElement,
|
| - FormalElement,
|
| - FunctionElement,
|
| - FunctionSignature,
|
| - FunctionTypedElement,
|
| - LibraryElement,
|
| - MemberElement,
|
| - Name,
|
| - ParameterElement,
|
| - PublicName;
|
| -
|
| -import 'package:compiler/src/universe/selector.dart' show
|
| - Selector;
|
| -
|
| -import 'package:compiler/src/universe/use.dart' show
|
| - DynamicUse,
|
| - StaticUse,
|
| - TypeUse,
|
| - TypeUseKind;
|
| -
|
| -import 'package:compiler/src/universe/call_structure.dart' show
|
| - CallStructure;
|
| -
|
| -import 'package:compiler/src/common.dart' show
|
| - Spannable;
|
| -
|
| -import 'package:compiler/src/elements/modelx.dart' show
|
| - FunctionElementX;
|
| -
|
| -import 'package:compiler/src/dart_backend/dart_backend.dart' show
|
| - DartConstantTask;
|
| -
|
| -import 'package:compiler/src/constants/constant_system.dart' show
|
| - ConstantSystem;
|
| -
|
| -import 'package:compiler/src/compile_time_constants.dart' show
|
| - BackendConstantEnvironment;
|
| -
|
| -import 'package:compiler/src/constants/values.dart' show
|
| - ConstantValue,
|
| - ConstructedConstantValue,
|
| - FunctionConstantValue,
|
| - ListConstantValue,
|
| - MapConstantValue,
|
| - StringConstantValue;
|
| -
|
| -import 'package:compiler/src/constants/expressions.dart' show
|
| - ConstantExpression;
|
| -
|
| -import 'package:compiler/src/resolution/tree_elements.dart' show
|
| - TreeElements;
|
| -
|
| -import 'package:compiler/src/library_loader.dart' show
|
| - LibraryLoader;
|
| -
|
| -import 'package:persistent/persistent.dart' show
|
| - PersistentMap;
|
| -
|
| -import 'fletch_function_builder.dart' show
|
| - FletchFunctionBuilder;
|
| -
|
| -import 'fletch_class_builder.dart' show
|
| - FletchClassBuilder;
|
| -
|
| -import 'fletch_system_builder.dart' show
|
| - FletchSystemBuilder;
|
| -
|
| -import '../incremental_backend.dart' show
|
| - IncrementalFletchBackend;
|
| -
|
| -import 'fletch_enqueuer.dart' show
|
| - FletchEnqueueTask,
|
| - shouldReportEnqueuingOfElement;
|
| -
|
| -import 'fletch_registry.dart' show
|
| - ClosureKind,
|
| - FletchRegistry;
|
| -
|
| -import 'diagnostic.dart' show
|
| - throwInternalError;
|
| -
|
| -import 'package:compiler/src/common/names.dart' show
|
| - Identifiers,
|
| - Names;
|
| -
|
| -import 'package:compiler/src/universe/world_impact.dart' show
|
| - TransformedWorldImpact,
|
| - WorldImpact,
|
| - WorldImpactBuilder;
|
| -
|
| -import 'class_debug_info.dart';
|
| -import 'codegen_visitor.dart';
|
| -import 'debug_info.dart';
|
| -import 'debug_info_constructor_codegen.dart';
|
| -import 'debug_info_function_codegen.dart';
|
| -import 'debug_info_lazy_field_initializer_codegen.dart';
|
| -import 'fletch_context.dart';
|
| -import 'fletch_selector.dart';
|
| -import 'function_codegen.dart';
|
| -import 'lazy_field_initializer_codegen.dart';
|
| -import 'constructor_codegen.dart';
|
| -import 'closure_environment.dart';
|
| -
|
| -import '../bytecodes.dart';
|
| -import '../vm_commands.dart';
|
| -import '../fletch_system.dart';
|
| -import 'package:compiler/src/common/resolution.dart';
|
| -
|
| -const FletchSystem BASE_FLETCH_SYSTEM = const FletchSystem(
|
| - const PersistentMap<int, FletchFunction>(),
|
| - const PersistentMap<Element, FletchFunction>(),
|
| - const PersistentMap<ConstructorElement, FletchFunction>(),
|
| - const PersistentMap<int, int>(),
|
| - const PersistentMap<int, FletchClass>(),
|
| - const PersistentMap<ClassElement, FletchClass>(),
|
| - const PersistentMap<int, FletchConstant>(),
|
| - const PersistentMap<ConstantValue, FletchConstant>(),
|
| - const PersistentMap<int, String>(),
|
| - const PersistentMap<int, int>(),
|
| - const PersistentMap<int, int>(),
|
| - const PersistentMap<ParameterStubSignature, FletchFunction>());
|
| -
|
| -class FletchBackend extends Backend
|
| - implements IncrementalFletchBackend {
|
| - static const String growableListName = '_GrowableList';
|
| - static const String constantListName = '_ConstantList';
|
| - static const String constantByteListName = '_ConstantByteList';
|
| - static const String constantMapName = '_ConstantMap';
|
| - static const String fletchNoSuchMethodErrorName = 'FletchNoSuchMethodError';
|
| - static const String noSuchMethodName = '_noSuchMethod';
|
| - static const String noSuchMethodTrampolineName = '_noSuchMethodTrampoline';
|
| -
|
| - final FletchContext context;
|
| -
|
| - final DartConstantTask constantCompilerTask;
|
| -
|
| - /// Constructors that need to have an initilizer compiled. See
|
| - /// [compilePendingConstructorInitializers].
|
| - final Queue<FletchFunctionBuilder> pendingConstructorInitializers =
|
| - new Queue<FletchFunctionBuilder>();
|
| -
|
| - final Set<FunctionElement> externals = new Set<FunctionElement>();
|
| -
|
| - // TODO(ahe): This should be queried from World.
|
| - final Map<ClassElement, Set<ClassElement>> directSubclasses =
|
| - <ClassElement, Set<ClassElement>>{};
|
| -
|
| - /// Set of classes that have special meaning to the Fletch VM. They're
|
| - /// created using [PushBuiltinClass] instead of [PushNewClass].
|
| - // TODO(ahe): Move this to FletchSystem?
|
| - final Set<ClassElement> builtinClasses = new Set<ClassElement>();
|
| -
|
| - // TODO(ahe): This should be invalidated by a new [FletchSystem].
|
| - final Map<MemberElement, ClosureEnvironment> closureEnvironments =
|
| - <MemberElement, ClosureEnvironment>{};
|
| -
|
| - // TODO(ahe): This should be moved to [FletchSystem].
|
| - final Map<FunctionElement, FletchClassBuilder> closureClasses =
|
| - <FunctionElement, FletchClassBuilder>{};
|
| -
|
| - // TODO(ahe): This should be moved to [FletchSystem].
|
| - final Map<FieldElement, FletchFunctionBuilder> lazyFieldInitializers =
|
| - <FieldElement, FletchFunctionBuilder>{};
|
| -
|
| - // TODO(ahe): This should be moved to [FletchSystem].
|
| - Map<FletchClassBuilder, FletchFunctionBuilder> tearoffFunctions;
|
| -
|
| - FletchCompilerImplementation get compiler => super.compiler;
|
| -
|
| - LibraryElement fletchSystemLibrary;
|
| - LibraryElement fletchFFILibrary;
|
| - LibraryElement collectionLibrary;
|
| - LibraryElement mathLibrary;
|
| - LibraryElement get asyncLibrary => compiler.asyncLibrary;
|
| - LibraryElement fletchLibrary;
|
| -
|
| - FunctionElement fletchSystemEntry;
|
| -
|
| - FunctionElement fletchExternalInvokeMain;
|
| -
|
| - FunctionElement fletchExternalYield;
|
| -
|
| - FunctionElement fletchExternalNativeError;
|
| -
|
| - FunctionElement fletchExternalCoroutineChange;
|
| -
|
| - FunctionElement fletchUnresolved;
|
| - FunctionElement fletchCompileError;
|
| -
|
| - FletchClassBuilder compiledObjectClass;
|
| -
|
| - ClassElement smiClass;
|
| - ClassElement mintClass;
|
| - ClassElement growableListClass;
|
| - ClassElement fletchNoSuchMethodErrorClass;
|
| - ClassElement bigintClass;
|
| - ClassElement uint32DigitsClass;
|
| -
|
| - FletchClassBuilder compiledClosureClass;
|
| -
|
| - /// Holds a reference to the class Coroutine if it exists.
|
| - ClassElement coroutineClass;
|
| -
|
| - FletchSystemBuilder systemBuilder;
|
| -
|
| - final Set<FunctionElement> alwaysEnqueue = new Set<FunctionElement>();
|
| -
|
| - FletchImpactTransformer impactTransformer;
|
| -
|
| - FletchBackend(FletchCompilerImplementation compiler)
|
| - : this.context = compiler.context,
|
| - this.constantCompilerTask = new DartConstantTask(compiler),
|
| - this.systemBuilder = new FletchSystemBuilder(BASE_FLETCH_SYSTEM),
|
| - super(compiler) {
|
| - this.impactTransformer = new FletchImpactTransformer(this);
|
| - }
|
| -
|
| - void newSystemBuilder(FletchSystem predecessorSystem) {
|
| - systemBuilder = new FletchSystemBuilder(predecessorSystem);
|
| - }
|
| -
|
| - FletchClassBuilder registerClassElement(ClassElement element) {
|
| - if (element == null) return null;
|
| - assert(element.isDeclaration);
|
| -
|
| - FletchClassBuilder classBuilder =
|
| - systemBuilder.lookupClassBuilderByElement(element);
|
| - if (classBuilder != null) return classBuilder;
|
| -
|
| - directSubclasses[element] = new Set<ClassElement>();
|
| - FletchClassBuilder superclass = registerClassElement(element.superclass);
|
| - if (superclass != null) {
|
| - Set<ClassElement> subclasses = directSubclasses[element.superclass];
|
| - subclasses.add(element);
|
| - }
|
| - classBuilder = systemBuilder.newClassBuilder(
|
| - element, superclass, builtinClasses.contains(element));
|
| -
|
| - // TODO(ajohnsen): Currently, the FletchRegistry does not enqueue fields.
|
| - // This is a workaround, where we basically add getters for all fields.
|
| - classBuilder.updateImplicitAccessors(this);
|
| -
|
| - Element callMember = element.lookupLocalMember(Identifiers.call);
|
| - if (callMember != null && callMember.isFunction) {
|
| - FunctionElement function = callMember;
|
| - classBuilder.createIsFunctionEntry(
|
| - this, function.functionSignature.parameterCount);
|
| - }
|
| - return classBuilder;
|
| - }
|
| -
|
| - FletchClassBuilder createCallableStubClass(
|
| - int fields, int arity, FletchClassBuilder superclass) {
|
| - FletchClassBuilder classBuilder = systemBuilder.newClassBuilder(
|
| - null, superclass, false, extraFields: fields);
|
| - classBuilder.createIsFunctionEntry(this, arity);
|
| - return classBuilder;
|
| - }
|
| -
|
| - List<CompilerTask> get tasks => <CompilerTask>[];
|
| -
|
| - ConstantSystem get constantSystem {
|
| - return constantCompilerTask.constantCompiler.constantSystem;
|
| - }
|
| -
|
| - BackendConstantEnvironment get constants => constantCompilerTask;
|
| -
|
| - bool classNeedsRti(ClassElement cls) => false;
|
| -
|
| - bool methodNeedsRti(FunctionElement function) => false;
|
| -
|
| - void enqueueHelpers(ResolutionEnqueuer world, Registry incomingRegistry) {
|
| - FletchRegistry registry = new FletchRegistry(compiler);
|
| - compiler.patchAnnotationClass = patchAnnotationClass;
|
| -
|
| - bool hasMissingHelpers = false;
|
| - loadHelperMethods((String name) {
|
| - LibraryElement library = fletchSystemLibrary;
|
| - Element helper = library.findLocal(name);
|
| - // TODO(ahe): Make it cleaner.
|
| - if (helper != null && helper.isAbstractField) {
|
| - AbstractFieldElement abstractField = helper;
|
| - helper = abstractField.getter;
|
| - }
|
| - if (helper == null) {
|
| - hasMissingHelpers = true;
|
| - compiler.reporter.reportErrorMessage(
|
| - library, MessageKind.GENERIC,
|
| - {'text': "Required implementation method '$name' not found."});
|
| - }
|
| - return helper;
|
| - });
|
| - if (hasMissingHelpers) {
|
| - throwInternalError(
|
| - "Some implementation methods are missing, see details above");
|
| - }
|
| - world.registerStaticUse(
|
| - new StaticUse.staticInvoke(fletchCompileError, CallStructure.ONE_ARG));
|
| - world.registerStaticUse(
|
| - new StaticUse.staticInvoke(fletchSystemEntry, CallStructure.ONE_ARG));
|
| - world.registerStaticUse(
|
| - new StaticUse.staticInvoke(fletchUnresolved, CallStructure.ONE_ARG));
|
| -
|
| - loadHelperClasses((
|
| - String name,
|
| - LibraryElement library,
|
| - {bool builtin: false}) {
|
| - var classImpl = library.findLocal(name);
|
| - if (classImpl == null) classImpl = library.implementation.find(name);
|
| - if (classImpl == null) {
|
| - compiler.reporter.reportErrorMessage(
|
| - library, MessageKind.GENERIC,
|
| - {'text': "Required implementation class '$name' not found."});
|
| - hasMissingHelpers = true;
|
| - return null;
|
| - }
|
| - if (hasMissingHelpers) return null;
|
| - if (builtin) builtinClasses.add(classImpl);
|
| - {
|
| - // TODO(ahe): Register in ResolutionCallbacks. The lines in this block
|
| - // should not happen at this point in time.
|
| - classImpl.ensureResolved(compiler.resolution);
|
| - world.registerInstantiatedType(classImpl.rawType);
|
| - // TODO(ahe): This is a hack to let both the world and the codegen know
|
| - // about the instantiated type.
|
| - registry.registerInstantiatedType(classImpl.rawType);
|
| - }
|
| - return registerClassElement(classImpl);
|
| - });
|
| - if (hasMissingHelpers) {
|
| - throwInternalError(
|
| - "Some implementation classes are missing, see details above");
|
| - }
|
| -
|
| - // Register list constructors to world.
|
| - // TODO(ahe): Register growableListClass through ResolutionCallbacks.
|
| - growableListClass.constructors.forEach((Element element) {
|
| - world.registerStaticUse(new StaticUse.constructorInvoke(element, null));
|
| - });
|
| -
|
| - // TODO(ajohnsen): Remove? String interpolation does not enqueue '+'.
|
| - // Investigate what else it may enqueue, could be StringBuilder, and then
|
| - // consider using that instead.
|
| - world.registerDynamicUse(
|
| - new DynamicUse(new Selector.binaryOperator('+'), null));
|
| -
|
| - world.registerDynamicUse(new DynamicUse(
|
| - new Selector.call(new PublicName('add'), CallStructure.ONE_ARG), null));
|
| -
|
| - alwaysEnqueue.add(
|
| - compiler.coreClasses.objectClass.implementation.lookupLocalMember(
|
| - noSuchMethodTrampolineName));
|
| - alwaysEnqueue.add(
|
| - compiler.coreClasses.objectClass.implementation.lookupLocalMember(
|
| - noSuchMethodName));
|
| -
|
| - if (coroutineClass != null) {
|
| - builtinClasses.add(coroutineClass);
|
| - alwaysEnqueue.add(coroutineClass.lookupLocalMember("_coroutineStart"));
|
| - }
|
| -
|
| - for (FunctionElement element in alwaysEnqueue) {
|
| - world.registerStaticUse(new StaticUse.foreignUse(element));
|
| - }
|
| - }
|
| -
|
| - void loadHelperMethods(
|
| - FunctionElement findHelper(String name)) {
|
| -
|
| - FunctionElement findExternal(String name) {
|
| - FunctionElement helper = findHelper(name);
|
| - if (helper != null) externals.add(helper);
|
| - return helper;
|
| - }
|
| -
|
| - fletchSystemEntry = findHelper('entry');
|
| - fletchExternalInvokeMain = findExternal('invokeMain');
|
| - fletchExternalYield = findExternal('yield');
|
| - fletchExternalCoroutineChange = findExternal('coroutineChange');
|
| - fletchExternalNativeError = findExternal('nativeError');
|
| - fletchUnresolved = findExternal('unresolved');
|
| - fletchCompileError = findExternal('compileError');
|
| - }
|
| -
|
| - void loadHelperClasses(
|
| - FletchClassBuilder loadClass(
|
| - String name,
|
| - LibraryElement library,
|
| - {bool builtin})) {
|
| - compiledObjectClass =
|
| - loadClass("Object", compiler.coreLibrary, builtin: true);
|
| - compiledClosureClass =
|
| - loadClass("_TearOffClosure", compiler.coreLibrary, builtin: true);
|
| - smiClass = loadClass("_Smi", compiler.coreLibrary, builtin: true)?.element;
|
| - mintClass =
|
| - loadClass("_Mint", compiler.coreLibrary, builtin: true)?.element;
|
| - loadClass("_OneByteString", compiler.coreLibrary, builtin: true);
|
| - loadClass("_TwoByteString", compiler.coreLibrary, builtin: true);
|
| - // TODO(ahe): Register _ConstantList through ResolutionCallbacks.
|
| - loadClass(constantListName, fletchSystemLibrary, builtin: true);
|
| - loadClass(constantByteListName, fletchSystemLibrary, builtin: true);
|
| - loadClass(constantMapName, fletchSystemLibrary, builtin: true);
|
| - loadClass("_DoubleImpl", compiler.coreLibrary, builtin: true);
|
| - loadClass("Null", compiler.coreLibrary, builtin: true);
|
| - loadClass("bool", compiler.coreLibrary, builtin: true);
|
| - loadClass("StackOverflowError", compiler.coreLibrary, builtin: true);
|
| - loadClass("Port", fletchLibrary, builtin: true);
|
| - loadClass("Process", fletchLibrary, builtin: true);
|
| - loadClass("ProcessDeath", fletchLibrary, builtin: true);
|
| - loadClass("ForeignMemory", fletchFFILibrary, builtin: true);
|
| - if (context.enableBigint) {
|
| - bigintClass = loadClass("_Bigint", compiler.coreLibrary)?.element;
|
| - uint32DigitsClass =
|
| - loadClass("_Uint32Digits", compiler.coreLibrary)?.element;
|
| - }
|
| - growableListClass =
|
| - loadClass(growableListName, fletchSystemLibrary)?.element;
|
| - fletchNoSuchMethodErrorClass =
|
| - loadClass(fletchNoSuchMethodErrorName,
|
| - fletchSystemLibrary,
|
| - builtin: true)?.element;
|
| -
|
| - // This class is optional.
|
| - coroutineClass = fletchSystemLibrary.implementation.find("Coroutine");
|
| - if (coroutineClass != null) {
|
| - coroutineClass.ensureResolved(compiler.resolution);
|
| - }
|
| - }
|
| -
|
| - void onElementResolved(Element element, TreeElements elements) {
|
| - if (alwaysEnqueue.contains(element)) {
|
| - var registry = new FletchRegistry(compiler);
|
| - if (element.isStatic || element.isTopLevel) {
|
| - registry.registerStaticUse(new StaticUse.foreignUse(element));
|
| - } else {
|
| - registry.registerDynamicUse(new Selector.fromElement(element));
|
| - }
|
| - }
|
| - }
|
| -
|
| - ClassElement get intImplementation => smiClass;
|
| -
|
| - /// Class of annotations to mark patches in patch files.
|
| - ///
|
| - /// The patch parser (pkg/compiler/lib/src/patch_parser.dart). The patch
|
| - /// parser looks for an annotation on the form "@patch", where "patch" is
|
| - /// compile-time constant instance of [patchAnnotationClass].
|
| - ClassElement get patchAnnotationClass {
|
| - // TODO(ahe): Introduce a proper constant class to identify constants. For
|
| - // now, we simply put "const patch = "patch";" in fletch._system.
|
| - return super.stringImplementation;
|
| - }
|
| -
|
| - FletchClassBuilder createClosureClass(
|
| - FunctionElement closure,
|
| - ClosureEnvironment closureEnvironment) {
|
| - return closureClasses.putIfAbsent(closure, () {
|
| - ClosureInfo info = closureEnvironment.closures[closure];
|
| - int fields = info.free.length;
|
| - if (info.isThisFree) fields++;
|
| - return createCallableStubClass(
|
| - fields,
|
| - closure.functionSignature.parameterCount,
|
| - compiledClosureClass);
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Create a tearoff class for function [function].
|
| - *
|
| - * The class will have one method named 'call', accepting the same arguments
|
| - * as [function]. The method will load the arguments received and statically
|
| - * call [function] (essential a tail-call).
|
| - *
|
| - * If [function] is an instance member, the class will have one field, the
|
| - * instance.
|
| - */
|
| - FletchClassBuilder createTearoffClass(FletchFunctionBase function) {
|
| - FletchClassBuilder tearoffClass =
|
| - systemBuilder.getTearoffClassBuilder(function, compiledClosureClass);
|
| - if (tearoffClass != null) return tearoffClass;
|
| - FunctionSignature signature = function.signature;
|
| - bool hasThis = function.isInstanceMember;
|
| - tearoffClass = createCallableStubClass(
|
| - hasThis ? 1 : 0,
|
| - signature.parameterCount,
|
| - compiledClosureClass);
|
| -
|
| - FletchFunctionBuilder functionBuilder =
|
| - systemBuilder.newTearOff(function, tearoffClass.classId);
|
| -
|
| - BytecodeAssembler assembler = functionBuilder.assembler;
|
| - int argumentCount = signature.parameterCount;
|
| - if (hasThis) {
|
| - argumentCount++;
|
| - // If the tearoff has a 'this' value, load it. It's the only field
|
| - // in the tearoff class.
|
| - assembler
|
| - ..loadParameter(0)
|
| - ..loadField(0);
|
| - }
|
| - for (int i = 0; i < signature.parameterCount; i++) {
|
| - // The closure-class is at parameter index 0, so argument i is at
|
| - // i + 1.
|
| - assembler.loadParameter(i + 1);
|
| - }
|
| - int constId = functionBuilder.allocateConstantFromFunction(
|
| - function.functionId);
|
| - // TODO(ajohnsen): Create a tail-call bytecode, so we don't have to
|
| - // load all the arguments.
|
| - assembler
|
| - ..invokeStatic(constId, argumentCount)
|
| - ..ret()
|
| - ..methodEnd();
|
| -
|
| - String symbol = context.getCallSymbol(signature);
|
| - int id = context.getSymbolId(symbol);
|
| - int fletchSelector = FletchSelector.encodeMethod(
|
| - id,
|
| - signature.parameterCount);
|
| - tearoffClass.addToMethodTable(fletchSelector, functionBuilder);
|
| -
|
| - if (!function.isInstanceMember) return tearoffClass;
|
| -
|
| - ClassElement classElement =
|
| - systemBuilder.lookupClassBuilder(function.memberOf).element;
|
| - if (classElement == null) return tearoffClass;
|
| -
|
| - // Create == function that tests for equality.
|
| - int isSelector = context.toFletchTearoffIsSelector(
|
| - function.name,
|
| - classElement);
|
| - tearoffClass.addIsSelector(isSelector);
|
| -
|
| - FletchFunctionBuilder equal = systemBuilder.newFunctionBuilder(
|
| - FletchFunctionKind.NORMAL,
|
| - 2);
|
| -
|
| - BytecodeLabel isFalse = new BytecodeLabel();
|
| - equal.assembler
|
| - // First test for class. This ensures it's the exact function that
|
| - // we expect.
|
| - ..loadParameter(1)
|
| - ..invokeTest(isSelector, 0)
|
| - ..branchIfFalse(isFalse)
|
| - // Then test that the receiver is identical.
|
| - ..loadParameter(0)
|
| - ..loadField(0)
|
| - ..loadParameter(1)
|
| - ..loadField(0)
|
| - ..identicalNonNumeric()
|
| - ..branchIfFalse(isFalse)
|
| - ..loadLiteralTrue()
|
| - ..ret()
|
| - ..bind(isFalse)
|
| - ..loadLiteralFalse()
|
| - ..ret()
|
| - ..methodEnd();
|
| -
|
| - id = context.getSymbolId("==");
|
| - int equalsSelector = FletchSelector.encodeMethod(id, 1);
|
| - tearoffClass.addToMethodTable(equalsSelector, equal);
|
| -
|
| - // Create hashCode getter. We simply add the object hashCode and the
|
| - // method id of the tearoff'ed function.
|
| - FletchFunctionBuilder hashCode = systemBuilder.newFunctionBuilder(
|
| - FletchFunctionKind.ACCESSOR,
|
| - 1);
|
| -
|
| - int hashCodeSelector = FletchSelector.encodeGetter(
|
| - context.getSymbolId("hashCode"));
|
| -
|
| - // TODO(ajohnsen): Use plus, we plus is always enqueued. Consider using
|
| - // xor when we have a way to enqueue it from here.
|
| - int plusSelector = FletchSelector.encodeMethod(
|
| - context.getSymbolId("+"), 1);
|
| -
|
| - hashCode.assembler
|
| - ..loadParameter(0)
|
| - ..loadField(0)
|
| - ..invokeMethod(hashCodeSelector, 0)
|
| - ..loadLiteral(function.functionId)
|
| - ..invokeMethod(plusSelector, 1)
|
| - ..ret()
|
| - ..methodEnd();
|
| -
|
| - tearoffClass.addToMethodTable(hashCodeSelector, hashCode);
|
| -
|
| - return tearoffClass;
|
| - }
|
| -
|
| - FletchFunctionBase getFunctionForElement(FunctionElement element) {
|
| - assert(element.memberContext == element);
|
| -
|
| - FletchFunctionBase function =
|
| - systemBuilder.lookupFunctionByElement(element);
|
| - if (function != null) return function;
|
| -
|
| - return createFletchFunctionBuilder(element);
|
| - }
|
| -
|
| - /// Get the constructor initializer function for [constructor]. The function
|
| - /// will be created the first time it's called for [constructor].
|
| - ///
|
| - /// See [compilePendingConstructorInitializers] for an overview of
|
| - /// constructor intializers and constructor bodies.
|
| - FletchFunctionBase getConstructorInitializerFunction(
|
| - ConstructorElement constructor) {
|
| - assert(constructor.isDeclaration);
|
| - constructor = constructor.implementation;
|
| - FletchFunctionBase base =
|
| - systemBuilder.lookupConstructorInitializerByElement(constructor);
|
| - if (base != null) return base;
|
| -
|
| - FletchFunctionBuilder builder = systemBuilder.newConstructorInitializer(
|
| - constructor);
|
| - pendingConstructorInitializers.addFirst(builder);
|
| -
|
| - return builder;
|
| - }
|
| -
|
| - FletchFunctionBuilder createFletchFunctionBuilder(FunctionElement function) {
|
| - assert(function.memberContext == function);
|
| -
|
| - FletchClassBuilder holderClass;
|
| - if (function.isInstanceMember || function.isGenerativeConstructor) {
|
| - ClassElement enclosingClass = function.enclosingClass.declaration;
|
| - holderClass = registerClassElement(enclosingClass);
|
| - }
|
| - return internalCreateFletchFunctionBuilder(
|
| - function,
|
| - function.name,
|
| - holderClass);
|
| - }
|
| -
|
| - FletchFunctionBuilder internalCreateFletchFunctionBuilder(
|
| - FunctionElement function,
|
| - String name,
|
| - FletchClassBuilder holderClass) {
|
| - FletchFunctionBuilder functionBuilder =
|
| - systemBuilder.lookupFunctionBuilderByElement(function.declaration);
|
| - if (functionBuilder != null) return functionBuilder;
|
| -
|
| - FunctionTypedElement implementation = function.implementation;
|
| - int memberOf = holderClass != null ? holderClass.classId : null;
|
| - return systemBuilder.newFunctionBuilderWithSignature(
|
| - name,
|
| - function,
|
| - // Parameter initializers are expressed in the potential
|
| - // implementation.
|
| - implementation.functionSignature,
|
| - memberOf,
|
| - kind: function.isAccessor
|
| - ? FletchFunctionKind.ACCESSOR
|
| - : FletchFunctionKind.NORMAL,
|
| - mapByElement: function.declaration);
|
| - }
|
| -
|
| - ClassDebugInfo createClassDebugInfo(FletchClass klass) {
|
| - return new ClassDebugInfo(klass);
|
| - }
|
| -
|
| - DebugInfo createDebugInfo(
|
| - FletchFunction function,
|
| - FletchSystem currentSystem) {
|
| - DebugInfo debugInfo = new DebugInfo(function);
|
| - AstElement element = function.element;
|
| - if (element == null) return debugInfo;
|
| - List<Bytecode> expectedBytecodes = function.bytecodes;
|
| - element = element.implementation;
|
| - TreeElements elements = element.resolvedAst.elements;
|
| - ClosureEnvironment closureEnvironment = createClosureEnvironment(
|
| - element,
|
| - elements);
|
| - CodegenVisitor codegen;
|
| - FletchFunctionBuilder builder =
|
| - new FletchFunctionBuilder.fromFletchFunction(function);
|
| - if (function.isLazyFieldInitializer) {
|
| - codegen = new DebugInfoLazyFieldInitializerCodegen(
|
| - debugInfo,
|
| - builder,
|
| - context,
|
| - elements,
|
| - closureEnvironment,
|
| - element,
|
| - compiler);
|
| - } else if (function.isInitializerList) {
|
| - ClassElement enclosingClass = element.enclosingClass;
|
| - // TODO(ajohnsen): Don't depend on the class builder.
|
| - FletchClassBuilder classBuilder =
|
| - systemBuilder.lookupClassBuilderByElement(enclosingClass.declaration);
|
| - codegen = new DebugInfoConstructorCodegen(
|
| - debugInfo,
|
| - builder,
|
| - context,
|
| - elements,
|
| - closureEnvironment,
|
| - element,
|
| - classBuilder,
|
| - compiler);
|
| - } else {
|
| - codegen = new DebugInfoFunctionCodegen(
|
| - debugInfo,
|
| - builder,
|
| - context,
|
| - elements,
|
| - closureEnvironment,
|
| - element,
|
| - compiler);
|
| - }
|
| - if (isNative(element)) {
|
| - compiler.reporter.withCurrentElement(element, () {
|
| - codegenNativeFunction(element, codegen);
|
| - });
|
| - } else if (isExternal(element)) {
|
| - compiler.reporter.withCurrentElement(element, () {
|
| - codegenExternalFunction(element, codegen);
|
| - });
|
| - } else {
|
| - compiler.reporter.withCurrentElement(element, () { codegen.compile(); });
|
| - }
|
| - // The debug codegen should generate the same bytecodes as the original
|
| - // codegen. If that is not the case debug information will be useless.
|
| - if (!Bytecode.identicalBytecodes(expectedBytecodes,
|
| - codegen.assembler.bytecodes)) {
|
| - throw 'Debug info code different from running code.';
|
| - }
|
| - return debugInfo;
|
| - }
|
| -
|
| - codegen(_) {
|
| - new UnsupportedError(
|
| - "Method [codegen] not supported, use [compileElement] instead");
|
| - }
|
| -
|
| - /// Invoked by [FletchEnqueuer] once per element that needs to be compiled.
|
| - ///
|
| - /// This is used to generate the bytecodes for [declaration].
|
| - void compileElement(
|
| - AstElement declaration,
|
| - TreeElements treeElements,
|
| - FletchRegistry registry) {
|
| - AstElement element = declaration.implementation;
|
| - compiler.reporter.withCurrentElement(element, () {
|
| - assert(declaration.isDeclaration);
|
| - context.compiler.reportVerboseInfo(element, 'Compiling $element');
|
| - if (element.isFunction ||
|
| - element.isGetter ||
|
| - element.isSetter ||
|
| - element.isGenerativeConstructor ||
|
| - element.isFactoryConstructor) {
|
| - // For a generative constructor, this means compile the constructor
|
| - // body. See [compilePendingConstructorInitializers] for an overview of
|
| - // how constructor initializers and constructor bodies are compiled.
|
| - codegenFunction(element, treeElements, registry);
|
| - } else if (element.isField) {
|
| - context.compiler.reportVerboseInfo(
|
| - element, "Asked to compile a field, but don't know how");
|
| - } else {
|
| - compiler.reporter.internalError(
|
| - element, "Uninimplemented element kind: ${element.kind}");
|
| - }
|
| - });
|
| - }
|
| -
|
| - /// Invoked by [FletchEnqueuer] once per [selector] that may invoke
|
| - /// [declaration].
|
| - ///
|
| - /// This is used to generate stubs for [declaration].
|
| - void compileElementUsage(
|
| - AstElement declaration,
|
| - Selector selector,
|
| - TreeElements treeElements,
|
| - FletchRegistry registry) {
|
| - AstElement element = declaration.implementation;
|
| - compiler.reporter.withCurrentElement(element, () {
|
| - assert(declaration.isDeclaration);
|
| - context.compiler.reportVerboseInfo(element, 'Compiling $element');
|
| - if (!element.isInstanceMember && !isLocalFunction(element)) {
|
| - // No stub needed. Optional arguments are handled at call-site.
|
| - } else if (element.isFunction) {
|
| - FletchFunctionBase function =
|
| - systemBuilder.lookupFunctionByElement(element.declaration);
|
| - CallStructure callStructure = selector.callStructure;
|
| - FunctionSignature signature = function.signature;
|
| - if (selector.isGetter) {
|
| - if (shouldReportEnqueuingOfElement(compiler, element)) {
|
| - context.compiler.reportVerboseInfo(
|
| - element, 'Adding tear-off stub');
|
| - }
|
| - createTearoffGetterForFunction(
|
| - function, isSpecialCallMethod: element.name == "call");
|
| - } else if (selector.isCall &&
|
| - callStructure.signatureApplies(signature) &&
|
| - !isExactParameterMatch(signature, callStructure)) {
|
| - if (shouldReportEnqueuingOfElement(compiler, element)) {
|
| - context.compiler.reportVerboseInfo(
|
| - element, 'Adding stub for $selector');
|
| - }
|
| - FletchFunctionBase stub = createParameterStub(function, selector);
|
| - patchClassWithStub(
|
| - stub, selector, function.memberOf, isLocalFunction(element));
|
| - }
|
| - } else if (element.isGetter || element.isSetter) {
|
| - // No stub needed. If a getter returns a closure, the VM's
|
| - // no-such-method handling will do the right thing.
|
| - } else {
|
| - context.compiler.reportVerboseInfo(
|
| - element, "Asked to compile this, but don't know how");
|
| - }
|
| - });
|
| - }
|
| -
|
| - /// Invoked by [FletchEnqueuer] once per `call` [selector] that may invoke
|
| - /// [declaration] as an implicit closure (for example, a tear-off).
|
| - ///
|
| - /// This is used to generate parameter stubs for the closures.
|
| - void compileClosurizationUsage(
|
| - AstElement declaration,
|
| - Selector selector,
|
| - TreeElements treeElements,
|
| - FletchRegistry registry,
|
| - ClosureKind kind) {
|
| - AstElement element = declaration.implementation;
|
| - compiler.reporter.withCurrentElement(element, () {
|
| - assert(declaration.isDeclaration);
|
| - if (shouldReportEnqueuingOfElement(compiler, element)) {
|
| - context.compiler.reportVerboseInfo(
|
| - element, 'Need tear-off parameter stub $selector');
|
| - }
|
| - FletchFunctionBase function =
|
| - systemBuilder.lookupFunctionByElement(element.declaration);
|
| - if (function == null) {
|
| - compiler.reporter.internalError(
|
| - element, "Has no fletch function, but used as tear-off");
|
| - }
|
| - if (selector.isGetter) {
|
| - // This is a special tear-off getter.
|
| -
|
| - // TODO(ahe): This code should probably use [kind] to detect the
|
| - // various cases instead of [isLocalFunction] and looking at names.
|
| -
|
| - assert(selector.memberName == Names.CALL_NAME);
|
| - if (isLocalFunction(element) ||
|
| - memberName(element) == Names.CALL_NAME) {
|
| - createTearoffGetterForFunction(
|
| - function, isSpecialCallMethod: true);
|
| - return;
|
| - }
|
| - int stub = systemBuilder.lookupTearOffById(function.functionId);
|
| - if (stub == null) {
|
| - compiler.reporter.internalError(
|
| - element, "No tear-off stub to compile `call` tear-off");
|
| - } else {
|
| - function = systemBuilder.lookupFunction(stub);
|
| - createTearoffGetterForFunction(function, isSpecialCallMethod: true);
|
| - return;
|
| - }
|
| - }
|
| - switch (kind) {
|
| - case ClosureKind.tearOff:
|
| - case ClosureKind.superTearOff:
|
| - if (memberName(element) == Names.CALL_NAME) {
|
| - // This is really a functionLikeTearOff.
|
| - break;
|
| - }
|
| - // A tear-off has a corresponding stub in a closure class. Look up
|
| - // that stub:
|
| - int stub = systemBuilder.lookupTearOffById(function.functionId);
|
| - if (stub == null) {
|
| - compiler.reporter
|
| - .internalError(element, "Couldn't find tear-off stub");
|
| - } else {
|
| - function = systemBuilder.lookupFunction(stub);
|
| - }
|
| - break;
|
| -
|
| - case ClosureKind.localFunction:
|
| - // A local function already is a member of its closure class, and
|
| - // doesn't have a stub.
|
| - break;
|
| -
|
| - case ClosureKind.functionLike:
|
| - case ClosureKind.functionLikeTearOff:
|
| - compiler.reporter.internalError(element, "Unimplemented: $kind");
|
| - break;
|
| - }
|
| -
|
| - if (!isExactParameterMatch(function.signature, selector.callStructure)) {
|
| - FletchFunctionBase stub = createParameterStub(function, selector);
|
| - patchClassWithStub(stub, selector, function.memberOf, true);
|
| - }
|
| - });
|
| - }
|
| -
|
| - void codegenFunction(
|
| - FunctionElement function,
|
| - TreeElements elements,
|
| - FletchRegistry registry) {
|
| - registry.registerStaticUse(new StaticUse.foreignUse(fletchSystemEntry));
|
| -
|
| - ClosureEnvironment closureEnvironment = createClosureEnvironment(
|
| - function,
|
| - elements);
|
| -
|
| - FletchFunctionBuilder functionBuilder;
|
| -
|
| - if (function.memberContext != function) {
|
| - functionBuilder = internalCreateFletchFunctionBuilder(
|
| - function,
|
| - Identifiers.call,
|
| - createClosureClass(function, closureEnvironment));
|
| - } else {
|
| - functionBuilder = createFletchFunctionBuilder(function);
|
| - }
|
| -
|
| - FunctionCodegen codegen = new FunctionCodegen(
|
| - functionBuilder,
|
| - context,
|
| - elements,
|
| - registry,
|
| - closureEnvironment,
|
| - function);
|
| -
|
| - if (isNative(function)) {
|
| - codegenNativeFunction(function, codegen);
|
| - } else if (isExternal(function)) {
|
| - codegenExternalFunction(function, codegen);
|
| - } else {
|
| - codegen.compile();
|
| - }
|
| -
|
| - if (functionBuilder.isInstanceMember && !function.isGenerativeConstructor) {
|
| - // Inject the function into the method table of the 'holderClass' class.
|
| - // Note that while constructor bodies has a this argument, we don't inject
|
| - // them into the method table.
|
| - String symbol = context.getSymbolForFunction(
|
| - functionBuilder.name,
|
| - function.functionSignature,
|
| - function.library);
|
| - int id = context.getSymbolId(symbol);
|
| - int arity = function.functionSignature.parameterCount;
|
| - SelectorKind kind = SelectorKind.Method;
|
| - if (function.isGetter) kind = SelectorKind.Getter;
|
| - if (function.isSetter) kind = SelectorKind.Setter;
|
| - int fletchSelector = FletchSelector.encode(id, kind, arity);
|
| - FletchClassBuilder classBuilder =
|
| - systemBuilder.lookupClassBuilder(functionBuilder.memberOf);
|
| - classBuilder.addToMethodTable(fletchSelector, functionBuilder);
|
| - // Inject method into all mixin usages.
|
| - getMixinApplicationsOfClass(classBuilder).forEach((ClassElement usage) {
|
| - FletchClassBuilder compiledUsage = registerClassElement(usage);
|
| - compiledUsage.addToMethodTable(fletchSelector, functionBuilder);
|
| - });
|
| - }
|
| -
|
| - if (compiler.verbose) {
|
| - context.compiler.reportVerboseInfo(
|
| - function, functionBuilder.verboseToString());
|
| - }
|
| - }
|
| -
|
| - List<ClassElement> getMixinApplicationsOfClass(FletchClassBuilder builder) {
|
| - ClassElement element = builder.element;
|
| - if (element == null) return [];
|
| - List<ClassElement> mixinUsage =
|
| - compiler.world.mixinUsesOf(element).toList();
|
| - for (int i = 0; i < mixinUsage.length; i++) {
|
| - ClassElement usage = mixinUsage[i];
|
| - // Recursively add mixin-usage of the current 'usage'.
|
| - assert(!compiler.world.mixinUsesOf(usage).any(mixinUsage.contains));
|
| - mixinUsage.addAll(compiler.world.mixinUsesOf(usage));
|
| - }
|
| - return mixinUsage;
|
| - }
|
| -
|
| - void codegenNativeFunction(
|
| - FunctionElement function,
|
| - FunctionCodegen codegen) {
|
| - String name = '.${function.name}';
|
| -
|
| - ClassElement enclosingClass = function.enclosingClass;
|
| - if (enclosingClass != null) name = '${enclosingClass.name}$name';
|
| -
|
| - FletchNativeDescriptor descriptor = context.nativeDescriptors[name];
|
| - if (descriptor == null) {
|
| - throw "Unsupported native function: $name";
|
| - }
|
| -
|
| - if (name == "Coroutine._coroutineNewStack") {
|
| - // The static native method `Coroutine._coroutineNewStack` will invoke
|
| - // the instance method `Coroutine._coroutineStart`.
|
| - if (coroutineClass == null) {
|
| - compiler.reporter.internalError(
|
| - function, "required class [Coroutine] not found");
|
| - }
|
| - FunctionElement coroutineStart =
|
| - coroutineClass.lookupLocalMember("_coroutineStart");
|
| - Selector selector = new Selector.fromElement(coroutineStart);
|
| - new FletchRegistry(compiler)
|
| - ..registerDynamicUse(selector);
|
| - } else if (name == "Process._spawn") {
|
| - // The native method `Process._spawn` will do a closure invoke with 0, 1,
|
| - // or 2 arguments.
|
| - new FletchRegistry(compiler)
|
| - ..registerDynamicUse(new Selector.callClosure(0))
|
| - ..registerDynamicUse(new Selector.callClosure(1))
|
| - ..registerDynamicUse(new Selector.callClosure(2));
|
| - }
|
| -
|
| - int arity = codegen.assembler.functionArity;
|
| - if (name == "Port.send" ||
|
| - name == "Port._sendList" ||
|
| - name == "Port._sendExit") {
|
| - codegen.assembler.invokeNativeYield(arity, descriptor.index);
|
| - } else {
|
| - if (descriptor.isDetachable) {
|
| - codegen.assembler.invokeDetachableNative(arity, descriptor.index);
|
| - } else {
|
| - codegen.assembler.invokeNative(arity, descriptor.index);
|
| - }
|
| - }
|
| -
|
| - EmptyStatement empty = function.node.body.asEmptyStatement();
|
| - if (empty != null) {
|
| - // A native method without a body.
|
| - codegen.assembler
|
| - ..emitThrow()
|
| - ..methodEnd();
|
| - } else {
|
| - codegen.compile();
|
| - }
|
| - }
|
| -
|
| - void codegenExternalFunction(
|
| - FunctionElement function,
|
| - FunctionCodegen codegen) {
|
| - if (function == fletchExternalYield) {
|
| - codegenExternalYield(function, codegen);
|
| - } else if (function == context.compiler.identicalFunction.implementation) {
|
| - codegenIdentical(function, codegen);
|
| - } else if (function == fletchExternalInvokeMain) {
|
| - codegenExternalInvokeMain(function, codegen);
|
| - } else if (function.name == noSuchMethodTrampolineName &&
|
| - function.library == compiler.coreLibrary) {
|
| - codegenExternalNoSuchMethodTrampoline(function, codegen);
|
| - } else {
|
| - DiagnosticMessage message = context.compiler.reporter
|
| - .createMessage(function.node,
|
| - MessageKind.GENERIC,
|
| - {'text':
|
| - 'External function "${function.name}" is not supported'});
|
| - compiler.reporter.reportError(message);
|
| - codegen
|
| - ..doCompileError(message)
|
| - ..assembler.ret()
|
| - ..assembler.methodEnd();
|
| - }
|
| - }
|
| -
|
| - void codegenIdentical(
|
| - FunctionElement function,
|
| - FunctionCodegen codegen) {
|
| - codegen.assembler
|
| - ..loadParameter(0)
|
| - ..loadParameter(1)
|
| - ..identical()
|
| - ..ret()
|
| - ..methodEnd();
|
| - }
|
| -
|
| - void codegenExternalYield(
|
| - FunctionElement function,
|
| - FunctionCodegen codegen) {
|
| - codegen.assembler
|
| - ..loadParameter(0)
|
| - ..processYield()
|
| - ..ret()
|
| - ..methodEnd();
|
| - }
|
| -
|
| - void codegenExternalInvokeMain(
|
| - FunctionElement function,
|
| - FunctionCodegen codegen) {
|
| - compiler.reporter.internalError(
|
| - function, "[codegenExternalInvokeMain] not implemented.");
|
| - // TODO(ahe): This code shouldn't normally be called, only if invokeMain is
|
| - // torn off. Perhaps we should just say we don't support that.
|
| - }
|
| -
|
| - void codegenExternalNoSuchMethodTrampoline(
|
| - FunctionElement function,
|
| - FunctionCodegen codegen) {
|
| - // NOTE: The number of arguments to the [noSuchMethodName] function must be
|
| - // kept in sync with:
|
| - // src/vm/interpreter.cc:HandleEnterNoSuchMethod
|
| - int id = context.getSymbolId(
|
| - context.mangleName(new Name(noSuchMethodName, compiler.coreLibrary)));
|
| - int fletchSelector = FletchSelector.encodeMethod(id, 3);
|
| - BytecodeLabel skipGetter = new BytecodeLabel();
|
| - codegen.assembler
|
| - ..enterNoSuchMethod(skipGetter)
|
| - // First invoke the getter.
|
| - ..invokeSelector(2)
|
| - // Then invoke 'call', with the receiver being the result of the
|
| - // previous invokeSelector.
|
| - ..invokeSelector(1)
|
| - ..exitNoSuchMethod()
|
| - ..bind(skipGetter)
|
| - ..invokeMethod(fletchSelector, 1)
|
| - ..exitNoSuchMethod()
|
| - ..methodEnd();
|
| - }
|
| -
|
| - bool isNative(Element element) {
|
| - if (element is FunctionElement) {
|
| - for (var metadata in element.metadata) {
|
| - // TODO(ahe): This code should ensure that @native resolves to precisely
|
| - // the native variable in dart:fletch._system.
|
| - if (metadata.constant == null) continue;
|
| - ConstantValue value = context.getConstantValue(metadata.constant);
|
| - if (!value.isString) continue;
|
| - StringConstantValue stringValue = value;
|
| - if (stringValue.toDartString().slowToString() != 'native') continue;
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - bool isExternal(Element element) {
|
| - if (element is FunctionElement) return element.isExternal;
|
| - return false;
|
| - }
|
| -
|
| - bool get canHandleCompilationFailed => true;
|
| -
|
| - ClosureEnvironment createClosureEnvironment(
|
| - ExecutableElement element,
|
| - TreeElements elements) {
|
| - MemberElement member = element.memberContext;
|
| - return closureEnvironments.putIfAbsent(member, () {
|
| - ClosureVisitor environment = new ClosureVisitor(member, elements);
|
| - return environment.compute();
|
| - });
|
| - }
|
| -
|
| - void markFunctionConstantAsUsed(FunctionConstantValue value) {
|
| - // TODO(ajohnsen): Use registry in CodegenVisitor to register the used
|
| - // constants.
|
| - FunctionElement function = value.element;
|
| - createTearoffClass(createFletchFunctionBuilder(function));
|
| - // Be sure to actually enqueue the function for compilation.
|
| - FletchRegistry registry = new FletchRegistry(compiler);
|
| - registry.registerStaticUse(new StaticUse.foreignUse(function));
|
| - }
|
| -
|
| - FletchFunctionBase createParameterStub(
|
| - FletchFunctionBase function,
|
| - Selector selector) {
|
| - CallStructure callStructure = selector.callStructure;
|
| - assert(callStructure.signatureApplies(function.signature));
|
| - ParameterStubSignature signature = new ParameterStubSignature(
|
| - function.functionId, callStructure);
|
| - FletchFunctionBase stub = systemBuilder.lookupParameterStub(signature);
|
| - if (stub != null) return stub;
|
| -
|
| - int arity = selector.argumentCount;
|
| - if (function.isInstanceMember) arity++;
|
| -
|
| - FletchFunctionBuilder builder = systemBuilder.newFunctionBuilder(
|
| - FletchFunctionKind.PARAMETER_STUB,
|
| - arity);
|
| -
|
| - BytecodeAssembler assembler = builder.assembler;
|
| -
|
| - void loadInitializerOrNull(ParameterElement parameter) {
|
| - Expression initializer = parameter.initializer;
|
| - if (initializer != null) {
|
| - ConstantExpression expression = context.compileConstant(
|
| - initializer,
|
| - parameter.memberContext.resolvedAst.elements,
|
| - isConst: true);
|
| - int constId = builder.allocateConstant(
|
| - context.getConstantValue(expression));
|
| - assembler.loadConst(constId);
|
| - } else {
|
| - assembler.loadLiteralNull();
|
| - }
|
| - }
|
| -
|
| - // Load this.
|
| - if (function.isInstanceMember) assembler.loadParameter(0);
|
| -
|
| - int index = function.isInstanceMember ? 1 : 0;
|
| - function.signature.orderedForEachParameter((ParameterElement parameter) {
|
| - if (!parameter.isOptional) {
|
| - assembler.loadParameter(index);
|
| - } else if (parameter.isNamed) {
|
| - int parameterIndex = selector.namedArguments.indexOf(parameter.name);
|
| - if (parameterIndex >= 0) {
|
| - if (function.isInstanceMember) parameterIndex++;
|
| - int position = selector.positionalArgumentCount + parameterIndex;
|
| - assembler.loadParameter(position);
|
| - } else {
|
| - loadInitializerOrNull(parameter);
|
| - }
|
| - } else {
|
| - if (index < arity) {
|
| - assembler.loadParameter(index);
|
| - } else {
|
| - loadInitializerOrNull(parameter);
|
| - }
|
| - }
|
| - index++;
|
| - });
|
| -
|
| - // TODO(ajohnsen): We have to be extra careful when overriding a
|
| - // method that takes optional arguments. We really should
|
| - // enumerate all the stubs in the superclasses and make sure
|
| - // they're overridden.
|
| - int constId = builder.allocateConstantFromFunction(function.functionId);
|
| - assembler
|
| - ..invokeStatic(constId, index)
|
| - ..ret()
|
| - ..methodEnd();
|
| -
|
| - systemBuilder.registerParameterStub(signature, builder);
|
| -
|
| - return builder;
|
| - }
|
| -
|
| - void patchClassWithStub(
|
| - FletchFunctionBase stub,
|
| - Selector selector,
|
| - int classId,
|
| - bool isClosureClass) {
|
| - int fletchSelector = context.toFletchSelector(selector);
|
| - FletchClassBuilder classBuilder = systemBuilder.lookupClassBuilder(classId);
|
| - if (classBuilder == null) {
|
| - if (isClosureClass) {
|
| - classBuilder =
|
| - systemBuilder.newPatchClassBuilder(classId, compiledClosureClass);
|
| - } else {
|
| - FletchClass klass = systemBuilder.lookupClass(classId);
|
| - assert(klass.element != null);
|
| - classBuilder = registerClassElement(klass.element);
|
| - }
|
| - }
|
| - classBuilder.addToMethodTable(fletchSelector, stub);
|
| -
|
| - // Inject parameter stub into all mixin usages.
|
| - getMixinApplicationsOfClass(classBuilder).forEach((ClassElement usage) {
|
| - FletchClassBuilder classBuilder =
|
| - systemBuilder.lookupClassBuilderByElement(usage);
|
| - classBuilder.addToMethodTable(fletchSelector, stub);
|
| - });
|
| - }
|
| -
|
| - /// Create a tear-off getter for [function]. If [isSpecialCallMethod] is
|
| - /// `true`, this is the special case for `someClosure.call` which should
|
| - /// always return `someClosure`. This implies that when [isSpecialCallMethod]
|
| - /// is true, we assume [function] is already a member of a closure class (or
|
| - /// a class with a `call` method [ClosureKind.functionLike]) and that the
|
| - /// getter should be added to that class.
|
| - void createTearoffGetterForFunction(
|
| - FletchFunctionBuilder function,
|
| - {bool isSpecialCallMethod}) {
|
| - if (isSpecialCallMethod == null) {
|
| - throw new ArgumentError("isSpecialCallMethod");
|
| - }
|
| - FletchFunctionBuilder getter = systemBuilder.newFunctionBuilder(
|
| - FletchFunctionKind.ACCESSOR,
|
| - 1);
|
| - // If the getter is of 'call', return the instance instead.
|
| - if (isSpecialCallMethod) {
|
| - getter.assembler
|
| - ..loadParameter(0)
|
| - ..ret()
|
| - ..methodEnd();
|
| - } else {
|
| - FletchClassBuilder tearoffClass = createTearoffClass(function);
|
| - int constId = getter.allocateConstantFromClass(tearoffClass.classId);
|
| - getter.assembler
|
| - ..loadParameter(0)
|
| - ..allocate(constId, tearoffClass.fields)
|
| - ..ret()
|
| - ..methodEnd();
|
| - }
|
| - // If the name is private, we need the library.
|
| - // Invariant: We only generate public stubs, e.g. 'call'.
|
| - LibraryElement library;
|
| - if (function.element != null) {
|
| - library = function.element.library;
|
| - }
|
| - // TODO(sigurdm): Avoid allocating new name here.
|
| - Name name = new Name(function.name, library);
|
| - int fletchSelector = context.toFletchSelector(
|
| - new Selector.getter(name));
|
| - FletchClassBuilder classBuilder = systemBuilder.lookupClassBuilder(
|
| - function.memberOf);
|
| - classBuilder.addToMethodTable(fletchSelector, getter);
|
| -
|
| - // Inject getter into all mixin usages.
|
| - getMixinApplicationsOfClass(classBuilder).forEach((ClassElement usage) {
|
| - FletchClassBuilder classBuilder =
|
| - systemBuilder.lookupClassBuilderByElement(usage);
|
| - classBuilder.addToMethodTable(fletchSelector, getter);
|
| - });
|
| - }
|
| -
|
| - void compileTypeTest(ClassElement element, InterfaceType type) {
|
| - assert(element.isDeclaration);
|
| - int fletchSelector = context.toFletchIsSelector(type.element);
|
| - FletchClassBuilder builder =
|
| - systemBuilder.lookupClassBuilderByElement(element);
|
| - if (builder != null) {
|
| - context.compiler.reportVerboseInfo(
|
| - element, 'Adding is-selector for $type');
|
| - builder.addIsSelector(fletchSelector);
|
| - }
|
| - }
|
| -
|
| - int assembleProgram() => 0;
|
| -
|
| - FletchDelta computeDelta() {
|
| -
|
| - if (fletchSystemLibrary == null && compiler.compilationFailed) {
|
| - // TODO(ahe): Ensure fletchSystemLibrary is not null.
|
| - return null;
|
| - }
|
| -
|
| - List<VmCommand> commands = <VmCommand>[
|
| - const NewMap(MapId.methods),
|
| - const NewMap(MapId.classes),
|
| - const NewMap(MapId.constants),
|
| - ];
|
| -
|
| - FletchSystem system = systemBuilder.computeSystem(context, commands);
|
| -
|
| - commands.add(const PushNewInteger(0));
|
| - commands.add(new PushFromMap(
|
| - MapId.methods,
|
| - system.lookupFunctionByElement(fletchSystemEntry).functionId));
|
| -
|
| - return new FletchDelta(system, systemBuilder.predecessorSystem, commands);
|
| - }
|
| -
|
| - bool enableCodegenWithErrorsIfSupported(Spannable spannable) {
|
| - return true;
|
| - }
|
| -
|
| - bool enableDeferredLoadingIfSupported(Spannable spannable, Registry registry) {
|
| - return false;
|
| - }
|
| -
|
| - bool registerDeferredLoading(Spannable node, Registry registry) {
|
| - compiler.reporter.reportWarningMessage(
|
| - node,
|
| - MessageKind.GENERIC,
|
| - {'text': "Deferred loading is not supported."});
|
| - return false;
|
| - }
|
| -
|
| - bool get supportsReflection => false;
|
| -
|
| - // TODO(sigurdm): Support async/await on the mobile platform.
|
| - bool get supportsAsyncAwait {
|
| - return !compiler.platformConfigUri.path.contains("embedded");
|
| - }
|
| -
|
| - Future onLibraryScanned(LibraryElement library, LibraryLoader loader) {
|
| - if (library.isPlatformLibrary) {
|
| - String path = library.canonicalUri.path;
|
| - switch(path) {
|
| - case 'fletch._system':
|
| - fletchSystemLibrary = library;
|
| - break;
|
| - case 'fletch.ffi':
|
| - fletchFFILibrary = library;
|
| - break;
|
| - case 'fletch.collection':
|
| - collectionLibrary = library;
|
| - break;
|
| - case 'math':
|
| - mathLibrary = library;
|
| - break;
|
| - case 'fletch':
|
| - fletchLibrary = library;
|
| - break;
|
| - }
|
| -
|
| - if (!library.isPatched) {
|
| - // Apply patch, if any.
|
| - Uri patchUri = compiler.resolvePatchUri(library.canonicalUri.path);
|
| - if (patchUri != null) {
|
| - return compiler.patchParser.patchLibrary(loader, patchUri, library);
|
| - }
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - bool isBackendLibrary(LibraryElement library) {
|
| - return library == fletchSystemLibrary;
|
| - }
|
| -
|
| - /// Return non-null to enable patching. Possible return values are 'new' and
|
| - /// 'old'. Referring to old and new emitter. Since the new emitter is the
|
| - /// future, we assume 'old' will go away. So it seems the best option for
|
| - /// Fletch is 'new'.
|
| - String get patchVersion => 'new';
|
| -
|
| - FunctionElement resolveExternalFunction(FunctionElement element) {
|
| - if (element.isPatched) {
|
| - FunctionElementX patch = element.patch;
|
| - compiler.reporter.withCurrentElement(patch, () {
|
| - patch.parseNode(compiler.parsing);
|
| - patch.computeType(compiler.resolution);
|
| - });
|
| - element = patch;
|
| - // TODO(ahe): Don't use ensureResolved (fix TODO in isNative instead).
|
| - element.metadata.forEach((m) => m.ensureResolved(compiler.resolution));
|
| - } else if (element.library == fletchSystemLibrary) {
|
| - // Nothing needed for now.
|
| - } else if (element.library == compiler.coreLibrary) {
|
| - // Nothing needed for now.
|
| - } else if (element.library == mathLibrary) {
|
| - // Nothing needed for now.
|
| - } else if (element.library == asyncLibrary) {
|
| - // Nothing needed for now.
|
| - } else if (element.library == fletchLibrary) {
|
| - // Nothing needed for now.
|
| - } else if (externals.contains(element)) {
|
| - // Nothing needed for now.
|
| - } else {
|
| - compiler.reporter.reportErrorMessage(
|
| - element, MessageKind.PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION);
|
| - }
|
| - return element;
|
| - }
|
| -
|
| - int compileLazyFieldInitializer(
|
| - FieldElement field,
|
| - FletchRegistry registry) {
|
| - int index = context.getStaticFieldIndex(field, null);
|
| -
|
| - if (field.initializer == null) return index;
|
| -
|
| - if (lazyFieldInitializers.containsKey(field)) return index;
|
| -
|
| - FletchFunctionBuilder functionBuilder = systemBuilder.newFunctionBuilder(
|
| - FletchFunctionKind.LAZY_FIELD_INITIALIZER,
|
| - 0,
|
| - name: "${field.name} lazy initializer",
|
| - element: field);
|
| - lazyFieldInitializers[field] = functionBuilder;
|
| -
|
| - TreeElements elements = field.resolvedAst.elements;
|
| -
|
| - ClosureEnvironment closureEnvironment = createClosureEnvironment(
|
| - field,
|
| - elements);
|
| -
|
| - LazyFieldInitializerCodegen codegen = new LazyFieldInitializerCodegen(
|
| - functionBuilder,
|
| - context,
|
| - elements,
|
| - registry,
|
| - closureEnvironment,
|
| - field);
|
| -
|
| - codegen.compile();
|
| -
|
| - return index;
|
| - }
|
| -
|
| - /// Compiles the initializer part of a constructor.
|
| - ///
|
| - /// See [compilePendingConstructorInitializers] for an overview of how
|
| - /// constructor initializer and bodies are compiled.
|
| - void compileConstructorInitializer(FletchFunctionBuilder functionBuilder) {
|
| - ConstructorElement constructor = functionBuilder.element;
|
| - assert(constructor.isImplementation);
|
| - compiler.reporter.withCurrentElement(constructor, () {
|
| - assert(functionBuilder ==
|
| - systemBuilder.lookupConstructorInitializerByElement(constructor));
|
| - context.compiler.reportVerboseInfo(
|
| - constructor, 'Compiling constructor initializer $constructor');
|
| -
|
| - TreeElements elements = constructor.resolvedAst.elements;
|
| -
|
| - // TODO(ahe): We shouldn't create a registry, but we have to as long as
|
| - // the enqueuer doesn't support elements with more than one compilation
|
| - // artifact.
|
| - FletchRegistry registry = new FletchRegistry(compiler);
|
| -
|
| - FletchClassBuilder classBuilder =
|
| - registerClassElement(constructor.enclosingClass.declaration);
|
| -
|
| - ClosureEnvironment closureEnvironment =
|
| - createClosureEnvironment(constructor, elements);
|
| -
|
| - ConstructorCodegen codegen = new ConstructorCodegen(
|
| - functionBuilder,
|
| - context,
|
| - elements,
|
| - registry,
|
| - closureEnvironment,
|
| - constructor,
|
| - classBuilder);
|
| -
|
| - codegen.compile();
|
| -
|
| - if (compiler.verbose) {
|
| - context.compiler.reportVerboseInfo(
|
| - constructor, functionBuilder.verboseToString());
|
| - }
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Generate a getter for field [fieldIndex].
|
| - */
|
| - int makeGetter(int fieldIndex) {
|
| - return systemBuilder.getGetterByFieldIndex(fieldIndex);
|
| - }
|
| -
|
| - /**
|
| - * Generate a setter for field [fieldIndex].
|
| - */
|
| - int makeSetter(int fieldIndex) {
|
| - return systemBuilder.getSetterByFieldIndex(fieldIndex);
|
| - }
|
| -
|
| - void generateUnimplementedError(
|
| - Spannable spannable,
|
| - String reason,
|
| - FletchFunctionBuilder function,
|
| - {bool suppressHint: false}) {
|
| - if (!suppressHint) {
|
| - compiler.reporter.reportHintMessage(
|
| - spannable, MessageKind.GENERIC, {'text': reason});
|
| - }
|
| - var constString = constantSystem.createString(
|
| - new DartString.literal(reason));
|
| - context.markConstantUsed(constString);
|
| - function
|
| - ..assembler.loadConst(function.allocateConstant(constString))
|
| - ..assembler.emitThrow();
|
| - }
|
| -
|
| - void forEachSubclassOf(ClassElement cls, void f(ClassElement cls)) {
|
| - Queue<ClassElement> queue = new Queue<ClassElement>();
|
| - queue.add(cls);
|
| - while (queue.isNotEmpty) {
|
| - ClassElement cls = queue.removeFirst();
|
| - if (compiler.world.isInstantiated(cls.declaration)) {
|
| - queue.addAll(compiler.world.strictSubclassesOf(cls));
|
| - }
|
| - f(cls);
|
| - }
|
| - }
|
| -
|
| - void newElement(Element element) {
|
| - if (element.isField && element.isInstanceMember) {
|
| - forEachSubclassOf(element.enclosingClass, (ClassElement cls) {
|
| - FletchClassBuilder builder = registerClassElement(cls);
|
| - builder.addField(element);
|
| - });
|
| - }
|
| - }
|
| -
|
| - void replaceFunctionUsageElement(Element element, List<Element> users) {
|
| - for (Element user in users) {
|
| - systemBuilder.replaceUsage(user, element);
|
| - }
|
| - }
|
| -
|
| - void forgetElement(Element element) {
|
| - // TODO(ahe): The front-end should remove the element from
|
| - // elementsWithCompileTimeErrors.
|
| - compiler.elementsWithCompileTimeErrors.remove(element);
|
| - FletchFunctionBase function =
|
| - systemBuilder.lookupFunctionByElement(element);
|
| - if (function == null) return;
|
| - systemBuilder.forgetFunction(function);
|
| - }
|
| -
|
| - void removeField(FieldElement element) {
|
| - if (!element.isInstanceMember) return;
|
| - ClassElement enclosingClass = element.enclosingClass;
|
| - forEachSubclassOf(enclosingClass, (ClassElement cls) {
|
| - FletchClassBuilder builder = registerClassElement(cls);
|
| - builder.removeField(element);
|
| - });
|
| - }
|
| -
|
| - void removeFunction(FunctionElement element) {
|
| - FletchFunctionBase function =
|
| - systemBuilder.lookupFunctionByElement(element);
|
| - if (function == null) return;
|
| - if (element.isInstanceMember) {
|
| - ClassElement enclosingClass = element.enclosingClass;
|
| - FletchClassBuilder builder = registerClassElement(enclosingClass);
|
| - builder.removeFromMethodTable(function);
|
| - }
|
| - }
|
| -
|
| - /// Invoked during codegen enqueuing to compile constructor initializers.
|
| - ///
|
| - /// There's only one [Element] representing a constructor, but Fletch uses
|
| - /// two different functions for implementing a constructor.
|
| - ///
|
| - /// The first function takes care of allocating the instance and initializing
|
| - /// fields (called the constructor initializer), the other function
|
| - /// implements the body of the constructor (what is between the curly
|
| - /// braces). A constructor initializer never calls constructor initializers
|
| - /// of a superclass. Instead field initializers from the superclass are
|
| - /// inlined in the constructor initializer. The constructor initializer will
|
| - /// call all the constructor bodies from superclasses in the correct order.
|
| - ///
|
| - /// The constructor bodies are basically special instance methods that can
|
| - /// only be called from constructor initializers. Unlike constructor bodies,
|
| - /// we only need constructor initializer for classes that are directly
|
| - /// instantiated (excluding, for example, abstract classes).
|
| - ///
|
| - /// Given this, we compile constructor bodies when the normal enqueuer tells
|
| - /// us to compile a generative constructor (see [codegen]), and track
|
| - /// constructor initializers in a separate queue.
|
| - void compilePendingConstructorInitializers() {
|
| - while (pendingConstructorInitializers.isNotEmpty) {
|
| - compileConstructorInitializer(
|
| - pendingConstructorInitializers.removeLast());
|
| - }
|
| - }
|
| -
|
| - bool onQueueEmpty(Enqueuer enqueuer, Iterable<ClassElement> recentClasses) {
|
| - if (enqueuer is! ResolutionEnqueuer) {
|
| - compilePendingConstructorInitializers();
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - FletchEnqueueTask makeEnqueuer() => new FletchEnqueueTask(compiler);
|
| -
|
| - static bool isExactParameterMatch(
|
| - FunctionSignature signature,
|
| - CallStructure callStructure) {
|
| - if (!callStructure.signatureApplies(signature)) {
|
| - return false;
|
| - }
|
| - if (!signature.hasOptionalParameters) {
|
| - // There are no optional parameters, and the signature applies, so this
|
| - // is an exact match.
|
| - return true;
|
| - }
|
| - if (!signature.optionalParametersAreNamed) {
|
| - // The optional parameters aren't named which means that they are
|
| - // optional positional parameters. So we have an exact match if the
|
| - // number of parameters matches the number of arguments.
|
| - return callStructure.argumentCount == signature.parameterCount;
|
| - }
|
| - // Otherwise, the optional parameters are named, and we have an exact match
|
| - // if the named arguments in the call occur in the same order as the
|
| - // parameters in the signature.
|
| - if (callStructure.namedArguments.length !=
|
| - signature.optionalParameterCount) {
|
| - return false;
|
| - }
|
| - int index = 0;
|
| - for (var parameter in signature.orderedOptionalParameters) {
|
| - if (parameter.name != callStructure.namedArguments[index++]) return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - static FletchBackend createInstance(FletchCompilerImplementation compiler) {
|
| - return new FletchBackend(compiler);
|
| - }
|
| -
|
| - Uri resolvePatchUri(String libraryName, Uri libraryRoot) {
|
| - throw "Not implemented";
|
| - }
|
| -
|
| -}
|
| -
|
| -class FletchImpactTransformer extends ImpactTransformer {
|
| - final FletchBackend backend;
|
| -
|
| - FletchImpactTransformer(this.backend);
|
| -
|
| - @override
|
| - WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) {
|
| - TransformedWorldImpact transformed =
|
| - new TransformedWorldImpact(worldImpact);
|
| -
|
| - bool anyChange = false;
|
| -
|
| - if (worldImpact.constSymbolNames.isNotEmpty) {
|
| - ClassElement symbolClass =
|
| - backend.compiler.coreClasses.symbolClass.declaration;
|
| - transformed.registerTypeUse(
|
| - new TypeUse.instantiation(symbolClass.rawType));
|
| - transformed.registerStaticUse(
|
| - new StaticUse.foreignUse(
|
| - symbolClass.lookupConstructor("")));
|
| - anyChange = true;
|
| - }
|
| -
|
| - for (MapLiteralUse mapLiteralUse in worldImpact.mapLiterals) {
|
| - if (mapLiteralUse.isConstant) continue;
|
| - transformed.registerTypeUse(
|
| - new TypeUse.instantiation(backend.mapImplementation.rawType));
|
| - transformed.registerStaticUse(
|
| - new StaticUse.constructorInvoke(
|
| - backend.mapImplementation.lookupConstructor(""),
|
| - CallStructure.NO_ARGS));
|
| - anyChange = true;
|
| - }
|
| - return anyChange ? transformed : worldImpact;
|
| - }
|
| -
|
| - @override
|
| - transformCodegenImpact(impact) => throw "unimplemented";
|
| -}
|
| -
|
| -bool isLocalFunction(Element element) {
|
| - if (!element.isFunction) return false;
|
| - if (element is ExecutableElement) {
|
| - return element.memberContext != element;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -Name memberName(AstElement element) {
|
| - if (isLocalFunction(element)) return null;
|
| - MemberElement member = element;
|
| - return member.memberName;
|
| -}
|
|
|