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