protected void generateBindableImpl() { if (classDefinition.needsEventDispatcher(classScope.getProject())) { // Generate a EventDispatcher member, equivalent to: // private var _bindingEventDispatcher : flash.events.EventDispatcher; // // Note that it is in a separate private namespace, so it won't conflict with user defined // private members // Add init code for the _bindingEventDispatcher to the ctor // this is the equivalent of: // _bindingEventDispatcher = new flash.events.EventDispatcher(this); iinitInsns.addAll(BindableHelper.generateBindingEventDispatcherInit(itraits, false)); BindableHelper.generateAddEventListener(classScope); BindableHelper.generateDispatchEvent(classScope); BindableHelper.generateHasEventListener(classScope); BindableHelper.generateRemoveEventListener(classScope); BindableHelper.generateWillTrigger(classScope); } if (classDefinition.needsStaticEventDispatcher(classScope.getProject())) { cinitInsns.addAll(BindableHelper.generateBindingEventDispatcherInit(ctraits, true)); BindableHelper.generateStaticEventDispatcherGetter(classStaticScope); } }
/** * 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 instructions for field initializers or static initialization statements. * * @param node - the AST at the root of the statement. * @param isStatic - true if the code should be generated in a static context. */ private void generateInstructions(IASNode node, final boolean isStatic) { // Do we need to create new information for the class' // static initialization method? Note that this may // be undone if the codgen fails or doesn't produce // any instructions. final boolean createNewCinit = isStatic && this.cinfo.cInit == null; if (createNewCinit) { // Speculatively initialize the class' cinit // (static class initializer routine)'s data // structures; the code generator may need to // store information in them. this.cinfo.cInit = new MethodInfo(); MethodBodyInfo cinit_info = new MethodBodyInfo(); cinit_info.setMethodInfo(this.cinfo.cInit); this.classStaticScope.setMethodInfo(this.cinfo.cInit); this.classStaticScope.methodVisitor = emitter.visitMethod(this.cinfo.cInit); this.classStaticScope.methodVisitor.visit(); this.classStaticScope.methodBodyVisitor = this.classStaticScope.methodVisitor.visitBody(cinit_info); this.classStaticScope.methodBodyVisitor.visit(); } InstructionList cgResult = null; LexicalScope ls = isStatic ? this.classStaticScope : this.classScope; ls.resetDebugInfo(); cgResult = ls.getGenerator().generateInstructions(node, CmcEmitter.__statement_NT, ls); // If nothing came back, revert any change made to the cinit information. if ((cgResult == null || cgResult.isEmpty()) && createNewCinit) { this.cinfo.cInit = null; this.classStaticScope.resetMethodInfo(); this.classStaticScope.methodVisitor = null; this.classStaticScope.methodBodyVisitor = null; } // Save the generated instructions, if present. if (cgResult != null) { if (isStatic) this.cinitInsns.addAll(cgResult); else this.iinitInsns.addAll(cgResult); } }
/** * Resets the entire method body at once. The Peephole optimizer will process the entire * instruction list, performing it's optimizations, before passing the InstructionList down to the * next IMethodBodyVisitor. So any MethodBodyVisitors that the optimizer delegates to will receive * a new InstructionList, with optimizations, instead of the InstructionList passed in. * * @param new_list The new Instructions for the method. */ @Override public void visitInstructionList(InstructionList new_list) { // for the delegates that run after this IVisitor, they will be processing // a new copy of the instruction list, which will have the optimized opcodes InstructionList newInstructions = new InstructionList(); InstructionFinisher old = this.finisher; // Create a new finisher, which will put the instruction into a new InstructionList // instead of passing them on to the next delegate this.finisher = new InstructionListFinisher(newInstructions); for (Instruction inst : new_list.getInstructions()) { visitInstruction(inst); } // Make sure we flush any remaining instructions to the IL flush(); // delegate the call with the new instruction list super.visitInstructionList(newInstructions); // Reset the finisher to the delegating one this.finisher = old; }
/** * 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 labelCurrent(Label l) { list.labelCurrent(l); }
public void labelNext(Label l) { list.labelNext(l); }
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()); }