/* * * 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.as.codegen; import org.apache.flex.abc.ABCConstants; import org.apache.flex.abc.semantics.Instruction; import org.apache.flex.abc.semantics.InstructionFactory; import org.apache.flex.abc.semantics.Name; import org.apache.flex.abc.visitors.ITraitsVisitor; import org.apache.flex.compiler.definitions.IDefinition; import org.apache.flex.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(); } } }