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.dex.file;
18
19import com.android.dx.dex.code.DalvCode;
20import com.android.dx.dex.code.DalvInsnList;
21import com.android.dx.dex.code.LocalList;
22import com.android.dx.dex.code.PositionList;
23import com.android.dx.rop.cst.CstMethodRef;
24import com.android.dx.util.AnnotatedOutput;
25import com.android.dx.util.ExceptionWithContext;
26
27import java.io.PrintWriter;
28
29public class DebugInfoItem extends OffsettedItem {
30    /** the required alignment for instances of this class */
31    private static final int ALIGNMENT = 1;
32
33    private static final boolean ENABLE_ENCODER_SELF_CHECK = false;
34
35    /** {@code non-null;} the code this item represents */
36    private final DalvCode code;
37
38    private byte[] encoded;
39
40    private final boolean isStatic;
41    private final CstMethodRef ref;
42
43    public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) {
44        // We don't know the write size yet.
45        super (ALIGNMENT, -1);
46
47        if (code == null) {
48            throw new NullPointerException("code == null");
49        }
50
51        this.code = code;
52        this.isStatic = isStatic;
53        this.ref = ref;
54    }
55
56    /** {@inheritDoc} */
57    @Override
58    public ItemType itemType() {
59        return ItemType.TYPE_DEBUG_INFO_ITEM;
60    }
61
62    /** {@inheritDoc} */
63    @Override
64    public void addContents(DexFile file) {
65        // No contents to add.
66    }
67
68    /** {@inheritDoc} */
69    @Override
70    protected void place0(Section addedTo, int offset) {
71        // Encode the data and note the size.
72
73        try {
74            encoded = encode(addedTo.getFile(), null, null, null, false);
75            setWriteSize(encoded.length);
76        } catch (RuntimeException ex) {
77            throw ExceptionWithContext.withContext(ex,
78                    "...while placing debug info for " + ref.toHuman());
79        }
80    }
81
82    /** {@inheritDoc} */
83    @Override
84    public String toHuman() {
85        throw new RuntimeException("unsupported");
86    }
87
88    /**
89     * Writes annotations for the elements of this list, as
90     * zero-length. This is meant to be used for dumping this instance
91     * directly after a code dump (with the real local list actually
92     * existing elsewhere in the output).
93     *
94     * @param file {@code non-null;} the file to use for referencing other sections
95     * @param out {@code non-null;} where to annotate to
96     * @param prefix {@code null-ok;} prefix to attach to each line of output
97     */
98    public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) {
99        encode(file, prefix, null, out, false);
100    }
101
102    /**
103     * Does a human-friendly dump of this instance.
104     *
105     * @param out {@code non-null;} where to dump
106     * @param prefix {@code non-null;} prefix to attach to each line of output
107     */
108    public void debugPrint(PrintWriter out, String prefix) {
109        encode(null, prefix, out, null, false);
110    }
111
112    /** {@inheritDoc} */
113    @Override
114    protected void writeTo0(DexFile file, AnnotatedOutput out) {
115        if (out.annotates()) {
116            /*
117             * Re-run the encoder to generate the annotations,
118             * but write the bits from the original encode
119             */
120
121            out.annotate(offsetString() + " debug info");
122            encode(file, null, null, out, true);
123        }
124
125        out.write(encoded);
126    }
127
128    /**
129     * Performs debug info encoding.
130     *
131     * @param file {@code null-ok;} file to refer to during encoding
132     * @param prefix {@code null-ok;} prefix to attach to each line of output
133     * @param debugPrint {@code null-ok;} if specified, an alternate output for
134     * annotations
135     * @param out {@code null-ok;} if specified, where annotations should go
136     * @param consume whether to claim to have consumed output for
137     * {@code out}
138     * @return {@code non-null;} the encoded array
139     */
140    private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint,
141            AnnotatedOutput out, boolean consume) {
142        byte[] result = encode0(file, prefix, debugPrint, out, consume);
143
144        if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
145            try {
146                DebugInfoDecoder.validateEncode(result, file, ref, code,
147                        isStatic);
148            } catch (RuntimeException ex) {
149                // Reconvert, annotating to System.err.
150                encode0(file, "", new PrintWriter(System.err, true), null,
151                        false);
152                throw ex;
153            }
154        }
155
156        return result;
157    }
158
159    /**
160     * Helper for {@link #encode} to do most of the work.
161     *
162     * @param file {@code null-ok;} file to refer to during encoding
163     * @param prefix {@code null-ok;} prefix to attach to each line of output
164     * @param debugPrint {@code null-ok;} if specified, an alternate output for
165     * annotations
166     * @param out {@code null-ok;} if specified, where annotations should go
167     * @param consume whether to claim to have consumed output for
168     * {@code out}
169     * @return {@code non-null;} the encoded array
170     */
171    private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint,
172            AnnotatedOutput out, boolean consume) {
173        PositionList positions = code.getPositions();
174        LocalList locals = code.getLocals();
175        DalvInsnList insns = code.getInsns();
176        int codeSize = insns.codeSize();
177        int regSize = insns.getRegistersSize();
178
179        DebugInfoEncoder encoder =
180            new DebugInfoEncoder(positions, locals,
181                    file, codeSize, regSize, isStatic, ref);
182
183        byte[] result;
184
185        if ((debugPrint == null) && (out == null)) {
186            result = encoder.convert();
187        } else {
188            result = encoder.convertAndAnnotate(prefix, debugPrint, out,
189                    consume);
190        }
191
192        return result;
193    }
194}
195