blob: 281a973933a24fd7640b204697e1feed0d872b9d [file] [log] [blame]
/*
*
* 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.*;
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.constants.INamespaceConstants;
import org.apache.royale.compiler.definitions.*;
import org.apache.royale.compiler.definitions.metadata.IMetaTag;
import org.apache.royale.compiler.definitions.metadata.IMetaTagAttribute;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.exceptions.CodegenInterruptedException;
import org.apache.royale.compiler.internal.definitions.*;
import org.apache.royale.compiler.internal.tree.as.*;
import org.apache.royale.compiler.problems.*;
import org.apache.royale.compiler.projects.ICompilerProject;
import org.apache.royale.compiler.scopes.IDefinitionSet;
import org.apache.royale.compiler.tree.ASTNodeID;
import org.apache.royale.compiler.tree.as.*;
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.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.utils.ASTUtil;
import org.apache.royale.utils.ArrayLikeUtil;
/**
* 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);
implementedAbstractClassSemanticChecks(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);
}
if (ArrayLikeUtil.definitionIsArrayLike(classDefinition)) {
ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>();
boolean valid = ArrayLikeUtil.validateArrayLikeDefinition(classDefinition, classScope.getProject(), problems);
if (!valid){
classScope.getProject().getProblems().addAll(problems);
}
}
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 contingent 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();
ASTUtil.processFunctionNode(func, project);
boolean isBindable = false;
if (funcDef instanceof AccessorDefinition)
{
boolean isClassBindable = BindableHelper.isClassCodeGenBindable(getClassDefinition());
AccessorDefinition definitionWithBindable = null;
boolean foundExplicitBindableTag = false;
isBindable = BindableHelper.isCodeGenBindableMember(funcDef, isClassBindable);
if (isBindable) {
definitionWithBindable = (AccessorDefinition) funcDef;
}
foundExplicitBindableTag = BindableHelper.hasExplicitBindable(funcDef);
AccessorDefinition otherDef =
((AccessorDefinition)funcDef).resolveCorrespondingAccessor(classScope.getProject());
if (!isBindable && !foundExplicitBindableTag)
{
if (otherDef != null && otherDef.getContainingScope().equals(funcDef.getContainingScope()))
{
isBindable = BindableHelper.isCodeGenBindableMember(otherDef, isClassBindable);
if (isBindable) {
definitionWithBindable = otherDef;
}
foundExplicitBindableTag = BindableHelper.hasExplicitBindable(otherDef);
//if a) class is Bindable, and b)there is a [Bindable] tag, and c) there is also a [Bindable(eventName)] tag
//then we can retain the [Bindable] codegen and reset foundExplicitBinding to false (assumption, check in flex)
if (isClassBindable && isBindable && foundExplicitBindableTag) {
foundExplicitBindableTag = false; //we will ignore it and keep codegen as well
}
}
}
if (isBindable && otherDef == null) {
//no other definition
isBindable = false;
//warning if explicit [Bindable] ?
}
if (isBindable
&& otherDef instanceof ISetterDefinition
&& !(otherDef.getContainingScope().equals(funcDef.getContainingScope()))) {
//if the setter is not defined in the same scope as this getter, that is an Error (same as Flex)
isBindable = false;
classScope.addProblem(new BindableGetterCodeGenProblem((IGetterDefinition) funcDef));
}
if (isClassBindable) {
//add a warning if redundant local 'codegen' [Bindable] tag
if (BindableHelper.hasCodegenBindable(definitionWithBindable)) {
classScope.addProblem(new RedundantBindableTagProblem(
BindableHelper.getProblemReportingNode(definitionWithBindable)
));
}
if (isBindable) {
if (!foundExplicitBindableTag && BindableHelper.hasExplicitBindable(otherDef)) foundExplicitBindableTag = true;
}
if (foundExplicitBindableTag) {
//logic:
//when class is [Bindable],
//if either getter/setter member has at least one explicit Bindable tag (Bindable(event='something'))
//then no code-gen is applied,
//even if one of them also has a local 'codegen' [Bindable] tag
isBindable = false;
}
}
}
functionSemanticChecks(func);
Name funcName = funcDef.getMName(classScope.getProject());
Name bindableName = null;
boolean wasOverride = false;
//from here on we are only interested in the setter definitions, leave getters as-is
isBindable = isBindable && funcDef instanceof ISetterDefinition;
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)
{ //only setters get modified in this case, getters remain the same as source code
if (wasOverride)
funcDef.setOverride();
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();
verifyFunctionNamespace(node, func);
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 = 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 )
{
if (classScope.getProject().getAllowPrivateConstructors())
{
if (node.getActualNamespaceNode().getName() != IASKeywordConstants.PUBLIC
&& !func.isPrivate())
{
problems.add(new ConstructorMustBePublicOrPrivateProblem(node.getActualNamespaceNode()));
}
}
else if (node.getActualNamespaceNode().getName() != IASKeywordConstants.PUBLIC || func.isPrivate())
{
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() )
{
//check conflicting language namespaces
if (func.getNamespaceReference().isLanguageNamespace()) {
IDefinitionSet others = func.getContainingScope().getLocalDefinitionSetByName(func.getBaseName());
if (others.getSize() > 1) {
for (int i=0;i< others.getSize(); i++) {
IDefinition other = others.getDefinition(i);
if (other == func) continue;
//ignore code-gen Bindable setters if we are checking against the original setter
if ((func instanceof SyntheticBindableSetterDefinition && other instanceof ISetterDefinition)
|| (other instanceof SyntheticBindableSetterDefinition && func instanceof ISetterDefinition)
&& (func.getNode().equals(other.getNode()))
) {
continue;
}
if (other.getNamespaceReference().isLanguageNamespace()) {
if (!SemanticUtils.isGetterSetterPair(func, other, classScope.getProject())) {
boolean issueProblem = true;
if (func instanceof IAccessorDefinition) {
if (((IAccessorDefinition)func).isProblematic()) issueProblem = false;
((IAccessorDefinition)func).setIsProblematic(true);
if (other instanceof IAccessorDefinition) {
if (((IAccessorDefinition)other).isProblematic()) issueProblem = false;
//don't explicitly set it as problematic here, that will happen when it undergoes its own checks
}
}
if (issueProblem) {
if (other instanceof IFunctionDefinition)
problems.add(new DuplicateFunctionDefinitionProblem(func.getFunctionNode(), func.getBaseName()));
else {
//in legacy compiler, this is also reported as a DuplicateFuncitonDefinitionProblem, but this seems more correct:
problems.add(new ConflictingDefinitionProblem(func.getFunctionNode(), func.getBaseName(), this.classDefinition.getQualifiedName(), true));
}
}
}
}
}
}
}
// We have to find the (potentially) overridden 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
{
boolean ignore = false;
if (classScope.getProject().getAllowPrivateNameConflicts()) {
if (override.isPrivate()) ignore = true;
}
// found overriden function, but function not marked as override
if (!ignore) 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());
}
}
}
/**
* Check the class definition for various errors related to extended
* abstract classes, such as making sure that all abstract methods are
* implemented
*
* @param cls the class definition to check
*/
void implementedAbstractClassSemanticChecks(ClassDefinition cls)
{
if(!classScope.getProject().getAllowAbstractClasses())
{
//don't do these checks if abstract classes aren't enabled
return;
}
if(cls.isAbstract())
{
// concrete classes don't need to implement abstract methods
return;
}
Iterator<IClassDefinition> it = cls.classIterator(classScope.getProject(), false);
while( it.hasNext() )
{
IClassDefinition otherClass = it.next();
if(!otherClass.isAbstract())
{
//if a subclass is already concrete, then we don't need to check
//this class for abstract methods too
break;
}
if( otherClass instanceof ClassDefinition && otherClass.isAbstract())
{
((ClassDefinition)otherClass).validateClassImplementsAllMethods(classScope.getProject(), cls, classScope.getProblems());
}
}
}
/**
* Verify that abstract function has an appropriate namespace. If it doesn't,
* print the appropriate error
*
* @param func is the function node do be analyzed
* @param func_def is the definition for func
*/
private void verifyFunctionNamespace(FunctionNode func, FunctionDefinition func_def)
{
if(!classScope.getProject().getAllowAbstractClasses())
{
//if abstract classes aren't allowed, other errors should take
//precedence
return;
}
if(!func_def.isAbstract())
{
return;
}
INamespaceDecorationNode nsNode = func.getActualNamespaceNode();
// if we have no "actual" node, then there is no namespace in front of our function
if (nsNode != null)
{
if (!INamespaceConstants.internal_.equals(nsNode.getName()))
{
INamespaceReference ns_ref = func_def.getNamespaceReference();
INamespaceDefinition ns_def = ns_ref.resolveNamespaceReference(classScope.getProject());
if (ns_def != null && ns_def instanceof INamespaceDefinition.IPrivateNamespaceDefinition)
{
classScope.addProblem(new BadAccessAbstractMethodProblem(func));
}
}
}
}
/**
*/
protected void verifyFunctionModifiers(FunctionNode f)
{
IExpressionNode site = f.getNameExpressionNode();
IDefinition functionDef = f.getDefinition();
boolean isStatic = false; //used below
ModifiersSet modifiersSet = f.getModifiers();
if (modifiersSet != null)
{
isStatic = modifiersSet.hasModifier(ASModifier.STATIC);
if(isStatic)
{
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
}
if (functionDef.isAbstract())
{
if (classScope.getProject().getAllowAbstractClasses())
{
if (!SemanticUtils.canBeAbstract(f, classScope.getProject()))
{
classScope.addProblem(new AbstractOutsideClassProblem(site) );
}
}
else
{
classScope.addProblem(new SyntaxProblem(site, IASKeywordConstants.ABSTRACT));
}
}
}
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));
}
else if( modifier == ASModifier.ABSTRACT )
{
if(classScope.getProject().getAllowAbstractClasses())
{
classScope.addProblem(new AbstractOutsideClassProblem(site));
}
else
{
classScope.addProblem(new SyntaxProblem(site, IASKeywordConstants.ABSTRACT));
}
}
}
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();
}
}
}