blob: bde11f31181b1f14759cbbf7ad33091ae7d2671a [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 org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.semantics.Instruction;
import org.apache.royale.abc.semantics.InstructionFactory;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.visitors.ITraitsVisitor;
import org.apache.royale.compiler.definitions.IDefinition;
import org.apache.royale.compiler.tree.as.IASNode;
/**
* A Binding collects information about a name that
* is relevant to the code generator:
* <ul>
* <li>Semantic definition
* <li>Local register access instructions --
* cached here so they can be shared and
* assigned concrete registers at function wrapup.
* </ul>
* Future enhancements:
* <ul>
* <li>Cache more binding information for better codegen.
* <li>Enforce const-ness of constant locals.
* </ul>
*/
public class Binding
{
/**
* The IASNode that contributed the Name,
* usually an IdentifierNode. May be null
* if the Binding is synthetic.
*/
private final IASNode node;
/**
* Data structure to hold information that can be shared between many bindings.
*
* These are things like the local register, the name, etc.
*/
private final SharedBindingInfo sharedBindingInfo;
/**
* Construct an empty Binding. Used for Bindings
* that refer to internal constructs, e.g., this.
*/
public Binding()
{
this(null, null, null);
}
/**
* Construct a Binding.
* @param name - the Binding's name.
* @param def - the Binding's definition, if known.
*/
public Binding(IASNode node, Name name, IDefinition def)
{
this.node = node;
this.sharedBindingInfo = new SharedBindingInfo(name, def);
}
/**
* Construct a Binding with the given node, but that otherwise has the same
* properties as the other binding passed in.
* @param node the node that generated this binding
* @param other the binding with the shared binding info to use
*/
public Binding(IASNode node, Binding other)
{
this.node = node;
this.sharedBindingInfo = other.sharedBindingInfo;
}
/**
* Get the multiname for this binding - used to generate access by name
* @return The name for the binding
*/
public Name getName()
{
return sharedBindingInfo.getName();
}
/**
* Get the AST node that generated this binding, used for error reporting.
* @return the IASNode, or null if not present.
*/
public IASNode getNode()
{
return node;
}
/*
* Get the definition for this binding.
* @return the definition associated with the binding.
*/
public IDefinition getDefinition()
{
return sharedBindingInfo.getDefinition();
}
/**
* @return true if this Binding has been
* flagged as resident in a local register.
*/
public boolean isLocal()
{
return sharedBindingInfo.isLocal();
}
/**
* Flag this Binding as resident in a
* local register (or unset the flag).
* @param is_local - the new value of the
* "binding lives in a register" flag.
*/
public void setIsLocal(boolean is_local)
{
sharedBindingInfo.setIsLocal(is_local);
}
/**
* @return true if this Binding's local number
* has already been set. Useful if a group of
* Bindings have subgroups that may get their
* local numbers set in distinct phases (e.g.,
* the set of Bindings in registers has a subset
* of parameter Bindings that have preallocated
* register numbers).
*/
public boolean localNumberIsSet()
{
return sharedBindingInfo.localNumberIsSet();
}
/**
* @return this Binding's local register number.
*/
public int getLocalRegister()
{
return sharedBindingInfo.getLocalRegister();
}
/**
* Set this binding's local register number.
* @param local_num - the binding's local register.
*/
void setLocalRegister(int local_num)
{
sharedBindingInfo.setLocalRegister(local_num);
}
/**
* Fetch this Binding's getlocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's getlocal instruction.
* The instruction will be created if not
* already present.
*/
public Instruction getlocal()
{
return sharedBindingInfo.getlocal();
}
/**
* Fetch this Binding's setlocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's setlocal instruction.
* The instruction will be created if not
* already present.
*/
public Instruction setlocal()
{
return sharedBindingInfo.setlocal();
}
/**
* Fetch this Binding's inclocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's inclocal instruction.
* The instruction will be created if not
* already present.
*/
public Instruction inclocal()
{
return sharedBindingInfo.inclocal();
}
/**
* Fetch this Binding's inclocal_i instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's inclocal_i instruction.
* The instruction will be created if not
* already present.
*/
public Instruction inclocal_i()
{
return sharedBindingInfo.inclocal_i();
}
/**
* Fetch this Binding's declocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's declocal instruction.
* The instruction will be created if not
* already present.
*/
public Instruction declocal()
{
return sharedBindingInfo.declocal();
}
/**
* Fetch this Binding's declocal_i instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's declocal_i instruction.
* The instruction will be created if not
* already present.
*/
public Instruction declocal_i()
{
return sharedBindingInfo.declocal_i();
}
/**
* Get the Binding's slot id. If this is 0, it
* is an AVM assigned id.
*
* @return slot id.
*/
public int getSlotId()
{
return sharedBindingInfo.getSlotId();
}
/**
* @return true if this Binding's slot id number
* has been set, rather than relying
* on a runtime slot.
*/
public boolean slotIdIsSet()
{
return sharedBindingInfo.slotIdIsSet();
}
/**
* Set the Binding's slot id. If this is 0, it
* is an AVM assigned id.
*/
public void setSlotId(int slotId)
{
sharedBindingInfo.setSlotId(slotId);
}
/**
* Fetch this Binding's kill instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
* @return the Binding's kill instruction.
* The instruction will be created if not
* already present.
*/
public Instruction kill()
{
return sharedBindingInfo.kill();
}
@Override
public String toString()
{
return sharedBindingInfo.toString();
}
/**
* Indicate that this Binding had a "super" qualifier.
* @param super_qualified - set true if the name that generated
* this Binding had an explicit "super" qualifier.
*/
public void setSuperQualified(final boolean super_qualified)
{
sharedBindingInfo.setSuperQualified(super_qualified);
}
/**
* Was this Binding explicitly qualified with "super"?
* @return true if the name that generated
* this Binding had an explicit "super" qualifier.
*/
public boolean isSuperQualified()
{
return sharedBindingInfo.isSuperQualified();
}
/**
* Data structure to hold information that can be shared between many bindings.
*
* These are things like the local register, the name, etc.
*/
public static class SharedBindingInfo
{
/**
* The multiname for this binding - used to generate lookups by name
*/
private final Name name;
/**
* The Binding's definition.
*/
private final IDefinition definition;
/**
* Use register-based instructions to manipulate
* this Binding's entity when this is set.
*/
private boolean isLocalDef;
/**
* The Binding's local register number.
* This is set at function wrap time.
*/
private Integer localNumber;
/**
* The Binding's slot id.
* This is zero by default to make it a
* AVM assigned slot.
*/
private int slotId = ITraitsVisitor.RUNTIME_SLOT;
/**
* The Binding's getlocal instruction.
* The Binding manages a single getlocal
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction getlocalIns = null;
/**
* The Binding's setlocal instruction.
* The Binding manages a single setlocal
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction setlocalIns = null;
/**
* The Binding's inclocal instruction.
* The Binding manages a single inclocal
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction inclocalIns = null;
/**
* The Binding's inclocal_i instruction.
* The Binding manages a single inclocal_i
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction inclocaliIns = null;
/**
* The Binding's declocal instruction.
* The Binding manages a single declocal
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction declocalIns = null;
/**
* The Binding's declocal_i instruction.
* The Binding manages a single declocal_
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction declocaliIns = null;
/**
* The Binding's kill instruction.
* The Binding manages a single kill
* to simplify setting its immediate value,
* i.e., the register number.
*/
private Instruction killIns = null;
/**
* Set when the Binding's name was
* explicitly qualified with "super."
*/
private boolean superQualified = false;
public SharedBindingInfo (Name n, IDefinition d)
{
this.name = n;
this.definition =d;
}
/**
* Get the multiname for this binding - used to generate access by name
*
* @return The name for the binding
*/
private Name getName ()
{
return name;
}/*
* Get the definition for this binding.
* @return the definition associated with the binding.
*/
private IDefinition getDefinition ()
{
return definition;
}
/**
* @return true if this Binding has been
* flagged as resident in a local register.
*/
private boolean isLocal ()
{
return isLocalDef;
}
/**
* Flag this Binding as resident in a
* local register (or unset the flag).
*
* @param is_local - the new value of the
* "binding lives in a register" flag.
*/
private void setIsLocal (boolean is_local)
{
this.isLocalDef = is_local;
}
/**
* @return true if this Binding's local number
* has already been set. Useful if a group of
* Bindings have subgroups that may get their
* local numbers set in distinct phases (e.g.,
* the set of Bindings in registers has a subset
* of parameter Bindings that have preallocated
* register numbers).
*/
private boolean localNumberIsSet ()
{
return this.localNumber != null;
}
/**
* @return this Binding's local register number.
*/
private int getLocalRegister ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
assert (this.localNumber != null) : "local register number no set:" + this;
return this.localNumber;
}
/**
* Set this binding's local register number.
*
* @param local_num - the binding's local register.
*/
private void setLocalRegister (int local_num)
{
assert (this.isLocalDef) : "not a local Binding:" + this;
assert (this.localNumber == null) : "local already set:" + this;
this.localNumber = local_num;
if (this.getlocalIns != null)
this.getlocalIns.setImmediate(local_num);
if (this.setlocalIns != null)
this.setlocalIns.setImmediate(local_num);
if (this.inclocalIns != null)
this.inclocalIns.setImmediate(local_num);
if (this.inclocaliIns != null)
this.inclocaliIns.setImmediate(local_num);
if (this.declocalIns != null)
this.declocalIns.setImmediate(local_num);
if (this.declocaliIns != null)
this.declocaliIns.setImmediate(local_num);
if (this.killIns != null)
this.killIns.setImmediate(local_num);
}
/**
* Fetch this Binding's getlocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's getlocal instruction.
* The instruction will be created if not
* already present.
*/
private Instruction getlocal ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.getlocalIns == null)
{
this.getlocalIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_getlocal);
if (this.localNumber != null)
this.getlocalIns.setImmediate(this.localNumber);
}
return this.getlocalIns;
}
/**
* Fetch this Binding's setlocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's setlocal instruction.
* The instruction will be created if not
* already present.
*/
private Instruction setlocal ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.setlocalIns == null)
{
this.setlocalIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_setlocal);
if (this.localNumber != null)
this.setlocalIns.setImmediate(this.localNumber);
}
return this.setlocalIns;
}
/**
* Fetch this Binding's inclocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's inclocal instruction.
* The instruction will be created if not
* already present.
*/
private Instruction inclocal ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.inclocalIns == null)
{
this.inclocalIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_inclocal);
if (this.localNumber != null)
this.inclocalIns.setImmediate(this.localNumber);
}
return this.inclocalIns;
}
/**
* Fetch this Binding's inclocal_i instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's inclocal_i instruction.
* The instruction will be created if not
* already present.
*/
private Instruction inclocal_i ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.inclocaliIns == null)
{
this.inclocaliIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_inclocal_i);
if (this.localNumber != null)
this.inclocaliIns.setImmediate(this.localNumber);
}
return this.inclocaliIns;
}
/**
* Fetch this Binding's declocal instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's declocal instruction.
* The instruction will be created if not
* already present.
*/
private Instruction declocal ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.declocalIns == null)
{
this.declocalIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_declocal);
if (this.localNumber != null)
this.declocalIns.setImmediate(this.localNumber);
}
return this.declocalIns;
}
/**
* Fetch this Binding's declocal_i instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's declocal_i instruction.
* The instruction will be created if not
* already present.
*/
private Instruction declocal_i ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.declocaliIns == null)
{
this.declocaliIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_declocal_i);
if (this.localNumber != null)
this.declocaliIns.setImmediate(this.localNumber);
}
return this.declocaliIns;
}
/**
* Get the Binding's slot id. If this is 0, it
* is an AVM assigned id.
*
* @return slot id.
*/
private int getSlotId()
{
return this.slotId;
}
/**
* Set the Binding's slot id. If this is 0, it
* is an AVM assigned id.
*/
private void setSlotId(int slotId)
{
assert (slotId != ITraitsVisitor.RUNTIME_SLOT) : "setSlotId should only be used manually allocated slot ids";
this.slotId = slotId;
}
/**
* @return true if this Binding's slot id number
* has been set, rather than relying
* on a runtime slot.
*/
private boolean slotIdIsSet()
{
return this.slotId != ITraitsVisitor.RUNTIME_SLOT;
}
/**
* Fetch this Binding's kill instruction;
* the Binding manages a single instruction
* so that its register number can be assigned
* at function wrap-up time.
*
* @return the Binding's kill instruction.
* The instruction will be created if not
* already present.
*/
private Instruction kill ()
{
assert (this.isLocalDef) : "not a local Binding:" + this;
if (this.killIns == null)
{
this.killIns = InstructionFactory.getDeferredImmediateInstruction(ABCConstants.OP_kill);
if (this.localNumber != null)
this.killIns.setImmediate(this.localNumber);
}
return this.killIns;
}
/**
* Indicate that this Binding had a "super" qualifier.
*
* @param super_qualified - set true if the name that generated
* this Binding had an explicit "super" qualifier.
*/
private void setSuperQualified (final boolean super_qualified)
{
this.superQualified = super_qualified;
}
/**
* Was this Binding explicitly qualified with "super"?
*
* @return true if the name that generated
* this Binding had an explicit "super" qualifier.
*/
private boolean isSuperQualified ()
{
return this.superQualified;
}
@Override
public String toString()
{
StringBuilder result = new StringBuilder();
result.append("Binding { name:");
result.append(getName() != null? getName() :"-null-");
result.append(" def:");
result.append(getDefinition() != null? getDefinition() :"-null-");
result.append(" local:");
result.append(localNumber != null? localNumber :"--");
result.append("}");
return result.toString();
}
}
}