/* * * 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.definitions; import static org.apache.flex.compiler.common.DependencyType.EXPRESSION; import static org.apache.flex.compiler.common.DependencyType.INHERITANCE; import static org.apache.flex.compiler.common.DependencyType.SIGNATURE; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import org.apache.flex.abc.semantics.Name; import org.apache.flex.compiler.common.ISourceLocation; 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.constants.IASLanguageConstants.BuiltinType; import org.apache.flex.compiler.definitions.IClassDefinition; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.compiler.definitions.IEffectDefinition; import org.apache.flex.compiler.definitions.IEventDefinition; import org.apache.flex.compiler.definitions.IFunctionDefinition; import org.apache.flex.compiler.definitions.IGetterDefinition; import org.apache.flex.compiler.definitions.INamespaceDefinition; import org.apache.flex.compiler.definitions.ISetterDefinition; import org.apache.flex.compiler.definitions.IStyleDefinition; import org.apache.flex.compiler.definitions.ITypeDefinition; import org.apache.flex.compiler.definitions.IVariableDefinition; import org.apache.flex.compiler.definitions.metadata.IMetaTag; import org.apache.flex.compiler.definitions.metadata.IMetaTagAttribute; import org.apache.flex.compiler.definitions.references.INamespaceReference; import org.apache.flex.compiler.definitions.references.IReference; import org.apache.flex.compiler.definitions.references.IResolvedQualifiersReference; import org.apache.flex.compiler.definitions.references.ReferenceFactory; import org.apache.flex.compiler.internal.config.QNameNormalization; import org.apache.flex.compiler.internal.definitions.metadata.MetaTag; import org.apache.flex.compiler.internal.embedding.EmbedData; import org.apache.flex.compiler.internal.projects.CompilerProject; import org.apache.flex.compiler.internal.scopes.ASFileScope; import org.apache.flex.compiler.internal.scopes.ASScope; import org.apache.flex.compiler.internal.scopes.TypeScope; import org.apache.flex.compiler.internal.units.EmbedCompilationUnitFactory; import org.apache.flex.compiler.mxml.IMXMLLanguageConstants; import org.apache.flex.compiler.problems.DuplicateSkinStateProblem; import org.apache.flex.compiler.problems.EmbedMultipleMetaTagsProblem; import org.apache.flex.compiler.problems.HostComponentClassNotFoundProblem; import org.apache.flex.compiler.problems.HostComponentMustHaveTypeProblem; import org.apache.flex.compiler.problems.ICompilerProblem; import org.apache.flex.compiler.problems.MissingSkinPartProblem; import org.apache.flex.compiler.problems.MissingSkinStateProblem; import org.apache.flex.compiler.problems.OnlyOneHostComponentAllowedProblem; import org.apache.flex.compiler.problems.SkinPartsMustBePublicProblem; import org.apache.flex.compiler.problems.WrongSkinPartProblem; import org.apache.flex.compiler.projects.ICompilerProject; import org.apache.flex.compiler.scopes.IASScope; import org.apache.flex.compiler.scopes.IDefinitionSet; import org.apache.flex.compiler.tree.metadata.IEventTagNode; import org.apache.flex.compiler.tree.metadata.IMetaTagNode; import org.apache.flex.compiler.tree.metadata.IStyleTagNode; import org.apache.flex.compiler.units.ICompilationUnit; import org.apache.flex.compiler.workspaces.IWorkspace; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; /** * Each instance of this class represents the definition of an ActionScript * class in the symbol table. * <p> * After a class definition is in the symbol table, it should always be accessed * through the read-only <code>IClassDefinition</code> interface. */ public class ClassDefinition extends ClassDefinitionBase implements IClassDefinition { // The ClassDefinitions for *, Null, Undefined, and void are implicit; // unlike for, say, Object, these are not read from any SWC. // They must be created by the compiler itself. private static final ClassDefinition ANY_TYPE; private static final ClassDefinition NULL; private static final ClassDefinition UNDEFINED; private static final ClassDefinition VOID; // Sentinel value used internally by this class to indicate that // the ABC class definition does not have [RemoteClass] meta-data. private static final String NO_REMOTE_CLASS_ALIAS = ""; private static final String HOST_COMPONENT = "hostComponent"; // this gets replaced in some projects public static String Event = IASLanguageConstants.Event; private static ClassDefinition makeImplicitClassDefinition(String name) { ClassDefinition def = new ClassDefinition(name, NamespaceDefinition.getPublicNamespaceDefinition()); def.setPublic(); def.setFinal(); def.setImplicit(); ASScope scope = new TypeScope(null, def); scope.setContainingDefinition(def); def.setContainedScope(scope); return def; } /** * Private class that bundles up information extracted from [Frame] * meta-data tags. */ private static class FrameInformation { IResolvedQualifiersReference factoryClass; Collection<IResolvedQualifiersReference> extraClasses; } static { ANY_TYPE = makeImplicitClassDefinition(IASLanguageConstants.ANY_TYPE); ANY_TYPE.setDynamic(); NULL = makeImplicitClassDefinition(IASLanguageConstants.Null); UNDEFINED = makeImplicitClassDefinition(IASLanguageConstants.Undefined); VOID = makeImplicitClassDefinition(IASLanguageConstants.void_); } public static IClassDefinition getAnyTypeClassDefinition() { return ANY_TYPE; } public static IClassDefinition getNullClassDefinition() { return NULL; } public static IClassDefinition getUndefinedClassDefinition() { return UNDEFINED; } public static IClassDefinition getVoidClassDefinition() { return VOID; } private static String getGeneratedURIPrefix(INamespaceReference namespaceRef) { if (namespaceRef instanceof NamespaceDefinition.INamespaceWithPackageName) return ((NamespaceDefinition.INamespaceWithPackageName)namespaceRef).getGeneratedURIPrefix(); return ""; } /** * Constructs a new class definition with an auto generated protected * namespace. * * @param name Base name of the class definition, eg: DisplayObject, not * flash.display.DisplayObject. * @param namespaceRef {@link INamespaceReference} qualifier. Usually this * will be a {@code INamespaceDefinition.IPublicNamespaceDefinition} whose * URI is a package name like flash.display. */ public ClassDefinition(String name, INamespaceReference namespaceRef) { this(name, namespaceRef, NamespaceDefinition.createProtectedNamespaceDefinition(getGeneratedURIPrefix(namespaceRef) + name)); } /** * Constructs a new class definition with an explicit protected namespace. * The protected namespace is used to qualify protected instance members of * the ABC class. * * @param name Base name of the class definition, eg: DisplayObject, not * flash.display.DisplayObject. * @param namespaceRef {@link INamespaceReference} qualifier. Usually this * will be a {@code INamespaceDefinition.IPublicNamespaceDefinition} whose * URI is a package name like flash.display. * @param protectedNamespace Protected namespace that will be used to * qualifier protected instance members of the ABC class. */ public ClassDefinition(String name, INamespaceReference namespaceRef, NamespaceDefinition.IProtectedNamespaceDefinition protectedNamespace) { super(name); assert name.indexOf('.') == -1 : "name must not be a qualified name!"; setNamespaceReference(namespaceRef); String generatedURI = getGeneratedURIPrefix(namespaceRef) + name; privateNamespaceReference = NamespaceDefinition.createPrivateNamespaceDefinition(generatedURI); protectedNamespaceReference = protectedNamespace; staticProtectedNamespaceReference = NamespaceDefinition.createStaticProtectedNamespaceDefinition(generatedURI); contingentDefinitions = new LinkedList<IDefinition>(); eventDefinitions = new AtomicReference<Map<String, IEventDefinition>>(); styleDefinitions = new AtomicReference<Map<String, IStyleDefinition>>(); effectDefinitions = new AtomicReference<Map<String, IEffectDefinition>>(); skinStates = new AtomicReference<String[]>(); skinParts = new AtomicReference<IMetaTag[]>(); stateNames = new HashSet<String>(); frameInformation = new AtomicReference<FrameInformation>(); remoteClassAlias = new AtomicReference<String>(); } private final NamespaceDefinition.ILanguageNamespaceDefinition privateNamespaceReference; private final NamespaceDefinition.ILanguageNamespaceDefinition protectedNamespaceReference; private final NamespaceDefinition.ILanguageNamespaceDefinition staticProtectedNamespaceReference; // A reference to the class that this class extends. private IReference baseClassReference; // References to the interfaces that this class implements. private IReference[] interfaceReferences; // The definition for the constructor of this class. // This can be stored as a definition rather than as a reference // because the definition is within this class // rather than in a different class. private IFunctionDefinition constructor; // This map has keys which are event names (such as "click") // and values which are EventDefinitions. // It contains event definitions created from [Event(...)] metadata // on this class, but not from such metadata on superclasses. // It is lazily created so that if no one asks for the events of a class, // the EventDefinitions don't get created; for example, if we are simply // compiling ActionScript files, their event metadata is irrelevant. // We keep an atomic reference to it because multiple threads // need to be able to lazily update this ClassDefinition after it // is in a scope. private AtomicReference<Map<String, IEventDefinition>> eventDefinitions; // This map has keys which are style names (such as "fontSize") // and values which are StyleDefinitions. // It contains style definitions created from [Style(...)] metadata // on this class, but not from such metadata on superclasses. // It is lazily created so that if no one asks for the styles of a class, // the StyleDefinitions don't get created; for example, if we are simply // compiling ActionScript files, their style metadata is irrelevant. // We keep an atomic reference to it because multiple threads // need to be able to lazily update this ClassDefinition after it // is in a scope. private final AtomicReference<Map<String, IStyleDefinition>> styleDefinitions; private final AtomicReference<Map<String, IEffectDefinition>> effectDefinitions; private final AtomicReference<String[]> skinStates; private final AtomicReference<IMetaTag[]> skinParts; private Set<String> stateNames; private final AtomicReference<FrameInformation> frameInformation; // Remote class alias for this class. Set from [RemoteClass] meta-data. private final AtomicReference<String> remoteClassAlias; /** * If this ClassDefinition is for an MXML class, this field is a set of * implicit imports determined by the MXML instance tags that are used * inside the class. */ private Set<String> implicitImports; private List<IDefinition> contingentDefinitions; @Override public ClassClassification getClassClassification() { if (inPackageNamespace()) return ClassClassification.PACKAGE_MEMBER; return ClassClassification.FILE_MEMBER; } @Override public IFunctionDefinition getConstructor() { return constructor; } protected void setConstructor(IFunctionDefinition constructor) { this.constructor = constructor; } @Override public String getBaseClassAsDisplayString() { return baseClassReference != null ? baseClassReference.getDisplayString() : ""; } @Override public IReference getBaseClassReference() { return baseClassReference; } /** * Sets a reference to the base class for this class. * * @param baseClassReference An {@link IReference} referring to the base * class. */ public void setBaseClassReference(IReference baseClassReference) { if (baseClassReference == null) { // The baseClassReference of Object is null. // The baseClassReference of other classes is a reference to Object, // until setBaseClass() gets called (which it may never be // in the case of a class declaration without an extends clause). boolean isObject = getBaseName().equals(IASLanguageConstants.Object) && getNamespaceReference().isPublicOrInternalNamespace() && ((INamespaceDefinition)getNamespaceReference()).getURI().isEmpty(); if (!isObject) baseClassReference = ReferenceFactory.builtinReference(BuiltinType.OBJECT); } this.baseClassReference = baseClassReference; } @Override public IClassDefinition resolveBaseClass(ICompilerProject project) { // Object has no base class. if (baseClassReference == null) return null; // Resolve the base class reference within the project, // and establish an inheritance dependency. ITypeDefinition typeDefinition = resolveType(baseClassReference, project, INHERITANCE); if (typeDefinition == null) return null; // A class cannot extend an interface. if (!(typeDefinition instanceof IClassDefinition)) return null; // The base class cannot be final. if (((IClassDefinition)typeDefinition).isFinal()) return null; // A class cannot extend itself. if (typeDefinition == this) return null; return (IClassDefinition)typeDefinition; } @Override public String[] getImplementedInterfacesAsDisplayStrings() { if (interfaceReferences == null) return new String[0]; int n = interfaceReferences.length; String[] interfaces = new String[n]; for (int i = 0; i < n; i++) interfaces[i] = interfaceReferences[i].getDisplayString(); return interfaces; } public void setImplementedInterfaceReferences(IReference[] interfaceReferences) { this.interfaceReferences = interfaceReferences; } @Override public IReference[] getImplementedInterfaceReferences() { if (interfaceReferences == null) return new IReference[0]; return interfaceReferences; } @Override public IEventDefinition getEventDefinition(IWorkspace w, String name) { if (eventDefinitions.get() == null) buildEventDefinitions(w); return eventDefinitions.get().get(name); } @Override public IEventDefinition[] getEventDefinitions(IWorkspace w) { if (eventDefinitions.get() == null) buildEventDefinitions(w); return eventDefinitions.get().values().toArray(new IEventDefinition[0]); } /* * Lazily creates a map of event names to event definitions from the event * metadata declared on this class. */ private void buildEventDefinitions(IWorkspace w) { Map<String, IEventDefinition> map = new HashMap<String, IEventDefinition>(); Map<String, IEventTagNode> availableTagNodes = new HashMap<String, IEventTagNode>(); // required because of CMP-2168 : can't get the event definition from the metatag. if (getNode() != null && getNode().getMetaTags() != null) { IMetaTagNode[] allNodes = this.getNode().getMetaTags().getTagsByName(IMetaAttributeConstants.ATTRIBUTE_EVENT); // eventDefinition.setNode((IDefinitionNode)metaTag.getTagNode()); for (IMetaTagNode iMetaTagNode : allNodes) { IEventTagNode eventTagNode = (IEventTagNode)iMetaTagNode; availableTagNodes.put(eventTagNode.getName(), (IEventTagNode)iMetaTagNode); } } IMetaTag[] metaTags = getAllMetaTags(); if (metaTags != null) { for (IMetaTag metaTag : metaTags) { if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_EVENT)) { // Most event metadata looks like this: // [Event(name="click", type="flash.events.MouseEvent")] String name = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EVENT_NAME); String type = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EVENT_TYPE); // However, // [Event(name="click")] // and // [Event("click")] // are also allowed. if (name == null) { IMetaTagAttribute[] attrs = metaTag.getAllAttributes(); if (attrs != null && attrs.length > 0) { name = attrs[0].getValue(); } } if (type == null) type = ClassDefinition.Event; if (name != null) { EventDefinition eventDefinition = new EventDefinition(name, this); // This scope is necessary for resolving the event's type. eventDefinition.setContainingScope(getContainingScope()); eventDefinition.setTypeReference(ReferenceFactory.packageQualifiedReference(w, type)); if (availableTagNodes.containsKey(name)) { eventDefinition.setNode(availableTagNodes.get(name)); } map.put(name, eventDefinition); } } } } // Let the first thread that builds the map atomically set it. eventDefinitions.compareAndSet(null, map); } @Override public IEventDefinition[] findEventDefinitions(ICompilerProject project) { IWorkspace workspace = project.getWorkspace(); Map<String, IEventDefinition> map = new HashMap<String, IEventDefinition>(); // Iterate over this class and its superclasses. for (IClassDefinition c : classIterable(project, true)) { for (IEventDefinition eventTag : c.getEventDefinitions(workspace)) { String eventName = eventTag.getBaseName(); // By checking whether the event is already in the map, // we can make sure that an event definition with a particular name // on a subclass overrides ones with the same name on superclasses. if (!map.containsKey(eventName)) map.put(eventName, eventTag); } } return map.values().toArray(new IEventDefinition[0]); } @Override public IStyleDefinition getStyleDefinition(IWorkspace workspace, String name) { if (styleDefinitions.get() == null) buildStyleDefinitions(workspace); return styleDefinitions.get().get(name); } @Override public IStyleDefinition[] getStyleDefinitions(IWorkspace w) { if (styleDefinitions.get() == null) buildStyleDefinitions(w); return styleDefinitions.get().values().toArray(new IStyleDefinition[0]); } /* * Lazily creates a map of style names to style definitions from the style * metadata declared on this class. */ private void buildStyleDefinitions(IWorkspace workspace) { Map<String, IStyleDefinition> styleMap = new HashMap<String, IStyleDefinition>(); for (IMetaTag metaTag : getAllMetaTags()) { if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_STYLE)) { String name = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_NAME); String type = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_TYPE); String format = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_FORMAT); String inherit = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_INHERIT); String themes = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_THEME); String minValue = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MIN_VALUE); String minValueExclusive = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MIN_VALUE_EXCLUSIVE); String maxValue = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MAX_VALUE); String maxValueExclusive = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_MAX_VALUE_EXCLUSIVE); String enumeration = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_ENUMERATION); String states = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_STATES); String arrayTypeName = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_STYLE_ARRAYTYPE); if (name == null) // If there is no Name, we can't create a definition, nor should we try continue; if (type == null) // missing type is perfectly legal. so let's patch // it up to something that will resolve correctly for us. type = "*"; StyleDefinition styleDefinition = new StyleDefinition(name, this); // This scope is necessary for resolving the style's type. styleDefinition.setContainingScope(getContainingScope()); styleDefinition.setTypeReference(ReferenceFactory.packageQualifiedReference(workspace, type)); styleDefinition.setFormat(format); styleDefinition.setInherit(inherit); styleDefinition.setThemes(themes); styleDefinition.setMinValue(minValue); styleDefinition.setMinValueExclusive(minValueExclusive); styleDefinition.setMaxValue(maxValue); styleDefinition.setMaxValueExclusive(maxValueExclusive); styleDefinition.setArrayType(arrayTypeName); if (states != null) { final Iterable<String> statesIterable = Splitter.on(",") .omitEmptyStrings() .trimResults() .split(states); final String[] statesArray = Iterables.toArray(statesIterable, String.class); styleDefinition.setStates(statesArray); } if (enumeration != null) { // enumerations are comma seprated, with no spaces String[] split = enumeration.split(","); styleDefinition.setEnumeration(split); } IMetaTagNode styleTagNode = metaTag.getTagNode(); if (styleTagNode != null && styleTagNode instanceof IStyleTagNode) { styleDefinition.setNode((IStyleTagNode)styleTagNode); } styleMap.put(name, styleDefinition); } } // Let the first thread that builds the map atomically set it. styleDefinitions.compareAndSet(null, styleMap); } @Override public IStyleDefinition[] findStyleDefinitions(ICompilerProject project) { IWorkspace workspace = project.getWorkspace(); Map<String, IStyleDefinition> map = new HashMap<String, IStyleDefinition>(); // Iterate over this class and its superclasses. for (IClassDefinition c : classIterable(project, true)) { for (IStyleDefinition styleTag : c.getStyleDefinitions(workspace)) { String styleName = styleTag.getBaseName(); // By checking whether the style is already in the map, // we can make sure that a style definition with a particular name // on a subclass overrides ones with the same name on superclasses. if (!map.containsKey(styleName)) map.put(styleName, styleTag); } } return map.values().toArray(new IStyleDefinition[0]); } @Override public IEffectDefinition getEffectDefinition(IWorkspace w, String name) { if (effectDefinitions.get() == null) buildEffectDefinitions(w); return effectDefinitions.get().get(name); } @Override public IEffectDefinition[] getEffectDefinitions(IWorkspace w) { if (effectDefinitions.get() == null) buildEffectDefinitions(w); return effectDefinitions.get().values().toArray(new IEffectDefinition[0]); } /* * Lazily creates a map of effect names to effect definitions from the * effect metadata declared on this class. */ private void buildEffectDefinitions(IWorkspace workspace) { Map<String, IEffectDefinition> effectMap = new HashMap<String, IEffectDefinition>(); for (IMetaTag metaTag : getAllMetaTags()) { if (metaTag.getTagName().equals(IMetaAttributeConstants.ATTRIBUTE_EFFECT)) { String name = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EFFECT_NAME); String eventName = metaTag.getAttributeValue(IMetaAttributeConstants.NAME_EFFECT_EVENT); EffectDefinition effectDefinition = new EffectDefinition(name, this); effectDefinition.setEvent(eventName); // Set the effect definition containing scope, so that clients // can find which file scope contains the effect definition. effectDefinition.setContainingScope(getContainingScope()); effectMap.put(name, effectDefinition); } } // Let the first thread that builds the map atomically set it. effectDefinitions.compareAndSet(null, effectMap); } @Override public IEffectDefinition[] findEffectDefinitions(ICompilerProject project) { IWorkspace workspace = project.getWorkspace(); Map<String, IEffectDefinition> map = new HashMap<String, IEffectDefinition>(); // Iterate over this class and its superclasses. for (IClassDefinition c : classIterable(project, true)) { for (IEffectDefinition effectTag : c.getEffectDefinitions(workspace)) { String effectName = effectTag.getBaseName(); // By checking whether the effect is already in the map, // we can make sure that an effect definition with a particular name // on a subclass overrides ones with the same name on superclasses. if (!map.containsKey(effectName)) map.put(effectName, effectTag); } } return map.values().toArray(new IEffectDefinition[0]); } /* * Lazily creates a array of skin state names from the skin state metadata * declared on this class. */ private void buildSkinStates(Collection<ICompilerProblem> problems) { IMetaTag[] tags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_SKIN_STATE); Set<String> states = new HashSet<String>(); for (IMetaTag tag : tags) { IMetaTagAttribute[] attributes = tag.getAllAttributes(); // old compiler only ever looked at first value, and ignored // anything else, so match that behavior here. if (attributes.length >= 1) { String state = attributes[0].getValue(); if (states.contains(state)) { ICompilerProblem problem = new DuplicateSkinStateProblem(tag, state); problems.add(problem); } states.add(state); } } // Let the first thread that builds the array atomically set it. skinStates.compareAndSet(null, states.toArray(new String[states.size()])); } @Override public String[] getSkinStates(Collection<ICompilerProblem> problems) { if (skinStates.get() == null) buildSkinStates(problems); return skinStates.get(); } @Override public String[] findSkinStates(ICompilerProject project, Collection<ICompilerProblem> problems) { Set<String> states = new HashSet<String>(); // Iterate over this class and its superclasses. for (IClassDefinition c : classIterable(project, true)) { for (String state : c.getSkinStates(problems)) { if (states.contains(state)) { ICompilerProblem problem = new DuplicateSkinStateProblem(c, state); problems.add(problem); } states.add(state); } } return states.toArray(new String[states.size()]); } /** * Get any embedded data decorating the class definition * * @param project Project to resolve against * @param problems Any problems resolving the embedded data * @return EmbedData or null if no embedded data, or a problem resolving * embedded data */ public EmbedData getEmbeddedAsset(CompilerProject project, Collection<ICompilerProblem> problems) { IMetaTag[] embedMetaTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_EMBED); if (embedMetaTags == null || embedMetaTags.length == 0) return null; if (embedMetaTags.length > 1) { problems.add(new EmbedMultipleMetaTagsProblem(getNode())); return null; } String containingSourceFilename = getFileSpecification().getPath(); ISourceLocation location = embedMetaTags[0]; IMetaTagAttribute[] attributes = embedMetaTags[0].getAllAttributes(); return EmbedCompilationUnitFactory.getEmbedData(project, getQualifiedName(), containingSourceFilename, location, attributes, problems); } private Collection<VariableDefinition> getAllLocalVariables() { Collection<IDefinitionSet> allDefinitions = getContainedScope().getAllLocalDefinitionSets(); List<VariableDefinition> variables = new LinkedList<VariableDefinition>(); for (IDefinitionSet defSet : allDefinitions) { for (int i = 0; i < defSet.getSize(); ++i) { IDefinition definition = defSet.getDefinition(i); if (definition instanceof VariableDefinition) variables.add((VariableDefinition)definition); } } return variables; } private Collection<GetterDefinition> getAllLocalGetters() { Collection<IDefinitionSet> allDefinitions = getContainedScope().getAllLocalDefinitionSets(); List<GetterDefinition> getters = new LinkedList<GetterDefinition>(); for (IDefinitionSet defSet : allDefinitions) { for (int i = 0; i < defSet.getSize(); ++i) { IDefinition definition = defSet.getDefinition(i); if (definition instanceof GetterDefinition) getters.add((GetterDefinition)definition); } } return getters; } /* * Lazily creates a array of skin parts from the skin part metadata declared * on this class. */ private void buildSkinParts(Collection<ICompilerProblem> problems) { Collection<VariableDefinition> variables = getAllLocalVariables(); List<IMetaTag> parts = new LinkedList<IMetaTag>(); for (VariableDefinition variable : variables) { IMetaTag skinPart = variable.getSkinPart(); if (skinPart != null) parts.add(skinPart); } Collection<GetterDefinition> getters = getAllLocalGetters(); for (GetterDefinition getter : getters) { IMetaTag skinPart = getter.getSkinPart(); if (skinPart != null) parts.add(skinPart); } for (IMetaTag part : parts) { IDefinition definition = part.getDecoratedDefinition(); if (!definition.getNamespaceReference().equals(NamespaceDefinition.getPublicNamespaceDefinition())) { ICompilerProblem problem = new SkinPartsMustBePublicProblem(definition); problems.add(problem); } } // Let the first thread that builds the array atomically set it. skinParts.compareAndSet(null, parts.toArray(new IMetaTag[parts.size()])); } @Override public IMetaTag[] getSkinParts(Collection<ICompilerProblem> problems) { if (skinParts.get() == null) buildSkinParts(problems); return skinParts.get(); } @Override public IMetaTag[] findSkinParts(ICompilerProject project, Collection<ICompilerProblem> problems) { Map<String, IMetaTag> map = new HashMap<String, IMetaTag>(); // Iterate over this class and its superclasses. for (IClassDefinition c : classIterable(project, true)) { for (IMetaTag skinPart : c.getSkinParts(problems)) { String variableName = skinPart.getDecoratedDefinition().getBaseName(); // By checking whether the variable is already in the map, // we can make sure that a variable definition with a particular name // on a subclass overrides ones with the same name on superclasses. if (!map.containsKey(variableName)) map.put(variableName, skinPart); } } return map.values().toArray(new IMetaTag[map.size()]); } /** * Add a state name to the class * * @param stateName The name to add */ public void addStateName(String stateName) { stateNames.add(stateName); } @Override public Set<String> getStateNames() { return stateNames; } @Override public Set<String> findStateNames(ICompilerProject project) { Set<String> set = new HashSet<String>(); // Iterate over this class and its superclasses. for (IClassDefinition c : classIterable(project, true)) { set.addAll(c.getStateNames()); } return set; } public List<IDefinition> getContingentDefinitions() { return contingentDefinitions; } /** * Build any contingent definitions that may be required for the class such * as hostComponent. These definitions will only be resolved against or * codegend if a superclass does not implement the definition already. Note * that this should only be called when constructring classes derived from * AS or MXML. It is not needed when creating ABC sourced classes. */ public void buildContingentDefinitions() { // for now, only host components need to be created IDefinition definition = buildHostComponentMember(); if (definition != null) contingentDefinitions.add(definition); } /** * If there is host component meta data on the class, run semantic checks on * it. * * @param project The compiler project. * @param problems The collection of compiler problems to which this method should add problems. */ public void verifyHostComponent(CompilerProject project, Collection<ICompilerProblem> problems) { IMetaTag[] metaTags = getHostComponentMetaData(); if (metaTags.length == 0) return; if (metaTags.length > 1) { ICompilerProblem problem = new OnlyOneHostComponentAllowedProblem(metaTags[1]); problems.add(problem); } String hostComponentClassName = getHostComponentClassName(metaTags[0]); if (hostComponentClassName == null) { ICompilerProblem problem = new HostComponentMustHaveTypeProblem(metaTags[0]); problems.add(problem); // without a host component name, there aren't any more checks we // can do, so just bail out. return; } IResolvedQualifiersReference hostComponentRef = ReferenceFactory.packageQualifiedReference(project.getWorkspace(), hostComponentClassName); ICompilationUnit referencingCU = project.getScope().getCompilationUnitForDefinition(this); IDefinition hostComponentDef = hostComponentRef.resolve(project, referencingCU, SIGNATURE); if (!(hostComponentDef instanceof IClassDefinition)) { ICompilerProblem problem = new HostComponentClassNotFoundProblem(metaTags[0], hostComponentClassName); problems.add(problem); // without a host component class definition, there aren't any more checks we // can do, so just bail out. return; } IClassDefinition hostComponentClassDef = (IClassDefinition)hostComponentDef; verifySkinParts(hostComponentClassDef, project, problems); verifySkinStates(hostComponentClassDef, project, problems); } private void verifySkinParts(IClassDefinition hostComponentDef, ICompilerProject project, Collection<ICompilerProblem> problems) { IMetaTag[] skinParts = hostComponentDef.findSkinParts(project, problems); for (IMetaTag skinPart : skinParts) { IVariableDefinition hostSkinPartDef = (IVariableDefinition)skinPart.getDecoratedDefinition(); String hostStringPartName = hostSkinPartDef.getBaseName(); IDefinition skinPartDef = getContainedScope().getPropertyFromDef(project, this, hostStringPartName, false); if (skinPartDef instanceof ISetterDefinition) skinPartDef = ((ISetterDefinition)skinPartDef).resolveGetter(project); // the skinPart definition needs to be either a variable or getter if (!((skinPartDef instanceof IVariableDefinition) || (skinPartDef instanceof IGetterDefinition))) skinPartDef = null; ITypeDefinition skinPartTypeDef = null; if (skinPartDef != null) skinPartTypeDef = skinPartDef.resolveType(project); if (skinPartTypeDef == null && hostSkinPartDef.isRequiredSkinPart()) { ICompilerProblem problem = new MissingSkinPartProblem(skinPart, hostComponentDef.getBaseName()); problems.add(problem); } else if (skinPartTypeDef != null && !skinPartTypeDef.isInstanceOf(hostSkinPartDef.resolveType(project), project)) { ICompilerProblem problem = new WrongSkinPartProblem(skinPart, skinPartTypeDef, hostSkinPartDef.resolveType(project)); problems.add(problem); } } } private void verifySkinStates(IClassDefinition hostComponentDef, ICompilerProject project, Collection<ICompilerProblem> problems) { Set<String> stateNames = findStateNames(project); String[] skinStates = hostComponentDef.findSkinStates(project, problems); for (String skinStateName : skinStates) { if (!stateNames.contains(skinStateName)) problems.add(new MissingSkinStateProblem(getFileSpecification().getPath(), skinStateName)); } } private VariableDefinition buildHostComponentMember() { // if there's no host component meta data, no need to create any contingent member IMetaTag[] metaTags = getHostComponentMetaData(); if (metaTags.length == 0) return null; String hostComponentClassName = getHostComponentClassName(metaTags[0]); if (hostComponentClassName == null) return null; ASScope containedScope = getContainedScope(); VariableDefinition hostComponentDef = new VariableDefinition(HOST_COMPONENT); hostComponentDef.setPublic(); hostComponentDef.setBindable(); hostComponentDef.setImplicit(); hostComponentDef.setContingent(); IReference typeRef = ReferenceFactory.packageQualifiedReference(containedScope.getWorkspace(), hostComponentClassName); hostComponentDef.setTypeReference(typeRef); containedScope.addDefinition(hostComponentDef); return hostComponentDef; } public VariableDefinition buildOuterDocumentMember(IReference outerClass) { ASScope containedScope = getContainedScope(); VariableDefinition outerDocumentDef = new VariableDefinition(IMXMLLanguageConstants.PROPERTY_OUTER_DOCUMENT); outerDocumentDef.setPublic(); outerDocumentDef.setBindable(); outerDocumentDef.setImplicit(); outerDocumentDef.setTypeReference(outerClass); containedScope.addDefinition(outerDocumentDef); // the outer document isn't really a contingent definition, as the user // should never define it, and we should always create one, but making // use of the contingent variable is useful as we can create it here // and then resolve it during codegen and create a problem if // we resolved to anything but the contingent definition outerDocument. outerDocumentDef.setContingent(); contingentDefinitions.add(outerDocumentDef); return outerDocumentDef; } private IMetaTag[] getHostComponentMetaData() { return getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_HOST_COMPONENT); } private static String getHostComponentClassName(IMetaTag metaTag) { assert IMetaAttributeConstants.ATTRIBUTE_HOST_COMPONENT.equals(metaTag.getTagName()); String hostComponentClassName = metaTag.getValue(); // no component name, so don't create a contingent member if (hostComponentClassName == null || hostComponentClassName.isEmpty()) return null; return hostComponentClassName; } private void buildFrameInformation(IWorkspace w) { FrameInformation result = new FrameInformation(); IMetaTag[] frameTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_FRAME); result.extraClasses = Collections.emptySet(); for (IMetaTag frameTag : frameTags) { String frameTagFactoryClass = frameTag.getAttributeValue(IMetaAttributeConstants.NAME_FRAME_FACTORY_CLASS); if (frameTagFactoryClass != null) result.factoryClass = ReferenceFactory.packageQualifiedReference(w, QNameNormalization.normalize(frameTagFactoryClass)); String frameTagExtraClass = frameTag.getAttributeValue(IMetaAttributeConstants.NAME_FRAME_EXTRA_CLASS); if (frameTagExtraClass != null) { if (result.extraClasses.size() == 0) result.extraClasses = new LinkedList<IResolvedQualifiersReference>(); result.extraClasses.add(ReferenceFactory.packageQualifiedReference(w, QNameNormalization.normalize(frameTagExtraClass))); } } frameInformation.compareAndSet(null, result); } private IResolvedQualifiersReference getFactoryClass(IWorkspace w) { if (frameInformation.get() == null) buildFrameInformation(w); return frameInformation.get().factoryClass; } public boolean hasOwnFactoryClass(IWorkspace w) { return getFactoryClass(w) != null; } private void buildRemoteClassAlias() { IMetaTag[] remoteClassTags = getMetaTagsByName(IMetaAttributeConstants.ATTRIBUTE_REMOTECLASS); String remoteClassAlias = NO_REMOTE_CLASS_ALIAS; if (remoteClassTags.length != 0) { // Reading the source for the Flex 4.5.X compiler, it seems that // you can only have one alias for each class and that the last alias // wins. IMetaTag lastRemoteClassTag = remoteClassTags[remoteClassTags.length - 1]; remoteClassAlias = lastRemoteClassTag.getAttributeValue(IMetaAttributeConstants.NAME_REMOTECLASS_ALIAS); // Goofy logic copied from flex2.compiler.as3.SyntaxTreeEvaluator. // Original code references a bug number 159983 in an unknown bug base. if (remoteClassAlias == null) remoteClassAlias = "<" + getQualifiedName(); } this.remoteClassAlias.compareAndSet(null, remoteClassAlias); } /** * Get's the remote class alias for this class. * * @return The remote class alias for this class. */ public String getRemoteClassAlias() { if (remoteClassAlias.get() == null) buildRemoteClassAlias(); String result = remoteClassAlias.get(); if (result == NO_REMOTE_CLASS_ALIAS) return null; return result; } public ClassDefinition resolveInheritedFactoryClass(ICompilerProject project) { for (IClassDefinition c : classIterable(project, true)) { assert c instanceof ClassDefinition : "IClassDefinition " + c.getBaseName() + "is not a ClassDefinition!"; ClassDefinition classDef = (ClassDefinition)c; IResolvedQualifiersReference factoryRef = classDef.getFactoryClass(project.getWorkspace()); if (factoryRef != null) { IDefinition factoryDef = factoryRef.resolve(project, (ASScope)this.getContainingScope(), EXPRESSION, true); if (factoryDef instanceof ClassDefinition) return (ClassDefinition)factoryDef; } } return null; } public Collection<IDefinition> resolveExtraClasses(ICompilerProject project) { if (frameInformation.get() == null) buildFrameInformation(project.getWorkspace()); LinkedList<IDefinition> result = new LinkedList<IDefinition>(); for (IResolvedQualifiersReference extraClassRef : frameInformation.get().extraClasses) { IDefinition extraClassDef = extraClassRef.resolve(project, (ASScope)this.getContainingScope(), EXPRESSION, true); if (extraClassDef != null) result.add(extraClassDef); } return ImmutableList.<IDefinition> copyOf(result); } @Override public String getDefaultPropertyName(ICompilerProject project) { // Look for [DefaultProperty("foo")] on this class its superclasses. for (IClassDefinition c : classIterable(project, true)) { IMetaTag defaultPropertyMetaData = c.getMetaTagByName(IMetaAttributeConstants.ATTRIBUTE_DEFAULTPROPERTY); if (defaultPropertyMetaData != null) { IMetaTagAttribute attrs[] = defaultPropertyMetaData.getAllAttributes(); if ((attrs.length == 1) && (!attrs[0].hasKey())) return attrs[0].getValue(); } } return null; } @Override public void setContainedScope(IASScope value) { super.setContainedScope(value); ((DefinitionBase)privateNamespaceReference).setContainingScope(value); } /** * Gets the {@link INamespaceReference} that resolves to the private * namespace for this {@link IClassDefinition}. * * @return The {@link INamespaceReference} that resolves to the private * namespace for this {@link IClassDefinition}. */ public NamespaceDefinition.ILanguageNamespaceDefinition getPrivateNamespaceReference() { return privateNamespaceReference; } /** * Gets the {@link INamespaceReference} that resolves to the protected * namespace for this {@link IClassDefinition}. * * @return The {@link INamespaceReference} that resolves to the protected * namespace for this {@link IClassDefinition}. */ @Override public NamespaceDefinition.ILanguageNamespaceDefinition getProtectedNamespaceReference() { return protectedNamespaceReference; } /** * Gets the {@link INamespaceReference} that resolves to the static * protected namespace for this {@link IClassDefinition}. * * @return The {@link INamespaceReference} that resolves to the static * protected namespace for this {@link IClassDefinition}. */ @Override public NamespaceDefinition.ILanguageNamespaceDefinition getStaticProtectedNamespaceReference() { return staticProtectedNamespaceReference; } @Override public Name getMName(ICompilerProject project) { // "*" isn't referenced by name in ABC - it's usually // just index 0, which is what AET does for a null name. if (getBaseName() == IASLanguageConstants.ANY_TYPE) return null; return super.getMName(project); } @Override public ASFileScope getFileScope() { if (isImplicit()) return null; else return super.getFileScope(); } /** * Adds a new node to the list of nodes representing the implicit imports * created by MXML tags. */ public void addImplicitImport(String qname) { if (implicitImports == null) implicitImports = new HashSet<String>(); implicitImports.add(qname); } /** * Gets the implicit imports created by MXML tags, for use by CodeModel. * * @return A list of {@code MXMLImplicitImportNode} objects. */ public String[] getImplicitImports() { return implicitImports != null ? implicitImports.toArray(new String[0]) : new String[0]; } /** * Adds implicit variable definitions for "this" and "super" to this class * definition's contained scope. These variable definitions are for code * model compatibility and are not codegen'd. */ public void setupThisAndSuper() { // Create an implicit VariableDefinition for "this". VariableDefinition thisDef = new VariableDefinition(IASKeywordConstants.THIS); ASScope containedScope = getContainedScope(); IWorkspace workspace = containedScope.getWorkspace(); // this is a lexical ref for codemodel backwards compat thisDef.setTypeReference(ReferenceFactory.lexicalReference(workspace, getBaseName())); thisDef.setImplicit(); thisDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace()); // Create an implicit VariableDefinition for "super". VariableDefinition superDef = new VariableDefinition(IASKeywordConstants.SUPER); IReference baseClassRef = getBaseClassReference(); if (baseClassRef == null) { baseClassRef = ReferenceFactory.builtinReference(BuiltinType.OBJECT); } superDef.setTypeReference(baseClassRef); superDef.setImplicit(); superDef.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace()); // Add these definitions to the class scope. containedScope.addDefinition(thisDef); containedScope.addDefinition(superDef); } /** * Mark this class as an excluded class by adding [ExcludeClass] meta data. */ public void setExcludedClass() { MetaTag excludeClassMetaTag = new MetaTag(this, IMetaAttributeConstants.ATTRIBUTE_EXCLUDECLASS, new IMetaTagAttribute[0]); addMetaTag(excludeClassMetaTag); } /** * For debugging only. Produces a string such as * <code>public class B extends A implements I1, I2</code>. */ @Override protected void buildInnerString(StringBuilder sb) { sb.append(getNamespaceReferenceAsString()); sb.append(' '); sb.append(IASKeywordConstants.CLASS); sb.append(' '); sb.append(getBaseName()); sb.append(' '); if (baseClassReference != null) { sb.append(IASKeywordConstants.EXTENDS); sb.append(' '); String baseClassName = getBaseClassAsDisplayString(); sb.append(baseClassName.isEmpty() ? IASLanguageConstants.Object : baseClassName); } String[] implementedInterfaces = getImplementedInterfacesAsDisplayStrings(); int n = implementedInterfaces.length; if (n > 0) { sb.append(' '); sb.append(IASKeywordConstants.IMPLEMENTS); sb.append(' '); for (int i = 0; i < n; i++) { sb.append(implementedInterfaces[i]); if (i < n - 1) { sb.append(','); sb.append(' '); } } } } public boolean getOwnNeedsProtected() { return ((TypeScope)getContainedScope()).getNeedsProtected(); } @Override public IClassDefinition resolveHostComponent(ICompilerProject project) { for (IClassDefinition c : classIterable(project, true)) { if (!(c instanceof ClassDefinition)) continue; ClassDefinition classDef = (ClassDefinition)c; IMetaTag[] hostComponentMetaData = classDef.getHostComponentMetaData(); if (hostComponentMetaData.length < 1) continue; String hostComponentName = getHostComponentClassName(hostComponentMetaData[0]); if (hostComponentName == null) return null; IResolvedQualifiersReference hostComponentRef = ReferenceFactory.packageQualifiedReference(project.getWorkspace(), hostComponentName); IDefinition hostComponentDef = hostComponentRef.resolve(project); if (!(hostComponentDef instanceof IClassDefinition)) return null; return (IClassDefinition)hostComponentDef; } return null; } @Override public boolean isInProject(ICompilerProject project) { if (!isImplicit()) return super.isInProject(project); // The implicit definitions are shared amoungst all projects. if (this == NULL) return true; if (this == VOID) return true; if (this == ANY_TYPE) return true; if (this == UNDEFINED) return true; // Some implicit class definition we have never seen before. return false; } }