/* * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.flex.compiler.internal.as.codegen; import static org.apache.flex.abc.ABCConstants.*; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.flex.abc.ABCConstants; import org.apache.flex.abc.instructionlist.InstructionList; import org.apache.flex.abc.semantics.ClassInfo; import org.apache.flex.abc.semantics.InstanceInfo; import org.apache.flex.abc.semantics.MethodBodyInfo; import org.apache.flex.abc.semantics.MethodInfo; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.semantics.Namespace; import org.apache.flex.abc.semantics.Nsset; import org.apache.flex.abc.semantics.PooledValue; import org.apache.flex.abc.semantics.Trait; import org.apache.flex.abc.visitors.IABCVisitor; import org.apache.flex.abc.visitors.IClassVisitor; import org.apache.flex.abc.visitors.IMethodBodyVisitor; import org.apache.flex.abc.visitors.IMethodVisitor; import org.apache.flex.abc.visitors.ITraitVisitor; import org.apache.flex.abc.visitors.ITraitsVisitor; import org.apache.flex.compiler.common.ASModifier; import org.apache.flex.compiler.common.DependencyType; import org.apache.flex.compiler.common.IMetaInfo; import org.apache.flex.compiler.common.ModifiersSet; import org.apache.flex.compiler.constants.IASKeywordConstants; import org.apache.flex.compiler.constants.IASLanguageConstants; import org.apache.flex.compiler.constants.IMetaAttributeConstants; import org.apache.flex.compiler.definitions.IAccessorDefinition; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IConstantDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IInterfaceDefinition; import org.apache.flex.compiler.definitions.metadata.IMetaTag; import org.apache.flex.compiler.definitions.metadata.IMetaTagAttribute; import org.apache.flex.compiler.exceptions.CodegenInterruptedException; import org.apache.flex.compiler.problems.CircularTypeReferenceProblem; import org.apache.flex.compiler.problems.ConstructorCannotHaveReturnTypeProblem; import org.apache.flex.compiler.problems.ConstructorIsGetterSetterProblem; import org.apache.flex.compiler.problems.ConstructorIsStaticProblem; import org.apache.flex.compiler.problems.ConstructorMustBePublicProblem; import org.apache.flex.compiler.problems.DuplicateClassDefinitionProblem; import org.apache.flex.compiler.problems.DynamicNotOnClassProblem; import org.apache.flex.compiler.problems.FinalOutsideClassProblem; import org.apache.flex.compiler.problems.ForwardReferenceToBaseClassProblem; import org.apache.flex.compiler.problems.FunctionNotMarkedOverrideProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.IncompatibleOverrideProblem; import org.apache.flex.compiler.problems.InvalidOverrideProblem; import org.apache.flex.compiler.problems.MultipleContructorDefinitionsProblem; import org.apache.flex.compiler.problems.NativeVariableProblem; import org.apache.flex.compiler.problems.OverrideFinalProblem; import org.apache.flex.compiler.problems.OverrideNotFoundProblem; import org.apache.flex.compiler.problems.StaticAndOverrideProblem; import org.apache.flex.compiler.problems.StaticNamespaceDefinitionProblem; import org.apache.flex.compiler.problems.VirtualOutsideClassProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.tree.ASTNodeID; import org.apache.flex.compiler.tree.as.IASNode; import org.apache.flex.compiler.tree.as.ICommonClassNode; import org.apache.flex.compiler.tree.as.IDefinitionNode; import org.apache.flex.compiler.tree.as.IExpressionNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode; import org.apache.flex.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; import org.apache.flex.compiler.internal.abc.FunctionGeneratorHelper; import org.apache.flex.compiler.internal.as.codegen.ICodeGenerator.IConstantValue; import org.apache.flex.compiler.internal.definitions.AccessorDefinition; import org.apache.flex.compiler.internal.definitions.ClassDefinition; import org.apache.flex.compiler.internal.definitions.DefinitionBase; import org.apache.flex.compiler.internal.definitions.FunctionDefinition; import org.apache.flex.compiler.internal.definitions.GetterDefinition; import org.apache.flex.compiler.internal.definitions.InterfaceDefinition; import org.apache.flex.compiler.internal.definitions.NamespaceDefinition; import org.apache.flex.compiler.internal.definitions.TypeDefinitionBase; import org.apache.flex.compiler.internal.definitions.VariableDefinition; import org.apache.flex.compiler.internal.definitions.metadata.MetaTag; import org.apache.flex.compiler.internal.definitions.metadata.ResourceBundleMetaTag; import org.apache.flex.compiler.internal.embedding.EmbedData; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.scopes.ASScope; import org.apache.flex.compiler.internal.semantics.MethodBodySemanticChecker; import org.apache.flex.compiler.internal.semantics.SemanticUtils; import org.apache.flex.compiler.internal.tree.as.BaseDefinitionNode; import org.apache.flex.compiler.internal.tree.as.ClassNode; import org.apache.flex.compiler.internal.tree.as.FunctionNode; import org.apache.flex.compiler.internal.tree.as.ImportNode; import org.apache.flex.compiler.internal.tree.as.NamespaceIdentifierNode; import org.apache.flex.compiler.internal.tree.as.NamespaceNode; import org.apache.flex.compiler.internal.tree.as.VariableNode; /** * A ClassDirectiveProcessor generates an ABC class * from a ClassNode and its contents. */ class ClassDirectiveProcessor extends DirectiveProcessor { /** * The namespace to put compiler generated skin part object into so that it does not conflict with any user defined * members. */ private static final Namespace skinPartPrivateNamespace = new Namespace(CONSTANT_PrivateNs, ".SkinPartNamespace"); private static final Name NAME_OBJECT = new Name(IASLanguageConstants.Object); /** * Get all the user defined metadata plus * "go to definition help" metadata. * * @param definition The definition to get the metadata for. * @return An array of {@link IMetaTag}. */ protected static IMetaInfo[] getAllMetaTags(IDefinition definition) { assert definition != null; IMetaInfo[] metaTags = definition.getAllMetaTags(); MetaTag metaTag = MetaTag.createGotoDefinitionHelp(definition, definition.getContainingFilePath(), Integer.toString(definition.getNameStart()), false); metaTags = MetaTag.addMetaTag(metaTags, metaTag); return metaTags; } /** The class' definition. */ ClassDefinition classDefinition; /** The class' instance lexical scope. */ LexicalScope classScope; /** The class' static lexical scope. */ LexicalScope classStaticScope; /** The class' name. */ Name className; /** The class' superclass' name. */ Name superclassName; /** The class' instance traits */ ITraitsVisitor itraits; /** The class' class (static) traits. */ ITraitsVisitor ctraits; /** The AET visitor implementing this class. */ IClassVisitor cv; /** The class' AET ClassInfo. */ ClassInfo cinfo = new ClassInfo(); /** The class' AET InstanceInfo. */ InstanceInfo iinfo = new InstanceInfo(); /** The class' initial AST node; used for diagnostics. */ IASNode definitionSource; /** * Instructions to place in the class' initializer. * Note that these are part of the class itself, as * opposed to the above instructions which create * the class at the global scope. */ InstructionList cinitInsns = new InstructionList(); /** * Instructions to place in the class' constructor. */ InstructionList iinitInsns = new InstructionList(); /** * The constructor definition; it's saved for code-gen * after the CG has generated code for all instance * variables that need initializers. */ FunctionNode ctorFunction = null; /** The AET IABCVisitor emitting this class' ABC. */ IABCVisitor emitter; /** * Collection of static variables with initial values which need to be initialized * before other static initialization code. */ protected final Collection<VariableNode> staticVariableInitializers = new ArrayList<VariableNode>(); /** * Constructor. * Initializes the ClassDirectiveProcessor and its * associated AET data structures. * @param c - the class' AST. * @param enclosing_scope - the immediately enclosing lexical scope. * @param emitter - the active ABC emitter. */ ClassDirectiveProcessor(ClassNode c, LexicalScope enclosing_scope, IABCVisitor emitter) { this(c, c.getDefinition(), enclosing_scope, emitter); } /** * 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. // XXX (mschmalle) Added for JS Object impl, shouldn't have side effects if (superclassDefinition != null) { 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)); } //handle the case where this class needs an EventDispatcher implementation for binding support //we check if we can replace EventDispatcher as the super class instead of Object, and if yes, //reset the ancestorClassNames to those of the binding EventDispatcher class if (class_definition.needsEventDispatcher(project)) { if (ancestorClassNames.size() == 1) { ClassDefinition objectClassDefinition = (ClassDefinition)project.getBuiltinType( IASLanguageConstants.BuiltinType.OBJECT); if (objectClassDefinition.equals(superclassDefinition)) { //the immediate and only ancestor is Object, we have a candidate for 'upgrading' the //ancestor chain to be the Binding EventDispatcher (which can be set via compiler configuration) IDefinition eventDispatcherCheck = project.resolveQNameToDefinition(BindableHelper.STRING_EVENT_DISPATCHER); if (eventDispatcherCheck !=null && eventDispatcherCheck instanceof ClassDefinition) { ClassDefinition eventDispatcherClass = (ClassDefinition) eventDispatcherCheck; // reset the superclass Name. // This can be used for testing to avoid adding IEventDispatcher implementation later this.superclassName = eventDispatcherClass.getMName(project); iinfo.superName = superclassName; //replace "Object" at the first position in ancestorClassNames // with the Binding EventDispatcher class // (which can sometimes be configured via compiler directives) ancestorClassNames.set(0, this.superclassName); //now get the ancestors of the binding EventDispatcher class c = null; IClassDefinition.IClassIterator eventDispatcherIterator = eventDispatcherClass.classIterator(project, false); needsProtected = class_definition.getOwnNeedsProtected() || eventDispatcherClass.getOwnNeedsProtected(); while (eventDispatcherIterator.hasNext()) { c = (ClassDefinition)eventDispatcherIterator.next(); needsProtected |= c.getOwnNeedsProtected(); if (c != classDefinition) ancestorClassNames.add(c.getMName(project)); } //This may not ever be needed here, but duping the safety logic from above, just in case (GD) if (eventDispatcherIterator.foundLoop()) classScope.addProblem(new CircularTypeReferenceProblem(c, c.getQualifiedName())); //Add the implicit dependency for EventDispatcher. This also adds IEventDispatcher addBindableDependencies(true); } } } } // 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()); } protected void processResourceBundles (IClassDefinition class_definition, ICompilerProject project, Collection<ICompilerProblem> problems) { IMetaTag[] rbs = class_definition.getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_RESOURCEBUNDLE); if( rbs != null ) { for ( IMetaTag meta : rbs ) { if( meta instanceof ResourceBundleMetaTag) { try { ((ResourceBundleMetaTag)meta).resolveDependencies(problems, project); } catch( InterruptedException ie) { throw new CodegenInterruptedException(ie); } } } } } private Boolean hasAddedDependency = false; /** * Supports the late addition of related dependencies for an implicit Bindable implementation. * @param implementationExtends true if the dependency implementation is to support extending (otherwise * it is for 'implements') */ void addBindableDependencies(Boolean implementationExtends) { if (!hasAddedDependency) { DependencyType dependencyType = implementationExtends ? DependencyType.INHERITANCE : DependencyType.EXPRESSION; //Add the implicit dependency for EventDispatcher. This also adds IEventDispatcher ASScope containingScope = (ASScope) getClassDefinition().getContainingScope(); containingScope.findPropertyQualified(classScope.getProject(), NamespaceDefinition.createPackagePublicNamespaceDefinition( BindableHelper.NAME_EVENT_DISPATCHER.getQualifiers().getSingleQualifier().getName()), BindableHelper.NAME_EVENT_DISPATCHER.getBaseName(), dependencyType); hasAddedDependency = true; } } /** * Finish the class' definition. */ void finishClassDefinition() { // should be able to pass null here because GlobalDirectiveProcessor // already called getSkinsParts and collected problems. This // call should get the cached array. IMetaTag[] skinParts = classDefinition.findSkinParts(classScope.getProject(), null); if (skinParts.length > 0) { Name var_name = new Name(CONSTANT_Qname, new Nsset(skinPartPrivateNamespace), "skinParts"); classStaticScope.declareVariableName(var_name); ITraitVisitor tv = classStaticScope.traitsVisitor.visitSlotTrait(TRAIT_Var, var_name, ITraitsVisitor.RUNTIME_SLOT, NAME_OBJECT, LexicalScope.noInitializer); tv.visitEnd(); cinitInsns.addInstruction(OP_findproperty, var_name); for (IMetaTag skinPart : skinParts) { cinitInsns.addInstruction(OP_pushstring, skinPart.getDecoratedDefinition().getBaseName()); cinitInsns.addInstruction(OP_convert_s); IMetaTagAttribute attr = skinPart.getAttribute("required"); if (attr == null || attr.getValue().equals("true")) cinitInsns.addInstruction(OP_pushtrue); else cinitInsns.addInstruction(OP_pushfalse); } cinitInsns.addInstruction(OP_newobject, skinParts.length); cinitInsns.addInstruction(OP_setproperty, var_name); // Equivalent AS: // // protected function get skinParts():Object // { // return ClassName._skinParts; // } // MethodInfo mi = new MethodInfo(); mi.setMethodName("skinParts"); mi.setReturnType(NAME_OBJECT); InstructionList insns = new InstructionList(3); insns.addInstruction(OP_getlocal0); insns.addInstruction(OP_findpropstrict, var_name); insns.addInstruction(OP_getproperty, var_name); insns.addInstruction(OP_returnvalue); FunctionGeneratorHelper.generateFunction(classScope.getEmitter(), mi, insns); NamespaceDefinition nd = (NamespaceDefinition)classDefinition.getProtectedNamespaceReference(); Name func_name = new Name(nd.getAETNamespace(), "skinParts"); tv = classScope.traitsVisitor.visitMethodTrait(TRAIT_Getter, func_name, 0, mi); tv.visitAttribute(Trait.TRAIT_OVERRIDE, Boolean.TRUE); tv.visitEnd(); } // 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, null); 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); createCInitIfNeeded(); 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(); } @Override public void declareBindableVariable(VariableNode varNode) { // Declaration may have side effects on the traits // and the initialization instructions. generateInstructions(varNode, varNode.getDefinition().isStatic()); } /** * accessor function used by code-gen helper classes. */ public LexicalScope getInstanceScope() { return this.classScope; } /** * accessor function used by code-gen helper classes. */ public ClassDefinition getClassDefinition() { return this.classDefinition; } protected void generateBindableImpl() { // initially double-check that this class has not already been set to be an // implicit subclass of EventDispatcher in this ClassDirectiveProcessor's constructor // before the needsEventDispatcher check (which doesn't know about the implementation) if((this.superclassName == null || !this.superclassName.equals(BindableHelper.NAME_EVENT_DISPATCHER)) && classDefinition.needsEventDispatcher(classScope.getProject()) ) { //Add the implicit dependency for EventDispatcher. This also adds IEventDispatcher addBindableDependencies(false); // 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); //Add the implicit dependency for EventDispatcher. This also adds IEventDispatcher addBindableDependencies(false); } } protected void generateRequiredContingentDefinitions() { List<IDefinition> definitons = classDefinition.getContingentDefinitions(); for (IDefinition definition : definitons) { if (!definition.isContingentNeeded(classScope.getProject())) continue; assert (definition instanceof VariableDefinition) : "The code generator only supports contigent variable definitions"; final IDefinitionNode node = definition.getNode(); declareVariable((VariableNode) node, (VariableDefinition)definition, definition.isStatic(), definition instanceof IConstantDefinition, LexicalScope.noInitializer); } } private void addAnyEmbeddedAsset() { ICompilerProject project = classScope.getProject(); if (!(project instanceof CompilerProject)) return; EmbedData embedData = classDefinition.getEmbeddedAsset((CompilerProject)project, classScope.getProblems()); if (embedData != null) classScope.getGlobalScope().getEmbeds().add(embedData); } /** * Declare a function. * TODO: static vs. instance. */ @Override void declareFunction(FunctionNode func) { func.parseFunctionBody(classScope.getProblems()); final FunctionDefinition funcDef = func.getDefinition(); final boolean is_constructor = func.isConstructor(); ICompilerProject project = classScope.getProject(); boolean isBindable = false; if (funcDef instanceof AccessorDefinition) { IMetaTag[] metaTags = funcDef.getAllMetaTags(); for (IMetaTag metaTag : metaTags) { if (metaTag.getTagName().equals(BindableHelper.BINDABLE)) { IMetaTagAttribute[] attrs = metaTag.getAllAttributes(); isBindable = attrs.length == 0; } } if (!isBindable) { AccessorDefinition otherDef = ((AccessorDefinition)funcDef).resolveCorrespondingAccessor(classScope.getProject()); // ignore if not in your class def if (otherDef != null && otherDef.getContainingScope().equals(funcDef.getContainingScope())) { metaTags = otherDef.getAllMetaTags(); for (IMetaTag metaTag : metaTags) { if (metaTag.getTagName().equals(BindableHelper.BINDABLE)) { IMetaTagAttribute[] attrs = metaTag.getAllAttributes(); isBindable = attrs.length == 0; } } } } if (!isBindable) { metaTags = getClassDefinition().getAllMetaTags(); for (IMetaTag metaTag : metaTags) { if (metaTag.getTagName().equals(BindableHelper.BINDABLE)) { IMetaTagAttribute[] attrs = metaTag.getAllAttributes(); isBindable = attrs.length == 0; } } } } functionSemanticChecks(func); Name funcName = funcDef.getMName(classScope.getProject()); Name bindableName = null; boolean wasOverride = false; if (isBindable) { // move function into bindable namespace bindableName = BindableHelper.getBackingPropertyName(funcName, "_" + this.classDefinition.getQualifiedName()); wasOverride = funcDef.isOverride(); funcDef.unsetOverride(); } // Save the constructor function until // we've seen all the instance variables // that might need initialization. if ( is_constructor ) { if (this.ctorFunction == null) this.ctorFunction = func; else { // If we already have a ctor, must be multiply defined. Ignore it and generate problem String name = this.className.getBaseName(); classScope.addProblem( new MultipleContructorDefinitionsProblem(func, name)); } } else { LexicalScope ls = funcDef.isStatic()? classStaticScope: classScope; MethodInfo mi = classScope.getGenerator().generateFunction(func, ls, null, bindableName); if ( mi != null ) { ITraitVisitor tv = ls.traitsVisitor.visitMethodTrait(functionTraitKind(func, TRAIT_Method), bindableName != null ? bindableName : funcName, 0, mi); if (funcName != null && bindableName == null) classScope.getMethodBodySemanticChecker().checkFunctionForConflictingDefinitions(func, funcDef); if ( ! funcDef.isStatic() && bindableName == null) if (funcDef.getNamespaceReference() instanceof NamespaceDefinition.IProtectedNamespaceDefinition) this.iinfo.flags |= ABCConstants.CLASS_FLAG_protected; ls.processMetadata(tv, getAllMetaTags(funcDef)); if ( func.hasModifier(ASModifier.FINAL)) tv.visitAttribute(Trait.TRAIT_FINAL, Boolean.TRUE); // don't set override if we've moved it to the bindable namespace if (!wasOverride && (func.hasModifier(ASModifier.OVERRIDE) || funcDef.isOverride())) tv.visitAttribute(Trait.TRAIT_OVERRIDE, Boolean.TRUE); tv.visitEnd(); } } if (isBindable) { if (wasOverride) funcDef.setOverride(); if (funcDef instanceof GetterDefinition) { Name funcTypeName; TypeDefinitionBase typeDef = funcDef.resolveType(project); if ( SemanticUtils.isType(typeDef) ) funcTypeName = typeDef.getMName(project); else funcTypeName = NAME_OBJECT; DefinitionBase bindableGetter = func.buildBindableGetter(funcName.getBaseName()); ASScope funcScope = (ASScope)funcDef.getContainingScope(); bindableGetter.setContainingScope(funcScope); LexicalScope ls = funcDef.isStatic()? classStaticScope: classScope; ls.generateBindableGetter(bindableGetter, funcName, bindableName, funcTypeName, getAllMetaTags(funcDef)); } else { Name funcTypeName; TypeDefinitionBase typeDef = funcDef.resolveType(project); if ( SemanticUtils.isType(typeDef) ) funcTypeName = typeDef.getMName(project); else funcTypeName = NAME_OBJECT; ASScope funcScope = (ASScope)funcDef.getContainingScope(); DefinitionBase bindableSetter = func.buildBindableSetter(funcName.getBaseName(), funcScope, funcDef.getTypeReference()); bindableSetter.setContainingScope(funcScope); LexicalScope ls = funcDef.isStatic()? classStaticScope: classScope; ls.generateBindableSetter(bindableSetter, funcName, bindableName, funcTypeName, getAllMetaTags(funcDef)); } } } /** * This method performs the semantic analysis of a function declared in a class. * @param node the FunctionNode to semantically analyze */ void functionSemanticChecks(FunctionNode node) { verifyFunctionModifiers(node); FunctionDefinition func = node.getDefinition(); Collection<ICompilerProblem> problems = classScope.getProblems(); // code model has some peculiar ideas about what makes a function a constructor or not boolean looks_like_ctor = func.isConstructor(); looks_like_ctor |= func.getBaseName() != null && this.className != null && func.getBaseName().equals(this.className.getBaseName()); if (! looks_like_ctor && (func.getBaseName() != null)) { SemanticUtils.checkScopedToDefaultNamespaceProblem(classScope, node, func, this.classDefinition.getQualifiedName()); } if( looks_like_ctor ) { // If a constructor has a namespace as part of it's declaration, it must be declared public. // It is ok to omit the namespace // We must check the AST, as CM treats all ctors as public no matter what the user typed in // so the FunctionDefinition will always be in the public namespace if( node.getActualNamespaceNode() != null && node.getActualNamespaceNode().getName() != IASKeywordConstants.PUBLIC) problems.add(new ConstructorMustBePublicProblem(node.getActualNamespaceNode())); // A constructor cannot be static if( func.isStatic() ) problems.add(new ConstructorIsStaticProblem(node)); // A constructor cannot declare a return type, other than void. IExpressionNode returnTypeExpression = node.getReturnTypeNode(); if (returnTypeExpression != null) { // We cannot check whether node.resolveReturnType() returns the definition // for the void type, because the return type of a constructor is considered // to be the class of the object being constructed, rather than void. // So instead we simply check whether the type annotation was void. boolean returnTypeIsVoid = false; if (returnTypeExpression instanceof ILanguageIdentifierNode) { LanguageIdentifierKind kind = ((ILanguageIdentifierNode)returnTypeExpression).getKind(); if (kind == LanguageIdentifierKind.VOID) returnTypeIsVoid = true; } if (!returnTypeIsVoid) { ICompilerProblem problem = new ConstructorCannotHaveReturnTypeProblem(returnTypeExpression); problems.add(problem); } } // Is it a getter or setter that appears to be the constructor? if( func instanceof IAccessorDefinition ) problems.add(new ConstructorIsGetterSetterProblem(node.getNameExpressionNode())); } else if( !func.isStatic() ) { // We have to find the (potentially) overriden function whether we are an override or // not/ FunctionDefinition override = func.resolveOverriddenFunction(classScope.getProject()); if( func.isOverride() ) { if( override == null ) { // Didn't find the function we are supposed to be overriding problems.add(new OverrideNotFoundProblem(node.getNameExpressionNode())); } else { if( !func.hasCompatibleSignature(override, classScope.getProject()) ) { // Signatures didn't match problems.add(new IncompatibleOverrideProblem(node.getNameExpressionNode())); } if( override.isFinal() ) { // overriding final problems.add(new OverrideFinalProblem(node.getNameExpressionNode())); } } } else if( override != null) { if (func.getBaseName().equals("toString") && classDefinition.getContainedScope().hasAnyBindableDefinitions()) func.setOverride(); else { // found overriden function, but function not marked as override problems.add(new FunctionNotMarkedOverrideProblem(node.getNameExpressionNode())); } } } } /** * Check the class definition for various errors related to implemented interfaces, * such as making sure that all interface methods are implemented * @param cls the class definition to check */ void implementedInterfaceSemanticChecks(ClassDefinition cls) { Iterator<IInterfaceDefinition> it = cls.interfaceIterator(classScope.getProject()); while( it.hasNext() ) { IInterfaceDefinition interf = it.next(); if( interf instanceof InterfaceDefinition ) { ((InterfaceDefinition)interf).validateClassImplementsAllMethods(classScope.getProject(), cls, classScope.getProblems()); } } } /** */ protected void verifyFunctionModifiers(FunctionNode f) { ModifiersSet modifiersSet = f.getModifiers(); if (modifiersSet == null) return; IExpressionNode site = f.getNameExpressionNode(); if( modifiersSet.hasModifier(ASModifier.STATIC) ) { if( modifiersSet.hasModifier(ASModifier.FINAL) ) { classScope.addProblem(new FinalOutsideClassProblem(site) ); } if( modifiersSet.hasModifier(ASModifier.OVERRIDE) ) { classScope.addProblem(new StaticAndOverrideProblem(site) ); } if( modifiersSet.hasModifier(ASModifier.DYNAMIC) ) { classScope.addProblem(new DynamicNotOnClassProblem(site) ); } if( modifiersSet.hasModifier(ASModifier.VIRTUAL) ) { classScope.addProblem(new VirtualOutsideClassProblem(site) ); } } classScope.getMethodBodySemanticChecker().checkForDuplicateModifiers(f); // Functions in a class allow all modifiers return; } protected void verifyVariableModifiers(VariableNode v) { ModifiersSet modifiersSet = v.getModifiers(); if (modifiersSet == null) return; ASModifier[] modifiers = modifiersSet.getAllModifiers(); IExpressionNode site = v.getNameExpressionNode(); for (ASModifier modifier : modifiers) { if( modifier == ASModifier.NATIVE ) { classScope.addProblem(new NativeVariableProblem(site)); } else if (modifier == ASModifier.DYNAMIC ) { classScope.addProblem(new DynamicNotOnClassProblem(site)); } else if( modifier == ASModifier.FINAL ) { classScope.addProblem(new FinalOutsideClassProblem(site)); } else if( modifier == ASModifier.OVERRIDE ) { classScope.addProblem(new InvalidOverrideProblem(site)); } else if( modifier == ASModifier.VIRTUAL ) { classScope.addProblem(new VirtualOutsideClassProblem(site)); } } classScope.getMethodBodySemanticChecker().checkForDuplicateModifiers(v); } /** * Declare a variable. */ @Override void declareVariable(VariableNode var) { verifyVariableModifiers(var); VariableDefinition varDef = (VariableDefinition)var.getDefinition(); boolean is_static = var.hasModifier(ASModifier.STATIC); boolean is_const = SemanticUtils.isConst(var, classScope.getProject()); final ICompilerProject project = this.classScope.getProject(); ICodeGenerator codeGenerator = classScope.getGenerator(); IExpressionNode assignedValueNode = var.getAssignedValueNode(); IConstantValue constantValue = codeGenerator.generateConstantValue(assignedValueNode, project); // initializer is null if no constant value // can be generated, and null is the correct value for "no value." Object initializer = constantValue != null ? constantValue.getValue() : null; // Reducing the constant value may have produced problems in the // LexicalScope used for constant reduction. Transfer them over // to the LexicalScope for this class. Collection<ICompilerProblem> problems = constantValue != null ? constantValue.getProblems() : null; if (problems != null) classScope.addProblems(problems); final MethodBodySemanticChecker checker = new MethodBodySemanticChecker(this.classScope); DefinitionBase varType = (DefinitionBase)varDef.resolveType(project); Object transformed_initializer = null; if ((initializer != null) && (varType != null)) { transformed_initializer = checker.checkInitialValue(var, new Binding(null, varType.getMName(this.classScope.getProject()), varType), new PooledValue(initializer)).getValue(); } else { transformed_initializer = initializer; } ITraitVisitor tv = declareVariable(var, varDef, is_static, is_const, transformed_initializer); if ( is_static ) this.classStaticScope.processMetadata(tv, getAllMetaTags(varDef)); else this.classScope.processMetadata(tv, getAllMetaTags(varDef)); tv.visitEnd(); // Generate variable initializers and append them to the // proper initialization list. if ( transformed_initializer == null && var.getAssignedValueNode() != null ) { // Emit initialization instructions for non-static vars. Static var // instructions will be emitted during finishClassDefinition() if (is_static) staticVariableInitializers.add(var); else generateInstructions(var, false); } else { checker.checkClassField(var); // Massive kludge -- grovel over chained variable decls and add them one by one for ( int i = 0; i < var.getChildCount(); i++ ) { IASNode candidate = var.getChild(i); if ( candidate instanceof VariableNode ) { declareVariable((VariableNode)candidate); } } } } ITraitVisitor declareVariable(VariableNode varNode, DefinitionBase varDef, boolean is_static, boolean is_const, Object initializer) { final ICompilerProject project = this.classScope.getProject(); Name var_name = varDef.getMName(project); TypeDefinitionBase typeDef = varDef.resolveType(project); Name var_type = typeDef != null ? typeDef.getMName(project) : null; int trait_kind = is_const? TRAIT_Const: TRAIT_Var; LexicalScope ls = is_static? this.classStaticScope: this.classScope; ls.declareVariableName(var_name); ITraitVisitor tv = ls.traitsVisitor.visitSlotTrait(trait_kind, var_name, ITraitsVisitor.RUNTIME_SLOT, var_type, initializer); if ( ! is_static ) if (varDef.getNamespaceReference() instanceof NamespaceDefinition.IProtectedNamespaceDefinition) this.iinfo.flags |= ABCConstants.CLASS_FLAG_protected; SemanticUtils.checkScopedToDefaultNamespaceProblem(this.classScope, varNode, varDef, this.className.getBaseName()); return tv; } /** * Process a namespace identifier. */ @Override void processNamespaceIdentifierDirective(NamespaceIdentifierNode ns) { super.traverse(ns); } /** * Process an import directive. */ @Override void processImportDirective(ImportNode imp) { // Run the BURM, but for the purpose of semantic checking not code generation. classScope.getGenerator().generateInstructions(imp, CmcEmitter.__statement_NT, this.classScope); } /** * Ignore modifier nodes that are in the AST, but processed * as attributes of the definition nodes. Other loose * directives are processed as statements and added * to the class' static init method. */ @Override void processDirective(IASNode n) { switch ( n.getNodeID() ) { case StaticID: case FinalID: case OverrideID: break; case NamespaceID: { NamespaceNode ns = (NamespaceNode) n; if ( ns.hasModifier(ASModifier.STATIC) ) { this.classScope.addProblem(new StaticNamespaceDefinitionProblem(ns)); } else { // This type of node won't generate instructions, // but it will add the namespace to the class' // static traits. generateInstructions(n, GENERATE_STATIC_INITIALIZER); } break; } default: { // Handle a static initialization statement: // Generate instructions as required. generateInstructions(n, GENERATE_STATIC_INITIALIZER); } } } /** * @see {@link #generateInstructions(IASNode node, boolean isStatic)} */ private static final boolean GENERATE_STATIC_INITIALIZER = true; /** * 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. */ protected 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 ) { createCInitIfNeeded(); } 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); } } /** * Create a class init method and associated structure if it does already * exist. */ private void createCInitIfNeeded() { if (this.cinfo.cInit == null) { // 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(); } } }