LocalVariableInfo.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.rop.code;
18
19import com.android.dx.rop.type.TypeBearer;
20import com.android.dx.util.MutabilityControl;
21
22import java.util.HashMap;
23
24/**
25 * Container for local variable information for a particular {@link
26 * RopMethod}.
27 */
28public final class LocalVariableInfo
29        extends MutabilityControl {
30    /** {@code >= 0;} the register count for the method */
31    private final int regCount;
32
33    /**
34     * {@code non-null;} {@link RegisterSpecSet} to use when indicating a block
35     * that has no locals; it is empty and immutable but has an appropriate
36     * max size for the method
37     */
38    private final RegisterSpecSet emptySet;
39
40    /**
41     * {@code non-null;} array consisting of register sets representing the
42     * sets of variables already assigned upon entry to each block,
43     * where array indices correspond to block labels
44     */
45    private final RegisterSpecSet[] blockStarts;
46
47    /** {@code non-null;} map from instructions to the variable each assigns */
48    private final HashMap<Insn, RegisterSpec> insnAssignments;
49
50    /**
51     * Constructs an instance.
52     *
53     * @param method {@code non-null;} the method being represented by this instance
54     */
55    public LocalVariableInfo(RopMethod method) {
56        if (method == null) {
57            throw new NullPointerException("method == null");
58        }
59
60        BasicBlockList blocks = method.getBlocks();
61        int maxLabel = blocks.getMaxLabel();
62
63        this.regCount = blocks.getRegCount();
64        this.emptySet = new RegisterSpecSet(regCount);
65        this.blockStarts = new RegisterSpecSet[maxLabel];
66        this.insnAssignments =
67            new HashMap<Insn, RegisterSpec>(blocks.getInstructionCount());
68
69        emptySet.setImmutable();
70    }
71
72    /**
73     * Sets the register set associated with the start of the block with
74     * the given label.
75     *
76     * @param label {@code >= 0;} the block label
77     * @param specs {@code non-null;} the register set to associate with the block
78     */
79    public void setStarts(int label, RegisterSpecSet specs) {
80        throwIfImmutable();
81
82        if (specs == null) {
83            throw new NullPointerException("specs == null");
84        }
85
86        try {
87            blockStarts[label] = specs;
88        } catch (ArrayIndexOutOfBoundsException ex) {
89            // Translate the exception.
90            throw new IllegalArgumentException("bogus label");
91        }
92    }
93
94    /**
95     * Merges the given register set into the set for the block with the
96     * given label. If there was not already an associated set, then this
97     * is the same as calling {@link #setStarts}. Otherwise, this will
98     * merge the two sets and call {@link #setStarts} on the result of the
99     * merge.
100     *
101     * @param label {@code >= 0;} the block label
102     * @param specs {@code non-null;} the register set to merge into the start set
103     * for the block
104     * @return {@code true} if the merge resulted in an actual change
105     * to the associated set (including storing one for the first time) or
106     * {@code false} if there was no change
107     */
108    public boolean mergeStarts(int label, RegisterSpecSet specs) {
109        RegisterSpecSet start = getStarts0(label);
110        boolean changed = false;
111
112        if (start == null) {
113            setStarts(label, specs);
114            return true;
115        }
116
117        RegisterSpecSet newStart = start.mutableCopy();
118        if (start.size() != 0) {
119            newStart.intersect(specs, true);
120        } else {
121            newStart = specs.mutableCopy();
122        }
123
124        if (start.equals(newStart)) {
125            return false;
126        }
127
128        newStart.setImmutable();
129        setStarts(label, newStart);
130
131        return true;
132    }
133
134    /**
135     * Gets the register set associated with the start of the block
136     * with the given label. This returns an empty set with the appropriate
137     * max size if no set was associated with the block in question.
138     *
139     * @param label {@code >= 0;} the block label
140     * @return {@code non-null;} the associated register set
141     */
142    public RegisterSpecSet getStarts(int label) {
143        RegisterSpecSet result = getStarts0(label);
144
145        return (result != null) ? result : emptySet;
146    }
147
148    /**
149     * Gets the register set associated with the start of the given
150     * block. This is just convenient shorthand for
151     * {@code getStarts(block.getLabel())}.
152     *
153     * @param block {@code non-null;} the block in question
154     * @return {@code non-null;} the associated register set
155     */
156    public RegisterSpecSet getStarts(BasicBlock block) {
157        return getStarts(block.getLabel());
158    }
159
160    /**
161     * Gets a mutable copy of the register set associated with the
162     * start of the block with the given label. This returns a
163     * newly-allocated empty {@link RegisterSpecSet} of appropriate
164     * max size if there is not yet any set associated with the block.
165     *
166     * @param label {@code >= 0;} the block label
167     * @return {@code non-null;} the associated register set
168     */
169    public RegisterSpecSet mutableCopyOfStarts(int label) {
170        RegisterSpecSet result = getStarts0(label);
171
172        return (result != null) ?
173            result.mutableCopy() : new RegisterSpecSet(regCount);
174    }
175
176    /**
177     * Adds an assignment association for the given instruction and
178     * register spec. This throws an exception if the instruction
179     * doesn't actually perform a named variable assignment.
180     *
181     * <b>Note:</b> Although the instruction contains its own spec for
182     * the result, it still needs to be passed in explicitly to this
183     * method, since the spec that is stored here should always have a
184     * simple type and the one in the instruction can be an arbitrary
185     * {@link TypeBearer} (such as a constant value).
186     *
187     * @param insn {@code non-null;} the instruction in question
188     * @param spec {@code non-null;} the associated register spec
189     */
190    public void addAssignment(Insn insn, RegisterSpec spec) {
191        throwIfImmutable();
192
193        if (insn == null) {
194            throw new NullPointerException("insn == null");
195        }
196
197        if (spec == null) {
198            throw new NullPointerException("spec == null");
199        }
200
201        insnAssignments.put(insn, spec);
202    }
203
204    /**
205     * Gets the named register being assigned by the given instruction, if
206     * previously stored in this instance.
207     *
208     * @param insn {@code non-null;} instruction in question
209     * @return {@code null-ok;} the named register being assigned, if any
210     */
211    public RegisterSpec getAssignment(Insn insn) {
212        return insnAssignments.get(insn);
213    }
214
215    /**
216     * Gets the number of assignments recorded by this instance.
217     *
218     * @return {@code >= 0;} the number of assignments
219     */
220    public int getAssignmentCount() {
221        return insnAssignments.size();
222    }
223
224    public void debugDump() {
225        for (int label = 0 ; label < blockStarts.length; label++) {
226            if (blockStarts[label] == null) {
227                continue;
228            }
229
230            if (blockStarts[label] == emptySet) {
231                System.out.printf("%04x: empty set\n", label);
232            } else {
233                System.out.printf("%04x: %s\n", label, blockStarts[label]);
234            }
235        }
236    }
237
238    /**
239     * Helper method, to get the starts for a label, throwing the
240     * right exception for range problems.
241     *
242     * @param label {@code >= 0;} the block label
243     * @return {@code null-ok;} associated register set or {@code null} if there
244     * is none
245     */
246    private RegisterSpecSet getStarts0(int label) {
247        try {
248            return blockStarts[label];
249        } catch (ArrayIndexOutOfBoundsException ex) {
250            // Translate the exception.
251            throw new IllegalArgumentException("bogus label");
252        }
253    }
254}
255