blob: 129088f9deda2abb880c3f0cd9d5226130ff8ba9 [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.abc;
import static org.apache.royale.abc.ABCConstants.OP_dup;
import static org.apache.royale.abc.ABCConstants.OP_findpropstrict;
import static org.apache.royale.abc.ABCConstants.OP_getlocal0;
import static org.apache.royale.abc.ABCConstants.OP_getproperty;
import static org.apache.royale.abc.ABCConstants.OP_getscopeobject;
import static org.apache.royale.abc.ABCConstants.OP_initproperty;
import static org.apache.royale.abc.ABCConstants.OP_newclass;
import static org.apache.royale.abc.ABCConstants.OP_popscope;
import static org.apache.royale.abc.ABCConstants.OP_pushscope;
import static org.apache.royale.abc.ABCConstants.OP_returnvoid;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Vector;
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.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.IScriptVisitor;
import org.apache.royale.abc.visitors.ITraitVisitor;
import org.apache.royale.abc.visitors.ITraitsVisitor;
import org.apache.royale.compiler.definitions.IClassDefinition;
import org.apache.royale.compiler.internal.as.codegen.LexicalScope;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.projects.ICompilerProject;
/**
* Utility class for writing a class definition into an IABCVisitor.
* <p>
* After construction:
* <ul>
* <li>Instance trait's can be added to the class by calling
* {@link #getITraitsVisitor()} and visiting each trait to be added.</li>
* <li>Class trait's can be added to the class by calling
* {@link #getCTraitsVisitor()} and visiting each trait to be added.</li>
* <li>Instance methods can be added to the class by calling
* {@link #addITraitsMethod(Name, Collection, Name, Collection, boolean, boolean, boolean, InstructionList)}
* .</li>
* </ul>
* <p>
* After all the trait's and instructions have been added to the class, either
* {@link #finishClass(InstructionList)} or {@link #finishScript()} must be
* called.
*/
public class ClassGeneratorHelper
{
/**
* @return An instruction list with only one "returnvoid" instruction.
*/
public static InstructionList returnVoid()
{
final InstructionList inst = new InstructionList(1);
inst.addInstruction(OP_returnvoid);
return inst;
}
/**
* Constuctor that thunks to
* {@link #ClassGeneratorHelper(ICompilerProject, IABCVisitor, Name, ClassDefinition, Collection, Collection, InstructionList, boolean)}.
*
* @param project A compiler project.
* @param visitor An ABC visitor.
* @param className The ABC name of the new class.
* @param baseClass The definition of the class being extended.
*/
public ClassGeneratorHelper(ICompilerProject project, IABCVisitor visitor, Name className, ClassDefinition baseClass, InstructionList constructorInstructions)
{
this(project, visitor, className, baseClass, Collections.<Name> emptyList(), Collections.<Name> emptyList(), constructorInstructions, false);
}
/**
* Constuctor that thunks to
* {@link #ClassGeneratorHelper(ICompilerProject, IABCVisitor, Name, ClassDefinition, Collection, Collection, InstructionList, boolean)}.
*
* @param project A compiler project.
* @param visitor An ABC visitor.
* @param className The ABC name of the new class.
* @param baseClass The definition of the class being extended.
* @param implementedInterfaces The ABC name of the interfaces being implemented.
*/
public ClassGeneratorHelper(ICompilerProject project, IABCVisitor visitor, Name className, ClassDefinition baseClass, Collection<Name> implementedInterfaces, InstructionList constructorInstructions)
{
this(project, visitor, className, baseClass, implementedInterfaces, Collections.<Name> emptyList(), constructorInstructions, false);
}
/**
* Generate an ABC class with constructor instructions.
*
* @see #ClassGeneratorHelper(ICompilerProject, IABCVisitor, Name, ClassDefinition, Collection, Collection, InstructionList, InstructionList, boolean)
*/
public ClassGeneratorHelper(ICompilerProject project, IABCVisitor visitor, Name className, ClassDefinition baseClass, Collection<Name> implementedInterfaces, Collection<Name> constructorParamTypes, InstructionList constructorInstructions, boolean hasProtectedMembers)
{
this(project, visitor, className, baseClass, implementedInterfaces, constructorParamTypes, constructorInstructions, returnVoid(), hasProtectedMembers);
}
/**
* Constructor
*
* @param project {@link ICompilerProject} project for which ABC is being
* generated.
* @param visitor {@link IABCVisitor} to write the new class into.
* @param className {@link Name} of the class to generate
* @param baseClass {@link ClassDefinition} for the base class the generated
* class extends.
* @param implementedInterfaces Collection of {@link Name}'s that at runtime
* will refer to interfaces the generated class implement.
* @param iinitParameterTypes Collection of {@link Name}'s that at runtime
* will refer to the types of the constructor arguments.
* @param iinitInstructions Instructions for the constructor.
* @param cinitInstructions Instructions for the {@code cinit()} method.
* @param hasProtectedMembers whether or not this class has protected members
* There must be one {@code returnvoid} instruction.
*/
public ClassGeneratorHelper(final ICompilerProject project,
final IABCVisitor visitor,
final Name className,
final ClassDefinition baseClass,
final Collection<Name> implementedInterfaces,
final Collection<Name> iinitParameterTypes,
final InstructionList iinitInstructions,
final InstructionList cinitInstructions,
boolean hasProtectedMembers)
{
if (iinitInstructions.canFallThrough())
throw new IllegalArgumentException("Expected a 'returnvoid' instruction in the iinit() instructions.");
if (cinitInstructions.canFallThrough())
throw new IllegalArgumentException("Expected a 'returnvoid' instruction in the cinit() instructions.");
this.project = project;
this.className = className;
this.baseClass = baseClass;
cinfo = new ClassInfo();
cinfo.cInit = new MethodInfo();
IMethodVisitor cInitVisitor = visitor.visitMethod(cinfo.cInit);
cInitVisitor.visit();
MethodBodyInfo cInitMethodBodyInfo = new MethodBodyInfo();
cInitMethodBodyInfo.setMethodInfo(cinfo.cInit);
IMethodBodyVisitor cInitMethodBodyVisitor = cInitVisitor.visitBody(cInitMethodBodyInfo);
cInitMethodBodyVisitor.visit();
cInitMethodBodyVisitor.visitInstructionList(cinitInstructions);
cInitMethodBodyVisitor.visitEnd();
cInitVisitor.visitEnd();
iinfo = new InstanceInfo();
if(hasProtectedMembers)
{
iinfo.flags |= ABCConstants.CONSTANT_ClassProtectedNs;
iinfo.protectedNs = new Namespace(ABCConstants.CONSTANT_ProtectedNs,
className.getSingleQualifier().getName() + ":" +className.getBaseName());
}
iinfo.interfaceNames = implementedInterfaces.toArray(new Name[implementedInterfaces.size()]);
iinfo.name = className;
iinfo.superName = baseClass.getMName(project);
iinfo.iInit = new MethodInfo();
iinfo.iInit.setParamTypes(new Vector<Name>(iinitParameterTypes));
iTraitsInitMethodVisitor = visitor.visitMethod(iinfo.iInit);
iTraitsInitMethodVisitor.visit();
MethodBodyInfo iTraitsInitMethodBodyInfo = new MethodBodyInfo();
iTraitsInitMethodBodyInfo.setMethodInfo(iinfo.iInit);
iTraitsInitMethodBodyVisitor = iTraitsInitMethodVisitor.visitBody(iTraitsInitMethodBodyInfo);
iTraitsInitMethodBodyVisitor.visit();
iTraitsInitMethodBodyVisitor.visitInstructionList(iinitInstructions);
this.visitor = visitor;
classVisitor = visitor.visitClass(iinfo, cinfo);
classVisitor.visit();
itraits = classVisitor.visitInstanceTraits();
itraits.visit();
ctraits = classVisitor.visitClassTraits();
ctraits.visit();
}
private final ICompilerProject project;
private final Name className;
private final ClassDefinition baseClass;
private final ClassInfo cinfo;
private final InstanceInfo iinfo;
private final IABCVisitor visitor;
private final IClassVisitor classVisitor;
private final ITraitsVisitor itraits;
private final ITraitsVisitor ctraits;
private final IMethodVisitor iTraitsInitMethodVisitor;
private final IMethodBodyVisitor iTraitsInitMethodBodyVisitor;
/**
* Adds a new script to the ABC with a trait for the class closure and an
* init with instructions to create the class closure for the generated
* class.
*
* <p>
* After this method is called no other methods on this class should be called.
*/
public void finishScript()
{
IScriptVisitor sv = visitor.visitScript();
sv.visit();
ITraitsVisitor scriptTraits = sv.visitTraits();
scriptTraits.visit();
scriptTraits.visitClassTrait(ABCConstants.TRAIT_Class, className, 0, cinfo);
scriptTraits.visitEnd();
MethodInfo scriptInitMethodInfo = new MethodInfo();
IMethodVisitor scriptInitMethodVisitor = visitor.visitMethod(scriptInitMethodInfo);
scriptInitMethodVisitor.visit();
MethodBodyInfo scriptInitMethodBodyInfo = new MethodBodyInfo();
scriptInitMethodBodyInfo.setMethodInfo(scriptInitMethodInfo);
IMethodBodyVisitor scriptInitMethodBodyVisitor = scriptInitMethodVisitor.visitBody(scriptInitMethodBodyInfo);
scriptInitMethodBodyVisitor.visit();
InstructionList scriptInitInstructions = new InstructionList();
scriptInitInstructions.addInstruction(OP_getlocal0);
scriptInitInstructions.addInstruction(OP_pushscope);
finishClass(scriptInitInstructions);
if (scriptInitInstructions.canFallThrough())
scriptInitInstructions.addInstruction(OP_returnvoid);
scriptInitMethodBodyVisitor.visitInstructionList(scriptInitInstructions);
scriptInitMethodBodyVisitor.visitEnd();
scriptInitMethodVisitor.visitEnd();
sv.visitInit(scriptInitMethodInfo);
sv.visitEnd();
}
/**
* Generates the instructions to create the class closure and adds them to
* the specified {@link InstructionList}.
*
* @param scriptInitInstructions {@link InstructionList} to add the
* instructions that create the class closure to.
*/
public void finishClass(InstructionList scriptInitInstructions)
{
iTraitsInitMethodBodyVisitor.visitEnd();
iTraitsInitMethodVisitor.visitEnd();
LinkedList<Name> ancestorNames = new LinkedList<Name>();
for (IClassDefinition ancestorIClass : baseClass.classIterable(project, true))
{
final ClassDefinition ancestorClass = (ClassDefinition) ancestorIClass;
ancestorNames.addFirst(ancestorClass.getMName(project));
}
scriptInitInstructions.addInstruction(OP_getscopeobject, 0);
// Push ancestor classes onto the scope stack in
// order by superclass relationship; the immediate
// superclass is handled specially just below.
assert ancestorNames.size() > 0;
Name superclassName = ancestorNames.removeLast();
for (Name ancestorName : ancestorNames)
{
scriptInitInstructions.addInstruction(OP_findpropstrict, ancestorName);
scriptInitInstructions.addInstruction(OP_getproperty, ancestorName);
scriptInitInstructions.addInstruction(OP_pushscope);
}
scriptInitInstructions.addInstruction(OP_findpropstrict, superclassName);
scriptInitInstructions.addInstruction(OP_getproperty, superclassName);
scriptInitInstructions.addInstruction(OP_dup);
scriptInitInstructions.addInstruction(OP_pushscope);
scriptInitInstructions.addInstruction(OP_newclass, cinfo);
for ( int i = 0; i < ancestorNames.size(); i++ )
scriptInitInstructions.addInstruction(OP_popscope);
scriptInitInstructions.addInstruction(OP_popscope);
scriptInitInstructions.addInstruction(OP_initproperty, className);
ctraits.visitEnd();
itraits.visitEnd();
classVisitor.visitEnd();
}
/**
* @return The {@link ITraitsVisitor} for the instance trait's of the generated class.
*/
public ITraitsVisitor getITraitsVisitor()
{
return itraits;
}
/**
* @return The {@link ITraitsVisitor} for the class trait's of the generated class.
*/
public ITraitsVisitor getCTraitsVisitor()
{
return ctraits;
}
private ITraitVisitor addMethodToTraits(ITraitsVisitor traits, Name methodName,
Collection<Name> parameterTypes,
Name returnType,
Collection<Object> defaultParameterValues,
boolean needsRest,
int functionTraitKind,
InstructionList body)
{
MethodInfo mi = new MethodInfo();
for (Object defaultParameterValue : defaultParameterValues)
mi.addDefaultValue(new PooledValue(defaultParameterValue));
mi.setParamTypes(new Vector<Name>(parameterTypes));
mi.setReturnType(returnType);
if (needsRest)
mi.setFlags(mi.getFlags() | ABCConstants.METHOD_Needrest);
FunctionGeneratorHelper.generateFunction(visitor, mi, body);
return traits.visitMethodTrait(functionTraitKind, methodName, 0, mi);
}
/**
* Utility method to add an instance method to the generated class.
*
* @param methodName {@link Name} of the method to add.
* @param parameterTypes Collection of {@link Name}'s of the parameters to
* the method.
* @param returnType {@link Name} of the return type of the method.
* @param defaultParameterValues Collection of object's that can be
* converted to ABC constants for the default values of parameters.
* @param needsRest true if the method needs the rest parameter, false otherwise
* @param isFinal true if the method is final, false otherwise
* @param isOverride true if the method is an override of another method, false otherwise
* @param body An {@link InstructionList} for the body of the method.
*/
public void addITraitsMethod(Name methodName,
Collection<Name> parameterTypes,
Name returnType,
Collection<Object> defaultParameterValues,
boolean needsRest,
boolean isFinal,
boolean isOverride,
InstructionList body)
{
addITraitsMethod(methodName, parameterTypes, returnType, defaultParameterValues,
needsRest, isFinal, isOverride, body, ABCConstants.TRAIT_Method);
}
/**
* Utility method to add an instance method to the generated class.
*
* @param methodName {@link Name} of the method to add.
* @param parameterTypes Collection of {@link Name}'s of the parameters to
* the method.
* @param returnType {@link Name} of the return type of the method.
* @param defaultParameterValues Collection of object's that can be
* converted to ABC constants for the default values of parameters.
* @param needsRest true if the method needs the rest parameter, false otherwise
* @param isFinal true if the method is final, false otherwise
* @param isOverride true if the method is an override of another method, false otherwise
* @param body An {@link InstructionList} for the body of the method.
* @param functionKindTrait One of ABCConstants, TRAIT_Method, TRAIT_Getter,
* TRAIT_Setter.
*/
public void addITraitsMethod(Name methodName,
Collection<Name> parameterTypes,
Name returnType,
Collection<Object> defaultParameterValues,
boolean needsRest,
boolean isFinal,
boolean isOverride,
InstructionList body,
int functionKindTrait)
{
ITraitVisitor traitVisitor = addMethodToTraits(itraits, methodName, parameterTypes,
returnType, defaultParameterValues, needsRest, functionKindTrait, body);
traitVisitor.visitStart();
if (isFinal)
traitVisitor.visitAttribute(Trait.TRAIT_FINAL, true);
if (isOverride)
traitVisitor.visitAttribute(Trait.TRAIT_OVERRIDE, true);
traitVisitor.visitEnd();
}
/**
* Utility method to add a static method to the generated class.
*
* @param methodName {@link Name} of the method to add.
* @param parameterTypes Collection of {@link Name}'s of the parameters to
* the method.
* @param returnType {@link Name} of the return type of the method.
* @param defaultParameterValues Collection of object's that can be
* converted to ABC constants for the default values of parameters.
* @param needsRest true if the method needs the rest parameter, false otherwise
* @param body An {@link InstructionList} for the body of the method.
*/
public void addCTraitsMethod(Name methodName,
Collection<Name> parameterTypes,
Name returnType,
Collection<Object> defaultParameterValues,
boolean needsRest,
InstructionList body)
{
ITraitVisitor traitVisitor = addMethodToTraits(ctraits, methodName, parameterTypes, returnType, defaultParameterValues, needsRest, ABCConstants.TRAIT_Method, body);
traitVisitor.visitStart();
traitVisitor.visitEnd();
}
/**
* Utility method to add an instance getter to the generated class.
*
* @param getterName {@link Name} of the method to add.
* @param returnType {@link Name} of the return type of the method.
* @param body An {@link InstructionList} for the body of the method.
*/
public void addITraitsGetter(Name getterName, Name returnType, InstructionList body)
{
ITraitVisitor traitVisitor = addMethodToTraits(itraits, getterName, Collections.<Name>emptyList(), returnType, Collections.<Object>emptyList(), false, ABCConstants.TRAIT_Getter, body);
traitVisitor.visitStart();
traitVisitor.visitEnd();
}
/**
* Utility method to add a static getter to the generated class.
*
* @param getterName {@link Name} of the method to add.
* @param returnType {@link Name} of the return type of the method.
* @param body An {@link InstructionList} for the body of the method.
*/
public void addCTraitsGetter(Name getterName, Name returnType, InstructionList body)
{
ITraitVisitor traitVisitor = addMethodToTraits(ctraits, getterName, Collections.<Name>emptyList(), returnType, Collections.<Object>emptyList(), false, ABCConstants.TRAIT_Getter, body);
traitVisitor.visitStart();
traitVisitor.visitEnd();
}
/**
* Utility method to add a member variable to a class.
*
* @param variableName {@link Name} of the member variable to add.
*/
public void addMemberVariable(Name variableName, Name type)
{
ITraitVisitor traitVisitor = itraits.visitSlotTrait(ABCConstants.TRAIT_Var, variableName, ITraitsVisitor.RUNTIME_SLOT, type, LexicalScope.noInitializer);
traitVisitor.visitStart();
traitVisitor.visitEnd();
}
/**
* @return protected namespace if it is asked for while creating this helper class
*/
public Namespace getProtectedNamespace() {
assert iinfo.protectedNs != null : "protected namespace is only available if you pass true for the hasProtectedMembers argument of this class' constructor";
return iinfo.protectedNs;
}
}