| /* |
| * |
| * 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.royale.compiler.internal.as.codegen; |
| |
| import static org.apache.royale.abc.ABCConstants.*; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.royale.abc.ABCConstants; |
| import org.apache.royale.abc.instructionlist.InstructionList; |
| import org.apache.royale.abc.semantics.ClassInfo; |
| import org.apache.royale.abc.semantics.InstanceInfo; |
| import org.apache.royale.abc.semantics.MethodBodyInfo; |
| import org.apache.royale.abc.semantics.MethodInfo; |
| import org.apache.royale.abc.semantics.Name; |
| import org.apache.royale.abc.semantics.Namespace; |
| import org.apache.royale.abc.semantics.Nsset; |
| import org.apache.royale.abc.semantics.PooledValue; |
| import org.apache.royale.abc.semantics.Trait; |
| import org.apache.royale.abc.visitors.IABCVisitor; |
| import org.apache.royale.abc.visitors.IClassVisitor; |
| import org.apache.royale.abc.visitors.IMethodBodyVisitor; |
| import org.apache.royale.abc.visitors.IMethodVisitor; |
| import org.apache.royale.abc.visitors.ITraitVisitor; |
| import org.apache.royale.abc.visitors.ITraitsVisitor; |
| import org.apache.royale.compiler.common.ASModifier; |
| import org.apache.royale.compiler.common.DependencyType; |
| import org.apache.royale.compiler.common.IMetaInfo; |
| import org.apache.royale.compiler.common.ModifiersSet; |
| import org.apache.royale.compiler.constants.IASKeywordConstants; |
| import org.apache.royale.compiler.constants.IASLanguageConstants; |
| import org.apache.royale.compiler.constants.IMetaAttributeConstants; |
| import org.apache.royale.compiler.definitions.IAccessorDefinition; |
| import org.apache.royale.compiler.definitions.IClassDefinition; |
| import org.apache.royale.compiler.definitions.IConstantDefinition; |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.definitions.IInterfaceDefinition; |
| import org.apache.royale.compiler.definitions.metadata.IMetaTag; |
| import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute; |
| import org.apache.royale.compiler.exceptions.CodegenInterruptedException; |
| import org.apache.royale.compiler.problems.CircularTypeReferenceProblem; |
| import org.apache.royale.compiler.problems.ConstructorCannotHaveReturnTypeProblem; |
| import org.apache.royale.compiler.problems.ConstructorIsGetterSetterProblem; |
| import org.apache.royale.compiler.problems.ConstructorIsStaticProblem; |
| import org.apache.royale.compiler.problems.ConstructorMustBePublicProblem; |
| import org.apache.royale.compiler.problems.DuplicateClassDefinitionProblem; |
| import org.apache.royale.compiler.problems.DynamicNotOnClassProblem; |
| import org.apache.royale.compiler.problems.FinalOutsideClassProblem; |
| import org.apache.royale.compiler.problems.ForwardReferenceToBaseClassProblem; |
| import org.apache.royale.compiler.problems.FunctionNotMarkedOverrideProblem; |
| import org.apache.royale.compiler.problems.ICompilerProblem; |
| import org.apache.royale.compiler.problems.IncompatibleOverrideProblem; |
| import org.apache.royale.compiler.problems.InvalidOverrideProblem; |
| import org.apache.royale.compiler.problems.MultipleContructorDefinitionsProblem; |
| import org.apache.royale.compiler.problems.NativeVariableProblem; |
| import org.apache.royale.compiler.problems.OverrideFinalProblem; |
| import org.apache.royale.compiler.problems.OverrideNotFoundProblem; |
| import org.apache.royale.compiler.problems.StaticAndOverrideProblem; |
| import org.apache.royale.compiler.problems.StaticNamespaceDefinitionProblem; |
| import org.apache.royale.compiler.problems.VirtualOutsideClassProblem; |
| import org.apache.royale.compiler.projects.ICompilerProject; |
| import org.apache.royale.compiler.tree.ASTNodeID; |
| import org.apache.royale.compiler.tree.as.IASNode; |
| import org.apache.royale.compiler.tree.as.ICommonClassNode; |
| import org.apache.royale.compiler.tree.as.IDefinitionNode; |
| import org.apache.royale.compiler.tree.as.IExpressionNode; |
| import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode; |
| import org.apache.royale.compiler.tree.as.ILanguageIdentifierNode.LanguageIdentifierKind; |
| import org.apache.royale.compiler.internal.abc.FunctionGeneratorHelper; |
| import org.apache.royale.compiler.internal.as.codegen.ICodeGenerator.IConstantValue; |
| import org.apache.royale.compiler.internal.definitions.AccessorDefinition; |
| import org.apache.royale.compiler.internal.definitions.ClassDefinition; |
| import org.apache.royale.compiler.internal.definitions.DefinitionBase; |
| import org.apache.royale.compiler.internal.definitions.FunctionDefinition; |
| import org.apache.royale.compiler.internal.definitions.GetterDefinition; |
| import org.apache.royale.compiler.internal.definitions.InterfaceDefinition; |
| import org.apache.royale.compiler.internal.definitions.NamespaceDefinition; |
| import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase; |
| import org.apache.royale.compiler.internal.definitions.VariableDefinition; |
| import org.apache.royale.compiler.internal.definitions.metadata.MetaTag; |
| import org.apache.royale.compiler.internal.definitions.metadata.ResourceBundleMetaTag; |
| import org.apache.royale.compiler.internal.embedding.EmbedData; |
| import org.apache.royale.compiler.internal.projects.CompilerProject; |
| import org.apache.royale.compiler.internal.scopes.ASScope; |
| import org.apache.royale.compiler.internal.semantics.MethodBodySemanticChecker; |
| import org.apache.royale.compiler.internal.semantics.SemanticUtils; |
| import org.apache.royale.compiler.internal.tree.as.BaseDefinitionNode; |
| import org.apache.royale.compiler.internal.tree.as.ClassNode; |
| import org.apache.royale.compiler.internal.tree.as.FunctionNode; |
| import org.apache.royale.compiler.internal.tree.as.ImportNode; |
| import org.apache.royale.compiler.internal.tree.as.NamespaceIdentifierNode; |
| import org.apache.royale.compiler.internal.tree.as.NamespaceNode; |
| import org.apache.royale.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(); |
| } |
| } |
| |
| } |