content: // Copyright (c) 2013, the Dart 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 file.
  
  part of js_backend;
  
  /**
   * Handles construction of TypeVariable constants needed at runtime.
   */
  class TypeVariableHandler {
    JavaScriptBackend backend;
    FunctionElement typeVariableConstructor;
    CompileTimeConstantEvaluator evaluator;
  
    /**
     * Contains all instantiated classes that have type variables and are needed
     * for reflection.
     */
    List<ClassElement> typeVariableClasses = new List<ClassElement>();
  
    /**
     *  Maps a class element to a list with indices that point to type variables
     *  constants for each of the class' type variables.
     */
    Map<ClassElement, List<int>> typeVariables =
        new Map<ClassElement, List<int>>();
  
    /**
     *  Maps a TypeVariableType to the index pointing to the constant representing
     *  the corresponding type variable at runtime.
     */
    Map<TypeVariableElement, int> typeVariableConstants =
        new Map<TypeVariableElement, int>();
  
    TypeVariableHandler(this.backend);
  
    ClassElement get typeVariableClass => backend.typeVariableClass;
    CodeEmitterTask get task => backend.emitter;
    MetadataEmitter get emitter => task.metadataEmitter;
    Compiler get compiler => backend.compiler;
  
    void registerClassWithTypeVariables(ClassElement cls) {
      typeVariableClasses.add(cls);
    }
  
    void onResolutionQueueEmpty(Enqueuer enqueuer) {
      if (typeVariableConstructor == null) {
        if (!typeVariableClass.constructors.isEmpty &&
            !typeVariableClass.constructors.tail.isEmpty) {
          compiler.reportInternalError(
              typeVariableClass,
              'Class $typeVariableClass should only have one constructor');
        }
        typeVariableConstructor = typeVariableClass.constructors.head;
        backend.enqueueClass(
            enqueuer, typeVariableClass, compiler.globalDependencies);
        backend.enqueueInResolution(
            typeVariableConstructor, compiler.globalDependencies);
      }
    }
  
    void onCodegenQueueEmpty() {
      evaluator = new CompileTimeConstantEvaluator(
          compiler.constantHandler, compiler.globalDependencies, compiler);
  
      for (ClassElement cls in typeVariableClasses) {
        // TODO(zarah): Running through all the members is suboptimal. Change this
        // as part of marking elements for reflection.
        bool hasMemberNeededForReflection(ClassElement cls) {
          bool result = false;
          cls.implementation.forEachMember((ClassElement cls, Element member) {
            result = result || backend.isNeededForReflection(member);
          });
          return result;
        }
  
        if (!backend.isNeededForReflection(cls) &&
            !hasMemberNeededForReflection(cls)) continue;
  
        InterfaceType typeVariableType = typeVariableClass.computeType(compiler);
        List<int> constants = <int>[];
        for (TypeVariableType currentTypeVariable in cls.typeVariables) {
          List<Constant> createArguments(FunctionElement constructor) {
          if (constructor != typeVariableConstructor) {
              compiler.internalErrorOnElement(
                  currentTypeVariable.element,
                  'Unexpected constructor $constructor');
            }
            Constant name = backend.constantSystem.createString(
                new DartString.literal(currentTypeVariable.name),
                null);
            Constant bound = backend.constantSystem.createInt(
                emitter.reifyType(currentTypeVariable.element.bound));
            Constant type = evaluator.makeTypeConstant(cls);
            return [type, name, bound];
          }
  
          Constant c = evaluator.makeConstructedConstant(
              currentTypeVariable.element, typeVariableType,
              typeVariableConstructor, createArguments);
          backend.registerCompileTimeConstant(c, compiler.globalDependencies);
          compiler.constantHandler.addCompileTimeConstantForEmission(c);
          constants.add(
              reifyTypeVariableConstant(c, currentTypeVariable.element));
        }
        typeVariables[cls] = constants;
      }
      typeVariableClasses.clear();
    }
  
    /**
     * Adds [c] to [emitter.globalMetadata] and returns the index pointing to
     * the entry.
     *
     * If the corresponding type variable has already been encountered an
     * entry in the list has already been reserved and the constant is added
     * there, otherwise a new entry for [c] is created.
     */
    int reifyTypeVariableConstant(Constant c, TypeVariableElement variable) {
      String name =
          jsAst.prettyPrint(task.constantReference(c), compiler).getText();
      int index;
      if (typeVariableConstants.containsKey(variable)) {
        index = typeVariableConstants[variable];
        emitter.globalMetadata[index] = name;
      } else {
        index = emitter.addGlobalMetadata(name);
        typeVariableConstants[variable] = index;
      }
      return index;
    }
  
    /**
     * Returns the index pointing to the constant in [emitter.globalMetadata]
     * representing this type variable
     *.
     *
     * If the constant has not yet been constructed, an entry is  allocated in
     * the global metadata list and the index pointing to this entry is returned.
     * When the corresponding constant is constructed later,
     * [reifyTypeVariableConstant] will be called and the constant will be added
     * on the allocated entry.
     */
    int reifyTypeVariable(TypeVariableElement variable) {
      if (typeVariableConstants.containsKey(variable)) {
        return typeVariableConstants[variable];
      }
  
      emitter.globalMetadata.add('Placeholder for ${variable}');
      return typeVariableConstants[variable] = emitter.globalMetadata.length - 1;
    }
  
    List<int> typeVariablesOf(ClassElement classElement) {
      return typeVariables[classElement];
    }
  }
  