/**
  * Generate the instructions to place the tag on the stack. This assumes that the base expr is
  * already in the instruction list <a><b><c></c></b></a> so if called on the C node above, it
  * would assume the instructions to place b on the stack were already generated - this method will
  * only compute the instructions to get c from b
  */
 private void generateGetInstructions(InstructionList il, MXMLTagData tag) {
   if (tag == rootTag) {
     il.addInstruction(ABCConstants.OP_getproperty, getNameForTag(tag));
   } else {
     il.addInstruction(ABCConstants.OP_getproperty, getNameForTag(tag));
     int index = getIndexOfTag(tag);
     il.addInstruction(ABCConstants.OP_getproperty, new Name(String.valueOf(index)));
   }
 }
  /**
   * Generate the instructions to set the target expression based on the Data and Attribute passed
   * in. This will walk up the parent chain to the root tag, and then proceed to emit get
   * instructions for all parents from the root tag down. For the actual target, instructions to set
   * the value will be generated. If an attribute is passed in then the set instructions will target
   * the attribute instead
   *
   * @param data The UnitData to target
   * @param attr The attribute to target, or null if there is no attribute
   * @return An InstructionList that contains the instructions to set the target expression
   */
  private InstructionList getTargetInstructions(MXMLUnitData data, MXMLTagAttributeData attr) {
    MXMLUnitData d = data;
    Stack<MXMLUnitData> parentStack = new Stack<MXMLUnitData>();

    if (isOnlyTextChild(d)) {
      // If we are the only text child of a TagData, then start with the TagData,
      // as that is what we'll be setting
      d = d.getParentUnitData();
    }
    MXMLUnitData target = d;

    // push parents onto a stack, so we can walk down from the parent later
    while (d != null) {
      parentStack.add(d);
      d = d == rootTag ? null : d.getParentUnitData();
    }

    InstructionList il = new InstructionList();
    // we're always going to start with "this"
    il.addInstruction(ABCConstants.OP_getlocal0);

    // Walk down the parent stack, and emit get instructions for each tag
    // except for the last one, which is the one we're targeting
    while (parentStack.size() > 1) {
      MXMLUnitData unitData = parentStack.pop();
      if (unitData instanceof MXMLTagData) {
        generateGetInstructions(il, (MXMLTagData) unitData);
      }
    }

    if (target instanceof MXMLTagData) {
      // Targeting a Tag
      if (attr == null) {
        // Just setting the tag value
        generateSetInstructions(il, (MXMLTagData) target);
      } else {
        // We have an attr, do a get for the tag, and a set
        // for the attr
        generateGetInstructions(il, (MXMLTagData) target);
        generateSetInstructions(il, attr);
      }
    } else if (target instanceof MXMLTextData) {
      // We're targeting a TextData
      generateSetInstructions(il, (MXMLTextData) target);
    }

    return il;
  }
 /**
  * Generate the instructions to set the value of a text data. This assumes that the base expr is
  * already in the instruction list <a><b><c>...<![CDATA[]]></![CDATA[]]></c></b></a> so if called
  * on the CDATA node above, it would assume the instructions to place c on the stack were already
  * generated - this method will only compute the instructions to set the CDATA in c This will
  * compute the index of the cdata, and then emit code like: .text()[index] = value Assumes that
  * the new value is stored in local 1 (will use OP_getlocal1 to get it). This code will be in a
  * function that has 1 argument, which is the new value, so we know it's passed in as the first
  * local.
  */
 private void generateSetInstructions(InstructionList il, MXMLTextData text) {
   il.addInstruction(ABCConstants.OP_callproperty, new Object[] {new Name("text"), 0});
   il.addInstruction(ABCConstants.OP_getlocal1);
   int index = getIndexOfText(text);
   il.addInstruction(ABCConstants.OP_setproperty, new Name(String.valueOf(index)));
 }
 /**
  * Generate the instructions to set the value of a tag. This assumes that the base expr is already
  * in the instruction list <a><b><c f="{blah}"></c></b></a> so if called on the f node above, it
  * would assume the instructions to place c on the stack were already generated - this method will
  * only compute the instructions to set f in c Assumes that the new value is stored in local 1
  * (will use OP_getlocal1 to get it). This code will be in a function that has 1 argument, which
  * is the new value, so we know it's passed in as the first local.
  */
 private void generateSetInstructions(InstructionList il, MXMLTagAttributeData attr) {
   il.addInstruction(ABCConstants.OP_getlocal1);
   il.addInstruction(ABCConstants.OP_setproperty, getNameForAttr(attr));
 }
 public void visitInstruction(Instruction i) {
   list.addInstruction(i);
 }
  /** Finish the class' definition. */
  void finishClassDefinition() {
    // the generation of instructions for variable initialization is delayed
    // until now, so we can add that initialization to the front of
    // the cinit instruction list.
    if (!staticVariableInitializers.isEmpty()) {
      InstructionList exisitingCinitInsns = null;
      if (!this.cinitInsns.isEmpty()) {
        exisitingCinitInsns = new InstructionList();
        exisitingCinitInsns.addAll(this.cinitInsns);
        this.cinitInsns = new InstructionList();
      }

      for (VariableNode var : staticVariableInitializers) generateInstructions(var, true);

      if (exisitingCinitInsns != null) this.cinitInsns.addAll(exisitingCinitInsns);
    }

    // add "goto_definition_help" metadata to user defined metadata.
    ITraitVisitor tv =
        classScope.getGlobalScope().traitsVisitor.visitClassTrait(TRAIT_Class, className, 0, cinfo);
    IMetaInfo[] metaTags = getAllMetaTags(classDefinition);

    // Add "goto definition help" metadata for the constructor.
    if (this.ctorFunction != null) {
      FunctionDefinition ctorDef = this.ctorFunction.getDefinition();
      MetaTag metaTag =
          MetaTag.createGotoDefinitionHelp(
              classDefinition,
              classDefinition.getContainingFilePath(),
              Integer.toString(ctorDef.getNameStart()),
              true);
      if (metaTag != null) metaTags = MetaTag.addMetaTag(metaTags, metaTag);
    }

    this.classScope.processMetadata(tv, metaTags);
    tv.visitEnd();

    // Need to do this before generating the CTOR as the generated code may result in insns that
    // need to be added to the ctor.
    generateBindableImpl();

    generateRequiredContingentDefinitions();

    addAnyEmbeddedAsset();

    // Make any vistEnd method calls
    // that were deferred.
    // callVisitEnds must be called on the same thread
    // that started code generation.  Since we don't generate
    // classes in parallel yet, we know that we are on the thread
    // that started code generation here.
    classScope.callVisitEnds();
    classStaticScope.callVisitEnds();

    //  Create the class' constructor function.
    if (this.ctorFunction != null) {
      MethodInfo mi =
          classScope
              .getGenerator()
              .generateFunction(this.ctorFunction, classScope, this.iinitInsns);

      if (mi != null) this.iinfo.iInit = mi;
    } else if (!this.iinitInsns.isEmpty()) {
      //  Synthesize a constructor.

      this.iinfo.iInit = new MethodInfo();
      MethodBodyInfo iinit = new MethodBodyInfo();
      iinit.setMethodInfo(this.iinfo.iInit);

      IMethodVisitor mv = emitter.visitMethod(this.iinfo.iInit);
      mv.visit();
      IMethodBodyVisitor mbv = mv.visitBody(iinit);
      InstructionList ctor_insns = new InstructionList();
      ctor_insns.addInstruction(OP_getlocal0);
      ctor_insns.addInstruction(OP_pushscope);

      ctor_insns.addAll(this.iinitInsns);

      //  Call the superclass' constructor after the instance
      //  init instructions; this doesn't seem like an abstractly
      //  correct sequence, but it's what ASC does.
      ctor_insns.addInstruction(OP_getlocal0);
      ctor_insns.addInstruction(OP_constructsuper, 0);

      ctor_insns.addInstruction(OP_returnvoid);
      mbv.visit();
      mbv.visitInstructionList(ctor_insns);
      mbv.visitEnd();
      mv.visitEnd();
    }

    //  If the class has static initialization
    //  logic, emit a cinit routine.
    if (!this.cinitInsns.isEmpty()) {
      InstructionList cinit_insns = new InstructionList();
      cinit_insns.addInstruction(OP_getlocal0);
      cinit_insns.addInstruction(OP_pushscope);

      //  TODO: Examine other end-of-function processing
      //  and ensure it's completed.
      this.classStaticScope.finishClassStaticInitializer(this.cinitInsns);
      cinit_insns.addAll(this.cinitInsns);

      cinit_insns.addInstruction(OP_returnvoid);

      this.classStaticScope.methodBodyVisitor.visitInstructionList(cinit_insns);
      this.classStaticScope.methodBodyVisitor.visitEnd();
      this.classStaticScope.methodVisitor.visitEnd();
    } else {
      //  Ensure nothing got dropped.
      assert (this.classStaticScope.methodBodyVisitor == null);
    }

    itraits.visitEnd();
    ctraits.visitEnd();

    cv.visitEnd();
  }
  /**
   * Constructor. Initializes the ClassDirectiveProcessor and its associated AET data structures.
   *
   * @param node - the AST that starts the class' definition in source; used for diagnostics.
   * @param class_definition - the class' definition
   * @param enclosing_scope - the immediately enclosing lexical scope.
   * @param emitter - the active ABC emitter.
   */
  ClassDirectiveProcessor(
      ICommonClassNode node,
      ClassDefinition class_definition,
      LexicalScope enclosing_scope,
      IABCVisitor emitter) {
    super(enclosing_scope.getProblems());

    this.emitter = emitter;
    this.definitionSource = node;
    assert (this.definitionSource != null) : "Class definition AST must be provided.";

    this.classScope = enclosing_scope.pushFrame();
    this.classStaticScope = enclosing_scope.pushFrame();

    if (node.getNodeID() == ASTNodeID.ClassID) {
      classScope.setInitialControlFlowRegionNode(((ClassNode) node).getScopedNode());
      classStaticScope.setInitialControlFlowRegionNode(((ClassNode) node).getScopedNode());
    }

    ICompilerProject project = classScope.getProject();

    // Set the class Name.
    this.classDefinition = class_definition;
    this.className = classDefinition.getMName(project);
    iinfo.name = className;

    // Check for a duplicate class name.
    switch (SemanticUtils.getMultiDefinitionType(this.classDefinition, project)) {
      case AMBIGUOUS:
        classScope.addProblem(
            new DuplicateClassDefinitionProblem(node, class_definition.getBaseName()));
        break;
      case NONE:
        break;
      default:
        assert false; // I don't think classes can have other type of multiple definitions
    }

    if (node instanceof BaseDefinitionNode) // test doesn't work for MXML, which is OK.
    {
      BaseDefinitionNode n = (BaseDefinitionNode) node;
      SemanticUtils.checkScopedToDefaultNamespaceProblem(classScope, n, classDefinition, null);
    }
    // Resolve the super class, checking that it exists,
    // that it is a class rather than an interface,
    // that it isn't final, and that it isn't the same as this class.
    ClassDefinition superclassDefinition =
        SemanticUtils.resolveBaseClass(node, class_definition, project, classScope.getProblems());

    // Check that the superclass isn't a forward reference, but only need to do this if both
    // definitions come from the same containing source.  getContainingFilePath() returns the file
    // from the ASFileScope, so no need to worry about included files.
    if (!classDefinition.isGeneratedEmbedClass()
        && classDefinition
            .getContainingFilePath()
            .equals(superclassDefinition.getContainingFilePath())) {
      // If the absolute offset in the class is less than the
      // offset of the super class, it must be a forward reference in the file
      int classOffset = classDefinition.getAbsoluteStart();
      int superClassOffset = superclassDefinition.getAbsoluteEnd();
      if (classOffset < superClassOffset)
        classScope.addProblem(
            new ForwardReferenceToBaseClassProblem(node, superclassDefinition.getQualifiedName()));
    }

    // Set the superclass Name.
    this.superclassName = superclassDefinition.getMName(project);
    iinfo.superName = superclassName;

    // Resolve the interfaces.
    IInterfaceDefinition[] interfaces =
        classDefinition.resolveImplementedInterfaces(project, classScope.getProblems());

    // Set the interface Names.
    int n_interfaces = interfaces.length;
    ArrayList<Name> interface_names = new ArrayList<Name>(n_interfaces);
    for (int i = 0; i < n_interfaces; i++) {
      InterfaceDefinition idef = (InterfaceDefinition) interfaces[i];
      if (idef != null) {
        Name interfaceName = ((InterfaceDefinition) interfaces[i]).getMName(project);
        interface_names.add(interfaceName);
      }
    }
    iinfo.interfaceNames = interface_names.toArray(new Name[interface_names.size()]);

    // Set the flags corresponding to 'final' and 'dynamic'.
    if (classDefinition.isFinal()) iinfo.flags |= ABCConstants.CLASS_FLAG_final;
    if (!classDefinition.isDynamic()) iinfo.flags |= ABCConstants.CLASS_FLAG_sealed;

    iinfo.protectedNs =
        ((NamespaceDefinition) classDefinition.getProtectedNamespaceReference()).getAETNamespace();

    this.cv = emitter.visitClass(iinfo, cinfo);
    cv.visit();

    this.itraits = cv.visitInstanceTraits();
    this.ctraits = cv.visitClassTraits();

    this.classScope.traitsVisitor = this.itraits;
    this.classStaticScope.traitsVisitor = this.ctraits;

    // Build an array of the names of all the ancestor classes.
    ArrayList<Name> ancestorClassNames = new ArrayList<Name>();

    // Walk the superclass chain, starting with this class
    // and (unless there are problems) ending with Object.
    // This will accomplish three things:
    // - find loops;
    // - build the array of names of ancestor classes;
    // - set the needsProtected flag if this class or any of its ancestor classes needs it.

    boolean needsProtected = false;

    //  Remember the most recently examined class in case there's a cycle in the superclass
    //  chain, in which case we'll need it to issue a diagnostic.
    ClassDefinition c = null;

    IClassDefinition.IClassIterator classIterator = classDefinition.classIterator(project, true);

    while (classIterator.hasNext()) {
      c = (ClassDefinition) classIterator.next();
      needsProtected |= c.getOwnNeedsProtected();
      if (c != classDefinition) ancestorClassNames.add(c.getMName(project));
    }

    // Report a loop in the superclass chain, such as A extends B and B extends A.
    // Note: A extends A was found previously by SemanticUtils.resolveBaseClass().
    if (classIterator.foundLoop())
      classScope.addProblem(new CircularTypeReferenceProblem(c, c.getQualifiedName()));

    // In the case of class A extends A, ancestorClassNames will be empty at this point.
    // Change it to be Object to prevent "Warning: Stack underflow" in the script init code below.
    if (ancestorClassNames.isEmpty()) {
      ClassDefinition objectDefinition =
          (ClassDefinition) project.getBuiltinType(IASLanguageConstants.BuiltinType.OBJECT);
      ancestorClassNames.add(objectDefinition.getMName(project));
    }

    // If this class or any of its ancestor classes needs the protected flag set, set it.
    if (needsProtected) iinfo.flags |= ABCConstants.CLASS_FLAG_protected;

    // Add the class initialization logic to the script init.
    // For class B extends A, where class A extends Object, this looks like
    // getscopeobject
    // findpropstrict Object
    // getproperty Object
    // pushscope
    // findpropstrict A
    // getproperty A
    // dup
    // pushscope
    // newclass
    // popscope
    // popscope
    // initproperty B
    InstructionList initInstructions = this.classScope.getInitInstructions();
    initInstructions.addInstruction(OP_getscopeobject, 0);

    // Push ancestor classes onto the scope stack.
    for (int i = ancestorClassNames.size() - 1; i >= 0; i--) {
      Name ancestorClassName = ancestorClassNames.get(i);
      initInstructions.addInstruction(OP_getlex, ancestorClassName);
      // The newclass instruction below also needs the superclass on the stack, so dup it
      if (i == 0) initInstructions.addInstruction(OP_dup);
      initInstructions.addInstruction(OP_pushscope);
    }

    initInstructions.addInstruction(OP_newclass, cinfo);

    for (int i = 0; i < ancestorClassNames.size(); i++)
      initInstructions.addInstruction(OP_popscope);

    initInstructions.addInstruction(OP_initproperty, className);

    implementedInterfaceSemanticChecks(class_definition);

    processResourceBundles(class_definition, project, classScope.getProblems());
  }