android 13 from xiaosuan

This commit is contained in:
cpeng 2025-08-25 08:26:03 +08:00
commit 405905f4e4
1875 changed files with 391581 additions and 0 deletions

21
Android.bp Normal file
View File

@ -0,0 +1,21 @@
package {
default_applicable_licenses: ["dalvik_license"],
}
// Added automatically by a large-scale-change
// See: http://go/android-license-faq
license {
name: "dalvik_license",
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
],
license_text: [
"NOTICE",
],
}
subdirs = [
"dx",
"tools/hprof-conv",
]

57
CleanSpec.mk Normal file
View File

@ -0,0 +1,57 @@
# Copyright (C) 2007 The Android Open Source Project
#
# Licensed 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.
#
# If you don't need to do a full clean build but would like to touch
# a file or delete some intermediate files, add a clean step to the end
# of the list. These steps will only be run once, if they haven't been
# run before.
#
# E.g.:
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
# $(call add-clean-step, rm -rf $(OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
#
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
# files that are missing or have been moved.
#
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
# Use $(OUT) to refer to the "out" directory.
#
# If you need to re-do something that's already mentioned, just copy
# the command and add it to the bottom of the list. E.g., if a change
# that you made last week required touching a file and a change you
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libdvm*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libdex_intermediates/import_includes)

223
NOTICE Normal file
View File

@ -0,0 +1,223 @@
=========================================================================
== NOTICE file corresponding to the section 4 d of ==
== the Apache License, Version 2.0, ==
== in this case for the Android-specific code. ==
=========================================================================
Copyright (c) 2005-2008, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
=========================================================================
== NOTICE file for the x86 JIT libenc subdirectory. ==
=========================================================================
Apache Harmony
Copyright 2006, 2010 The Apache Software Foundation.
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Portions of Harmony were originally developed by
Intel Corporation and are licensed to the Apache Software
Foundation under the "Software Grant and Corporate Contribution
License Agreement" and for which the following copyright notices
apply
(C) Copyright 2005 Intel Corporation
(C) Copyright 2005-2006 Intel Corporation
(C) Copyright 2006 Intel Corporation
Portions of the Apache Portable Runtime used by DRLVM were
developed at the National Center for Supercomputing Applications
(NCSA) at the University of Illinois at Urbana-Champaign.
This software contains code derived from the RSA Data Security
Inc. MD5 Message-Digest Algorithm.
This software contains code derived from UNIX V7, Copyright(C)
Caldera International Inc.

9
OWNERS Normal file
View File

@ -0,0 +1,9 @@
# Studio
gavra@google.com
cmw@google.com
# ART / libcore
calin@google.com
dsrbecky@google.com
narayan@google.com
ngeoffray@google.com
oth@google.com

28
dexgen/Android.bp Normal file
View File

@ -0,0 +1,28 @@
// Copyright (C) 2010 The Android Open Source Project
//
// Licensed 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 {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "dalvik_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["dalvik_license"],
}
java_library {
name: "dexgen",
sdk_version: "4",
srcs: ["src/**/*.java"],
}

3
dexgen/README.txt Normal file
View File

@ -0,0 +1,3 @@
Home of dexgen, the dex code generator project. It provides API for
creating dex classes in runtime which is needed e.g. for class mocking.
This solution is based on the dx tool and uses its classes extensively.

View File

@ -0,0 +1,199 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.rop.cst.*;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.util.ArrayList;
/**
* Pseudo-instruction which holds fill array data.
*/
public final class ArrayData extends VariableSizeInsn {
/**
* {@code non-null;} address representing the instruction that uses this
* instance
*/
private final CodeAddress user;
/** {@code non-null;} initial values to be filled into an array */
private final ArrayList<Constant> values;
/** non-null: type of constant that initializes the array */
private final Constant arrayType;
/** Width of the init value element */
private final int elemWidth;
/** Length of the init list */
private final int initLength;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param user {@code non-null;} address representing the instruction that
* uses this instance
* @param values {@code non-null;} initial values to be filled into an array
*/
public ArrayData(SourcePosition position, CodeAddress user,
ArrayList<Constant> values,
Constant arrayType) {
super(position, RegisterSpecList.EMPTY);
if (user == null) {
throw new NullPointerException("user == null");
}
if (values == null) {
throw new NullPointerException("values == null");
}
int sz = values.size();
if (sz <= 0) {
throw new IllegalArgumentException("Illegal number of init values");
}
this.arrayType = arrayType;
if (arrayType == CstType.BYTE_ARRAY ||
arrayType == CstType.BOOLEAN_ARRAY) {
elemWidth = 1;
} else if (arrayType == CstType.SHORT_ARRAY ||
arrayType == CstType.CHAR_ARRAY) {
elemWidth = 2;
} else if (arrayType == CstType.INT_ARRAY ||
arrayType == CstType.FLOAT_ARRAY) {
elemWidth = 4;
} else if (arrayType == CstType.LONG_ARRAY ||
arrayType == CstType.DOUBLE_ARRAY) {
elemWidth = 8;
} else {
throw new IllegalArgumentException("Unexpected constant type");
}
this.user = user;
this.values = values;
initLength = values.size();
}
/** {@inheritDoc} */
@Override
public int codeSize() {
int sz = initLength;
// Note: the unit here is 16-bit
return 4 + ((sz * elemWidth) + 1) / 2;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out) {
int sz = values.size();
out.writeShort(0x300 | DalvOps.NOP);
out.writeShort(elemWidth);
out.writeInt(initLength);
// For speed reasons, replicate the for loop in each case
switch (elemWidth) {
case 1: {
for (int i = 0; i < sz; i++) {
Constant cst = values.get(i);
out.writeByte((byte) ((CstLiteral32) cst).getIntBits());
}
break;
}
case 2: {
for (int i = 0; i < sz; i++) {
Constant cst = values.get(i);
out.writeShort((short) ((CstLiteral32) cst).getIntBits());
}
break;
}
case 4: {
for (int i = 0; i < sz; i++) {
Constant cst = values.get(i);
out.writeInt(((CstLiteral32) cst).getIntBits());
}
break;
}
case 8: {
for (int i = 0; i < sz; i++) {
Constant cst = values.get(i);
out.writeLong(((CstLiteral64) cst).getLongBits());
}
break;
}
default:
break;
}
// Pad one byte to make the size of data table multiples of 16-bits
if (elemWidth == 1 && (sz % 2 != 0)) {
out.writeByte(0x00);
}
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new ArrayData(getPosition(), user, values, arrayType);
}
/** {@inheritDoc} */
@Override
protected String argString() {
StringBuffer sb = new StringBuffer(100);
int sz = values.size();
for (int i = 0; i < sz; i++) {
sb.append("\n ");
sb.append(i);
sb.append(": ");
sb.append(values.get(i).toHuman());
}
return sb.toString();
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
int baseAddress = user.getAddress();
StringBuffer sb = new StringBuffer(100);
int sz = values.size();
sb.append("array-data // for fill-array-data @ ");
sb.append(Hex.u2(baseAddress));
for (int i = 0; i < sz; i++) {
sb.append("\n ");
sb.append(i);
sb.append(": ");
sb.append(values.get(i).toHuman());
}
return sb.toString();
}
}

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.BasicBlock;
import com.android.dexgen.rop.code.BasicBlockList;
import com.android.dexgen.rop.code.Insn;
import com.android.dexgen.rop.code.RopMethod;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Container for the set of {@link CodeAddress} instances associated with
* the blocks of a particular method. Each block has a corresponding
* start address, end address, and last instruction address.
*/
public final class BlockAddresses {
/** {@code non-null;} array containing addresses for the start of each basic
* block (indexed by basic block label) */
private final CodeAddress[] starts;
/** {@code non-null;} array containing addresses for the final instruction
* of each basic block (indexed by basic block label) */
private final CodeAddress[] lasts;
/** {@code non-null;} array containing addresses for the end (just past the
* final instruction) of each basic block (indexed by basic block
* label) */
private final CodeAddress[] ends;
/**
* Constructs an instance.
*
* @param method {@code non-null;} the method to have block addresses for
*/
public BlockAddresses(RopMethod method) {
BasicBlockList blocks = method.getBlocks();
int maxLabel = blocks.getMaxLabel();
this.starts = new CodeAddress[maxLabel];
this.lasts = new CodeAddress[maxLabel];
this.ends = new CodeAddress[maxLabel];
setupArrays(method);
}
/**
* Gets the instance for the start of the given block.
*
* @param block {@code non-null;} the block in question
* @return {@code non-null;} the appropriate instance
*/
public CodeAddress getStart(BasicBlock block) {
return starts[block.getLabel()];
}
/**
* Gets the instance for the start of the block with the given label.
*
* @param label {@code non-null;} the label of the block in question
* @return {@code non-null;} the appropriate instance
*/
public CodeAddress getStart(int label) {
return starts[label];
}
/**
* Gets the instance for the final instruction of the given block.
*
* @param block {@code non-null;} the block in question
* @return {@code non-null;} the appropriate instance
*/
public CodeAddress getLast(BasicBlock block) {
return lasts[block.getLabel()];
}
/**
* Gets the instance for the final instruction of the block with
* the given label.
*
* @param label {@code non-null;} the label of the block in question
* @return {@code non-null;} the appropriate instance
*/
public CodeAddress getLast(int label) {
return lasts[label];
}
/**
* Gets the instance for the end (address after the final instruction)
* of the given block.
*
* @param block {@code non-null;} the block in question
* @return {@code non-null;} the appropriate instance
*/
public CodeAddress getEnd(BasicBlock block) {
return ends[block.getLabel()];
}
/**
* Gets the instance for the end (address after the final instruction)
* of the block with the given label.
*
* @param label {@code non-null;} the label of the block in question
* @return {@code non-null;} the appropriate instance
*/
public CodeAddress getEnd(int label) {
return ends[label];
}
/**
* Sets up the address arrays.
*/
private void setupArrays(RopMethod method) {
BasicBlockList blocks = method.getBlocks();
int sz = blocks.size();
for (int i = 0; i < sz; i++) {
BasicBlock one = blocks.get(i);
int label = one.getLabel();
Insn insn = one.getInsns().get(0);
starts[label] = new CodeAddress(insn.getPosition());
SourcePosition pos = one.getLastInsn().getPosition();
lasts[label] = new CodeAddress(pos);
ends[label] = new CodeAddress(pos);
}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.type.Type;
import java.util.HashSet;
/**
* Interface for the construction of {@link CatchTable} instances.
*/
public interface CatchBuilder {
/**
* Builds and returns the catch table for this instance.
*
* @return {@code non-null;} the constructed table
*/
public CatchTable build();
/**
* Gets whether this instance has any catches at all (either typed
* or catch-all).
*
* @return whether this instance has any catches at all
*/
public boolean hasAnyCatches();
/**
* Gets the set of catch types associated with this instance.
*
* @return {@code non-null;} the set of catch types
*/
public HashSet<Type> getCatchTypes();
}

View File

@ -0,0 +1,238 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.FixedSizeList;
import com.android.dexgen.util.Hex;
/**
* Ordered list of (exception type, handler address) entries.
*/
public final class CatchHandlerList extends FixedSizeList
implements Comparable<CatchHandlerList> {
/** {@code non-null;} empty instance */
public static final CatchHandlerList EMPTY = new CatchHandlerList(0);
/**
* Constructs an instance. All indices initially contain {@code null}.
*
* @param size {@code >= 0;} the size of the list
*/
public CatchHandlerList(int size) {
super(size);
}
/**
* Gets the element at the given index. It is an error to call
* this with the index for an element which was never set; if you
* do that, this will throw {@code NullPointerException}.
*
* @param n {@code >= 0, < size();} which index
* @return {@code non-null;} element at that index
*/
public Entry get(int n) {
return (Entry) get0(n);
}
/** {@inheritDoc} */
public String toHuman() {
return toHuman("", "");
}
/**
* Get the human form of this instance, prefixed on each line
* with the string.
*
* @param prefix {@code non-null;} the prefix for every line
* @param header {@code non-null;} the header for the first line (after the
* first prefix)
* @return {@code non-null;} the human form
*/
public String toHuman(String prefix, String header) {
StringBuilder sb = new StringBuilder(100);
int size = size();
sb.append(prefix);
sb.append(header);
sb.append("catch ");
for (int i = 0; i < size; i++) {
Entry entry = get(i);
if (i != 0) {
sb.append(",\n");
sb.append(prefix);
sb.append(" ");
}
if ((i == (size - 1)) && catchesAll()) {
sb.append("<any>");
} else {
sb.append(entry.getExceptionType().toHuman());
}
sb.append(" -> ");
sb.append(Hex.u2or4(entry.getHandler()));
}
return sb.toString();
}
/**
* Returns whether or not this instance ends with a "catch-all"
* handler.
*
* @return {@code true} if this instance ends with a "catch-all"
* handler or {@code false} if not
*/
public boolean catchesAll() {
int size = size();
if (size == 0) {
return false;
}
Entry last = get(size - 1);
return last.getExceptionType().equals(CstType.OBJECT);
}
/**
* Sets the entry at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param exceptionType {@code non-null;} type of exception handled
* @param handler {@code >= 0;} exception handler address
*/
public void set(int n, CstType exceptionType, int handler) {
set0(n, new Entry(exceptionType, handler));
}
/**
* Sets the entry at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param entry {@code non-null;} the entry to set at {@code n}
*/
public void set(int n, Entry entry) {
set0(n, entry);
}
/** {@inheritDoc} */
public int compareTo(CatchHandlerList other) {
if (this == other) {
// Easy out.
return 0;
}
int thisSize = size();
int otherSize = other.size();
int checkSize = Math.min(thisSize, otherSize);
for (int i = 0; i < checkSize; i++) {
Entry thisEntry = get(i);
Entry otherEntry = other.get(i);
int compare = thisEntry.compareTo(otherEntry);
if (compare != 0) {
return compare;
}
}
if (thisSize < otherSize) {
return -1;
} else if (thisSize > otherSize) {
return 1;
}
return 0;
}
/**
* Entry in the list.
*/
public static class Entry implements Comparable<Entry> {
/** {@code non-null;} type of exception handled */
private final CstType exceptionType;
/** {@code >= 0;} exception handler address */
private final int handler;
/**
* Constructs an instance.
*
* @param exceptionType {@code non-null;} type of exception handled
* @param handler {@code >= 0;} exception handler address
*/
public Entry(CstType exceptionType, int handler) {
if (handler < 0) {
throw new IllegalArgumentException("handler < 0");
}
if (exceptionType == null) {
throw new NullPointerException("exceptionType == null");
}
this.handler = handler;
this.exceptionType = exceptionType;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return (handler * 31) + exceptionType.hashCode();
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (other instanceof Entry) {
return (compareTo((Entry) other) == 0);
}
return false;
}
/** {@inheritDoc} */
public int compareTo(Entry other) {
if (handler < other.handler) {
return -1;
} else if (handler > other.handler) {
return 1;
}
return exceptionType.compareTo(other.exceptionType);
}
/**
* Gets the exception type handled.
*
* @return {@code non-null;} the exception type
*/
public CstType getExceptionType() {
return exceptionType;
}
/**
* Gets the handler address.
*
* @return {@code >= 0;} the handler address
*/
public int getHandler() {
return handler;
}
}
}

View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.FixedSizeList;
/**
* Table of catch entries. Each entry includes a range of code
* addresses for which it is valid and an associated {@link
* CatchHandlerList}.
*/
public final class CatchTable extends FixedSizeList
implements Comparable<CatchTable> {
/** {@code non-null;} empty instance */
public static final CatchTable EMPTY = new CatchTable(0);
/**
* Constructs an instance. All indices initially contain {@code null}.
*
* @param size {@code >= 0;} the size of the table
*/
public CatchTable(int size) {
super(size);
}
/**
* Gets the element at the given index. It is an error to call
* this with the index for an element which was never set; if you
* do that, this will throw {@code NullPointerException}.
*
* @param n {@code >= 0, < size();} which index
* @return {@code non-null;} element at that index
*/
public Entry get(int n) {
return (Entry) get0(n);
}
/**
* Sets the entry at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param entry {@code non-null;} the entry to set at {@code n}
*/
public void set(int n, Entry entry) {
set0(n, entry);
}
/** {@inheritDoc} */
public int compareTo(CatchTable other) {
if (this == other) {
// Easy out.
return 0;
}
int thisSize = size();
int otherSize = other.size();
int checkSize = Math.min(thisSize, otherSize);
for (int i = 0; i < checkSize; i++) {
Entry thisEntry = get(i);
Entry otherEntry = other.get(i);
int compare = thisEntry.compareTo(otherEntry);
if (compare != 0) {
return compare;
}
}
if (thisSize < otherSize) {
return -1;
} else if (thisSize > otherSize) {
return 1;
}
return 0;
}
/**
* Entry in a catch list.
*/
public static class Entry implements Comparable<Entry> {
/** {@code >= 0;} start address */
private final int start;
/** {@code > start;} end address (exclusive) */
private final int end;
/** {@code non-null;} list of catch handlers */
private final CatchHandlerList handlers;
/**
* Constructs an instance.
*
* @param start {@code >= 0;} start address
* @param end {@code > start;} end address (exclusive)
* @param handlers {@code non-null;} list of catch handlers
*/
public Entry(int start, int end, CatchHandlerList handlers) {
if (start < 0) {
throw new IllegalArgumentException("start < 0");
}
if (end <= start) {
throw new IllegalArgumentException("end <= start");
}
if (handlers.isMutable()) {
throw new IllegalArgumentException("handlers.isMutable()");
}
this.start = start;
this.end = end;
this.handlers = handlers;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int hash = (start * 31) + end;
hash = (hash * 31) + handlers.hashCode();
return hash;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (other instanceof Entry) {
return (compareTo((Entry) other) == 0);
}
return false;
}
/** {@inheritDoc} */
public int compareTo(Entry other) {
if (start < other.start) {
return -1;
} else if (start > other.start) {
return 1;
}
if (end < other.end) {
return -1;
} else if (end > other.end) {
return 1;
}
return handlers.compareTo(other.handlers);
}
/**
* Gets the start address.
*
* @return {@code >= 0;} the start address
*/
public int getStart() {
return start;
}
/**
* Gets the end address (exclusive).
*
* @return {@code > start;} the end address (exclusive)
*/
public int getEnd() {
return end;
}
/**
* Gets the handlers.
*
* @return {@code non-null;} the handlers
*/
public CatchHandlerList getHandlers() {
return handlers;
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Pseudo-instruction which is used to track an address within a code
* array. Instances are used for such things as branch targets and
* exception handler ranges. Its code size is zero, and so instances
* do not in general directly wind up in any output (either
* human-oriented or binary file).
*/
public final class CodeAddress extends ZeroSizeInsn {
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
*/
public CodeAddress(SourcePosition position) {
super(position);
}
/** {@inheritDoc} */
@Override
public final DalvInsn withRegisters(RegisterSpecList registers) {
return new CodeAddress(getPosition());
}
/** {@inheritDoc} */
@Override
protected String argString() {
return null;
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
return "code-address";
}
}

View File

@ -0,0 +1,205 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.rop.cst.Constant;
/**
* Instruction which has a single constant argument in addition
* to all the normal instruction information.
*/
public final class CstInsn extends FixedSizeInsn {
/** {@code non-null;} the constant argument for this instruction */
private final Constant constant;
/**
* {@code >= -1;} the constant pool index for {@link #constant}, or
* {@code -1} if not yet set
*/
private int index;
/**
* {@code >= -1;} the constant pool index for the class reference in
* {@link #constant} if any, or {@code -1} if not yet set
*/
private int classIndex;
/**
* Constructs an instance. The output address of this instance is
* initially unknown ({@code -1}) as is the constant pool index.
*
* @param opcode the opcode; one of the constants from {@link Dops}
* @param position {@code non-null;} source position
* @param registers {@code non-null;} register list, including a
* result register if appropriate (that is, registers may be either
* ins or outs)
* @param constant {@code non-null;} constant argument
*/
public CstInsn(Dop opcode, SourcePosition position,
RegisterSpecList registers, Constant constant) {
super(opcode, position, registers);
if (constant == null) {
throw new NullPointerException("constant == null");
}
this.constant = constant;
this.index = -1;
this.classIndex = -1;
}
/** {@inheritDoc} */
@Override
public DalvInsn withOpcode(Dop opcode) {
CstInsn result =
new CstInsn(opcode, getPosition(), getRegisters(), constant);
if (index >= 0) {
result.setIndex(index);
}
if (classIndex >= 0) {
result.setClassIndex(classIndex);
}
return result;
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
CstInsn result =
new CstInsn(getOpcode(), getPosition(), registers, constant);
if (index >= 0) {
result.setIndex(index);
}
if (classIndex >= 0) {
result.setClassIndex(classIndex);
}
return result;
}
/**
* Gets the constant argument.
*
* @return {@code non-null;} the constant argument
*/
public Constant getConstant() {
return constant;
}
/**
* Gets the constant's index. It is only valid to call this after
* {@link #setIndex} has been called.
*
* @return {@code >= 0;} the constant pool index
*/
public int getIndex() {
if (index < 0) {
throw new RuntimeException("index not yet set for " + constant);
}
return index;
}
/**
* Returns whether the constant's index has been set for this instance.
*
* @see #setIndex
*
* @return {@code true} iff the index has been set
*/
public boolean hasIndex() {
return (index >= 0);
}
/**
* Sets the constant's index. It is only valid to call this method once
* per instance.
*
* @param index {@code >= 0;} the constant pool index
*/
public void setIndex(int index) {
if (index < 0) {
throw new IllegalArgumentException("index < 0");
}
if (this.index >= 0) {
throw new RuntimeException("index already set");
}
this.index = index;
}
/**
* Gets the constant's class index. It is only valid to call this after
* {@link #setClassIndex} has been called.
*
* @return {@code >= 0;} the constant's class's constant pool index
*/
public int getClassIndex() {
if (classIndex < 0) {
throw new RuntimeException("class index not yet set");
}
return classIndex;
}
/**
* Returns whether the constant's class index has been set for this
* instance.
*
* @see #setClassIndex
*
* @return {@code true} iff the index has been set
*/
public boolean hasClassIndex() {
return (classIndex >= 0);
}
/**
* Sets the constant's class index. This is the constant pool index
* for the class referred to by this instance's constant. Only
* reference constants have a class, so it is only on instances
* with reference constants that this method should ever be
* called. It is only valid to call this method once per instance.
*
* @param index {@code >= 0;} the constant's class's constant pool index
*/
public void setClassIndex(int index) {
if (index < 0) {
throw new IllegalArgumentException("index < 0");
}
if (this.classIndex >= 0) {
throw new RuntimeException("class index already set");
}
this.classIndex = index;
}
/** {@inheritDoc} */
@Override
protected String argString() {
return constant.toHuman();
}
}

View File

@ -0,0 +1,232 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.type.Type;
import java.util.HashSet;
/**
* Container for all the pieces of a concrete method. Each instance
* corresponds to a {@code code} structure in a {@code .dex} file.
*/
public final class DalvCode {
/**
* how much position info to preserve; one of the static
* constants in {@link PositionList}
*/
private final int positionInfo;
/**
* {@code null-ok;} the instruction list, ready for final processing;
* nulled out in {@link #finishProcessingIfNecessary}
*/
private OutputFinisher unprocessedInsns;
/**
* {@code non-null;} unprocessed catch table;
* nulled out in {@link #finishProcessingIfNecessary}
*/
private CatchBuilder unprocessedCatches;
/**
* {@code null-ok;} catch table; set in
* {@link #finishProcessingIfNecessary}
*/
private CatchTable catches;
/**
* {@code null-ok;} source positions list; set in
* {@link #finishProcessingIfNecessary}
*/
private PositionList positions;
/**
* {@code null-ok;} local variable list; set in
* {@link #finishProcessingIfNecessary}
*/
private LocalList locals;
/**
* {@code null-ok;} the processed instruction list; set in
* {@link #finishProcessingIfNecessary}
*/
private DalvInsnList insns;
/**
* Constructs an instance.
*
* @param positionInfo how much position info to preserve; one of the
* static constants in {@link PositionList}
* @param unprocessedInsns {@code non-null;} the instruction list, ready
* for final processing
* @param unprocessedCatches {@code non-null;} unprocessed catch
* (exception handler) table
*/
public DalvCode(int positionInfo, OutputFinisher unprocessedInsns,
CatchBuilder unprocessedCatches) {
if (unprocessedInsns == null) {
throw new NullPointerException("unprocessedInsns == null");
}
if (unprocessedCatches == null) {
throw new NullPointerException("unprocessedCatches == null");
}
this.positionInfo = positionInfo;
this.unprocessedInsns = unprocessedInsns;
this.unprocessedCatches = unprocessedCatches;
this.catches = null;
this.positions = null;
this.locals = null;
this.insns = null;
}
/**
* Finish up processing of the method.
*/
private void finishProcessingIfNecessary() {
if (insns != null) {
return;
}
insns = unprocessedInsns.finishProcessingAndGetList();
positions = PositionList.make(insns, positionInfo);
locals = LocalList.make(insns);
catches = unprocessedCatches.build();
// Let them be gc'ed.
unprocessedInsns = null;
unprocessedCatches = null;
}
/**
* Assign indices in all instructions that need them, using the
* given callback to perform lookups. This must be called before
* {@link #getInsns}.
*
* @param callback {@code non-null;} callback object
*/
public void assignIndices(AssignIndicesCallback callback) {
unprocessedInsns.assignIndices(callback);
}
/**
* Gets whether this instance has any position data to represent.
*
* @return {@code true} iff this instance has any position
* data to represent
*/
public boolean hasPositions() {
return (positionInfo != PositionList.NONE)
&& unprocessedInsns.hasAnyPositionInfo();
}
/**
* Gets whether this instance has any local variable data to represent.
*
* @return {@code true} iff this instance has any local variable
* data to represent
*/
public boolean hasLocals() {
return unprocessedInsns.hasAnyLocalInfo();
}
/**
* Gets whether this instance has any catches at all (either typed
* or catch-all).
*
* @return whether this instance has any catches at all
*/
public boolean hasAnyCatches() {
return unprocessedCatches.hasAnyCatches();
}
/**
* Gets the set of catch types handled anywhere in the code.
*
* @return {@code non-null;} the set of catch types
*/
public HashSet<Type> getCatchTypes() {
return unprocessedCatches.getCatchTypes();
}
/**
* Gets the set of all constants referred to by instructions in
* the code.
*
* @return {@code non-null;} the set of constants
*/
public HashSet<Constant> getInsnConstants() {
return unprocessedInsns.getAllConstants();
}
/**
* Gets the list of instructions.
*
* @return {@code non-null;} the instruction list
*/
public DalvInsnList getInsns() {
finishProcessingIfNecessary();
return insns;
}
/**
* Gets the catch (exception handler) table.
*
* @return {@code non-null;} the catch table
*/
public CatchTable getCatches() {
finishProcessingIfNecessary();
return catches;
}
/**
* Gets the source positions list.
*
* @return {@code non-null;} the source positions list
*/
public PositionList getPositions() {
finishProcessingIfNecessary();
return positions;
}
/**
* Gets the source positions list.
*
* @return {@code non-null;} the source positions list
*/
public LocalList getLocals() {
finishProcessingIfNecessary();
return locals;
}
/**
* Class used as a callback for {@link #assignIndices}.
*/
public static interface AssignIndicesCallback {
/**
* Gets the index for the given constant.
*
* @param cst {@code non-null;} the constant
* @return {@code >= -1;} the index or {@code -1} if the constant
* shouldn't actually be reified with an index
*/
public int getIndex(Constant cst);
}
}

View File

@ -0,0 +1,422 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.TwoColumnOutput;
/**
* Base class for Dalvik instructions.
*/
public abstract class DalvInsn {
/**
* the actual output address of this instance, if known, or
* {@code -1} if not
*/
private int address;
/** the opcode; one of the constants from {@link Dops} */
private final Dop opcode;
/** {@code non-null;} source position */
private final SourcePosition position;
/** {@code non-null;} list of register arguments */
private final RegisterSpecList registers;
/**
* Makes a move instruction, appropriate and ideal for the given arguments.
*
* @param position {@code non-null;} source position information
* @param dest {@code non-null;} destination register
* @param src {@code non-null;} source register
* @return {@code non-null;} an appropriately-constructed instance
*/
public static SimpleInsn makeMove(SourcePosition position,
RegisterSpec dest, RegisterSpec src) {
boolean category1 = dest.getCategory() == 1;
boolean reference = dest.getType().isReference();
int destReg = dest.getReg();
int srcReg = src.getReg();
Dop opcode;
if ((srcReg | destReg) < 16) {
opcode = reference ? Dops.MOVE_OBJECT :
(category1 ? Dops.MOVE : Dops.MOVE_WIDE);
} else if (destReg < 256) {
opcode = reference ? Dops.MOVE_OBJECT_FROM16 :
(category1 ? Dops.MOVE_FROM16 : Dops.MOVE_WIDE_FROM16);
} else {
opcode = reference ? Dops.MOVE_OBJECT_16 :
(category1 ? Dops.MOVE_16 : Dops.MOVE_WIDE_16);
}
return new SimpleInsn(opcode, position,
RegisterSpecList.make(dest, src));
}
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* <p><b>Note:</b> In the unlikely event that an instruction takes
* absolutely no registers (e.g., a {@code nop} or a
* no-argument no-result static method call), then the given
* register list may be passed as {@link
* RegisterSpecList#EMPTY}.</p>
*
* @param opcode the opcode; one of the constants from {@link Dops}
* @param position {@code non-null;} source position
* @param registers {@code non-null;} register list, including a
* result register if appropriate (that is, registers may be either
* ins and outs)
*/
public DalvInsn(Dop opcode, SourcePosition position,
RegisterSpecList registers) {
if (opcode == null) {
throw new NullPointerException("opcode == null");
}
if (position == null) {
throw new NullPointerException("position == null");
}
if (registers == null) {
throw new NullPointerException("registers == null");
}
this.address = -1;
this.opcode = opcode;
this.position = position;
this.registers = registers;
}
/** {@inheritDoc} */
@Override
public final String toString() {
StringBuffer sb = new StringBuffer(100);
sb.append(identifierString());
sb.append(' ');
sb.append(position);
sb.append(": ");
sb.append(opcode.getName());
boolean needComma = false;
if (registers.size() != 0) {
sb.append(registers.toHuman(" ", ", ", null));
needComma = true;
}
String extra = argString();
if (extra != null) {
if (needComma) {
sb.append(',');
}
sb.append(' ');
sb.append(extra);
}
return sb.toString();
}
/**
* Gets whether the address of this instruction is known.
*
* @see #getAddress
* @see #setAddress
*/
public final boolean hasAddress() {
return (address >= 0);
}
/**
* Gets the output address of this instruction, if it is known. This throws
* a {@code RuntimeException} if it has not yet been set.
*
* @see #setAddress
*
* @return {@code >= 0;} the output address
*/
public final int getAddress() {
if (address < 0) {
throw new RuntimeException("address not yet known");
}
return address;
}
/**
* Gets the opcode.
*
* @return {@code non-null;} the opcode
*/
public final Dop getOpcode() {
return opcode;
}
/**
* Gets the source position.
*
* @return {@code non-null;} the source position
*/
public final SourcePosition getPosition() {
return position;
}
/**
* Gets the register list for this instruction.
*
* @return {@code non-null;} the registers
*/
public final RegisterSpecList getRegisters() {
return registers;
}
/**
* Returns whether this instance's opcode uses a result register.
* This method is a convenient shorthand for
* {@code getOpcode().hasResult()}.
*
* @return {@code true} iff this opcode uses a result register
*/
public final boolean hasResult() {
return opcode.hasResult();
}
/**
* Gets the minimum distinct registers required for this instruction.
* This assumes that the result (if any) can share registers with the
* sources (if any), that each source register is unique, and that
* (to be explicit here) category-2 values take up two consecutive
* registers.
*
* @return {@code >= 0;} the minimum distinct register requirement
*/
public final int getMinimumRegisterRequirement() {
boolean hasResult = hasResult();
int regSz = registers.size();
int resultRequirement = hasResult ? registers.get(0).getCategory() : 0;
int sourceRequirement = 0;
for (int i = hasResult ? 1 : 0; i < regSz; i++) {
sourceRequirement += registers.get(i).getCategory();
}
return Math.max(sourceRequirement, resultRequirement);
}
/**
* Gets the instruction prefix required, if any, to use in a high
* register transformed version of this instance.
*
* @see #hrVersion
*
* @return {@code null-ok;} the prefix, if any
*/
public DalvInsn hrPrefix() {
RegisterSpecList regs = registers;
int sz = regs.size();
if (hasResult()) {
if (sz == 1) {
return null;
}
regs = regs.withoutFirst();
} else if (sz == 0) {
return null;
}
return new HighRegisterPrefix(position, regs);
}
/**
* Gets the instruction suffix required, if any, to use in a high
* register transformed version of this instance.
*
* @see #hrVersion
*
* @return {@code null-ok;} the suffix, if any
*/
public DalvInsn hrSuffix() {
if (hasResult()) {
RegisterSpec r = registers.get(0);
return makeMove(position, r, r.withReg(0));
} else {
return null;
}
}
/**
* Gets the instruction that is equivalent to this one, except that
* uses sequential registers starting at {@code 0} (storing
* the result, if any, in register {@code 0} as well). The
* sequence of instructions from {@link #hrPrefix} and {@link
* #hrSuffix} (if non-null) surrounding the result of a call to
* this method are the high register transformation of this
* instance, and it is guaranteed that the number of low registers
* used will be the number returned by {@link
* #getMinimumRegisterRequirement}.
*
* @return {@code non-null;} the replacement
*/
public DalvInsn hrVersion() {
RegisterSpecList regs =
registers.withSequentialRegisters(0, hasResult());
return withRegisters(regs);
}
/**
* Gets the short identifier for this instruction. This is its
* address, if assigned, or its identity hashcode if not.
*
* @return {@code non-null;} the identifier
*/
public final String identifierString() {
if (address != -1) {
return String.format("%04x", address);
}
return Hex.u4(System.identityHashCode(this));
}
/**
* Returns the string form of this instance suitable for inclusion in
* a human-oriented listing dump. This method will return {@code null}
* if this instance should not appear in a listing.
*
* @param prefix {@code non-null;} prefix before the address; each follow-on
* line will be indented to match as well
* @param width {@code >= 0;} the width of the output or {@code 0} for
* unlimited width
* @param noteIndices whether to include an explicit notation of
* constant pool indices
* @return {@code null-ok;} the string form or {@code null} if this
* instance should not appear in a listing
*/
public final String listingString(String prefix, int width,
boolean noteIndices) {
String insnPerSe = listingString0(noteIndices);
if (insnPerSe == null) {
return null;
}
String addr = prefix + identifierString() + ": ";
int w1 = addr.length();
int w2 = (width == 0) ? insnPerSe.length() : (width - w1);
return TwoColumnOutput.toString(addr, w1, "", insnPerSe, w2);
}
/**
* Sets the output address.
*
* @param address {@code >= 0;} the output address
*/
public final void setAddress(int address) {
if (address < 0) {
throw new IllegalArgumentException("address < 0");
}
this.address = address;
}
/**
* Gets the address immediately after this instance. This is only
* calculable if this instance's address is known, and it is equal
* to the address plus the length of the instruction format of this
* instance's opcode.
*
* @return {@code >= 0;} the next address
*/
public final int getNextAddress() {
return getAddress() + codeSize();
}
/**
* Gets the size of this instruction, in 16-bit code units.
*
* @return {@code >= 0;} the code size of this instruction
*/
public abstract int codeSize();
/**
* Writes this instance to the given output. This method should
* never annotate the output.
*
* @param out {@code non-null;} where to write to
*/
public abstract void writeTo(AnnotatedOutput out);
/**
* Returns an instance that is just like this one, except that its
* opcode is replaced by the one given, and its address is reset.
*
* @param opcode {@code non-null;} the new opcode
* @return {@code non-null;} an appropriately-constructed instance
*/
public abstract DalvInsn withOpcode(Dop opcode);
/**
* Returns an instance that is just like this one, except that all
* register references have been offset by the given delta, and its
* address is reset.
*
* @param delta the amount to offset register references by
* @return {@code non-null;} an appropriately-constructed instance
*/
public abstract DalvInsn withRegisterOffset(int delta);
/**
* Returns an instance that is just like this one, except that the
* register list is replaced by the given one, and its address is
* reset.
*
* @param registers {@code non-null;} new register list
* @return {@code non-null;} an appropriately-constructed instance
*/
public abstract DalvInsn withRegisters(RegisterSpecList registers);
/**
* Gets the string form for any arguments to this instance. Subclasses
* must override this.
*
* @return {@code null-ok;} the string version of any arguments or
* {@code null} if there are none
*/
protected abstract String argString();
/**
* Helper for {@link #listingString}, which returns the string
* form of this instance suitable for inclusion in a
* human-oriented listing dump, not including the instruction
* address and without respect for any output formatting. This
* method should return {@code null} if this instance should
* not appear in a listing.
*
* @param noteIndices whether to include an explicit notation of
* constant pool indices
* @return {@code null-ok;} the listing string
*/
protected abstract String listingString0(boolean noteIndices);
}

View File

@ -0,0 +1,268 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstBaseMethodRef;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ExceptionWithContext;
import com.android.dexgen.util.FixedSizeList;
import com.android.dexgen.util.IndentingWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
/**
* List of {@link DalvInsn} instances.
*/
public final class DalvInsnList extends FixedSizeList {
/**
* The amount of register space, in register units, required for this
* code block. This may be greater than the largest observed register+
* category because the method this code block exists in may
* specify arguments that are unused by the method.
*/
private final int regCount;
/**
* Constructs and returns an immutable instance whose elements are
* identical to the ones in the given list, in the same order.
*
* @param list {@code non-null;} the list to use for elements
* @param regCount count, in register-units, of the number of registers
* this code block requires.
* @return {@code non-null;} an appropriately-constructed instance of this
* class
*/
public static DalvInsnList makeImmutable(ArrayList<DalvInsn> list,
int regCount) {
int size = list.size();
DalvInsnList result = new DalvInsnList(size, regCount);
for (int i = 0; i < size; i++) {
result.set(i, list.get(i));
}
result.setImmutable();
return result;
}
/**
* Constructs an instance. All indices initially contain {@code null}.
*
* @param size the size of the list
*/
public DalvInsnList(int size, int regCount) {
super(size);
this.regCount = regCount;
}
/**
* Gets the element at the given index. It is an error to call
* this with the index for an element which was never set; if you
* do that, this will throw {@code NullPointerException}.
*
* @param n {@code >= 0, < size();} which index
* @return {@code non-null;} element at that index
*/
public DalvInsn get(int n) {
return (DalvInsn) get0(n);
}
/**
* Sets the instruction at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param insn {@code non-null;} the instruction to set at {@code n}
*/
public void set(int n, DalvInsn insn) {
set0(n, insn);
}
/**
* Gets the size of this instance, in 16-bit code units. This will only
* return a meaningful result if the instructions in this instance all
* have valid addresses.
*
* @return {@code >= 0;} the size
*/
public int codeSize() {
int sz = size();
if (sz == 0) {
return 0;
}
DalvInsn last = get(sz - 1);
return last.getNextAddress();
}
/**
* Writes all the instructions in this instance to the given output
* destination.
*
* @param out {@code non-null;} where to write to
*/
public void writeTo(AnnotatedOutput out) {
int startCursor = out.getCursor();
int sz = size();
if (out.annotates()) {
boolean verbose = out.isVerbose();
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
int codeBytes = insn.codeSize() * 2;
String s;
if ((codeBytes != 0) || verbose) {
s = insn.listingString(" ", out.getAnnotationWidth(),
true);
} else {
s = null;
}
if (s != null) {
out.annotate(codeBytes, s);
} else if (codeBytes != 0) {
out.annotate(codeBytes, "");
}
}
}
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
try {
insn.writeTo(out);
} catch (RuntimeException ex) {
throw ExceptionWithContext.withContext(ex,
"...while writing " + insn);
}
}
// Check the amount written.
int written = (out.getCursor() - startCursor) / 2;
if (written != codeSize()) {
throw new RuntimeException("write length mismatch; expected " +
codeSize() + " but actually wrote " + written);
}
}
/**
* Gets the minimum required register count implied by this
* instance. This includes any unused parameters that could
* potentially be at the top of the register space.
* @return {@code >= 0;} the required registers size
*/
public int getRegistersSize() {
return regCount;
}
/**
* Gets the size of the outgoing arguments area required by this
* method. This is equal to the largest argument word count of any
* method referred to by this instance.
*
* @return {@code >= 0;} the required outgoing arguments size
*/
public int getOutsSize() {
int sz = size();
int result = 0;
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
if (!(insn instanceof CstInsn)) {
continue;
}
Constant cst = ((CstInsn) insn).getConstant();
if (!(cst instanceof CstBaseMethodRef)) {
continue;
}
boolean isStatic =
(insn.getOpcode().getFamily() == DalvOps.INVOKE_STATIC);
int count =
((CstBaseMethodRef) cst).getParameterWordCount(isStatic);
if (count > result) {
result = count;
}
}
return result;
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
* @param verbose whether to be verbose; verbose output includes
* lines for zero-size instructions and explicit constant pool indices
*/
public void debugPrint(Writer out, String prefix, boolean verbose) {
IndentingWriter iw = new IndentingWriter(out, 0, prefix);
int sz = size();
try {
for (int i = 0; i < sz; i++) {
DalvInsn insn = (DalvInsn) get0(i);
String s;
if ((insn.codeSize() != 0) || verbose) {
s = insn.listingString("", 0, verbose);
} else {
s = null;
}
if (s != null) {
iw.write(s);
}
}
iw.flush();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
* @param verbose whether to be verbose; verbose output includes
* lines for zero-size instructions
*/
public void debugPrint(OutputStream out, String prefix, boolean verbose) {
Writer w = new OutputStreamWriter(out);
debugPrint(w, prefix, verbose);
try {
w.flush();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
/**
* All the Dalvik opcode value constants. See the related spec
* document for the meaning and instruction format of each opcode.
*/
public final class DalvOps {
/** pseudo-opcode used for nonstandard format "instructions" */
public static final int SPECIAL_FORMAT = -1;
/** minimum valid opcode value */
public static final int MIN_VALUE = -1;
/** maximum valid opcode value */
public static final int MAX_VALUE = 0xff;
// BEGIN(opcodes); GENERATED AUTOMATICALLY BY opcode-gen
public static final int NOP = 0x00;
public static final int MOVE = 0x01;
public static final int MOVE_FROM16 = 0x02;
public static final int MOVE_16 = 0x03;
public static final int MOVE_WIDE = 0x04;
public static final int MOVE_WIDE_FROM16 = 0x05;
public static final int MOVE_WIDE_16 = 0x06;
public static final int MOVE_OBJECT = 0x07;
public static final int MOVE_OBJECT_FROM16 = 0x08;
public static final int MOVE_OBJECT_16 = 0x09;
public static final int MOVE_RESULT = 0x0a;
public static final int MOVE_RESULT_WIDE = 0x0b;
public static final int MOVE_RESULT_OBJECT = 0x0c;
public static final int MOVE_EXCEPTION = 0x0d;
public static final int RETURN_VOID = 0x0e;
public static final int RETURN = 0x0f;
public static final int RETURN_WIDE = 0x10;
public static final int RETURN_OBJECT = 0x11;
public static final int CONST_4 = 0x12;
public static final int CONST_16 = 0x13;
public static final int CONST = 0x14;
public static final int CONST_HIGH16 = 0x15;
public static final int CONST_WIDE_16 = 0x16;
public static final int CONST_WIDE_32 = 0x17;
public static final int CONST_WIDE = 0x18;
public static final int CONST_WIDE_HIGH16 = 0x19;
public static final int CONST_STRING = 0x1a;
public static final int CONST_STRING_JUMBO = 0x1b;
public static final int CONST_CLASS = 0x1c;
public static final int MONITOR_ENTER = 0x1d;
public static final int MONITOR_EXIT = 0x1e;
public static final int CHECK_CAST = 0x1f;
public static final int INSTANCE_OF = 0x20;
public static final int ARRAY_LENGTH = 0x21;
public static final int NEW_INSTANCE = 0x22;
public static final int NEW_ARRAY = 0x23;
public static final int FILLED_NEW_ARRAY = 0x24;
public static final int FILLED_NEW_ARRAY_RANGE = 0x25;
public static final int FILL_ARRAY_DATA = 0x26;
public static final int THROW = 0x27;
public static final int GOTO = 0x28;
public static final int GOTO_16 = 0x29;
public static final int GOTO_32 = 0x2a;
public static final int PACKED_SWITCH = 0x2b;
public static final int SPARSE_SWITCH = 0x2c;
public static final int CMPL_FLOAT = 0x2d;
public static final int CMPG_FLOAT = 0x2e;
public static final int CMPL_DOUBLE = 0x2f;
public static final int CMPG_DOUBLE = 0x30;
public static final int CMP_LONG = 0x31;
public static final int IF_EQ = 0x32;
public static final int IF_NE = 0x33;
public static final int IF_LT = 0x34;
public static final int IF_GE = 0x35;
public static final int IF_GT = 0x36;
public static final int IF_LE = 0x37;
public static final int IF_EQZ = 0x38;
public static final int IF_NEZ = 0x39;
public static final int IF_LTZ = 0x3a;
public static final int IF_GEZ = 0x3b;
public static final int IF_GTZ = 0x3c;
public static final int IF_LEZ = 0x3d;
public static final int UNUSED_3E = 0x3e;
public static final int UNUSED_3F = 0x3f;
public static final int UNUSED_40 = 0x40;
public static final int UNUSED_41 = 0x41;
public static final int UNUSED_42 = 0x42;
public static final int UNUSED_43 = 0x43;
public static final int AGET = 0x44;
public static final int AGET_WIDE = 0x45;
public static final int AGET_OBJECT = 0x46;
public static final int AGET_BOOLEAN = 0x47;
public static final int AGET_BYTE = 0x48;
public static final int AGET_CHAR = 0x49;
public static final int AGET_SHORT = 0x4a;
public static final int APUT = 0x4b;
public static final int APUT_WIDE = 0x4c;
public static final int APUT_OBJECT = 0x4d;
public static final int APUT_BOOLEAN = 0x4e;
public static final int APUT_BYTE = 0x4f;
public static final int APUT_CHAR = 0x50;
public static final int APUT_SHORT = 0x51;
public static final int IGET = 0x52;
public static final int IGET_WIDE = 0x53;
public static final int IGET_OBJECT = 0x54;
public static final int IGET_BOOLEAN = 0x55;
public static final int IGET_BYTE = 0x56;
public static final int IGET_CHAR = 0x57;
public static final int IGET_SHORT = 0x58;
public static final int IPUT = 0x59;
public static final int IPUT_WIDE = 0x5a;
public static final int IPUT_OBJECT = 0x5b;
public static final int IPUT_BOOLEAN = 0x5c;
public static final int IPUT_BYTE = 0x5d;
public static final int IPUT_CHAR = 0x5e;
public static final int IPUT_SHORT = 0x5f;
public static final int SGET = 0x60;
public static final int SGET_WIDE = 0x61;
public static final int SGET_OBJECT = 0x62;
public static final int SGET_BOOLEAN = 0x63;
public static final int SGET_BYTE = 0x64;
public static final int SGET_CHAR = 0x65;
public static final int SGET_SHORT = 0x66;
public static final int SPUT = 0x67;
public static final int SPUT_WIDE = 0x68;
public static final int SPUT_OBJECT = 0x69;
public static final int SPUT_BOOLEAN = 0x6a;
public static final int SPUT_BYTE = 0x6b;
public static final int SPUT_CHAR = 0x6c;
public static final int SPUT_SHORT = 0x6d;
public static final int INVOKE_VIRTUAL = 0x6e;
public static final int INVOKE_SUPER = 0x6f;
public static final int INVOKE_DIRECT = 0x70;
public static final int INVOKE_STATIC = 0x71;
public static final int INVOKE_INTERFACE = 0x72;
public static final int UNUSED_73 = 0x73;
public static final int INVOKE_VIRTUAL_RANGE = 0x74;
public static final int INVOKE_SUPER_RANGE = 0x75;
public static final int INVOKE_DIRECT_RANGE = 0x76;
public static final int INVOKE_STATIC_RANGE = 0x77;
public static final int INVOKE_INTERFACE_RANGE = 0x78;
public static final int UNUSED_79 = 0x79;
public static final int UNUSED_7A = 0x7a;
public static final int NEG_INT = 0x7b;
public static final int NOT_INT = 0x7c;
public static final int NEG_LONG = 0x7d;
public static final int NOT_LONG = 0x7e;
public static final int NEG_FLOAT = 0x7f;
public static final int NEG_DOUBLE = 0x80;
public static final int INT_TO_LONG = 0x81;
public static final int INT_TO_FLOAT = 0x82;
public static final int INT_TO_DOUBLE = 0x83;
public static final int LONG_TO_INT = 0x84;
public static final int LONG_TO_FLOAT = 0x85;
public static final int LONG_TO_DOUBLE = 0x86;
public static final int FLOAT_TO_INT = 0x87;
public static final int FLOAT_TO_LONG = 0x88;
public static final int FLOAT_TO_DOUBLE = 0x89;
public static final int DOUBLE_TO_INT = 0x8a;
public static final int DOUBLE_TO_LONG = 0x8b;
public static final int DOUBLE_TO_FLOAT = 0x8c;
public static final int INT_TO_BYTE = 0x8d;
public static final int INT_TO_CHAR = 0x8e;
public static final int INT_TO_SHORT = 0x8f;
public static final int ADD_INT = 0x90;
public static final int SUB_INT = 0x91;
public static final int MUL_INT = 0x92;
public static final int DIV_INT = 0x93;
public static final int REM_INT = 0x94;
public static final int AND_INT = 0x95;
public static final int OR_INT = 0x96;
public static final int XOR_INT = 0x97;
public static final int SHL_INT = 0x98;
public static final int SHR_INT = 0x99;
public static final int USHR_INT = 0x9a;
public static final int ADD_LONG = 0x9b;
public static final int SUB_LONG = 0x9c;
public static final int MUL_LONG = 0x9d;
public static final int DIV_LONG = 0x9e;
public static final int REM_LONG = 0x9f;
public static final int AND_LONG = 0xa0;
public static final int OR_LONG = 0xa1;
public static final int XOR_LONG = 0xa2;
public static final int SHL_LONG = 0xa3;
public static final int SHR_LONG = 0xa4;
public static final int USHR_LONG = 0xa5;
public static final int ADD_FLOAT = 0xa6;
public static final int SUB_FLOAT = 0xa7;
public static final int MUL_FLOAT = 0xa8;
public static final int DIV_FLOAT = 0xa9;
public static final int REM_FLOAT = 0xaa;
public static final int ADD_DOUBLE = 0xab;
public static final int SUB_DOUBLE = 0xac;
public static final int MUL_DOUBLE = 0xad;
public static final int DIV_DOUBLE = 0xae;
public static final int REM_DOUBLE = 0xaf;
public static final int ADD_INT_2ADDR = 0xb0;
public static final int SUB_INT_2ADDR = 0xb1;
public static final int MUL_INT_2ADDR = 0xb2;
public static final int DIV_INT_2ADDR = 0xb3;
public static final int REM_INT_2ADDR = 0xb4;
public static final int AND_INT_2ADDR = 0xb5;
public static final int OR_INT_2ADDR = 0xb6;
public static final int XOR_INT_2ADDR = 0xb7;
public static final int SHL_INT_2ADDR = 0xb8;
public static final int SHR_INT_2ADDR = 0xb9;
public static final int USHR_INT_2ADDR = 0xba;
public static final int ADD_LONG_2ADDR = 0xbb;
public static final int SUB_LONG_2ADDR = 0xbc;
public static final int MUL_LONG_2ADDR = 0xbd;
public static final int DIV_LONG_2ADDR = 0xbe;
public static final int REM_LONG_2ADDR = 0xbf;
public static final int AND_LONG_2ADDR = 0xc0;
public static final int OR_LONG_2ADDR = 0xc1;
public static final int XOR_LONG_2ADDR = 0xc2;
public static final int SHL_LONG_2ADDR = 0xc3;
public static final int SHR_LONG_2ADDR = 0xc4;
public static final int USHR_LONG_2ADDR = 0xc5;
public static final int ADD_FLOAT_2ADDR = 0xc6;
public static final int SUB_FLOAT_2ADDR = 0xc7;
public static final int MUL_FLOAT_2ADDR = 0xc8;
public static final int DIV_FLOAT_2ADDR = 0xc9;
public static final int REM_FLOAT_2ADDR = 0xca;
public static final int ADD_DOUBLE_2ADDR = 0xcb;
public static final int SUB_DOUBLE_2ADDR = 0xcc;
public static final int MUL_DOUBLE_2ADDR = 0xcd;
public static final int DIV_DOUBLE_2ADDR = 0xce;
public static final int REM_DOUBLE_2ADDR = 0xcf;
public static final int ADD_INT_LIT16 = 0xd0;
public static final int RSUB_INT = 0xd1;
public static final int MUL_INT_LIT16 = 0xd2;
public static final int DIV_INT_LIT16 = 0xd3;
public static final int REM_INT_LIT16 = 0xd4;
public static final int AND_INT_LIT16 = 0xd5;
public static final int OR_INT_LIT16 = 0xd6;
public static final int XOR_INT_LIT16 = 0xd7;
public static final int ADD_INT_LIT8 = 0xd8;
public static final int RSUB_INT_LIT8 = 0xd9;
public static final int MUL_INT_LIT8 = 0xda;
public static final int DIV_INT_LIT8 = 0xdb;
public static final int REM_INT_LIT8 = 0xdc;
public static final int AND_INT_LIT8 = 0xdd;
public static final int OR_INT_LIT8 = 0xde;
public static final int XOR_INT_LIT8 = 0xdf;
public static final int SHL_INT_LIT8 = 0xe0;
public static final int SHR_INT_LIT8 = 0xe1;
public static final int USHR_INT_LIT8 = 0xe2;
public static final int UNUSED_E3 = 0xe3;
public static final int UNUSED_E4 = 0xe4;
public static final int UNUSED_E5 = 0xe5;
public static final int UNUSED_E6 = 0xe6;
public static final int UNUSED_E7 = 0xe7;
public static final int UNUSED_E8 = 0xe8;
public static final int UNUSED_E9 = 0xe9;
public static final int UNUSED_EA = 0xea;
public static final int UNUSED_EB = 0xeb;
public static final int UNUSED_EC = 0xec;
public static final int UNUSED_ED = 0xed;
public static final int UNUSED_EE = 0xee;
public static final int UNUSED_EF = 0xef;
public static final int UNUSED_F0 = 0xf0;
public static final int UNUSED_F1 = 0xf1;
public static final int UNUSED_F2 = 0xf2;
public static final int UNUSED_F3 = 0xf3;
public static final int UNUSED_F4 = 0xf4;
public static final int UNUSED_F5 = 0xf5;
public static final int UNUSED_F6 = 0xf6;
public static final int UNUSED_F7 = 0xf7;
public static final int UNUSED_F8 = 0xf8;
public static final int UNUSED_F9 = 0xf9;
public static final int INVOKE_POLYMORPHIC = 0xfa;
public static final int INVOKE_POLYMORPHIC_RANGE = 0xfb;
public static final int UNUSED_FC = 0xfc;
public static final int UNUSED_FD = 0xfd;
public static final int UNUSED_FE = 0xfe;
public static final int UNUSED_FF = 0xff;
// END(opcodes)
/**
* This class is uninstantiable.
*/
private DalvOps() {
// This space intentionally left blank.
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
/**
* Representation of an opcode.
*/
public final class Dop {
/** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value itself */
private final int opcode;
/** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family */
private final int family;
/** {@code non-null;} the instruction format */
private final InsnFormat format;
/** whether this opcode uses a result register */
private final boolean hasResult;
/** {@code non-null;} the name */
private final String name;
/**
* Constructs an instance.
*
* @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode
* value itself
* @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
* @param format {@code non-null;} the instruction format
* @param hasResult whether the opcode has a result register; if so it
* is always the first register
* @param name {@code non-null;} the name
*/
public Dop(int opcode, int family, InsnFormat format,
boolean hasResult, String name) {
if ((opcode < DalvOps.MIN_VALUE) || (opcode > DalvOps.MAX_VALUE)) {
throw new IllegalArgumentException("bogus opcode");
}
if ((family < DalvOps.MIN_VALUE) || (family > DalvOps.MAX_VALUE)) {
throw new IllegalArgumentException("bogus family");
}
if (format == null) {
throw new NullPointerException("format == null");
}
if (name == null) {
throw new NullPointerException("name == null");
}
this.opcode = opcode;
this.family = family;
this.format = format;
this.hasResult = hasResult;
this.name = name;
}
/** {@inheritDoc} */
@Override
public String toString() {
return name;
}
/**
* Gets the opcode value.
*
* @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value
*/
public int getOpcode() {
return opcode;
}
/**
* Gets the opcode family. The opcode family is the unmarked (no
* "/...") opcode that has equivalent semantics to this one.
*
* @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family
*/
public int getFamily() {
return family;
}
/**
* Gets the instruction format.
*
* @return {@code non-null;} the instruction format
*/
public InsnFormat getFormat() {
return format;
}
/**
* Returns whether this opcode uses a result register.
*
* @return {@code true} iff this opcode uses a result register
*/
public boolean hasResult() {
return hasResult;
}
/**
* Gets the opcode name.
*
* @return {@code non-null;} the opcode name
*/
public String getName() {
return name;
}
/**
* Gets the opcode for the opposite test of this instance. This is only
* valid for opcodes which are in fact tests.
*
* @return {@code non-null;} the opposite test
*/
public Dop getOppositeTest() {
switch (opcode) {
case DalvOps.IF_EQ: return Dops.IF_NE;
case DalvOps.IF_NE: return Dops.IF_EQ;
case DalvOps.IF_LT: return Dops.IF_GE;
case DalvOps.IF_GE: return Dops.IF_LT;
case DalvOps.IF_GT: return Dops.IF_LE;
case DalvOps.IF_LE: return Dops.IF_GT;
case DalvOps.IF_EQZ: return Dops.IF_NEZ;
case DalvOps.IF_NEZ: return Dops.IF_EQZ;
case DalvOps.IF_LTZ: return Dops.IF_GEZ;
case DalvOps.IF_GEZ: return Dops.IF_LTZ;
case DalvOps.IF_GTZ: return Dops.IF_LEZ;
case DalvOps.IF_LEZ: return Dops.IF_GTZ;
}
throw new IllegalArgumentException("bogus opcode: " + this);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Base class for instructions which are of a fixed code size and which use {@link InsnFormat} methods to write themselves. This
* includes most &mdash; but not all &mdash; instructions.
*/
public abstract class FixedSizeInsn extends DalvInsn {
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* <p><b>Note:</b> In the unlikely event that an instruction takes
* absolutely no registers (e.g., a {@code nop} or a
* no-argument no-result * static method call), then the given
* register list may be passed as {@link
* RegisterSpecList#EMPTY}.</p>
*
* @param opcode the opcode; one of the constants from {@link Dops}
* @param position {@code non-null;} source position
* @param registers {@code non-null;} register list, including a
* result register if appropriate (that is, registers may be either
* ins or outs)
*/
public FixedSizeInsn(Dop opcode, SourcePosition position,
RegisterSpecList registers) {
super(opcode, position, registers);
}
/** {@inheritDoc} */
@Override
public final int codeSize() {
return getOpcode().getFormat().codeSize();
}
/** {@inheritDoc} */
@Override
public final void writeTo(AnnotatedOutput out) {
getOpcode().getFormat().writeTo(out, this);
}
/** {@inheritDoc} */
@Override
public final DalvInsn withRegisterOffset(int delta) {
return withRegisters(getRegisters().withOffset(delta));
}
/** {@inheritDoc} */
@Override
protected final String listingString0(boolean noteIndices) {
return getOpcode().getFormat().listingString(this, noteIndices);
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Combination instruction which turns into a variable number of
* {@code move*} instructions to move a set of registers into
* registers starting at {@code 0} sequentially. This is used
* in translating an instruction whose register requirements cannot
* be met using a straightforward choice of a single opcode.
*/
public final class HighRegisterPrefix extends VariableSizeInsn {
/** {@code null-ok;} cached instructions, if constructed */
private SimpleInsn[] insns;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param registers {@code non-null;} source registers
*/
public HighRegisterPrefix(SourcePosition position,
RegisterSpecList registers) {
super(position, registers);
if (registers.size() == 0) {
throw new IllegalArgumentException("registers.size() == 0");
}
insns = null;
}
/** {@inheritDoc} */
@Override
public int codeSize() {
int result = 0;
calculateInsnsIfNecessary();
for (SimpleInsn insn : insns) {
result += insn.codeSize();
}
return result;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out) {
calculateInsnsIfNecessary();
for (SimpleInsn insn : insns) {
insn.writeTo(out);
}
}
/**
* Helper for {@link #codeSize} and {@link #writeTo} which sets up
* {@link #insns} if not already done.
*/
private void calculateInsnsIfNecessary() {
if (insns != null) {
return;
}
RegisterSpecList registers = getRegisters();
int sz = registers.size();
insns = new SimpleInsn[sz];
for (int i = 0, outAt = 0; i < sz; i++) {
RegisterSpec src = registers.get(i);
insns[i] = moveInsnFor(src, outAt);
outAt += src.getCategory();
}
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new HighRegisterPrefix(getPosition(), registers);
}
/** {@inheritDoc} */
@Override
protected String argString() {
return null;
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
RegisterSpecList registers = getRegisters();
int sz = registers.size();
StringBuffer sb = new StringBuffer(100);
for (int i = 0, outAt = 0; i < sz; i++) {
RegisterSpec src = registers.get(i);
SimpleInsn insn = moveInsnFor(src, outAt);
if (i != 0) {
sb.append('\n');
}
sb.append(insn.listingString0(noteIndices));
outAt += src.getCategory();
}
return sb.toString();
}
/**
* Returns the proper move instruction for the given source spec
* and destination index.
*
* @param src {@code non-null;} the source register spec
* @param destIndex {@code >= 0;} the destination register index
* @return {@code non-null;} the appropriate move instruction
*/
private static SimpleInsn moveInsnFor(RegisterSpec src, int destIndex) {
return DalvInsn.makeMove(SourcePosition.NO_INFO,
RegisterSpec.make(destIndex, src.getType()),
src);
}
}

View File

@ -0,0 +1,578 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstInteger;
import com.android.dexgen.rop.cst.CstKnownNull;
import com.android.dexgen.rop.cst.CstLiteral64;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
/**
* Base class for all instruction format handlers. Instruction format
* handlers know how to translate {@link DalvInsn} instances into
* streams of code words, as well as human-oriented listing strings
* representing such translations.
*/
public abstract class InsnFormat {
/**
* Returns the string form, suitable for inclusion in a listing
* dump, of the given instruction. The instruction must be of this
* instance's format for proper operation.
*
* @param insn {@code non-null;} the instruction
* @param noteIndices whether to include an explicit notation of
* constant pool indices
* @return {@code non-null;} the string form
*/
public final String listingString(DalvInsn insn, boolean noteIndices) {
String op = insn.getOpcode().getName();
String arg = insnArgString(insn);
String comment = insnCommentString(insn, noteIndices);
StringBuilder sb = new StringBuilder(100);
sb.append(op);
if (arg.length() != 0) {
sb.append(' ');
sb.append(arg);
}
if (comment.length() != 0) {
sb.append(" // ");
sb.append(comment);
}
return sb.toString();
}
/**
* Returns the string form of the arguments to the given instruction.
* The instruction must be of this instance's format. If the instruction
* has no arguments, then the result should be {@code ""}, not
* {@code null}.
*
* <p>Subclasses must override this method.</p>
*
* @param insn {@code non-null;} the instruction
* @return {@code non-null;} the string form
*/
public abstract String insnArgString(DalvInsn insn);
/**
* Returns the associated comment for the given instruction, if any.
* The instruction must be of this instance's format. If the instruction
* has no comment, then the result should be {@code ""}, not
* {@code null}.
*
* <p>Subclasses must override this method.</p>
*
* @param insn {@code non-null;} the instruction
* @param noteIndices whether to include an explicit notation of
* constant pool indices
* @return {@code non-null;} the string form
*/
public abstract String insnCommentString(DalvInsn insn,
boolean noteIndices);
/**
* Gets the code size of instructions that use this format. The
* size is a number of 16-bit code units, not bytes. This should
* throw an exception if this format is of variable size.
*
* @return {@code >= 0;} the instruction length in 16-bit code units
*/
public abstract int codeSize();
/**
* Returns whether or not the given instruction's arguments will
* fit in this instance's format. This includes such things as
* counting register arguments, checking register ranges, and
* making sure that additional arguments are of appropriate types
* and are in-range. If this format has a branch target but the
* instruction's branch offset is unknown, this method will simply
* not check the offset.
*
* <p>Subclasses must override this method.</p>
*
* @param insn {@code non-null;} the instruction to check
* @return {@code true} iff the instruction's arguments are
* appropriate for this instance, or {@code false} if not
*/
public abstract boolean isCompatible(DalvInsn insn);
/**
* Returns whether or not the given instruction's branch offset will
* fit in this instance's format. This always returns {@code false}
* for formats that don't include a branch offset.
*
* <p>The default implementation of this method always returns
* {@code false}. Subclasses must override this method if they
* include branch offsets.</p>
*
* @param insn {@code non-null;} the instruction to check
* @return {@code true} iff the instruction's branch offset is
* appropriate for this instance, or {@code false} if not
*/
public boolean branchFits(TargetInsn insn) {
return false;
}
/**
* Returns the next instruction format to try to match an instruction
* with, presuming that this instance isn't compatible, if any.
*
* <p>Subclasses must override this method.</p>
*
* @return {@code null-ok;} the next format to try, or {@code null} if
* there are no suitable alternatives
*/
public abstract InsnFormat nextUp();
/**
* Writes the code units for the given instruction to the given
* output destination. The instruction must be of this instance's format.
*
* <p>Subclasses must override this method.</p>
*
* @param out {@code non-null;} the output destination to write to
* @param insn {@code non-null;} the instruction to write
*/
public abstract void writeTo(AnnotatedOutput out, DalvInsn insn);
/**
* Helper method to return a register list string.
*
* @param list {@code non-null;} the list of registers
* @return {@code non-null;} the string form
*/
protected static String regListString(RegisterSpecList list) {
int sz = list.size();
StringBuffer sb = new StringBuffer(sz * 5 + 2);
sb.append('{');
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(list.get(i).regString());
}
sb.append('}');
return sb.toString();
}
/**
* Helper method to return a literal bits argument string.
*
* @param value the value
* @return {@code non-null;} the string form
*/
protected static String literalBitsString(CstLiteralBits value) {
StringBuffer sb = new StringBuffer(100);
sb.append('#');
if (value instanceof CstKnownNull) {
sb.append("null");
} else {
sb.append(value.typeName());
sb.append(' ');
sb.append(value.toHuman());
}
return sb.toString();
}
/**
* Helper method to return a literal bits comment string.
*
* @param value the value
* @param width the width of the constant, in bits (used for displaying
* the uninterpreted bits; one of: {@code 4 8 16 32 64}
* @return {@code non-null;} the comment
*/
protected static String literalBitsComment(CstLiteralBits value,
int width) {
StringBuffer sb = new StringBuffer(20);
sb.append("#");
long bits;
if (value instanceof CstLiteral64) {
bits = ((CstLiteral64) value).getLongBits();
} else {
bits = value.getIntBits();
}
switch (width) {
case 4: sb.append(Hex.uNibble((int) bits)); break;
case 8: sb.append(Hex.u1((int) bits)); break;
case 16: sb.append(Hex.u2((int) bits)); break;
case 32: sb.append(Hex.u4((int) bits)); break;
case 64: sb.append(Hex.u8(bits)); break;
default: {
throw new RuntimeException("shouldn't happen");
}
}
return sb.toString();
}
/**
* Helper method to return a branch address string.
*
* @param insn {@code non-null;} the instruction in question
* @return {@code non-null;} the string form of the instruction's branch target
*/
protected static String branchString(DalvInsn insn) {
TargetInsn ti = (TargetInsn) insn;
int address = ti.getTargetAddress();
return (address == (char) address) ? Hex.u2(address) : Hex.u4(address);
}
/**
* Helper method to return the comment for a branch.
*
* @param insn {@code non-null;} the instruction in question
* @return {@code non-null;} the comment
*/
protected static String branchComment(DalvInsn insn) {
TargetInsn ti = (TargetInsn) insn;
int offset = ti.getTargetOffset();
return (offset == (short) offset) ? Hex.s2(offset) : Hex.s4(offset);
}
/**
* Helper method to return a constant string.
*
* @param insn {@code non-null;} a constant-bearing instruction
* @return {@code non-null;} the string form of the contained constant
*/
protected static String cstString(DalvInsn insn) {
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
return cst.toHuman();
}
/**
* Helper method to return an instruction comment for a constant.
*
* @param insn {@code non-null;} a constant-bearing instruction
* @return {@code non-null;} comment string representing the constant
*/
protected static String cstComment(DalvInsn insn) {
CstInsn ci = (CstInsn) insn;
if (! ci.hasIndex()) {
return "";
}
StringBuilder sb = new StringBuilder(20);
int index = ci.getIndex();
sb.append(ci.getConstant().typeName());
sb.append('@');
if (index < 65536) {
sb.append(Hex.u2(index));
} else {
sb.append(Hex.u4(index));
}
return sb.toString();
}
/**
* Helper method to determine if a signed int value fits in a nibble.
*
* @param value the value in question
* @return {@code true} iff it's in the range -8..+7
*/
protected static boolean signedFitsInNibble(int value) {
return (value >= -8) && (value <= 7);
}
/**
* Helper method to determine if an unsigned int value fits in a nibble.
*
* @param value the value in question
* @return {@code true} iff it's in the range 0..0xf
*/
protected static boolean unsignedFitsInNibble(int value) {
return value == (value & 0xf);
}
/**
* Helper method to determine if a signed int value fits in a byte.
*
* @param value the value in question
* @return {@code true} iff it's in the range -0x80..+0x7f
*/
protected static boolean signedFitsInByte(int value) {
return (byte) value == value;
}
/**
* Helper method to determine if an unsigned int value fits in a byte.
*
* @param value the value in question
* @return {@code true} iff it's in the range 0..0xff
*/
protected static boolean unsignedFitsInByte(int value) {
return value == (value & 0xff);
}
/**
* Helper method to determine if a signed int value fits in a short.
*
* @param value the value in question
* @return {@code true} iff it's in the range -0x8000..+0x7fff
*/
protected static boolean signedFitsInShort(int value) {
return (short) value == value;
}
/**
* Helper method to determine if an unsigned int value fits in a short.
*
* @param value the value in question
* @return {@code true} iff it's in the range 0..0xffff
*/
protected static boolean unsignedFitsInShort(int value) {
return value == (value & 0xffff);
}
/**
* Helper method to determine if a signed int value fits in three bytes.
*
* @param value the value in question
* @return {@code true} iff it's in the range -0x800000..+0x7fffff
*/
protected static boolean signedFitsIn3Bytes(int value) {
return value == ((value << 8) >> 8);
}
/**
* Helper method to extract the callout-argument index from an
* appropriate instruction.
*
* @param insn {@code non-null;} the instruction
* @return {@code >= 0;} the callout argument index
*/
protected static int argIndex(DalvInsn insn) {
int arg = ((CstInteger) ((CstInsn) insn).getConstant()).getValue();
if (arg < 0) {
throw new IllegalArgumentException("bogus insn");
}
return arg;
}
/**
* Helper method to combine an opcode and a second byte of data into
* the appropriate form for emitting into a code buffer.
*
* @param insn {@code non-null;} the instruction containing the opcode
* @param arg {@code 0..255;} arbitrary other byte value
* @return combined value
*/
protected static short opcodeUnit(DalvInsn insn, int arg) {
if ((arg & 0xff) != arg) {
throw new IllegalArgumentException("arg out of range 0..255");
}
int opcode = insn.getOpcode().getOpcode();
if ((opcode & 0xff) != opcode) {
throw new IllegalArgumentException("opcode out of range 0..255");
}
return (short) (opcode | (arg << 8));
}
/**
* Helper method to combine two bytes into a code unit.
*
* @param low {@code 0..255;} low byte
* @param high {@code 0..255;} high byte
* @return combined value
*/
protected static short codeUnit(int low, int high) {
if ((low & 0xff) != low) {
throw new IllegalArgumentException("low out of range 0..255");
}
if ((high & 0xff) != high) {
throw new IllegalArgumentException("high out of range 0..255");
}
return (short) (low | (high << 8));
}
/**
* Helper method to combine four nibbles into a code unit.
*
* @param n0 {@code 0..15;} low nibble
* @param n1 {@code 0..15;} medium-low nibble
* @param n2 {@code 0..15;} medium-high nibble
* @param n3 {@code 0..15;} high nibble
* @return combined value
*/
protected static short codeUnit(int n0, int n1, int n2, int n3) {
if ((n0 & 0xf) != n0) {
throw new IllegalArgumentException("n0 out of range 0..15");
}
if ((n1 & 0xf) != n1) {
throw new IllegalArgumentException("n1 out of range 0..15");
}
if ((n2 & 0xf) != n2) {
throw new IllegalArgumentException("n2 out of range 0..15");
}
if ((n3 & 0xf) != n3) {
throw new IllegalArgumentException("n3 out of range 0..15");
}
return (short) (n0 | (n1 << 4) | (n2 << 8) | (n3 << 12));
}
/**
* Helper method to combine two nibbles into a byte.
*
* @param low {@code 0..15;} low nibble
* @param high {@code 0..15;} high nibble
* @return {@code 0..255;} combined value
*/
protected static int makeByte(int low, int high) {
if ((low & 0xf) != low) {
throw new IllegalArgumentException("low out of range 0..15");
}
if ((high & 0xf) != high) {
throw new IllegalArgumentException("high out of range 0..15");
}
return low | (high << 4);
}
/**
* Writes one code unit to the given output destination.
*
* @param out {@code non-null;} where to write to
* @param c0 code unit to write
*/
protected static void write(AnnotatedOutput out, short c0) {
out.writeShort(c0);
}
/**
* Writes two code units to the given output destination.
*
* @param out {@code non-null;} where to write to
* @param c0 code unit to write
* @param c1 code unit to write
*/
protected static void write(AnnotatedOutput out, short c0, short c1) {
out.writeShort(c0);
out.writeShort(c1);
}
/**
* Writes three code units to the given output destination.
*
* @param out {@code non-null;} where to write to
* @param c0 code unit to write
* @param c1 code unit to write
* @param c2 code unit to write
*/
protected static void write(AnnotatedOutput out, short c0, short c1,
short c2) {
out.writeShort(c0);
out.writeShort(c1);
out.writeShort(c2);
}
/**
* Writes four code units to the given output destination.
*
* @param out {@code non-null;} where to write to
* @param c0 code unit to write
* @param c1 code unit to write
* @param c2 code unit to write
* @param c3 code unit to write
*/
protected static void write(AnnotatedOutput out, short c0, short c1,
short c2, short c3) {
out.writeShort(c0);
out.writeShort(c1);
out.writeShort(c2);
out.writeShort(c3);
}
/**
* Writes five code units to the given output destination.
*
* @param out {@code non-null;} where to write to
* @param c0 code unit to write
* @param c1 code unit to write
* @param c2 code unit to write
* @param c3 code unit to write
* @param c4 code unit to write
*/
protected static void write(AnnotatedOutput out, short c0, short c1,
short c2, short c3, short c4) {
out.writeShort(c0);
out.writeShort(c1);
out.writeShort(c2);
out.writeShort(c3);
out.writeShort(c4);
}
/**
* Writes six code units to the given output destination.
*
* @param out {@code non-null;} where to write to
* @param c0 code unit to write
* @param c1 code unit to write
* @param c2 code unit to write
* @param c3 code unit to write
* @param c4 code unit to write
* @param c5 code unit to write
*/
protected static void write(AnnotatedOutput out, short c0, short c1,
short c2, short c3, short c4, short c5) {
out.writeShort(c0);
out.writeShort(c1);
out.writeShort(c2);
out.writeShort(c3);
out.writeShort(c4);
out.writeShort(c5);
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Pseudo-instruction which is used to explicitly end the mapping of a
* register to a named local variable. That is, an instance of this
* class in an instruction stream indicates that starting with the
* subsequent instruction, the indicated variable is no longer valid.
*/
public final class LocalEnd extends ZeroSizeInsn {
/**
* {@code non-null;} register spec representing the local variable ended
* by this instance. <b>Note:</b> Technically, only the register
* number needs to be recorded here as the rest of the information
* is implicit in the ambient local variable state, but other code
* will check the other info for consistency.
*/
private final RegisterSpec local;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param local {@code non-null;} register spec representing the local
* variable introduced by this instance
*/
public LocalEnd(SourcePosition position, RegisterSpec local) {
super(position);
if (local == null) {
throw new NullPointerException("local == null");
}
this.local = local;
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisterOffset(int delta) {
return new LocalEnd(getPosition(), local.withOffset(delta));
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new LocalEnd(getPosition(), local);
}
/**
* Gets the register spec representing the local variable ended
* by this instance.
*
* @return {@code non-null;} the register spec
*/
public RegisterSpec getLocal() {
return local;
}
/** {@inheritDoc} */
@Override
protected String argString() {
return local.toString();
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
return "local-end " + LocalStart.localString(local);
}
}

View File

@ -0,0 +1,948 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecSet;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.FixedSizeList;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
/**
* List of local variables. Each local variable entry indicates a
* range of code which it is valid for, a register number, a name,
* and a type.
*/
public final class LocalList extends FixedSizeList {
/** {@code non-null;} empty instance */
public static final LocalList EMPTY = new LocalList(0);
/** whether to run the self-check code */
private static final boolean DEBUG = false;
/**
* Constructs an instance. All indices initially contain {@code null}.
*
* @param size {@code >= 0;} the size of the list
*/
public LocalList(int size) {
super(size);
}
/**
* Gets the element at the given index. It is an error to call
* this with the index for an element which was never set; if you
* do that, this will throw {@code NullPointerException}.
*
* @param n {@code >= 0, < size();} which index
* @return {@code non-null;} element at that index
*/
public Entry get(int n) {
return (Entry) get0(n);
}
/**
* Sets the entry at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param entry {@code non-null;} the entry to set at {@code n}
*/
public void set(int n, Entry entry) {
set0(n, entry);
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
*/
public void debugPrint(PrintStream out, String prefix) {
int sz = size();
for (int i = 0; i < sz; i++) {
out.print(prefix);
out.println(get(i));
}
}
/**
* Disposition of a local entry.
*/
public static enum Disposition {
/** local started (introduced) */
START,
/** local ended without being replaced */
END_SIMPLY,
/** local ended because it was directly replaced */
END_REPLACED,
/** local ended because it was moved to a different register */
END_MOVED,
/**
* local ended because the previous local clobbered this one
* (because it is category-2)
*/
END_CLOBBERED_BY_PREV,
/**
* local ended because the next local clobbered this one
* (because this one is a category-2)
*/
END_CLOBBERED_BY_NEXT;
}
/**
* Entry in a local list.
*/
public static class Entry implements Comparable<Entry> {
/** {@code >= 0;} address */
private final int address;
/** {@code non-null;} disposition of the local */
private final Disposition disposition;
/** {@code non-null;} register spec representing the variable */
private final RegisterSpec spec;
/** {@code non-null;} variable type (derived from {@code spec}) */
private final CstType type;
/**
* Constructs an instance.
*
* @param address {@code >= 0;} address
* @param disposition {@code non-null;} disposition of the local
* @param spec {@code non-null;} register spec representing
* the variable
*/
public Entry(int address, Disposition disposition, RegisterSpec spec) {
if (address < 0) {
throw new IllegalArgumentException("address < 0");
}
if (disposition == null) {
throw new NullPointerException("disposition == null");
}
try {
if (spec.getLocalItem() == null) {
throw new NullPointerException(
"spec.getLocalItem() == null");
}
} catch (NullPointerException ex) {
// Elucidate the exception.
throw new NullPointerException("spec == null");
}
this.address = address;
this.disposition = disposition;
this.spec = spec;
this.type = CstType.intern(spec.getType());
}
/** {@inheritDoc} */
public String toString() {
return Integer.toHexString(address) + " " + disposition + " " +
spec;
}
/** {@inheritDoc} */
public boolean equals(Object other) {
if (!(other instanceof Entry)) {
return false;
}
return (compareTo((Entry) other) == 0);
}
/**
* Compares by (in priority order) address, end then start
* disposition (variants of end are all consistered
* equivalent), and spec.
*
* @param other {@code non-null;} entry to compare to
* @return {@code -1..1;} standard result of comparison
*/
public int compareTo(Entry other) {
if (address < other.address) {
return -1;
} else if (address > other.address) {
return 1;
}
boolean thisIsStart = isStart();
boolean otherIsStart = other.isStart();
if (thisIsStart != otherIsStart) {
return thisIsStart ? 1 : -1;
}
return spec.compareTo(other.spec);
}
/**
* Gets the address.
*
* @return {@code >= 0;} the address
*/
public int getAddress() {
return address;
}
/**
* Gets the disposition.
*
* @return {@code non-null;} the disposition
*/
public Disposition getDisposition() {
return disposition;
}
/**
* Gets whether this is a local start. This is just shorthand for
* {@code getDisposition() == Disposition.START}.
*
* @return {@code true} iff this is a start
*/
public boolean isStart() {
return disposition == Disposition.START;
}
/**
* Gets the variable name.
*
* @return {@code null-ok;} the variable name
*/
public CstUtf8 getName() {
return spec.getLocalItem().getName();
}
/**
* Gets the variable signature.
*
* @return {@code null-ok;} the variable signature
*/
public CstUtf8 getSignature() {
return spec.getLocalItem().getSignature();
}
/**
* Gets the variable's type.
*
* @return {@code non-null;} the type
*/
public CstType getType() {
return type;
}
/**
* Gets the number of the register holding the variable.
*
* @return {@code >= 0;} the number of the register holding
* the variable
*/
public int getRegister() {
return spec.getReg();
}
/**
* Gets the RegisterSpec of the register holding the variable.
*
* @return {@code non-null;} RegisterSpec of the holding register.
*/
public RegisterSpec getRegisterSpec() {
return spec;
}
/**
* Returns whether or not this instance matches the given spec.
*
* @param otherSpec {@code non-null;} the spec in question
* @return {@code true} iff this instance matches
* {@code spec}
*/
public boolean matches(RegisterSpec otherSpec) {
return spec.equalsUsingSimpleType(otherSpec);
}
/**
* Returns whether or not this instance matches the spec in
* the given instance.
*
* @param other {@code non-null;} another entry
* @return {@code true} iff this instance's spec matches
* {@code other}
*/
public boolean matches(Entry other) {
return matches(other.spec);
}
/**
* Returns an instance just like this one but with the disposition
* set as given.
*
* @param disposition {@code non-null;} the new disposition
* @return {@code non-null;} an appropriately-constructed instance
*/
public Entry withDisposition(Disposition disposition) {
if (disposition == this.disposition) {
return this;
}
return new Entry(address, disposition, spec);
}
}
/**
* Constructs an instance for the given method, based on the given
* block order and intermediate local information.
*
* @param insns {@code non-null;} instructions to convert
* @return {@code non-null;} the constructed list
*/
public static LocalList make(DalvInsnList insns) {
int sz = insns.size();
/*
* Go through the insn list, looking for all the local
* variable pseudoinstructions, splitting out LocalSnapshots
* into separate per-variable starts, adding explicit ends
* wherever a variable is replaced or moved, and collecting
* these and all the other local variable "activity"
* together into an output list (without the other insns).
*
* Note: As of this writing, this method won't be handed any
* insn lists that contain local ends, but I (danfuzz) expect
* that to change at some point, when we start feeding that
* info explicitly into the rop layer rather than only trying
* to infer it. So, given that expectation, this code is
* written to deal with them.
*/
MakeState state = new MakeState(sz);
for (int i = 0; i < sz; i++) {
DalvInsn insn = insns.get(i);
if (insn instanceof LocalSnapshot) {
RegisterSpecSet snapshot =
((LocalSnapshot) insn).getLocals();
state.snapshot(insn.getAddress(), snapshot);
} else if (insn instanceof LocalStart) {
RegisterSpec local = ((LocalStart) insn).getLocal();
state.startLocal(insn.getAddress(), local);
} else if (insn instanceof LocalEnd) {
RegisterSpec local = ((LocalEnd) insn).getLocal();
state.endLocal(insn.getAddress(), local);
}
}
LocalList result = state.finish();
if (DEBUG) {
debugVerify(result);
}
return result;
}
/**
* Debugging helper that verifies the constraint that a list doesn't
* contain any redundant local starts and that local ends that are
* due to replacements are properly annotated.
*/
private static void debugVerify(LocalList locals) {
try {
debugVerify0(locals);
} catch (RuntimeException ex) {
int sz = locals.size();
for (int i = 0; i < sz; i++) {
System.err.println(locals.get(i));
}
throw ex;
}
}
/**
* Helper for {@link #debugVerify} which does most of the work.
*/
private static void debugVerify0(LocalList locals) {
int sz = locals.size();
Entry[] active = new Entry[65536];
for (int i = 0; i < sz; i++) {
Entry e = locals.get(i);
int reg = e.getRegister();
if (e.isStart()) {
Entry already = active[reg];
if ((already != null) && e.matches(already)) {
throw new RuntimeException("redundant start at " +
Integer.toHexString(e.getAddress()) + ": got " +
e + "; had " + already);
}
active[reg] = e;
} else {
if (active[reg] == null) {
throw new RuntimeException("redundant end at " +
Integer.toHexString(e.getAddress()));
}
int addr = e.getAddress();
boolean foundStart = false;
for (int j = i + 1; j < sz; j++) {
Entry test = locals.get(j);
if (test.getAddress() != addr) {
break;
}
if (test.getRegisterSpec().getReg() == reg) {
if (test.isStart()) {
if (e.getDisposition()
!= Disposition.END_REPLACED) {
throw new RuntimeException(
"improperly marked end at " +
Integer.toHexString(addr));
}
foundStart = true;
} else {
throw new RuntimeException(
"redundant end at " +
Integer.toHexString(addr));
}
}
}
if (!foundStart &&
(e.getDisposition() == Disposition.END_REPLACED)) {
throw new RuntimeException(
"improper end replacement claim at " +
Integer.toHexString(addr));
}
active[reg] = null;
}
}
}
/**
* Intermediate state when constructing a local list.
*/
public static class MakeState {
/** {@code non-null;} result being collected */
private final ArrayList<Entry> result;
/**
* {@code >= 0;} running count of nulled result entries, to help with
* sizing the final list
*/
private int nullResultCount;
/** {@code null-ok;} current register mappings */
private RegisterSpecSet regs;
/** {@code null-ok;} result indices where local ends are stored */
private int[] endIndices;
/** {@code >= 0;} last address seen */
private int lastAddress;
/**
* Constructs an instance.
*/
public MakeState(int initialSize) {
result = new ArrayList<Entry>(initialSize);
nullResultCount = 0;
regs = null;
endIndices = null;
lastAddress = 0;
}
/**
* Checks the address and other vitals as a prerequisite to
* further processing.
*
* @param address {@code >= 0;} address about to be processed
* @param reg {@code >= 0;} register number about to be processed
*/
private void aboutToProcess(int address, int reg) {
boolean first = (endIndices == null);
if ((address == lastAddress) && !first) {
return;
}
if (address < lastAddress) {
throw new RuntimeException("shouldn't happen");
}
if (first || (reg >= endIndices.length)) {
/*
* This is the first allocation of the state set and
* index array, or we need to grow. (The latter doesn't
* happen much; in fact, we have only ever observed
* it happening in test cases, never in "real" code.)
*/
int newSz = reg + 1;
RegisterSpecSet newRegs = new RegisterSpecSet(newSz);
int[] newEnds = new int[newSz];
Arrays.fill(newEnds, -1);
if (!first) {
newRegs.putAll(regs);
System.arraycopy(endIndices, 0, newEnds, 0,
endIndices.length);
}
regs = newRegs;
endIndices = newEnds;
}
}
/**
* Sets the local state at the given address to the given snapshot.
* The first call on this instance must be to this method, so that
* the register state can be properly sized.
*
* @param address {@code >= 0;} the address
* @param specs {@code non-null;} spec set representing the locals
*/
public void snapshot(int address, RegisterSpecSet specs) {
if (DEBUG) {
System.err.printf("%04x snapshot %s\n", address, specs);
}
int sz = specs.getMaxSize();
aboutToProcess(address, sz - 1);
for (int i = 0; i < sz; i++) {
RegisterSpec oldSpec = regs.get(i);
RegisterSpec newSpec = filterSpec(specs.get(i));
if (oldSpec == null) {
if (newSpec != null) {
startLocal(address, newSpec);
}
} else if (newSpec == null) {
endLocal(address, oldSpec);
} else if (! newSpec.equalsUsingSimpleType(oldSpec)) {
endLocal(address, oldSpec);
startLocal(address, newSpec);
}
}
if (DEBUG) {
System.err.printf("%04x snapshot done\n", address);
}
}
/**
* Starts a local at the given address.
*
* @param address {@code >= 0;} the address
* @param startedLocal {@code non-null;} spec representing the
* started local
*/
public void startLocal(int address, RegisterSpec startedLocal) {
if (DEBUG) {
System.err.printf("%04x start %s\n", address, startedLocal);
}
int regNum = startedLocal.getReg();
startedLocal = filterSpec(startedLocal);
aboutToProcess(address, regNum);
RegisterSpec existingLocal = regs.get(regNum);
if (startedLocal.equalsUsingSimpleType(existingLocal)) {
// Silently ignore a redundant start.
return;
}
RegisterSpec movedLocal = regs.findMatchingLocal(startedLocal);
if (movedLocal != null) {
/*
* The same variable was moved from one register to another.
* So add an end for its old location.
*/
addOrUpdateEnd(address, Disposition.END_MOVED, movedLocal);
}
int endAt = endIndices[regNum];
if (existingLocal != null) {
/*
* There is an existing (but non-matching) local.
* Add an explicit end for it.
*/
add(address, Disposition.END_REPLACED, existingLocal);
} else if (endAt >= 0) {
/*
* Look for an end local for the same register at the
* same address. If found, then update it or delete
* it, depending on whether or not it represents the
* same variable as the one being started.
*/
Entry endEntry = result.get(endAt);
if (endEntry.getAddress() == address) {
if (endEntry.matches(startedLocal)) {
/*
* There was already an end local for the same
* variable at the same address. This turns
* out to be superfluous, as we are starting
* up the exact same local. This situation can
* happen when a single local variable got
* somehow "split up" during intermediate
* processing. In any case, rather than represent
* the end-then-start, just remove the old end.
*/
result.set(endAt, null);
nullResultCount++;
regs.put(startedLocal);
endIndices[regNum] = -1;
return;
} else {
/*
* There was a different variable ended at the
* same address. Update it to indicate that
* it was ended due to a replacement (rather than
* ending for no particular reason).
*/
endEntry = endEntry.withDisposition(
Disposition.END_REPLACED);
result.set(endAt, endEntry);
}
}
}
/*
* The code above didn't find and remove an unnecessary
* local end, so we now have to add one or more entries to
* the output to capture the transition.
*/
/*
* If the local just below (in the register set at reg-1)
* is of category-2, then it is ended by this new start.
*/
if (regNum > 0) {
RegisterSpec justBelow = regs.get(regNum - 1);
if ((justBelow != null) && justBelow.isCategory2()) {
addOrUpdateEnd(address,
Disposition.END_CLOBBERED_BY_NEXT,
justBelow);
}
}
/*
* Similarly, if this local is category-2, then the local
* just above (if any) is ended by the start now being
* emitted.
*/
if (startedLocal.isCategory2()) {
RegisterSpec justAbove = regs.get(regNum + 1);
if (justAbove != null) {
addOrUpdateEnd(address,
Disposition.END_CLOBBERED_BY_PREV,
justAbove);
}
}
/*
* TODO: Add an end for the same local in a different reg,
* if any (that is, if the local migrates from vX to vY,
* we should note that as a local end in vX).
*/
add(address, Disposition.START, startedLocal);
}
/**
* Ends a local at the given address, using the disposition
* {@code END_SIMPLY}.
*
* @param address {@code >= 0;} the address
* @param endedLocal {@code non-null;} spec representing the
* local being ended
*/
public void endLocal(int address, RegisterSpec endedLocal) {
endLocal(address, endedLocal, Disposition.END_SIMPLY);
}
/**
* Ends a local at the given address.
*
* @param address {@code >= 0;} the address
* @param endedLocal {@code non-null;} spec representing the
* local being ended
* @param disposition reason for the end
*/
public void endLocal(int address, RegisterSpec endedLocal,
Disposition disposition) {
if (DEBUG) {
System.err.printf("%04x end %s\n", address, endedLocal);
}
int regNum = endedLocal.getReg();
endedLocal = filterSpec(endedLocal);
aboutToProcess(address, regNum);
int endAt = endIndices[regNum];
if (endAt >= 0) {
/*
* The local in the given register is already ended.
* Silently return without adding anything to the result.
*/
return;
}
// Check for start and end at the same address.
if (checkForEmptyRange(address, endedLocal)) {
return;
}
add(address, disposition, endedLocal);
}
/**
* Helper for {@link #endLocal}, which handles the cases where
* and end local is issued at the same address as a start local
* for the same register. If this case is found, then this
* method will remove the start (as the local was never actually
* active), update the {@link #endIndices} to be accurate, and
* if needed update the newly-active end to reflect an altered
* disposition.
*
* @param address {@code >= 0;} the address
* @param endedLocal {@code non-null;} spec representing the
* local being ended
* @return {@code true} iff this method found the case in question
* and adjusted things accordingly
*/
private boolean checkForEmptyRange(int address,
RegisterSpec endedLocal) {
int at = result.size() - 1;
Entry entry;
// Look for a previous entry at the same address.
for (/*at*/; at >= 0; at--) {
entry = result.get(at);
if (entry == null) {
continue;
}
if (entry.getAddress() != address) {
// We didn't find any match at the same address.
return false;
}
if (entry.matches(endedLocal)) {
break;
}
}
/*
* In fact, we found that the endedLocal had started at the
* same address, so do all the requisite cleanup.
*/
regs.remove(endedLocal);
result.set(at, null);
nullResultCount++;
int regNum = endedLocal.getReg();
boolean found = false;
entry = null;
// Now look back further to update where the register ended.
for (at--; at >= 0; at--) {
entry = result.get(at);
if (entry == null) {
continue;
}
if (entry.getRegisterSpec().getReg() == regNum) {
found = true;
break;
}
}
if (found) {
// We found an end for the same register.
endIndices[regNum] = at;
if (entry.getAddress() == address) {
/*
* It's still the same address, so update the
* disposition.
*/
result.set(at,
entry.withDisposition(Disposition.END_SIMPLY));
}
}
return true;
}
/**
* Converts a given spec into the form acceptable for use in a
* local list. This, in particular, transforms the "known
* null" type into simply {@code Object}. This method needs to
* be called for any spec that is on its way into a locals
* list.
*
* <p>This isn't necessarily the cleanest way to achieve the
* goal of not representing known nulls in a locals list, but
* it gets the job done.</p>
*
* @param orig {@code null-ok;} the original spec
* @return {@code null-ok;} an appropriately modified spec, or the
* original if nothing needs to be done
*/
private static RegisterSpec filterSpec(RegisterSpec orig) {
if ((orig != null) && (orig.getType() == Type.KNOWN_NULL)) {
return orig.withType(Type.OBJECT);
}
return orig;
}
/**
* Adds an entry to the result, updating the adjunct tables
* accordingly.
*
* @param address {@code >= 0;} the address
* @param disposition {@code non-null;} the disposition
* @param spec {@code non-null;} spec representing the local
*/
private void add(int address, Disposition disposition,
RegisterSpec spec) {
int regNum = spec.getReg();
result.add(new Entry(address, disposition, spec));
if (disposition == Disposition.START) {
regs.put(spec);
endIndices[regNum] = -1;
} else {
regs.remove(spec);
endIndices[regNum] = result.size() - 1;
}
}
/**
* Adds or updates an end local (changing its disposition). If
* this would cause an empty range for a local, this instead
* removes the local entirely.
*
* @param address {@code >= 0;} the address
* @param disposition {@code non-null;} the disposition
* @param spec {@code non-null;} spec representing the local
*/
private void addOrUpdateEnd(int address, Disposition disposition,
RegisterSpec spec) {
if (disposition == Disposition.START) {
throw new RuntimeException("shouldn't happen");
}
int regNum = spec.getReg();
int endAt = endIndices[regNum];
if (endAt >= 0) {
// There is a previous end.
Entry endEntry = result.get(endAt);
if ((endEntry.getAddress() == address) &&
endEntry.getRegisterSpec().equals(spec)) {
/*
* The end is for the right address and variable, so
* update it.
*/
result.set(endAt, endEntry.withDisposition(disposition));
regs.remove(spec); // TODO: Is this line superfluous?
return;
}
}
endLocal(address, spec, disposition);
}
/**
* Finishes processing altogether and gets the result.
*
* @return {@code non-null;} the result list
*/
public LocalList finish() {
aboutToProcess(Integer.MAX_VALUE, 0);
int resultSz = result.size();
int finalSz = resultSz - nullResultCount;
if (finalSz == 0) {
return EMPTY;
}
/*
* Collect an array of only the non-null entries, and then
* sort it to get a consistent order for everything: Local
* ends and starts for a given address could come in any
* order, but we want ends before starts as well as
* registers in order (within ends or starts).
*/
Entry[] resultArr = new Entry[finalSz];
if (resultSz == finalSz) {
result.toArray(resultArr);
} else {
int at = 0;
for (Entry e : result) {
if (e != null) {
resultArr[at++] = e;
}
}
}
Arrays.sort(resultArr);
LocalList resultList = new LocalList(finalSz);
for (int i = 0; i < finalSz; i++) {
resultList.set(i, resultArr[i]);
}
resultList.setImmutable();
return resultList;
}
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.RegisterSpecSet;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Pseudo-instruction which is used to hold a snapshot of the
* state of local variable name mappings that exists immediately after
* the instance in an instruction array.
*/
public final class LocalSnapshot extends ZeroSizeInsn {
/** {@code non-null;} local state associated with this instance */
private final RegisterSpecSet locals;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param locals {@code non-null;} associated local variable state
*/
public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) {
super(position);
if (locals == null) {
throw new NullPointerException("locals == null");
}
this.locals = locals;
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisterOffset(int delta) {
return new LocalSnapshot(getPosition(), locals.withOffset(delta));
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new LocalSnapshot(getPosition(), locals);
}
/**
* Gets the local state associated with this instance.
*
* @return {@code non-null;} the state
*/
public RegisterSpecSet getLocals() {
return locals;
}
/** {@inheritDoc} */
@Override
protected String argString() {
return locals.toString();
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
int sz = locals.size();
int max = locals.getMaxSize();
StringBuffer sb = new StringBuffer(100 + sz * 40);
sb.append("local-snapshot");
for (int i = 0; i < max; i++) {
RegisterSpec spec = locals.get(i);
if (spec != null) {
sb.append("\n ");
sb.append(LocalStart.localString(spec));
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Pseudo-instruction which is used to introduce a new local variable. That
* is, an instance of this class in an instruction stream indicates that
* starting with the subsequent instruction, the indicated variable
* is bound.
*/
public final class LocalStart extends ZeroSizeInsn {
/**
* {@code non-null;} register spec representing the local variable introduced
* by this instance
*/
private final RegisterSpec local;
/**
* Returns the local variable listing string for a single register spec.
*
* @param spec {@code non-null;} the spec to convert
* @return {@code non-null;} the string form
*/
public static String localString(RegisterSpec spec) {
return spec.regString() + ' ' + spec.getLocalItem().toString() + ": " +
spec.getTypeBearer().toHuman();
}
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param local {@code non-null;} register spec representing the local
* variable introduced by this instance
*/
public LocalStart(SourcePosition position, RegisterSpec local) {
super(position);
if (local == null) {
throw new NullPointerException("local == null");
}
this.local = local;
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisterOffset(int delta) {
return new LocalStart(getPosition(), local.withOffset(delta));
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new LocalStart(getPosition(), local);
}
/**
* Gets the register spec representing the local variable introduced
* by this instance.
*
* @return {@code non-null;} the register spec
*/
public RegisterSpec getLocal() {
return local;
}
/** {@inheritDoc} */
@Override
protected String argString() {
return local.toString();
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
return "local-start " + localString(local);
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Pseudo-instruction which either turns into a {@code nop} or
* nothingness, in order to make the subsequent instruction have an
* even address. This is used to align (subsequent) instructions that
* require it.
*/
public final class OddSpacer extends VariableSizeInsn {
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
*/
public OddSpacer(SourcePosition position) {
super(position, RegisterSpecList.EMPTY);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return (getAddress() & 1);
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out) {
if (codeSize() != 0) {
out.writeShort(InsnFormat.codeUnit(DalvOps.NOP, 0));
}
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new OddSpacer(getPosition());
}
/** {@inheritDoc} */
@Override
protected String argString() {
return null;
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
if (codeSize() == 0) {
return null;
}
return "nop // spacer";
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import java.util.ArrayList;
/**
* Destination for {@link DalvInsn} instances being output. This class
* receives and collects instructions in two pieces &mdash; a primary
* list and a suffix (generally consisting of adjunct data referred to
* by the primary list, such as switch case tables) &mdash; which it
* merges and emits back out in the form of a {@link DalvInsnList}
* instance.
*/
public final class OutputCollector {
/**
* {@code non-null;} the associated finisher (which holds the instruction
* list in-progress)
*/
private final OutputFinisher finisher;
/**
* {@code null-ok;} suffix for the output, or {@code null} if the suffix
* has been appended to the main output (by {@link #appendSuffixToOutput})
*/
private ArrayList<DalvInsn> suffix;
/**
* Constructs an instance.
*
* @param initialCapacity {@code >= 0;} initial capacity of the output list
* @param suffixInitialCapacity {@code >= 0;} initial capacity of the output
* suffix
* @param regCount {@code >= 0;} register count for the method
*/
public OutputCollector(int initialCapacity, int suffixInitialCapacity,
int regCount) {
this.finisher = new OutputFinisher(initialCapacity, regCount);
this.suffix = new ArrayList<DalvInsn>(suffixInitialCapacity);
}
/**
* Adds an instruction to the output.
*
* @param insn {@code non-null;} the instruction to add
*/
public void add(DalvInsn insn) {
finisher.add(insn);
}
/**
* Reverses a branch which is buried a given number of instructions
* backward in the output. It is illegal to call this unless the
* indicated instruction really is a reversible branch.
*
* @param which how many instructions back to find the branch;
* {@code 0} is the most recently added instruction,
* {@code 1} is the instruction before that, etc.
* @param newTarget {@code non-null;} the new target for the reversed branch
*/
public void reverseBranch(int which, CodeAddress newTarget) {
finisher.reverseBranch(which, newTarget);
}
/**
* Adds an instruction to the output suffix.
*
* @param insn {@code non-null;} the instruction to add
*/
public void addSuffix(DalvInsn insn) {
suffix.add(insn);
}
/**
* Gets the results of all the calls on this instance, in the form of
* an {@link OutputFinisher}.
*
* @return {@code non-null;} the output finisher
* @throws UnsupportedOperationException if this method has
* already been called
*/
public OutputFinisher getFinisher() {
if (suffix == null) {
throw new UnsupportedOperationException("already processed");
}
appendSuffixToOutput();
return finisher;
}
/**
* Helper for {@link #getFinisher}, which appends the suffix to
* the primary output.
*/
private void appendSuffixToOutput() {
int size = suffix.size();
for (int i = 0; i < size; i++) {
finisher.add(suffix.get(i));
}
suffix = null;
}
}

View File

@ -0,0 +1,737 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.LocalItem;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.RegisterSpecSet;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstMemberRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Type;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Processor for instruction lists, which takes a "first cut" of
* instruction selection as a basis and produces a "final cut" in the
* form of a {@link DalvInsnList} instance.
*/
public final class OutputFinisher {
/**
* {@code >= 0;} register count for the method, not including any extra
* "reserved" registers needed to translate "difficult" instructions
*/
private final int unreservedRegCount;
/** {@code non-null;} the list of instructions, per se */
private ArrayList<DalvInsn> insns;
/** whether any instruction has position info */
private boolean hasAnyPositionInfo;
/** whether any instruction has local variable info */
private boolean hasAnyLocalInfo;
/**
* {@code >= 0;} the count of reserved registers (low-numbered
* registers used when expanding instructions that can't be
* represented simply); becomes valid after a call to {@link
* #massageInstructions}
*/
private int reservedCount;
/**
* Constructs an instance. It initially contains no instructions.
*
* @param regCount {@code >= 0;} register count for the method
* @param initialCapacity {@code >= 0;} initial capacity of the instructions
* list
*/
public OutputFinisher(int initialCapacity, int regCount) {
this.unreservedRegCount = regCount;
this.insns = new ArrayList<DalvInsn>(initialCapacity);
this.reservedCount = -1;
this.hasAnyPositionInfo = false;
this.hasAnyLocalInfo = false;
}
/**
* Returns whether any of the instructions added to this instance
* come with position info.
*
* @return whether any of the instructions added to this instance
* come with position info
*/
public boolean hasAnyPositionInfo() {
return hasAnyPositionInfo;
}
/**
* Returns whether this instance has any local variable information.
*
* @return whether this instance has any local variable information
*/
public boolean hasAnyLocalInfo() {
return hasAnyLocalInfo;
}
/**
* Helper for {@link #add} which scrutinizes a single
* instruction for local variable information.
*
* @param insn {@code non-null;} instruction to scrutinize
* @return {@code true} iff the instruction refers to any
* named locals
*/
private static boolean hasLocalInfo(DalvInsn insn) {
if (insn instanceof LocalSnapshot) {
RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
int size = specs.size();
for (int i = 0; i < size; i++) {
if (hasLocalInfo(specs.get(i))) {
return true;
}
}
} else if (insn instanceof LocalStart) {
RegisterSpec spec = ((LocalStart) insn).getLocal();
if (hasLocalInfo(spec)) {
return true;
}
}
return false;
}
/**
* Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
* register spec.
*
* @param spec {@code non-null;} spec to scrutinize
* @return {@code true} iff the spec refers to any
* named locals
*/
private static boolean hasLocalInfo(RegisterSpec spec) {
return (spec != null)
&& (spec.getLocalItem().getName() != null);
}
/**
* Returns the set of all constants referred to by instructions added
* to this instance.
*
* @return {@code non-null;} the set of constants
*/
public HashSet<Constant> getAllConstants() {
HashSet<Constant> result = new HashSet<Constant>(20);
for (DalvInsn insn : insns) {
addConstants(result, insn);
}
return result;
}
/**
* Helper for {@link #getAllConstants} which adds all the info for
* a single instruction.
*
* @param result {@code non-null;} result set to add to
* @param insn {@code non-null;} instruction to scrutinize
*/
private static void addConstants(HashSet<Constant> result,
DalvInsn insn) {
if (insn instanceof CstInsn) {
Constant cst = ((CstInsn) insn).getConstant();
result.add(cst);
} else if (insn instanceof LocalSnapshot) {
RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
int size = specs.size();
for (int i = 0; i < size; i++) {
addConstants(result, specs.get(i));
}
} else if (insn instanceof LocalStart) {
RegisterSpec spec = ((LocalStart) insn).getLocal();
addConstants(result, spec);
}
}
/**
* Helper for {@link #getAllConstants} which adds all the info for
* a single {@code RegisterSpec}.
*
* @param result {@code non-null;} result set to add to
* @param spec {@code null-ok;} register spec to add
*/
private static void addConstants(HashSet<Constant> result,
RegisterSpec spec) {
if (spec == null) {
return;
}
LocalItem local = spec.getLocalItem();
CstUtf8 name = local.getName();
CstUtf8 signature = local.getSignature();
Type type = spec.getType();
if (type != Type.KNOWN_NULL) {
result.add(CstType.intern(type));
}
if (name != null) {
result.add(name);
}
if (signature != null) {
result.add(signature);
}
}
/**
* Adds an instruction to the output.
*
* @param insn {@code non-null;} the instruction to add
*/
public void add(DalvInsn insn) {
insns.add(insn);
updateInfo(insn);
}
/**
* Inserts an instruction in the output at the given offset.
*
* @param at {@code >= 0;} what index to insert at
* @param insn {@code non-null;} the instruction to insert
*/
public void insert(int at, DalvInsn insn) {
insns.add(at, insn);
updateInfo(insn);
}
/**
* Helper for {@link #add} and {@link #insert},
* which updates the position and local info flags.
*
* @param insn {@code non-null;} an instruction that was just introduced
*/
private void updateInfo(DalvInsn insn) {
if (! hasAnyPositionInfo) {
SourcePosition pos = insn.getPosition();
if (pos.getLine() >= 0) {
hasAnyPositionInfo = true;
}
}
if (! hasAnyLocalInfo) {
if (hasLocalInfo(insn)) {
hasAnyLocalInfo = true;
}
}
}
/**
* Reverses a branch which is buried a given number of instructions
* backward in the output. It is illegal to call this unless the
* indicated instruction really is a reversible branch.
*
* @param which how many instructions back to find the branch;
* {@code 0} is the most recently added instruction,
* {@code 1} is the instruction before that, etc.
* @param newTarget {@code non-null;} the new target for the reversed branch
*/
public void reverseBranch(int which, CodeAddress newTarget) {
int size = insns.size();
int index = size - which - 1;
TargetInsn targetInsn;
try {
targetInsn = (TargetInsn) insns.get(index);
} catch (IndexOutOfBoundsException ex) {
// Translate the exception.
throw new IllegalArgumentException("too few instructions");
} catch (ClassCastException ex) {
// Translate the exception.
throw new IllegalArgumentException("non-reversible instruction");
}
/*
* No need to call this.set(), since the format and other info
* are the same.
*/
insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
}
/**
* Assigns indices in all instructions that need them, using the
* given callback to perform lookups. This should be called before
* calling {@link #finishProcessingAndGetList}.
*
* @param callback {@code non-null;} callback object
*/
public void assignIndices(DalvCode.AssignIndicesCallback callback) {
for (DalvInsn insn : insns) {
if (insn instanceof CstInsn) {
assignIndices((CstInsn) insn, callback);
}
}
}
/**
* Helper for {@link #assignIndices} which does assignment for one
* instruction.
*
* @param insn {@code non-null;} the instruction
* @param callback {@code non-null;} the callback
*/
private static void assignIndices(CstInsn insn,
DalvCode.AssignIndicesCallback callback) {
Constant cst = insn.getConstant();
int index = callback.getIndex(cst);
if (index >= 0) {
insn.setIndex(index);
}
if (cst instanceof CstMemberRef) {
CstMemberRef member = (CstMemberRef) cst;
CstType definer = member.getDefiningClass();
index = callback.getIndex(definer);
if (index >= 0) {
insn.setClassIndex(index);
}
}
}
/**
* Does final processing on this instance and gets the output as
* a {@link DalvInsnList}. Final processing consists of:
*
* <ul>
* <li>optionally renumbering registers (to make room as needed for
* expanded instructions)</li>
* <li>picking a final opcode for each instruction</li>
* <li>rewriting instructions, because of register number,
* constant pool index, or branch target size issues</li>
* <li>assigning final addresses</li>
* </ul>
*
* <p><b>Note:</b> This method may only be called once per instance
* of this class.</p>
*
* @return {@code non-null;} the output list
* @throws UnsupportedOperationException if this method has
* already been called
*/
public DalvInsnList finishProcessingAndGetList() {
if (reservedCount >= 0) {
throw new UnsupportedOperationException("already processed");
}
InsnFormat[] formats = makeFormatsArray();
reserveRegisters(formats);
massageInstructions(formats);
assignAddressesAndFixBranches();
return DalvInsnList.makeImmutable(insns,
reservedCount + unreservedRegCount);
}
/**
* Helper for {@link #finishProcessingAndGetList}, which extracts
* the format out of each instruction into a separate array, to be
* further manipulated as things progress.
*
* @return {@code non-null;} the array of formats
*/
private InsnFormat[] makeFormatsArray() {
int size = insns.size();
InsnFormat[] result = new InsnFormat[size];
for (int i = 0; i < size; i++) {
result[i] = insns.get(i).getOpcode().getFormat();
}
return result;
}
/**
* Helper for {@link #finishProcessingAndGetList}, which figures
* out how many reserved registers are required and then reserving
* them. It also updates the given {@code formats} array so
* as to avoid extra work when constructing the massaged
* instruction list.
*
* @param formats {@code non-null;} array of per-instruction format selections
*/
private void reserveRegisters(InsnFormat[] formats) {
int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
/*
* Call calculateReservedCount() and then perform register
* reservation, repeatedly until no new reservations happen.
*/
for (;;) {
int newReservedCount = calculateReservedCount(formats);
if (oldReservedCount >= newReservedCount) {
break;
}
int reservedDifference = newReservedCount - oldReservedCount;
int size = insns.size();
for (int i = 0; i < size; i++) {
/*
* CodeAddress instance identity is used to link
* TargetInsns to their targets, so it is
* inappropriate to make replacements, and they don't
* have registers in any case. Hence, the instanceof
* test below.
*/
DalvInsn insn = insns.get(i);
if (!(insn instanceof CodeAddress)) {
/*
* No need to call this.set() since the format and
* other info are the same.
*/
insns.set(i, insn.withRegisterOffset(reservedDifference));
}
}
oldReservedCount = newReservedCount;
}
reservedCount = oldReservedCount;
}
/**
* Helper for {@link #reserveRegisters}, which does one
* pass over the instructions, calculating the number of
* registers that need to be reserved. It also updates the
* {@code formats} list to help avoid extra work in future
* register reservation passes.
*
* @param formats {@code non-null;} array of per-instruction format selections
* @return {@code >= 0;} the count of reserved registers
*/
private int calculateReservedCount(InsnFormat[] formats) {
int size = insns.size();
/*
* Potential new value of reservedCount, which gets updated in the
* following loop. It starts out with the existing reservedCount
* and gets increased if it turns out that additional registers
* need to be reserved.
*/
int newReservedCount = reservedCount;
for (int i = 0; i < size; i++) {
DalvInsn insn = insns.get(i);
InsnFormat originalFormat = formats[i];
InsnFormat newFormat = findFormatForInsn(insn, originalFormat);
if (originalFormat == newFormat) {
continue;
}
if (newFormat == null) {
/*
* The instruction will need to be expanded, so reserve
* registers for it.
*/
int reserve = insn.getMinimumRegisterRequirement();
if (reserve > newReservedCount) {
newReservedCount = reserve;
}
}
formats[i] = newFormat;
}
return newReservedCount;
}
/**
* Attempts to fit the given instruction into a format, returning
* either a format that the instruction fits into or {@code null}
* to indicate that the instruction will need to be expanded. This
* fitting process starts with the given format as a first "best
* guess" and then pessimizes from there if necessary.
*
* @param insn {@code non-null;} the instruction in question
* @param format {@code null-ok;} the current guess as to the best instruction
* format to use; {@code null} means that no simple format fits
* @return {@code null-ok;} a possibly-different format, which is either a
* good fit or {@code null} to indicate that no simple format
* fits
*/
private InsnFormat findFormatForInsn(DalvInsn insn, InsnFormat format) {
if (format == null) {
// The instruction is already known not to fit any simple format.
return format;
}
if (format.isCompatible(insn)) {
// The instruction already fits in the current best-known format.
return format;
}
Dop dop = insn.getOpcode();
int family = dop.getFamily();
for (;;) {
format = format.nextUp();
if ((format == null) ||
(format.isCompatible(insn) &&
(Dops.getOrNull(family, format) != null))) {
break;
}
}
return format;
}
/**
* Helper for {@link #finishProcessingAndGetList}, which goes
* through each instruction in the output, making sure its opcode
* can accomodate its arguments. In cases where the opcode is
* unable to do so, this replaces the instruction with a larger
* instruction with identical semantics that <i>will</i> work.
*
* <p>This method may also reserve a number of low-numbered
* registers, renumbering the instructions' original registers, in
* order to have register space available in which to move
* very-high registers when expanding instructions into
* multi-instruction sequences. This expansion is done when no
* simple instruction format can be found for a given instruction that
* is able to accomodate that instruction's registers.</p>
*
* <p>This method ignores issues of branch target size, since
* final addresses aren't known at the point that this method is
* called.</p>
*
* @param formats {@code non-null;} array of per-instruction format selections
*/
private void massageInstructions(InsnFormat[] formats) {
if (reservedCount == 0) {
/*
* The easy common case: No registers were reserved, so we
* merely need to replace any instructions whose format changed
* during the reservation pass, but all instructions will stay
* at their original indices, and the instruction list doesn't
* grow.
*/
int size = insns.size();
for (int i = 0; i < size; i++) {
DalvInsn insn = insns.get(i);
Dop dop = insn.getOpcode();
InsnFormat format = formats[i];
if (format != dop.getFormat()) {
dop = Dops.getOrNull(dop.getFamily(), format);
insns.set(i, insn.withOpcode(dop));
}
}
} else {
/*
* The difficult uncommon case: Some instructions have to be
* expanded to deal with high registers.
*/
insns = performExpansion(formats);
}
}
/**
* Helper for {@link #massageInstructions}, which constructs a
* replacement list, where each {link DalvInsn} instance that
* couldn't be represented simply (due to register representation
* problems) is expanded into a series of instances that together
* perform the proper function.
*
* @param formats {@code non-null;} array of per-instruction format selections
* @return {@code non-null;} the replacement list
*/
private ArrayList<DalvInsn> performExpansion(InsnFormat[] formats) {
int size = insns.size();
ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
for (int i = 0; i < size; i++) {
DalvInsn insn = insns.get(i);
Dop dop = insn.getOpcode();
InsnFormat originalFormat = dop.getFormat();
InsnFormat currentFormat = formats[i];
DalvInsn prefix;
DalvInsn suffix;
if (currentFormat != null) {
// No expansion is necessary.
prefix = null;
suffix = null;
} else {
// Expansion is required.
prefix = insn.hrPrefix();
suffix = insn.hrSuffix();
/*
* Get the initial guess as to the hr version, but then
* let findFormatForInsn() pick a better format, if any.
*/
insn = insn.hrVersion();
originalFormat = insn.getOpcode().getFormat();
currentFormat = findFormatForInsn(insn, originalFormat);
}
if (prefix != null) {
result.add(prefix);
}
if (currentFormat != originalFormat) {
dop = Dops.getOrNull(dop.getFamily(), currentFormat);
insn = insn.withOpcode(dop);
}
result.add(insn);
if (suffix != null) {
result.add(suffix);
}
}
return result;
}
/**
* Helper for {@link #finishProcessingAndGetList}, which assigns
* addresses to each instruction, possibly rewriting branches to
* fix ones that wouldn't otherwise be able to reach their
* targets.
*/
private void assignAddressesAndFixBranches() {
for (;;) {
assignAddresses();
if (!fixBranches()) {
break;
}
}
}
/**
* Helper for {@link #assignAddressesAndFixBranches}, which
* assigns an address to each instruction, in order.
*/
private void assignAddresses() {
int address = 0;
int size = insns.size();
for (int i = 0; i < size; i++) {
DalvInsn insn = insns.get(i);
insn.setAddress(address);
address += insn.codeSize();
}
}
/**
* Helper for {@link #assignAddressesAndFixBranches}, which checks
* the branch target size requirement of each branch instruction
* to make sure it fits. For instructions that don't fit, this
* rewrites them to use a {@code goto} of some sort. In the
* case of a conditional branch that doesn't fit, the sense of the
* test is reversed in order to branch around a {@code goto}
* to the original target.
*
* @return whether any branches had to be fixed
*/
private boolean fixBranches() {
int size = insns.size();
boolean anyFixed = false;
for (int i = 0; i < size; i++) {
DalvInsn insn = insns.get(i);
if (!(insn instanceof TargetInsn)) {
// This loop only needs to inspect TargetInsns.
continue;
}
Dop dop = insn.getOpcode();
InsnFormat format = dop.getFormat();
TargetInsn target = (TargetInsn) insn;
if (format.branchFits(target)) {
continue;
}
if (dop.getFamily() == DalvOps.GOTO) {
// It is a goto; widen it if possible.
InsnFormat newFormat = findFormatForInsn(insn, format);
if (newFormat == null) {
/*
* The branch is already maximally large. This should
* only be possible if a method somehow manages to have
* more than 2^31 code units.
*/
throw new UnsupportedOperationException("method too long");
}
dop = Dops.getOrNull(dop.getFamily(), newFormat);
insn = insn.withOpcode(dop);
insns.set(i, insn);
} else {
/*
* It is a conditional: Reverse its sense, and arrange for
* it to branch around an absolute goto to the original
* branch target.
*
* Note: An invariant of the list being processed is
* that every TargetInsn is followed by a CodeAddress.
* Hence, it is always safe to get the next element
* after a TargetInsn and cast it to CodeAddress, as
* is happening a few lines down.
*
* Also note: Size gets incremented by one here, as we
* have -- in the net -- added one additional element
* to the list, so we increment i to match. The added
* and changed elements will be inspected by a repeat
* call to this method after this invocation returns.
*/
CodeAddress newTarget;
try {
newTarget = (CodeAddress) insns.get(i + 1);
} catch (IndexOutOfBoundsException ex) {
// The TargetInsn / CodeAddress invariant was violated.
throw new IllegalStateException(
"unpaired TargetInsn (dangling)");
} catch (ClassCastException ex) {
// The TargetInsn / CodeAddress invariant was violated.
throw new IllegalStateException("unpaired TargetInsn");
}
TargetInsn gotoInsn =
new TargetInsn(Dops.GOTO, target.getPosition(),
RegisterSpecList.EMPTY, target.getTarget());
insns.set(i, gotoInsn);
insns.add(i, target.withNewTargetAndReversed(newTarget));
size++;
i++;
}
anyFixed = true;
}
return anyFixed;
}
}

View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.util.FixedSizeList;
/**
* List of source position entries. This class includes a utility
* method to extract an instance out of a {@link DalvInsnList}.
*/
public final class PositionList extends FixedSizeList {
/** {@code non-null;} empty instance */
public static final PositionList EMPTY = new PositionList(0);
/**
* constant for {@link #make} to indicate that no actual position
* information should be returned
*/
public static final int NONE = 1;
/**
* constant for {@link #make} to indicate that only line number
* transitions should be returned
*/
public static final int LINES = 2;
/**
* constant for {@link #make} to indicate that only "important" position
* information should be returned. This includes block starts and
* instructions that might throw.
*/
public static final int IMPORTANT = 3;
/**
* Extracts and returns the source position information out of an
* instruction list.
*
* @param insns {@code non-null;} instructions to convert
* @param howMuch how much information should be included; one of the
* static constants defined by this class
* @return {@code non-null;} the positions list
*/
public static PositionList make(DalvInsnList insns, int howMuch) {
switch (howMuch) {
case NONE: {
return EMPTY;
}
case LINES:
case IMPORTANT: {
// Valid.
break;
}
default: {
throw new IllegalArgumentException("bogus howMuch");
}
}
SourcePosition noInfo = SourcePosition.NO_INFO;
SourcePosition cur = noInfo;
int sz = insns.size();
PositionList.Entry[] arr = new PositionList.Entry[sz];
boolean lastWasTarget = false;
int at = 0;
for (int i = 0; i < sz; i++) {
DalvInsn insn = insns.get(i);
if (insn instanceof CodeAddress) {
lastWasTarget = true;;
continue;
}
SourcePosition pos = insn.getPosition();
if (pos.equals(noInfo) || pos.sameLine(cur)) {
continue;
}
if ((howMuch == IMPORTANT) && !lastWasTarget) {
continue;
}
cur = pos;
arr[at] = new PositionList.Entry(insn.getAddress(), pos);
at++;
lastWasTarget = false;
}
PositionList result = new PositionList(at);
for (int i = 0; i < at; i++) {
result.set(i, arr[i]);
}
result.setImmutable();
return result;
}
/**
* Constructs an instance. All indices initially contain {@code null}.
*
* @param size {@code >= 0;} the size of the list
*/
public PositionList(int size) {
super(size);
}
/**
* Gets the element at the given index. It is an error to call
* this with the index for an element which was never set; if you
* do that, this will throw {@code NullPointerException}.
*
* @param n {@code >= 0, < size();} which index
* @return {@code non-null;} element at that index
*/
public Entry get(int n) {
return (Entry) get0(n);
}
/**
* Sets the entry at the given index.
*
* @param n {@code >= 0, < size();} which index
* @param entry {@code non-null;} the entry to set at {@code n}
*/
public void set(int n, Entry entry) {
set0(n, entry);
}
/**
* Entry in a position list.
*/
public static class Entry {
/** {@code >= 0;} address of this entry */
private final int address;
/** {@code non-null;} corresponding source position information */
private final SourcePosition position;
/**
* Constructs an instance.
*
* @param address {@code >= 0;} address of this entry
* @param position {@code non-null;} corresponding source position information
*/
public Entry (int address, SourcePosition position) {
if (address < 0) {
throw new IllegalArgumentException("address < 0");
}
if (position == null) {
throw new NullPointerException("position == null");
}
this.address = address;
this.position = position;
}
/**
* Gets the address.
*
* @return {@code >= 0;} the address
*/
public int getAddress() {
return address;
}
/**
* Gets the source position information.
*
* @return {@code non-null;} the position information
*/
public SourcePosition getPosition() {
return position;
}
}
}

View File

@ -0,0 +1,415 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.Insn;
import com.android.dexgen.rop.code.RegOps;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.Rop;
import com.android.dexgen.rop.code.Rops;
import com.android.dexgen.rop.code.ThrowingCstInsn;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.Type;
import java.util.HashMap;
/**
* Translator from rop-level {@link Insn} instances to corresponding
* {@link Dop} instances.
*/
public final class RopToDop {
/** {@code non-null;} map from all the common rops to dalvik opcodes */
private static final HashMap<Rop, Dop> MAP;
/**
* This class is uninstantiable.
*/
private RopToDop() {
// This space intentionally left blank.
}
static {
/*
* Note: The choices made here are to pick the optimistically
* smallest Dalvik opcode, and leave it to later processing to
* pessimize.
*/
MAP = new HashMap<Rop, Dop>(400);
MAP.put(Rops.NOP, Dops.NOP);
MAP.put(Rops.MOVE_INT, Dops.MOVE);
MAP.put(Rops.MOVE_LONG, Dops.MOVE_WIDE);
MAP.put(Rops.MOVE_FLOAT, Dops.MOVE);
MAP.put(Rops.MOVE_DOUBLE, Dops.MOVE_WIDE);
MAP.put(Rops.MOVE_OBJECT, Dops.MOVE_OBJECT);
MAP.put(Rops.MOVE_PARAM_INT, Dops.MOVE);
MAP.put(Rops.MOVE_PARAM_LONG, Dops.MOVE_WIDE);
MAP.put(Rops.MOVE_PARAM_FLOAT, Dops.MOVE);
MAP.put(Rops.MOVE_PARAM_DOUBLE, Dops.MOVE_WIDE);
MAP.put(Rops.MOVE_PARAM_OBJECT, Dops.MOVE_OBJECT);
/*
* Note: No entry for MOVE_EXCEPTION, since it varies by
* exception type. (That is, there is no unique instance to
* add to the map.)
*/
MAP.put(Rops.CONST_INT, Dops.CONST_4);
MAP.put(Rops.CONST_LONG, Dops.CONST_WIDE_16);
MAP.put(Rops.CONST_FLOAT, Dops.CONST_4);
MAP.put(Rops.CONST_DOUBLE, Dops.CONST_WIDE_16);
/*
* Note: No entry for CONST_OBJECT, since it needs to turn
* into either CONST_STRING or CONST_CLASS.
*/
/*
* TODO: I think the only case of this is for null, and
* const/4 should cover that.
*/
MAP.put(Rops.CONST_OBJECT_NOTHROW, Dops.CONST_4);
MAP.put(Rops.GOTO, Dops.GOTO);
MAP.put(Rops.IF_EQZ_INT, Dops.IF_EQZ);
MAP.put(Rops.IF_NEZ_INT, Dops.IF_NEZ);
MAP.put(Rops.IF_LTZ_INT, Dops.IF_LTZ);
MAP.put(Rops.IF_GEZ_INT, Dops.IF_GEZ);
MAP.put(Rops.IF_LEZ_INT, Dops.IF_LEZ);
MAP.put(Rops.IF_GTZ_INT, Dops.IF_GTZ);
MAP.put(Rops.IF_EQZ_OBJECT, Dops.IF_EQZ);
MAP.put(Rops.IF_NEZ_OBJECT, Dops.IF_NEZ);
MAP.put(Rops.IF_EQ_INT, Dops.IF_EQ);
MAP.put(Rops.IF_NE_INT, Dops.IF_NE);
MAP.put(Rops.IF_LT_INT, Dops.IF_LT);
MAP.put(Rops.IF_GE_INT, Dops.IF_GE);
MAP.put(Rops.IF_LE_INT, Dops.IF_LE);
MAP.put(Rops.IF_GT_INT, Dops.IF_GT);
MAP.put(Rops.IF_EQ_OBJECT, Dops.IF_EQ);
MAP.put(Rops.IF_NE_OBJECT, Dops.IF_NE);
MAP.put(Rops.SWITCH, Dops.SPARSE_SWITCH);
MAP.put(Rops.ADD_INT, Dops.ADD_INT_2ADDR);
MAP.put(Rops.ADD_LONG, Dops.ADD_LONG_2ADDR);
MAP.put(Rops.ADD_FLOAT, Dops.ADD_FLOAT_2ADDR);
MAP.put(Rops.ADD_DOUBLE, Dops.ADD_DOUBLE_2ADDR);
MAP.put(Rops.SUB_INT, Dops.SUB_INT_2ADDR);
MAP.put(Rops.SUB_LONG, Dops.SUB_LONG_2ADDR);
MAP.put(Rops.SUB_FLOAT, Dops.SUB_FLOAT_2ADDR);
MAP.put(Rops.SUB_DOUBLE, Dops.SUB_DOUBLE_2ADDR);
MAP.put(Rops.MUL_INT, Dops.MUL_INT_2ADDR);
MAP.put(Rops.MUL_LONG, Dops.MUL_LONG_2ADDR);
MAP.put(Rops.MUL_FLOAT, Dops.MUL_FLOAT_2ADDR);
MAP.put(Rops.MUL_DOUBLE, Dops.MUL_DOUBLE_2ADDR);
MAP.put(Rops.DIV_INT, Dops.DIV_INT_2ADDR);
MAP.put(Rops.DIV_LONG, Dops.DIV_LONG_2ADDR);
MAP.put(Rops.DIV_FLOAT, Dops.DIV_FLOAT_2ADDR);
MAP.put(Rops.DIV_DOUBLE, Dops.DIV_DOUBLE_2ADDR);
MAP.put(Rops.REM_INT, Dops.REM_INT_2ADDR);
MAP.put(Rops.REM_LONG, Dops.REM_LONG_2ADDR);
MAP.put(Rops.REM_FLOAT, Dops.REM_FLOAT_2ADDR);
MAP.put(Rops.REM_DOUBLE, Dops.REM_DOUBLE_2ADDR);
MAP.put(Rops.NEG_INT, Dops.NEG_INT);
MAP.put(Rops.NEG_LONG, Dops.NEG_LONG);
MAP.put(Rops.NEG_FLOAT, Dops.NEG_FLOAT);
MAP.put(Rops.NEG_DOUBLE, Dops.NEG_DOUBLE);
MAP.put(Rops.AND_INT, Dops.AND_INT_2ADDR);
MAP.put(Rops.AND_LONG, Dops.AND_LONG_2ADDR);
MAP.put(Rops.OR_INT, Dops.OR_INT_2ADDR);
MAP.put(Rops.OR_LONG, Dops.OR_LONG_2ADDR);
MAP.put(Rops.XOR_INT, Dops.XOR_INT_2ADDR);
MAP.put(Rops.XOR_LONG, Dops.XOR_LONG_2ADDR);
MAP.put(Rops.SHL_INT, Dops.SHL_INT_2ADDR);
MAP.put(Rops.SHL_LONG, Dops.SHL_LONG_2ADDR);
MAP.put(Rops.SHR_INT, Dops.SHR_INT_2ADDR);
MAP.put(Rops.SHR_LONG, Dops.SHR_LONG_2ADDR);
MAP.put(Rops.USHR_INT, Dops.USHR_INT_2ADDR);
MAP.put(Rops.USHR_LONG, Dops.USHR_LONG_2ADDR);
MAP.put(Rops.NOT_INT, Dops.NOT_INT);
MAP.put(Rops.NOT_LONG, Dops.NOT_LONG);
MAP.put(Rops.ADD_CONST_INT, Dops.ADD_INT_LIT8);
// Note: No dalvik ops for other types of add_const.
/*
* Note: No dalvik ops for any type of sub_const; there's a
* *reverse* sub (constant - reg) for ints, though, but that
* should end up getting handled at optimization time.
*/
MAP.put(Rops.MUL_CONST_INT, Dops.MUL_INT_LIT8);
// Note: No dalvik ops for other types of mul_const.
MAP.put(Rops.DIV_CONST_INT, Dops.DIV_INT_LIT8);
// Note: No dalvik ops for other types of div_const.
MAP.put(Rops.REM_CONST_INT, Dops.REM_INT_LIT8);
// Note: No dalvik ops for other types of rem_const.
MAP.put(Rops.AND_CONST_INT, Dops.AND_INT_LIT8);
// Note: No dalvik op for and_const_long.
MAP.put(Rops.OR_CONST_INT, Dops.OR_INT_LIT8);
// Note: No dalvik op for or_const_long.
MAP.put(Rops.XOR_CONST_INT, Dops.XOR_INT_LIT8);
// Note: No dalvik op for xor_const_long.
MAP.put(Rops.SHL_CONST_INT, Dops.SHL_INT_LIT8);
// Note: No dalvik op for shl_const_long.
MAP.put(Rops.SHR_CONST_INT, Dops.SHR_INT_LIT8);
// Note: No dalvik op for shr_const_long.
MAP.put(Rops.USHR_CONST_INT, Dops.USHR_INT_LIT8);
// Note: No dalvik op for shr_const_long.
MAP.put(Rops.CMPL_LONG, Dops.CMP_LONG);
MAP.put(Rops.CMPL_FLOAT, Dops.CMPL_FLOAT);
MAP.put(Rops.CMPL_DOUBLE, Dops.CMPL_DOUBLE);
MAP.put(Rops.CMPG_FLOAT, Dops.CMPG_FLOAT);
MAP.put(Rops.CMPG_DOUBLE, Dops.CMPG_DOUBLE);
MAP.put(Rops.CONV_L2I, Dops.LONG_TO_INT);
MAP.put(Rops.CONV_F2I, Dops.FLOAT_TO_INT);
MAP.put(Rops.CONV_D2I, Dops.DOUBLE_TO_INT);
MAP.put(Rops.CONV_I2L, Dops.INT_TO_LONG);
MAP.put(Rops.CONV_F2L, Dops.FLOAT_TO_LONG);
MAP.put(Rops.CONV_D2L, Dops.DOUBLE_TO_LONG);
MAP.put(Rops.CONV_I2F, Dops.INT_TO_FLOAT);
MAP.put(Rops.CONV_L2F, Dops.LONG_TO_FLOAT);
MAP.put(Rops.CONV_D2F, Dops.DOUBLE_TO_FLOAT);
MAP.put(Rops.CONV_I2D, Dops.INT_TO_DOUBLE);
MAP.put(Rops.CONV_L2D, Dops.LONG_TO_DOUBLE);
MAP.put(Rops.CONV_F2D, Dops.FLOAT_TO_DOUBLE);
MAP.put(Rops.TO_BYTE, Dops.INT_TO_BYTE);
MAP.put(Rops.TO_CHAR, Dops.INT_TO_CHAR);
MAP.put(Rops.TO_SHORT, Dops.INT_TO_SHORT);
MAP.put(Rops.RETURN_VOID, Dops.RETURN_VOID);
MAP.put(Rops.RETURN_INT, Dops.RETURN);
MAP.put(Rops.RETURN_LONG, Dops.RETURN_WIDE);
MAP.put(Rops.RETURN_FLOAT, Dops.RETURN);
MAP.put(Rops.RETURN_DOUBLE, Dops.RETURN_WIDE);
MAP.put(Rops.RETURN_OBJECT, Dops.RETURN_OBJECT);
MAP.put(Rops.ARRAY_LENGTH, Dops.ARRAY_LENGTH);
MAP.put(Rops.THROW, Dops.THROW);
MAP.put(Rops.MONITOR_ENTER, Dops.MONITOR_ENTER);
MAP.put(Rops.MONITOR_EXIT, Dops.MONITOR_EXIT);
MAP.put(Rops.AGET_INT, Dops.AGET);
MAP.put(Rops.AGET_LONG, Dops.AGET_WIDE);
MAP.put(Rops.AGET_FLOAT, Dops.AGET);
MAP.put(Rops.AGET_DOUBLE, Dops.AGET_WIDE);
MAP.put(Rops.AGET_OBJECT, Dops.AGET_OBJECT);
MAP.put(Rops.AGET_BOOLEAN, Dops.AGET_BOOLEAN);
MAP.put(Rops.AGET_BYTE, Dops.AGET_BYTE);
MAP.put(Rops.AGET_CHAR, Dops.AGET_CHAR);
MAP.put(Rops.AGET_SHORT, Dops.AGET_SHORT);
MAP.put(Rops.APUT_INT, Dops.APUT);
MAP.put(Rops.APUT_LONG, Dops.APUT_WIDE);
MAP.put(Rops.APUT_FLOAT, Dops.APUT);
MAP.put(Rops.APUT_DOUBLE, Dops.APUT_WIDE);
MAP.put(Rops.APUT_OBJECT, Dops.APUT_OBJECT);
MAP.put(Rops.APUT_BOOLEAN, Dops.APUT_BOOLEAN);
MAP.put(Rops.APUT_BYTE, Dops.APUT_BYTE);
MAP.put(Rops.APUT_CHAR, Dops.APUT_CHAR);
MAP.put(Rops.APUT_SHORT, Dops.APUT_SHORT);
MAP.put(Rops.NEW_INSTANCE, Dops.NEW_INSTANCE);
MAP.put(Rops.CHECK_CAST, Dops.CHECK_CAST);
MAP.put(Rops.INSTANCE_OF, Dops.INSTANCE_OF);
MAP.put(Rops.GET_FIELD_LONG, Dops.IGET_WIDE);
MAP.put(Rops.GET_FIELD_FLOAT, Dops.IGET);
MAP.put(Rops.GET_FIELD_DOUBLE, Dops.IGET_WIDE);
MAP.put(Rops.GET_FIELD_OBJECT, Dops.IGET_OBJECT);
/*
* Note: No map entries for get_field_* for non-long integral types,
* since they need to be handled specially (see dopFor() below).
*/
MAP.put(Rops.GET_STATIC_LONG, Dops.SGET_WIDE);
MAP.put(Rops.GET_STATIC_FLOAT, Dops.SGET);
MAP.put(Rops.GET_STATIC_DOUBLE, Dops.SGET_WIDE);
MAP.put(Rops.GET_STATIC_OBJECT, Dops.SGET_OBJECT);
/*
* Note: No map entries for get_static* for non-long integral types,
* since they need to be handled specially (see dopFor() below).
*/
MAP.put(Rops.PUT_FIELD_LONG, Dops.IPUT_WIDE);
MAP.put(Rops.PUT_FIELD_FLOAT, Dops.IPUT);
MAP.put(Rops.PUT_FIELD_DOUBLE, Dops.IPUT_WIDE);
MAP.put(Rops.PUT_FIELD_OBJECT, Dops.IPUT_OBJECT);
/*
* Note: No map entries for put_field_* for non-long integral types,
* since they need to be handled specially (see dopFor() below).
*/
MAP.put(Rops.PUT_STATIC_LONG, Dops.SPUT_WIDE);
MAP.put(Rops.PUT_STATIC_FLOAT, Dops.SPUT);
MAP.put(Rops.PUT_STATIC_DOUBLE, Dops.SPUT_WIDE);
MAP.put(Rops.PUT_STATIC_OBJECT, Dops.SPUT_OBJECT);
/*
* Note: No map entries for put_static* for non-long integral types,
* since they need to be handled specially (see dopFor() below).
*/
/*
* Note: No map entries for invoke*, new_array, and
* filled_new_array, since they need to be handled specially
* (see dopFor() below).
*/
}
/**
* Returns the dalvik opcode appropriate for the given register-based
* instruction.
*
* @param insn {@code non-null;} the original instruction
* @return the corresponding dalvik opcode; one of the constants in
* {@link Dops}
*/
public static Dop dopFor(Insn insn) {
Rop rop = insn.getOpcode();
/*
* First, just try looking up the rop in the MAP of easy
* cases.
*/
Dop result = MAP.get(rop);
if (result != null) {
return result;
}
/*
* There was no easy case for the rop, so look up the opcode, and
* do something special for each:
*
* The move_exception, new_array, filled_new_array, and
* invoke* opcodes won't be found in MAP, since they'll each
* have different source and/or result register types / lists.
*
* The get* and put* opcodes for (non-long) integral types
* aren't in the map, since the type signatures aren't
* sufficient to distinguish between the types (the salient
* source or result will always be just "int").
*
* And const instruction need to distinguish between strings and
* classes.
*/
switch (rop.getOpcode()) {
case RegOps.MOVE_EXCEPTION: return Dops.MOVE_EXCEPTION;
case RegOps.INVOKE_STATIC: return Dops.INVOKE_STATIC;
case RegOps.INVOKE_VIRTUAL: return Dops.INVOKE_VIRTUAL;
case RegOps.INVOKE_SUPER: return Dops.INVOKE_SUPER;
case RegOps.INVOKE_DIRECT: return Dops.INVOKE_DIRECT;
case RegOps.INVOKE_INTERFACE: return Dops.INVOKE_INTERFACE;
case RegOps.NEW_ARRAY: return Dops.NEW_ARRAY;
case RegOps.FILLED_NEW_ARRAY: return Dops.FILLED_NEW_ARRAY;
case RegOps.FILL_ARRAY_DATA: return Dops.FILL_ARRAY_DATA;
case RegOps.MOVE_RESULT: {
RegisterSpec resultReg = insn.getResult();
if (resultReg == null) {
return Dops.NOP;
} else {
switch (resultReg.getBasicType()) {
case Type.BT_INT:
case Type.BT_FLOAT:
case Type.BT_BOOLEAN:
case Type.BT_BYTE:
case Type.BT_CHAR:
case Type.BT_SHORT:
return Dops.MOVE_RESULT;
case Type.BT_LONG:
case Type.BT_DOUBLE:
return Dops.MOVE_RESULT_WIDE;
case Type.BT_OBJECT:
return Dops.MOVE_RESULT_OBJECT;
default: {
throw new RuntimeException("Unexpected basic type");
}
}
}
}
case RegOps.GET_FIELD: {
CstFieldRef ref =
(CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
int basicType = ref.getBasicType();
switch (basicType) {
case Type.BT_BOOLEAN: return Dops.IGET_BOOLEAN;
case Type.BT_BYTE: return Dops.IGET_BYTE;
case Type.BT_CHAR: return Dops.IGET_CHAR;
case Type.BT_SHORT: return Dops.IGET_SHORT;
case Type.BT_INT: return Dops.IGET;
}
break;
}
case RegOps.PUT_FIELD: {
CstFieldRef ref =
(CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
int basicType = ref.getBasicType();
switch (basicType) {
case Type.BT_BOOLEAN: return Dops.IPUT_BOOLEAN;
case Type.BT_BYTE: return Dops.IPUT_BYTE;
case Type.BT_CHAR: return Dops.IPUT_CHAR;
case Type.BT_SHORT: return Dops.IPUT_SHORT;
case Type.BT_INT: return Dops.IPUT;
}
break;
}
case RegOps.GET_STATIC: {
CstFieldRef ref =
(CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
int basicType = ref.getBasicType();
switch (basicType) {
case Type.BT_BOOLEAN: return Dops.SGET_BOOLEAN;
case Type.BT_BYTE: return Dops.SGET_BYTE;
case Type.BT_CHAR: return Dops.SGET_CHAR;
case Type.BT_SHORT: return Dops.SGET_SHORT;
case Type.BT_INT: return Dops.SGET;
}
break;
}
case RegOps.PUT_STATIC: {
CstFieldRef ref =
(CstFieldRef) ((ThrowingCstInsn) insn).getConstant();
int basicType = ref.getBasicType();
switch (basicType) {
case Type.BT_BOOLEAN: return Dops.SPUT_BOOLEAN;
case Type.BT_BYTE: return Dops.SPUT_BYTE;
case Type.BT_CHAR: return Dops.SPUT_CHAR;
case Type.BT_SHORT: return Dops.SPUT_SHORT;
case Type.BT_INT: return Dops.SPUT;
}
break;
}
case RegOps.CONST: {
Constant cst = ((ThrowingCstInsn) insn).getConstant();
if (cst instanceof CstType) {
return Dops.CONST_CLASS;
} else if (cst instanceof CstString) {
return Dops.CONST_STRING;
}
break;
}
}
throw new RuntimeException("unknown rop: " + rop);
}
}

View File

@ -0,0 +1,872 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.BasicBlock;
import com.android.dexgen.rop.code.BasicBlockList;
import com.android.dexgen.rop.code.FillArrayDataInsn;
import com.android.dexgen.rop.code.Insn;
import com.android.dexgen.rop.code.LocalVariableInfo;
import com.android.dexgen.rop.code.PlainCstInsn;
import com.android.dexgen.rop.code.PlainInsn;
import com.android.dexgen.rop.code.RegOps;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.RegisterSpecSet;
import com.android.dexgen.rop.code.Rop;
import com.android.dexgen.rop.code.RopMethod;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.rop.code.SwitchInsn;
import com.android.dexgen.rop.code.ThrowingCstInsn;
import com.android.dexgen.rop.code.ThrowingInsn;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstInteger;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.Bits;
import com.android.dexgen.util.IntList;
import java.util.ArrayList;
/**
* Translator from {@link RopMethod} to {@link DalvCode}. The {@link
* #translate} method is the thing to call on this class.
*/
public final class RopTranslator {
/** {@code non-null;} method to translate */
private final RopMethod method;
/**
* how much position info to preserve; one of the static
* constants in {@link PositionList}
*/
private final int positionInfo;
/** {@code null-ok;} local variable info to use */
private final LocalVariableInfo locals;
/** {@code non-null;} container for all the address objects for the method */
private final BlockAddresses addresses;
/** {@code non-null;} list of output instructions in-progress */
private final OutputCollector output;
/** {@code non-null;} visitor to use during translation */
private final TranslationVisitor translationVisitor;
/** {@code >= 0;} register count for the method */
private final int regCount;
/** {@code null-ok;} block output order; becomes non-null in {@link #pickOrder} */
private int[] order;
/** size, in register units, of all the parameters to this method */
private final int paramSize;
/**
* true if the parameters to this method happen to be in proper order
* at the end of the frame (as the optimizer emits them)
*/
private boolean paramsAreInOrder;
/**
* Translates a {@link RopMethod}. This may modify the given
* input.
*
* @param method {@code non-null;} the original method
* @param positionInfo how much position info to preserve; one of the
* static constants in {@link PositionList}
* @param locals {@code null-ok;} local variable information to use
* @param paramSize size, in register units, of all the parameters to
* this method
* @return {@code non-null;} the translated version
*/
public static DalvCode translate(RopMethod method, int positionInfo,
LocalVariableInfo locals, int paramSize) {
RopTranslator translator =
new RopTranslator(method, positionInfo, locals,
paramSize);
return translator.translateAndGetResult();
}
/**
* Constructs an instance. This method is private. Use {@link #translate}.
*
* @param method {@code non-null;} the original method
* @param positionInfo how much position info to preserve; one of the
* static constants in {@link PositionList}
* @param locals {@code null-ok;} local variable information to use
* @param paramSize size, in register units, of all the parameters to
* this method
*/
private RopTranslator(RopMethod method, int positionInfo,
LocalVariableInfo locals, int paramSize) {
this.method = method;
this.positionInfo = positionInfo;
this.locals = locals;
this.addresses = new BlockAddresses(method);
this.paramSize = paramSize;
this.order = null;
this.paramsAreInOrder = calculateParamsAreInOrder(method, paramSize);
BasicBlockList blocks = method.getBlocks();
int bsz = blocks.size();
/*
* Max possible instructions includes three code address
* objects per basic block (to the first and last instruction,
* and just past the end of the block), and the possibility of
* an extra goto at the end of each basic block.
*/
int maxInsns = (bsz * 3) + blocks.getInstructionCount();
if (locals != null) {
/*
* If we're tracking locals, then there's could be another
* extra instruction per block (for the locals state at the
* start of the block) as well as one for each interblock
* local introduction.
*/
maxInsns += bsz + locals.getAssignmentCount();
}
/*
* If params are not in order, we will need register space
* for them before this is all over...
*/
this.regCount = blocks.getRegCount()
+ (paramsAreInOrder ? 0 : this.paramSize);
this.output = new OutputCollector(maxInsns, bsz * 3, regCount);
if (locals != null) {
this.translationVisitor =
new LocalVariableAwareTranslationVisitor(output, locals);
} else {
this.translationVisitor = new TranslationVisitor(output);
}
}
/**
* Checks to see if the move-param instructions that occur in this
* method happen to slot the params in an order at the top of the
* stack frame that matches dalvik's calling conventions. This will
* alway result in "true" for methods that have run through the
* SSA optimizer.
*
* @param paramSize size, in register units, of all the parameters
* to this method
*/
private static boolean calculateParamsAreInOrder(RopMethod method,
final int paramSize) {
final boolean[] paramsAreInOrder = { true };
final int initialRegCount = method.getBlocks().getRegCount();
/*
* We almost could just check the first block here, but the
* {@code cf} layer will put in a second move-param in a
* subsequent block in the case of synchronized methods.
*/
method.getBlocks().forEachInsn(new Insn.BaseVisitor() {
public void visitPlainCstInsn(PlainCstInsn insn) {
if (insn.getOpcode().getOpcode()== RegOps.MOVE_PARAM) {
int param =
((CstInteger) insn.getConstant()).getValue();
paramsAreInOrder[0] = paramsAreInOrder[0]
&& ((initialRegCount - paramSize + param)
== insn.getResult().getReg());
}
}
});
return paramsAreInOrder[0];
}
/**
* Does the translation and returns the result.
*
* @return {@code non-null;} the result
*/
private DalvCode translateAndGetResult() {
pickOrder();
outputInstructions();
StdCatchBuilder catches =
new StdCatchBuilder(method, order, addresses);
return new DalvCode(positionInfo, output.getFinisher(), catches);
}
/**
* Performs initial creation of output instructions based on the
* original blocks.
*/
private void outputInstructions() {
BasicBlockList blocks = method.getBlocks();
int[] order = this.order;
int len = order.length;
// Process the blocks in output order.
for (int i = 0; i < len; i++) {
int nextI = i + 1;
int nextLabel = (nextI == order.length) ? -1 : order[nextI];
outputBlock(blocks.labelToBlock(order[i]), nextLabel);
}
}
/**
* Helper for {@link #outputInstructions}, which does the processing
* and output of one block.
*
* @param block {@code non-null;} the block to process and output
* @param nextLabel {@code >= -1;} the next block that will be processed, or
* {@code -1} if there is no next block
*/
private void outputBlock(BasicBlock block, int nextLabel) {
// Append the code address for this block.
CodeAddress startAddress = addresses.getStart(block);
output.add(startAddress);
// Append the local variable state for the block.
if (locals != null) {
RegisterSpecSet starts = locals.getStarts(block);
output.add(new LocalSnapshot(startAddress.getPosition(),
starts));
}
/*
* Choose and append an output instruction for each original
* instruction.
*/
translationVisitor.setBlock(block, addresses.getLast(block));
block.getInsns().forEach(translationVisitor);
// Insert the block end code address.
output.add(addresses.getEnd(block));
// Set up for end-of-block activities.
int succ = block.getPrimarySuccessor();
Insn lastInsn = block.getLastInsn();
/*
* Check for (and possibly correct for) a non-optimal choice of
* which block will get output next.
*/
if ((succ >= 0) && (succ != nextLabel)) {
/*
* The block has a "primary successor" and that primary
* successor isn't the next block to be output.
*/
Rop lastRop = lastInsn.getOpcode();
if ((lastRop.getBranchingness() == Rop.BRANCH_IF) &&
(block.getSecondarySuccessor() == nextLabel)) {
/*
* The block ends with an "if" of some sort, and its
* secondary successor (the "then") is in fact the
* next block to output. So, reverse the sense of
* the test, so that we can just emit the next block
* without an interstitial goto.
*/
output.reverseBranch(1, addresses.getStart(succ));
} else {
/*
* Our only recourse is to add a goto here to get the
* flow to be correct.
*/
TargetInsn insn =
new TargetInsn(Dops.GOTO, lastInsn.getPosition(),
RegisterSpecList.EMPTY,
addresses.getStart(succ));
output.add(insn);
}
}
}
/**
* Picks an order for the blocks by doing "trace" analysis.
*/
private void pickOrder() {
BasicBlockList blocks = method.getBlocks();
int sz = blocks.size();
int maxLabel = blocks.getMaxLabel();
int[] workSet = Bits.makeBitSet(maxLabel);
int[] tracebackSet = Bits.makeBitSet(maxLabel);
for (int i = 0; i < sz; i++) {
BasicBlock one = blocks.get(i);
Bits.set(workSet, one.getLabel());
}
int[] order = new int[sz];
int at = 0;
/*
* Starting with the designated "first label" (that is, the
* first block of the method), add that label to the order,
* and then pick its first as-yet unordered successor to
* immediately follow it, giving top priority to the primary
* (aka default) successor (if any). Keep following successors
* until the trace runs out of possibilities. Then, continue
* by finding an unordered chain containing the first as-yet
* unordered block, and adding it to the order, and so on.
*/
for (int label = method.getFirstLabel();
label != -1;
label = Bits.findFirst(workSet, 0)) {
/*
* Attempt to trace backward from the chosen block to an
* as-yet unordered predecessor which lists the chosen
* block as its primary successor, and so on, until we
* fail to find such an unordered predecessor. Start the
* trace with that block. Note that the first block in the
* method has no predecessors, so in that case this loop
* will simply terminate with zero iterations and without
* picking a new starter block.
*/
traceBack:
for (;;) {
IntList preds = method.labelToPredecessors(label);
int psz = preds.size();
for (int i = 0; i < psz; i++) {
int predLabel = preds.get(i);
if (Bits.get(tracebackSet, predLabel)) {
/*
* We found a predecessor loop; stop tracing back
* from here.
*/
break;
}
if (!Bits.get(workSet, predLabel)) {
// This one's already ordered.
continue;
}
BasicBlock pred = blocks.labelToBlock(predLabel);
if (pred.getPrimarySuccessor() == label) {
// Found one!
label = predLabel;
Bits.set(tracebackSet, label);
continue traceBack;
}
}
// Failed to find a better block to start the trace.
break;
}
/*
* Trace a path from the chosen block to one of its
* unordered successors (hopefully the primary), and so
* on, until we run out of unordered successors.
*/
while (label != -1) {
Bits.clear(workSet, label);
Bits.clear(tracebackSet, label);
order[at] = label;
at++;
BasicBlock one = blocks.labelToBlock(label);
BasicBlock preferredBlock = blocks.preferredSuccessorOf(one);
if (preferredBlock == null) {
break;
}
int preferred = preferredBlock.getLabel();
int primary = one.getPrimarySuccessor();
if (Bits.get(workSet, preferred)) {
/*
* Order the current block's preferred successor
* next, as it has yet to be scheduled.
*/
label = preferred;
} else if ((primary != preferred) && (primary >= 0)
&& Bits.get(workSet, primary)) {
/*
* The primary is available, so use that.
*/
label = primary;
} else {
/*
* There's no obvious candidate, so pick the first
* one that's available, if any.
*/
IntList successors = one.getSuccessors();
int ssz = successors.size();
label = -1;
for (int i = 0; i < ssz; i++) {
int candidate = successors.get(i);
if (Bits.get(workSet, candidate)) {
label = candidate;
break;
}
}
}
}
}
if (at != sz) {
// There was a duplicate block label.
throw new RuntimeException("shouldn't happen");
}
this.order = order;
}
/**
* Gets the complete register list (result and sources) out of a
* given rop instruction. For insns that are commutative, have
* two register sources, and have a source equal to the result,
* place that source first.
*
* @param insn {@code non-null;} instruction in question
* @return {@code non-null;} the instruction's complete register list
*/
private static RegisterSpecList getRegs(Insn insn) {
return getRegs(insn, insn.getResult());
}
/**
* Gets the complete register list (result and sources) out of a
* given rop instruction. For insns that are commutative, have
* two register sources, and have a source equal to the result,
* place that source first.
*
* @param insn {@code non-null;} instruction in question
* @param resultReg {@code null-ok;} the real result to use (ignore the insn's)
* @return {@code non-null;} the instruction's complete register list
*/
private static RegisterSpecList getRegs(Insn insn,
RegisterSpec resultReg) {
RegisterSpecList regs = insn.getSources();
if (insn.getOpcode().isCommutative()
&& (regs.size() == 2)
&& (resultReg.getReg() == regs.get(1).getReg())) {
/*
* For commutative ops which have two register sources,
* if the second source is the same register as the result,
* swap the sources so that an opcode of form 12x can be selected
* instead of one of form 23x
*/
regs = RegisterSpecList.make(regs.get(1), regs.get(0));
}
if (resultReg == null) {
return regs;
}
return regs.withFirst(resultReg);
}
/**
* Instruction visitor class for doing the instruction translation per se.
*/
private class TranslationVisitor implements Insn.Visitor {
/** {@code non-null;} list of output instructions in-progress */
private final OutputCollector output;
/** {@code non-null;} basic block being worked on */
private BasicBlock block;
/**
* {@code null-ok;} code address for the salient last instruction of the
* block (used before switches and throwing instructions)
*/
private CodeAddress lastAddress;
/**
* Constructs an instance.
*
* @param output {@code non-null;} destination for instruction output
*/
public TranslationVisitor(OutputCollector output) {
this.output = output;
}
/**
* Sets the block currently being worked on.
*
* @param block {@code non-null;} the block
* @param lastAddress {@code non-null;} code address for the salient
* last instruction of the block
*/
public void setBlock(BasicBlock block, CodeAddress lastAddress) {
this.block = block;
this.lastAddress = lastAddress;
}
/** {@inheritDoc} */
public void visitPlainInsn(PlainInsn insn) {
Rop rop = insn.getOpcode();
if (rop.getOpcode() == RegOps.MARK_LOCAL) {
/*
* Ignore these. They're dealt with by
* the LocalVariableAwareTranslationVisitor
*/
return;
}
if (rop.getOpcode() == RegOps.MOVE_RESULT_PSEUDO) {
// These get skipped
return;
}
SourcePosition pos = insn.getPosition();
Dop opcode = RopToDop.dopFor(insn);
DalvInsn di;
switch (rop.getBranchingness()) {
case Rop.BRANCH_NONE:
case Rop.BRANCH_RETURN:
case Rop.BRANCH_THROW: {
di = new SimpleInsn(opcode, pos, getRegs(insn));
break;
}
case Rop.BRANCH_GOTO: {
/*
* Code in the main translation loop will emit a
* goto if necessary (if the branch isn't to the
* immediately subsequent block).
*/
return;
}
case Rop.BRANCH_IF: {
int target = block.getSuccessors().get(1);
di = new TargetInsn(opcode, pos, getRegs(insn),
addresses.getStart(target));
break;
}
default: {
throw new RuntimeException("shouldn't happen");
}
}
addOutput(di);
}
/** {@inheritDoc} */
public void visitPlainCstInsn(PlainCstInsn insn) {
SourcePosition pos = insn.getPosition();
Dop opcode = RopToDop.dopFor(insn);
Rop rop = insn.getOpcode();
int ropOpcode = rop.getOpcode();
DalvInsn di;
if (rop.getBranchingness() != Rop.BRANCH_NONE) {
throw new RuntimeException("shouldn't happen");
}
if (ropOpcode == RegOps.MOVE_PARAM) {
if (!paramsAreInOrder) {
/*
* Parameters are not in order at the top of the reg space.
* We need to add moves.
*/
RegisterSpec dest = insn.getResult();
int param =
((CstInteger) insn.getConstant()).getValue();
RegisterSpec source =
RegisterSpec.make(regCount - paramSize + param,
dest.getType());
di = new SimpleInsn(opcode, pos,
RegisterSpecList.make(dest, source));
addOutput(di);
}
} else {
// No moves required for the parameters
RegisterSpecList regs = getRegs(insn);
di = new CstInsn(opcode, pos, regs, insn.getConstant());
addOutput(di);
}
}
/** {@inheritDoc} */
public void visitSwitchInsn(SwitchInsn insn) {
SourcePosition pos = insn.getPosition();
IntList cases = insn.getCases();
IntList successors = block.getSuccessors();
int casesSz = cases.size();
int succSz = successors.size();
int primarySuccessor = block.getPrimarySuccessor();
/*
* Check the assumptions that the number of cases is one
* less than the number of successors and that the last
* successor in the list is the primary (in this case, the
* default). This test is here to guard against forgetting
* to change this code if the way switch instructions are
* constructed also gets changed.
*/
if ((casesSz != (succSz - 1)) ||
(primarySuccessor != successors.get(casesSz))) {
throw new RuntimeException("shouldn't happen");
}
CodeAddress[] switchTargets = new CodeAddress[casesSz];
for (int i = 0; i < casesSz; i++) {
int label = successors.get(i);
switchTargets[i] = addresses.getStart(label);
}
CodeAddress dataAddress = new CodeAddress(pos);
SwitchData dataInsn =
new SwitchData(pos, lastAddress, cases, switchTargets);
Dop opcode = dataInsn.isPacked() ?
Dops.PACKED_SWITCH : Dops.SPARSE_SWITCH;
TargetInsn switchInsn =
new TargetInsn(opcode, pos, getRegs(insn), dataAddress);
addOutput(lastAddress);
addOutput(switchInsn);
addOutputSuffix(new OddSpacer(pos));
addOutputSuffix(dataAddress);
addOutputSuffix(dataInsn);
}
/**
* Looks forward to the current block's primary successor, returning
* the RegisterSpec of the result of the move-result-pseudo at the
* top of that block or null if none.
*
* @return {@code null-ok;} result of move-result-pseudo at the beginning of
* primary successor
*/
private RegisterSpec getNextMoveResultPseudo()
{
int label = block.getPrimarySuccessor();
if (label < 0) {
return null;
}
Insn insn
= method.getBlocks().labelToBlock(label).getInsns().get(0);
if (insn.getOpcode().getOpcode() != RegOps.MOVE_RESULT_PSEUDO) {
return null;
} else {
return insn.getResult();
}
}
/** {@inheritDoc} */
public void visitThrowingCstInsn(ThrowingCstInsn insn) {
SourcePosition pos = insn.getPosition();
Dop opcode = RopToDop.dopFor(insn);
Rop rop = insn.getOpcode();
Constant cst = insn.getConstant();
if (rop.getBranchingness() != Rop.BRANCH_THROW) {
throw new RuntimeException("shouldn't happen");
}
addOutput(lastAddress);
if (rop.isCallLike()) {
RegisterSpecList regs = insn.getSources();
DalvInsn di = new CstInsn(opcode, pos, regs, cst);
addOutput(di);
} else {
RegisterSpec realResult = getNextMoveResultPseudo();
RegisterSpecList regs = getRegs(insn, realResult);
DalvInsn di;
boolean hasResult = opcode.hasResult()
|| (rop.getOpcode() == RegOps.CHECK_CAST);
if (hasResult != (realResult != null)) {
throw new RuntimeException(
"Insn with result/move-result-pseudo mismatch " +
insn);
}
if ((rop.getOpcode() == RegOps.NEW_ARRAY) &&
(opcode.getOpcode() != DalvOps.NEW_ARRAY)) {
/*
* It's a type-specific new-array-<primitive>, and
* so it should be turned into a SimpleInsn (no
* constant ref as it's implicit).
*/
di = new SimpleInsn(opcode, pos, regs);
} else {
/*
* This is the general case for constant-bearing
* instructions.
*/
di = new CstInsn(opcode, pos, regs, cst);
}
addOutput(di);
}
}
/** {@inheritDoc} */
public void visitThrowingInsn(ThrowingInsn insn) {
SourcePosition pos = insn.getPosition();
Dop opcode = RopToDop.dopFor(insn);
Rop rop = insn.getOpcode();
RegisterSpec realResult;
if (rop.getBranchingness() != Rop.BRANCH_THROW) {
throw new RuntimeException("shouldn't happen");
}
realResult = getNextMoveResultPseudo();
if (opcode.hasResult() != (realResult != null)) {
throw new RuntimeException(
"Insn with result/move-result-pseudo mismatch" + insn);
}
addOutput(lastAddress);
DalvInsn di = new SimpleInsn(opcode, pos,
getRegs(insn, realResult));
addOutput(di);
}
/** {@inheritDoc} */
public void visitFillArrayDataInsn(FillArrayDataInsn insn) {
SourcePosition pos = insn.getPosition();
Constant cst = insn.getConstant();
ArrayList<Constant> values = insn.getInitValues();
Rop rop = insn.getOpcode();
if (rop.getBranchingness() != Rop.BRANCH_NONE) {
throw new RuntimeException("shouldn't happen");
}
CodeAddress dataAddress = new CodeAddress(pos);
ArrayData dataInsn =
new ArrayData(pos, lastAddress, values, cst);
TargetInsn fillArrayDataInsn =
new TargetInsn(Dops.FILL_ARRAY_DATA, pos, getRegs(insn),
dataAddress);
addOutput(lastAddress);
addOutput(fillArrayDataInsn);
addOutputSuffix(new OddSpacer(pos));
addOutputSuffix(dataAddress);
addOutputSuffix(dataInsn);
}
/**
* Adds to the output.
*
* @param insn {@code non-null;} instruction to add
*/
protected void addOutput(DalvInsn insn) {
output.add(insn);
}
/**
* Adds to the output suffix.
*
* @param insn {@code non-null;} instruction to add
*/
protected void addOutputSuffix(DalvInsn insn) {
output.addSuffix(insn);
}
}
/**
* Instruction visitor class for doing instruction translation with
* local variable tracking
*/
private class LocalVariableAwareTranslationVisitor
extends TranslationVisitor {
/** {@code non-null;} local variable info */
private LocalVariableInfo locals;
/**
* Constructs an instance.
*
* @param output {@code non-null;} destination for instruction output
* @param locals {@code non-null;} the local variable info
*/
public LocalVariableAwareTranslationVisitor(OutputCollector output,
LocalVariableInfo locals) {
super(output);
this.locals = locals;
}
/** {@inheritDoc} */
@Override
public void visitPlainInsn(PlainInsn insn) {
super.visitPlainInsn(insn);
addIntroductionIfNecessary(insn);
}
/** {@inheritDoc} */
@Override
public void visitPlainCstInsn(PlainCstInsn insn) {
super.visitPlainCstInsn(insn);
addIntroductionIfNecessary(insn);
}
/** {@inheritDoc} */
@Override
public void visitSwitchInsn(SwitchInsn insn) {
super.visitSwitchInsn(insn);
addIntroductionIfNecessary(insn);
}
/** {@inheritDoc} */
@Override
public void visitThrowingCstInsn(ThrowingCstInsn insn) {
super.visitThrowingCstInsn(insn);
addIntroductionIfNecessary(insn);
}
/** {@inheritDoc} */
@Override
public void visitThrowingInsn(ThrowingInsn insn) {
super.visitThrowingInsn(insn);
addIntroductionIfNecessary(insn);
}
/**
* Adds a {@link LocalStart} to the output if the given
* instruction in fact introduces a local variable.
*
* @param insn {@code non-null;} instruction in question
*/
public void addIntroductionIfNecessary(Insn insn) {
RegisterSpec spec = locals.getAssignment(insn);
if (spec != null) {
addOutput(new LocalStart(insn.getPosition(), spec));
}
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Instruction which has no extra info beyond the basics provided for in
* the base class.
*/
public final class SimpleInsn extends FixedSizeInsn {
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param opcode the opcode; one of the constants from {@link Dops}
* @param position {@code non-null;} source position
* @param registers {@code non-null;} register list, including a
* result register if appropriate (that is, registers may be either
* ins or outs)
*/
public SimpleInsn(Dop opcode, SourcePosition position,
RegisterSpecList registers) {
super(opcode, position, registers);
}
/** {@inheritDoc} */
@Override
public DalvInsn withOpcode(Dop opcode) {
return new SimpleInsn(opcode, getPosition(), getRegisters());
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new SimpleInsn(getOpcode(), getPosition(), registers);
}
/** {@inheritDoc} */
@Override
protected String argString() {
return null;
}
}

View File

@ -0,0 +1,316 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.BasicBlock;
import com.android.dexgen.rop.code.BasicBlockList;
import com.android.dexgen.rop.code.RopMethod;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.util.IntList;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Constructor of {@link CatchTable} instances from {@link RopMethod}
* and associated data.
*/
public final class StdCatchBuilder implements CatchBuilder {
/** the maximum range of a single catch handler, in code units */
private static final int MAX_CATCH_RANGE = 65535;
/** {@code non-null;} method to build the list for */
private final RopMethod method;
/** {@code non-null;} block output order */
private final int[] order;
/** {@code non-null;} address objects for each block */
private final BlockAddresses addresses;
/**
* Constructs an instance. It merely holds onto its parameters for
* a subsequent call to {@link #build}.
*
* @param method {@code non-null;} method to build the list for
* @param order {@code non-null;} block output order
* @param addresses {@code non-null;} address objects for each block
*/
public StdCatchBuilder(RopMethod method, int[] order,
BlockAddresses addresses) {
if (method == null) {
throw new NullPointerException("method == null");
}
if (order == null) {
throw new NullPointerException("order == null");
}
if (addresses == null) {
throw new NullPointerException("addresses == null");
}
this.method = method;
this.order = order;
this.addresses = addresses;
}
/** {@inheritDoc} */
public CatchTable build() {
return build(method, order, addresses);
}
/** {@inheritDoc} */
public boolean hasAnyCatches() {
BasicBlockList blocks = method.getBlocks();
int size = blocks.size();
for (int i = 0; i < size; i++) {
BasicBlock block = blocks.get(i);
TypeList catches = block.getLastInsn().getCatches();
if (catches.size() != 0) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
public HashSet<Type> getCatchTypes() {
HashSet<Type> result = new HashSet<Type>(20);
BasicBlockList blocks = method.getBlocks();
int size = blocks.size();
for (int i = 0; i < size; i++) {
BasicBlock block = blocks.get(i);
TypeList catches = block.getLastInsn().getCatches();
int catchSize = catches.size();
for (int j = 0; j < catchSize; j++) {
result.add(catches.getType(j));
}
}
return result;
}
/**
* Builds and returns the catch table for a given method.
*
* @param method {@code non-null;} method to build the list for
* @param order {@code non-null;} block output order
* @param addresses {@code non-null;} address objects for each block
* @return {@code non-null;} the constructed table
*/
public static CatchTable build(RopMethod method, int[] order,
BlockAddresses addresses) {
int len = order.length;
BasicBlockList blocks = method.getBlocks();
ArrayList<CatchTable.Entry> resultList =
new ArrayList<CatchTable.Entry>(len);
CatchHandlerList currentHandlers = CatchHandlerList.EMPTY;
BasicBlock currentStartBlock = null;
BasicBlock currentEndBlock = null;
for (int i = 0; i < len; i++) {
BasicBlock block = blocks.labelToBlock(order[i]);
if (!block.canThrow()) {
/*
* There is no need to concern ourselves with the
* placement of blocks that can't throw with respect
* to the blocks that *can* throw.
*/
continue;
}
CatchHandlerList handlers = handlersFor(block, addresses);
if (currentHandlers.size() == 0) {
// This is the start of a new catch range.
currentStartBlock = block;
currentEndBlock = block;
currentHandlers = handlers;
continue;
}
if (currentHandlers.equals(handlers)
&& rangeIsValid(currentStartBlock, block, addresses)) {
/*
* The block we are looking at now has the same handlers
* as the block that started the currently open catch
* range, and adding it to the currently open range won't
* cause it to be too long.
*/
currentEndBlock = block;
continue;
}
/*
* The block we are looking at now has incompatible handlers,
* so we need to finish off the last entry and start a new
* one. Note: We only emit an entry if it has associated handlers.
*/
if (currentHandlers.size() != 0) {
CatchTable.Entry entry =
makeEntry(currentStartBlock, currentEndBlock,
currentHandlers, addresses);
resultList.add(entry);
}
currentStartBlock = block;
currentEndBlock = block;
currentHandlers = handlers;
}
if (currentHandlers.size() != 0) {
// Emit an entry for the range that was left hanging.
CatchTable.Entry entry =
makeEntry(currentStartBlock, currentEndBlock,
currentHandlers, addresses);
resultList.add(entry);
}
// Construct the final result.
int resultSz = resultList.size();
if (resultSz == 0) {
return CatchTable.EMPTY;
}
CatchTable result = new CatchTable(resultSz);
for (int i = 0; i < resultSz; i++) {
result.set(i, resultList.get(i));
}
result.setImmutable();
return result;
}
/**
* Makes the {@link CatchHandlerList} for the given basic block.
*
* @param block {@code non-null;} block to get entries for
* @param addresses {@code non-null;} address objects for each block
* @return {@code non-null;} array of entries
*/
private static CatchHandlerList handlersFor(BasicBlock block,
BlockAddresses addresses) {
IntList successors = block.getSuccessors();
int succSize = successors.size();
int primary = block.getPrimarySuccessor();
TypeList catches = block.getLastInsn().getCatches();
int catchSize = catches.size();
if (catchSize == 0) {
return CatchHandlerList.EMPTY;
}
if (((primary == -1) && (succSize != catchSize))
|| ((primary != -1) &&
((succSize != (catchSize + 1))
|| (primary != successors.get(catchSize))))) {
/*
* Blocks that throw are supposed to list their primary
* successor -- if any -- last in the successors list, but
* that constraint appears to be violated here.
*/
throw new RuntimeException(
"shouldn't happen: weird successors list");
}
/*
* Reduce the effective catchSize if we spot a catch-all that
* isn't at the end.
*/
for (int i = 0; i < catchSize; i++) {
Type type = catches.getType(i);
if (type.equals(Type.OBJECT)) {
catchSize = i + 1;
break;
}
}
CatchHandlerList result = new CatchHandlerList(catchSize);
for (int i = 0; i < catchSize; i++) {
CstType oneType = new CstType(catches.getType(i));
CodeAddress oneHandler = addresses.getStart(successors.get(i));
result.set(i, oneType, oneHandler.getAddress());
}
result.setImmutable();
return result;
}
/**
* Makes a {@link CatchTable#Entry} for the given block range and
* handlers.
*
* @param start {@code non-null;} the start block for the range (inclusive)
* @param end {@code non-null;} the start block for the range (also inclusive)
* @param handlers {@code non-null;} the handlers for the range
* @param addresses {@code non-null;} address objects for each block
*/
private static CatchTable.Entry makeEntry(BasicBlock start,
BasicBlock end, CatchHandlerList handlers,
BlockAddresses addresses) {
/*
* We start at the *last* instruction of the start block, since
* that's the instruction that can throw...
*/
CodeAddress startAddress = addresses.getLast(start);
// ...And we end *after* the last instruction of the end block.
CodeAddress endAddress = addresses.getEnd(end);
return new CatchTable.Entry(startAddress.getAddress(),
endAddress.getAddress(), handlers);
}
/**
* Gets whether the address range for the given two blocks is valid
* for a catch handler. This is true as long as the covered range is
* under 65536 code units.
*
* @param start {@code non-null;} the start block for the range (inclusive)
* @param end {@code non-null;} the start block for the range (also inclusive)
* @param addresses {@code non-null;} address objects for each block
* @return {@code true} if the range is valid as a catch range
*/
private static boolean rangeIsValid(BasicBlock start, BasicBlock end,
BlockAddresses addresses) {
if (start == null) {
throw new NullPointerException("start == null");
}
if (end == null) {
throw new NullPointerException("end == null");
}
// See above about selection of instructions.
int startAddress = addresses.getLast(start).getAddress();
int endAddress = addresses.getEnd(end).getAddress();
return (endAddress - startAddress) <= MAX_CATCH_RANGE;
}
}

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.IntList;
/**
* Pseudo-instruction which holds switch data. The switch data is
* a map of values to target addresses, and this class writes the data
* in either a "packed" or "sparse" form.
*/
public final class SwitchData extends VariableSizeInsn {
/**
* {@code non-null;} address representing the instruction that uses this
* instance
*/
private final CodeAddress user;
/** {@code non-null;} sorted list of switch cases (keys) */
private final IntList cases;
/**
* {@code non-null;} corresponding list of code addresses; the branch
* target for each case
*/
private final CodeAddress[] targets;
/** whether the output table will be packed (vs. sparse) */
private final boolean packed;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param user {@code non-null;} address representing the instruction that
* uses this instance
* @param cases {@code non-null;} sorted list of switch cases (keys)
* @param targets {@code non-null;} corresponding list of code addresses; the
* branch target for each case
*/
public SwitchData(SourcePosition position, CodeAddress user,
IntList cases, CodeAddress[] targets) {
super(position, RegisterSpecList.EMPTY);
if (user == null) {
throw new NullPointerException("user == null");
}
if (cases == null) {
throw new NullPointerException("cases == null");
}
if (targets == null) {
throw new NullPointerException("targets == null");
}
int sz = cases.size();
if (sz != targets.length) {
throw new IllegalArgumentException("cases / targets mismatch");
}
if (sz > 65535) {
throw new IllegalArgumentException("too many cases");
}
this.user = user;
this.cases = cases;
this.targets = targets;
this.packed = shouldPack(cases);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return packed ? (int) packedCodeSize(cases) :
(int) sparseCodeSize(cases);
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out) {
int baseAddress = user.getAddress();
int defaultTarget = Dops.PACKED_SWITCH.getFormat().codeSize();
int sz = targets.length;
if (packed) {
int firstCase = (sz == 0) ? 0 : cases.get(0);
int lastCase = (sz == 0) ? 0 : cases.get(sz - 1);
int outSz = lastCase - firstCase + 1;
out.writeShort(0x100 | DalvOps.NOP);
out.writeShort(outSz);
out.writeInt(firstCase);
int caseAt = 0;
for (int i = 0; i < outSz; i++) {
int outCase = firstCase + i;
int oneCase = cases.get(caseAt);
int relTarget;
if (oneCase > outCase) {
relTarget = defaultTarget;
} else {
relTarget = targets[caseAt].getAddress() - baseAddress;
caseAt++;
}
out.writeInt(relTarget);
}
} else {
out.writeShort(0x200 | DalvOps.NOP);
out.writeShort(sz);
for (int i = 0; i < sz; i++) {
out.writeInt(cases.get(i));
}
for (int i = 0; i < sz; i++) {
int relTarget = targets[i].getAddress() - baseAddress;
out.writeInt(relTarget);
}
}
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new SwitchData(getPosition(), user, cases, targets);
}
/**
* Returns whether or not this instance's data will be output as packed.
*
* @return {@code true} iff the data is to be packed
*/
public boolean isPacked() {
return packed;
}
/** {@inheritDoc} */
@Override
protected String argString() {
StringBuffer sb = new StringBuffer(100);
int sz = targets.length;
for (int i = 0; i < sz; i++) {
sb.append("\n ");
sb.append(cases.get(i));
sb.append(": ");
sb.append(targets[i]);
}
return sb.toString();
}
/** {@inheritDoc} */
@Override
protected String listingString0(boolean noteIndices) {
int baseAddress = user.getAddress();
StringBuffer sb = new StringBuffer(100);
int sz = targets.length;
sb.append(packed ? "packed" : "sparse");
sb.append("-switch-data // for switch @ ");
sb.append(Hex.u2(baseAddress));
for (int i = 0; i < sz; i++) {
int absTarget = targets[i].getAddress();
int relTarget = absTarget - baseAddress;
sb.append("\n ");
sb.append(cases.get(i));
sb.append(": ");
sb.append(Hex.u4(absTarget));
sb.append(" // ");
sb.append(Hex.s4(relTarget));
}
return sb.toString();
}
/**
* Gets the size of a packed table for the given cases, in 16-bit code
* units.
*
* @param cases {@code non-null;} sorted list of cases
* @return {@code >= -1;} the packed table size or {@code -1} if the
* cases couldn't possibly be represented as a packed table
*/
private static long packedCodeSize(IntList cases) {
int sz = cases.size();
long low = cases.get(0);
long high = cases.get(sz - 1);
long result = ((high - low + 1)) * 2 + 4;
return (result <= 0x7fffffff) ? result : -1;
}
/**
* Gets the size of a sparse table for the given cases, in 16-bit code
* units.
*
* @param cases {@code non-null;} sorted list of cases
* @return {@code > 0;} the sparse table size
*/
private static long sparseCodeSize(IntList cases) {
int sz = cases.size();
return (sz * 4L) + 2;
}
/**
* Determines whether the given list of cases warrant being packed.
*
* @param cases {@code non-null;} sorted list of cases
* @return {@code true} iff the table encoding the cases
* should be packed
*/
private static boolean shouldPack(IntList cases) {
int sz = cases.size();
if (sz < 2) {
return true;
}
long packedSize = packedCodeSize(cases);
long sparseSize = sparseCodeSize(cases);
/*
* We pick the packed representation if it is possible and
* would be as small or smaller than 5/4 of the sparse
* representation. That is, we accept some size overhead on
* the packed representation, since that format is faster to
* execute at runtime.
*/
return (packedSize >= 0) && (packedSize <= ((sparseSize * 5) / 4));
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Instruction which has a single branch target.
*/
public final class TargetInsn extends FixedSizeInsn {
/** {@code non-null;} the branch target */
private CodeAddress target;
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}), and the target is initially
* {@code null}.
*
* @param opcode the opcode; one of the constants from {@link Dops}
* @param position {@code non-null;} source position
* @param registers {@code non-null;} register list, including a
* result register if appropriate (that is, registers may be either
* ins or outs)
* @param target {@code non-null;} the branch target
*/
public TargetInsn(Dop opcode, SourcePosition position,
RegisterSpecList registers, CodeAddress target) {
super(opcode, position, registers);
if (target == null) {
throw new NullPointerException("target == null");
}
this.target = target;
}
/** {@inheritDoc} */
@Override
public DalvInsn withOpcode(Dop opcode) {
return new TargetInsn(opcode, getPosition(), getRegisters(), target);
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisters(RegisterSpecList registers) {
return new TargetInsn(getOpcode(), getPosition(), registers, target);
}
/**
* Returns an instance that is just like this one, except that its
* opcode has the opposite sense (as a test; e.g. a
* {@code lt} test becomes a {@code ge}), and its branch
* target is replaced by the one given, and all set-once values
* associated with the class (such as its address) are reset.
*
* @param target {@code non-null;} the new branch target
* @return {@code non-null;} an appropriately-constructed instance
*/
public TargetInsn withNewTargetAndReversed(CodeAddress target) {
Dop opcode = getOpcode().getOppositeTest();
return new TargetInsn(opcode, getPosition(), getRegisters(), target);
}
/**
* Gets the unique branch target of this instruction.
*
* @return {@code non-null;} the branch target
*/
public CodeAddress getTarget() {
return target;
}
/**
* Gets the target address of this instruction. This is only valid
* to call if the target instruction has been assigned an address,
* and it is merely a convenient shorthand for
* {@code getTarget().getAddress()}.
*
* @return {@code >= 0;} the target address
*/
public int getTargetAddress() {
return target.getAddress();
}
/**
* Gets the branch offset of this instruction. This is only valid to
* call if both this and the target instruction each has been assigned
* an address, and it is merely a convenient shorthand for
* {@code getTargetAddress() - getAddress()}.
*
* @return the branch offset
*/
public int getTargetOffset() {
return target.getAddress() - getAddress();
}
/**
* Returns whether the target offset is known.
*
* @return {@code true} if the target offset is known or
* {@code false} if not
*/
public boolean hasTargetOffset() {
return hasAddress() && target.hasAddress();
}
/** {@inheritDoc} */
@Override
protected String argString() {
if (target == null) {
return "????";
}
return target.identifierString();
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
/**
* Pseudo-instruction base class for variable-sized instructions.
*/
public abstract class VariableSizeInsn extends DalvInsn {
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
* @param registers {@code non-null;} source registers
*/
public VariableSizeInsn(SourcePosition position,
RegisterSpecList registers) {
super(Dops.SPECIAL_FORMAT, position, registers);
}
/** {@inheritDoc} */
@Override
public final DalvInsn withOpcode(Dop opcode) {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
public final DalvInsn withRegisterOffset(int delta) {
return withRegisters(getRegisters().withOffset(delta));
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Pseudo-instruction base class for zero-size (no code emitted)
* instructions, which are generally used for tracking metainformation
* about the code they are adjacent to.
*/
public abstract class ZeroSizeInsn extends DalvInsn {
/**
* Constructs an instance. The output address of this instance is initially
* unknown ({@code -1}).
*
* @param position {@code non-null;} source position
*/
public ZeroSizeInsn(SourcePosition position) {
super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY);
}
/** {@inheritDoc} */
@Override
public final int codeSize() {
return 0;
}
/** {@inheritDoc} */
@Override
public final void writeTo(AnnotatedOutput out) {
// Nothing to do here, for this class.
}
/** {@inheritDoc} */
@Override
public final DalvInsn withOpcode(Dop opcode) {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
public DalvInsn withRegisterOffset(int delta) {
return withRegisters(getRegisters().withOffset(delta));
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.TargetInsn;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 10t}. See the instruction format spec
* for details.
*/
public final class Form10t extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form10t();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form10t() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
return branchString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
return branchComment(insn);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 1;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!((insn instanceof TargetInsn) &&
(insn.getRegisters().size() == 0))) {
return false;
}
TargetInsn ti = (TargetInsn) insn;
return ti.hasTargetOffset() ? branchFits(ti) : true;
}
/** {@inheritDoc} */
@Override
public boolean branchFits(TargetInsn insn) {
int offset = insn.getTargetOffset();
// Note: A zero offset would fit, but it is prohibited by the spec.
return (offset != 0) && signedFitsInByte(offset);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form20t.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
int offset = ((TargetInsn) insn).getTargetOffset();
write(out, opcodeUnit(insn, (offset & 0xff)));
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.SimpleInsn;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 10x}. See the instruction format spec
* for details.
*/
public final class Form10x extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form10x();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form10x() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
// This format has no arguments.
return "";
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
// This format has no comment.
return "";
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 1;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
return (insn instanceof SimpleInsn) &&
(insn.getRegisters().size() == 0);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
write(out, opcodeUnit(insn, 0));
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 11n}. See the instruction format spec
* for details.
*/
public final class Form11n extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form11n();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form11n() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return literalBitsComment(value, 4);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 1;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 1) &&
unsignedFitsInNibble(regs.get(0).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
if (!(cst instanceof CstLiteralBits)) {
return false;
}
CstLiteralBits cb = (CstLiteralBits) cst;
return cb.fitsInInt() && signedFitsInNibble(cb.getIntBits());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form21s.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int value =
((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
write(out,
opcodeUnit(insn, makeByte(regs.get(0).getReg(), value & 0xf)));
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.SimpleInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 11x}. See the instruction format spec
* for details.
*/
public final class Form11x extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form11x();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form11x() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
// This format has no comment.
return "";
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 1;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return (insn instanceof SimpleInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
write(out, opcodeUnit(insn, regs.get(0).getReg()));
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.HighRegisterPrefix;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.SimpleInsn;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 12x}. See the instruction format spec
* for details.
*/
public final class Form12x extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form12x();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form12x() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int sz = regs.size();
/*
* The (sz - 2) and (sz - 1) below makes this code work for
* both the two- and three-register ops. (See "case 3" in
* isCompatible(), below.)
*/
return regs.get(sz - 2).regString() + ", " +
regs.get(sz - 1).regString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
// This format has no comment.
return "";
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 1;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof SimpleInsn)) {
return false;
}
RegisterSpecList regs = insn.getRegisters();
RegisterSpec rs1;
RegisterSpec rs2;
switch (regs.size()) {
case 2: {
rs1 = regs.get(0);
rs2 = regs.get(1);
break;
}
case 3: {
/*
* This format is allowed for ops that are effectively
* 3-arg but where the first two args are identical.
*/
rs1 = regs.get(1);
rs2 = regs.get(2);
if (rs1.getReg() != regs.get(0).getReg()) {
return false;
}
break;
}
default: {
return false;
}
}
return unsignedFitsInNibble(rs1.getReg()) &&
unsignedFitsInNibble(rs2.getReg());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form22x.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int sz = regs.size();
/*
* The (sz - 2) and (sz - 1) below makes this code work for
* both the two- and three-register ops. (See "case 3" in
* isCompatible(), above.)
*/
write(out, opcodeUnit(insn,
makeByte(regs.get(sz - 2).getReg(),
regs.get(sz - 1).getReg())));
}
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.TargetInsn;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 20t}. See the instruction format spec
* for details.
*/
public final class Form20t extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form20t();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form20t() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
return branchString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
return branchComment(insn);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!((insn instanceof TargetInsn) &&
(insn.getRegisters().size() == 0))) {
return false;
}
TargetInsn ti = (TargetInsn) insn;
return ti.hasTargetOffset() ? branchFits(ti) : true;
}
/** {@inheritDoc} */
@Override
public boolean branchFits(TargetInsn insn) {
int offset = insn.getTargetOffset();
// Note: A zero offset would fit, but it is prohibited by the spec.
return (offset != 0) && signedFitsInShort(offset);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form30t.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
int offset = ((TargetInsn) insn).getTargetOffset();
write(out, opcodeUnit(insn, 0), (short) offset);
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 21c}. See the instruction format spec
* for details.
*/
public final class Form21c extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form21c();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form21c() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + cstString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof CstInsn)) {
return false;
}
RegisterSpecList regs = insn.getRegisters();
RegisterSpec reg;
switch (regs.size()) {
case 1: {
reg = regs.get(0);
break;
}
case 2: {
/*
* This format is allowed for ops that are effectively
* 2-arg but where the two args are identical.
*/
reg = regs.get(0);
if (reg.getReg() != regs.get(1).getReg()) {
return false;
}
break;
}
default: {
return false;
}
}
if (!unsignedFitsInByte(reg.getReg())) {
return false;
}
CstInsn ci = (CstInsn) insn;
int cpi = ci.getIndex();
if (! unsignedFitsInShort(cpi)) {
return false;
}
Constant cst = ci.getConstant();
return (cst instanceof CstType) ||
(cst instanceof CstFieldRef) ||
(cst instanceof CstString);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form31c.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int cpi = ((CstInsn) insn).getIndex();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) cpi);
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 21h}. See the instruction format spec
* for details.
*/
public final class Form21h extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form21h();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form21h() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return
literalBitsComment(value,
(regs.get(0).getCategory() == 1) ? 32 : 64);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
if (!(cst instanceof CstLiteralBits)) {
return false;
}
CstLiteralBits cb = (CstLiteralBits) cst;
// Where the high bits are depends on the category of the target.
if (regs.get(0).getCategory() == 1) {
int bits = cb.getIntBits();
return ((bits & 0xffff) == 0);
} else {
long bits = cb.getLongBits();
return ((bits & 0xffffffffffffL) == 0);
}
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form31i.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits cb = (CstLiteralBits) ((CstInsn) insn).getConstant();
short bits;
// Where the high bits are depends on the category of the target.
if (regs.get(0).getCategory() == 1) {
bits = (short) (cb.getIntBits() >>> 16);
} else {
bits = (short) (cb.getLongBits() >>> 48);
}
write(out, opcodeUnit(insn, regs.get(0).getReg()), bits);
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 21s}. See the instruction format spec
* for details.
*/
public final class Form21s extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form21s();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form21s() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return literalBitsComment(value, 16);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
if (!(cst instanceof CstLiteralBits)) {
return false;
}
CstLiteralBits cb = (CstLiteralBits) cst;
return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form21h.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int value =
((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) value);
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.TargetInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 21t}. See the instruction format spec
* for details.
*/
public final class Form21t extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form21t();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form21t() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + branchString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
return branchComment(insn);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof TargetInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg()))) {
return false;
}
TargetInsn ti = (TargetInsn) insn;
return ti.hasTargetOffset() ? branchFits(ti) : true;
}
/** {@inheritDoc} */
@Override
public boolean branchFits(TargetInsn insn) {
int offset = insn.getTargetOffset();
// Note: A zero offset would fit, but it is prohibited by the spec.
return (offset != 0) && signedFitsInShort(offset);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form31t.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int offset = ((TargetInsn) insn).getTargetOffset();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) offset);
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 22b}. See the instruction format spec
* for details.
*/
public final class Form22b extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form22b();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form22b() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + regs.get(1).regString() +
", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return literalBitsComment(value, 8);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 2) &&
unsignedFitsInByte(regs.get(0).getReg()) &&
unsignedFitsInByte(regs.get(1).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
if (!(cst instanceof CstLiteralBits)) {
return false;
}
CstLiteralBits cb = (CstLiteralBits) cst;
return cb.fitsInInt() && signedFitsInByte(cb.getIntBits());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form22s.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int value =
((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
codeUnit(regs.get(1).getReg(), value & 0xff));
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 22c}. See the instruction format spec
* for details.
*/
public final class Form22c extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form22c();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form22c() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + regs.get(1).regString() +
", " + cstString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 2) &&
unsignedFitsInNibble(regs.get(0).getReg()) &&
unsignedFitsInNibble(regs.get(1).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
int cpi = ci.getIndex();
if (! unsignedFitsInShort(cpi)) {
return false;
}
Constant cst = ci.getConstant();
return (cst instanceof CstType) ||
(cst instanceof CstFieldRef);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int cpi = ((CstInsn) insn).getIndex();
write(out,
opcodeUnit(insn,
makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
(short) cpi);
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 22s}. See the instruction format spec
* for details.
*/
public final class Form22s extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form22s();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form22s() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + regs.get(1).regString()
+ ", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return literalBitsComment(value, 16);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 2) &&
unsignedFitsInNibble(regs.get(0).getReg()) &&
unsignedFitsInNibble(regs.get(1).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
if (!(cst instanceof CstLiteralBits)) {
return false;
}
CstLiteralBits cb = (CstLiteralBits) cst;
return cb.fitsInInt() && signedFitsInShort(cb.getIntBits());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int value =
((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
write(out,
opcodeUnit(insn,
makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
(short) value);
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.TargetInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 22t}. See the instruction format spec
* for details.
*/
public final class Form22t extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form22t();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form22t() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + regs.get(1).regString() +
", " + branchString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
return branchComment(insn);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof TargetInsn) &&
(regs.size() == 2) &&
unsignedFitsInNibble(regs.get(0).getReg()) &&
unsignedFitsInNibble(regs.get(1).getReg()))) {
return false;
}
TargetInsn ti = (TargetInsn) insn;
return ti.hasTargetOffset() ? branchFits(ti) : true;
}
/** {@inheritDoc} */
@Override
public boolean branchFits(TargetInsn insn) {
int offset = insn.getTargetOffset();
// Note: A zero offset would fit, but it is prohibited by the spec.
return (offset != 0) && signedFitsInShort(offset);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int offset = ((TargetInsn) insn).getTargetOffset();
write(out,
opcodeUnit(insn,
makeByte(regs.get(0).getReg(), regs.get(1).getReg())),
(short) offset);
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.SimpleInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 22x}. See the instruction format spec
* for details.
*/
public final class Form22x extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form22x();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form22x() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + regs.get(1).regString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
// This format has no comment.
return "";
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return (insn instanceof SimpleInsn) &&
(regs.size() == 2) &&
unsignedFitsInByte(regs.get(0).getReg()) &&
unsignedFitsInShort(regs.get(1).getReg());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form23x.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) regs.get(1).getReg());
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.SimpleInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 23x}. See the instruction format spec
* for details.
*/
public final class Form23x extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form23x();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form23x() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + regs.get(1).regString() +
", " + regs.get(2).regString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
// This format has no comment.
return "";
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 2;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return (insn instanceof SimpleInsn) &&
(regs.size() == 3) &&
unsignedFitsInByte(regs.get(0).getReg()) &&
unsignedFitsInByte(regs.get(1).getReg()) &&
unsignedFitsInByte(regs.get(2).getReg());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form32x.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
codeUnit(regs.get(1).getReg(), regs.get(2).getReg()));
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.TargetInsn;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 30t}. See the instruction format spec
* for details.
*/
public final class Form30t extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form30t();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form30t() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
return branchString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
return branchComment(insn);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!((insn instanceof TargetInsn) &&
(insn.getRegisters().size() == 0))) {
return false;
}
return true;
}
/** {@inheritDoc} */
@Override
public boolean branchFits(TargetInsn insn) {
return true;
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
int offset = ((TargetInsn) insn).getTargetOffset();
write(out, opcodeUnit(insn, 0),
(short) offset,
(short) (offset >> 16));
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 31c}. See the instruction format spec
* for details.
*/
public final class Form31c extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form31c();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form31c() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + cstString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof CstInsn)) {
return false;
}
RegisterSpecList regs = insn.getRegisters();
RegisterSpec reg;
switch (regs.size()) {
case 1: {
reg = regs.get(0);
break;
}
case 2: {
/*
* This format is allowed for ops that are effectively
* 2-arg but where the two args are identical.
*/
reg = regs.get(0);
if (reg.getReg() != regs.get(1).getReg()) {
return false;
}
break;
}
default: {
return false;
}
}
if (!unsignedFitsInByte(reg.getReg())) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
return ((cst instanceof CstType) ||
(cst instanceof CstFieldRef) ||
(cst instanceof CstString));
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int cpi = ((CstInsn) insn).getIndex();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) cpi,
(short) (cpi >> 16));
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 31i}. See the instruction format spec
* for details.
*/
public final class Form31i extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form31i();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form31i() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return literalBitsComment(value, 32);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
if (!(cst instanceof CstLiteralBits)) {
return false;
}
return ((CstLiteralBits) cst).fitsInInt();
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form51l.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int value =
((CstLiteralBits) ((CstInsn) insn).getConstant()).getIntBits();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) value,
(short) (value >> 16));
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.TargetInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 31t}. See the instruction format spec
* for details.
*/
public final class Form31t extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form31t();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form31t() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + branchString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
return branchComment(insn);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof TargetInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg()))) {
return false;
}
return true;
}
/** {@inheritDoc} */
@Override
public boolean branchFits(TargetInsn insn) {
return true;
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int offset = ((TargetInsn) insn).getTargetOffset();
write(out, opcodeUnit(insn, regs.get(0).getReg()),
(short) offset,
(short) (offset >> 16));
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.dex.code.SimpleInsn;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 32x}. See the instruction format spec
* for details.
*/
public final class Form32x extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form32x();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form32x() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return regs.get(0).regString() + ", " + regs.get(1).regString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
// This format has no comment.
return "";
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
return (insn instanceof SimpleInsn) &&
(regs.size() == 2) &&
unsignedFitsInShort(regs.get(0).getReg()) &&
unsignedFitsInShort(regs.get(1).getReg());
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
write(out,
opcodeUnit(insn, 0),
(short) regs.get(0).getReg(),
(short) regs.get(1).getReg());
}
}

View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 35c}. See the instruction format spec
* for details.
*/
public final class Form35c extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form35c();
/** Maximal number of operands */
private static final int MAX_NUM_OPS = 5;
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form35c() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = explicitize(insn.getRegisters());
return regListString(regs) + ", " + cstString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof CstInsn)) {
return false;
}
CstInsn ci = (CstInsn) insn;
int cpi = ci.getIndex();
if (! unsignedFitsInShort(cpi)) {
return false;
}
Constant cst = ci.getConstant();
if (!((cst instanceof CstMethodRef) ||
(cst instanceof CstType))) {
return false;
}
RegisterSpecList regs = ci.getRegisters();
return (wordCount(regs) >= 0);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form3rc.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
int cpi = ((CstInsn) insn).getIndex();
RegisterSpecList regs = explicitize(insn.getRegisters());
int sz = regs.size();
int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
write(out,
opcodeUnit(insn,
makeByte(r4, sz)), // encode the fifth operand here
(short) cpi,
codeUnit(r0, r1, r2, r3));
}
/**
* Gets the number of words required for the given register list, where
* category-2 values count as two words. Return {@code -1} if the
* list requires more than five words or contains registers that need
* more than a nibble to identify them.
*
* @param regs {@code non-null;} the register list in question
* @return {@code >= -1;} the number of words required, or {@code -1}
* if the list couldn't possibly fit in this format
*/
private static int wordCount(RegisterSpecList regs) {
int sz = regs.size();
if (sz > MAX_NUM_OPS) {
// It can't possibly fit.
return -1;
}
int result = 0;
for (int i = 0; i < sz; i++) {
RegisterSpec one = regs.get(i);
result += one.getCategory();
/*
* The check below adds (category - 1) to the register, to
* account for the fact that the second half of a
* category-2 register has to be represented explicitly in
* the result.
*/
if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
return -1;
}
}
return (result <= MAX_NUM_OPS) ? result : -1;
}
/**
* Returns a register list which is equivalent to the given one,
* except that it splits category-2 registers into two explicit
* entries. This returns the original list if no modification is
* required
*
* @param orig {@code non-null;} the original list
* @return {@code non-null;} the list with the described transformation
*/
private static RegisterSpecList explicitize(RegisterSpecList orig) {
int wordCount = wordCount(orig);
int sz = orig.size();
if (wordCount == sz) {
return orig;
}
RegisterSpecList result = new RegisterSpecList(wordCount);
int wordAt = 0;
for (int i = 0; i < sz; i++) {
RegisterSpec one = orig.get(i);
result.set(wordAt, one);
if (one.getCategory() == 2) {
result.set(wordAt + 1,
RegisterSpec.make(one.getReg() + 1, Type.VOID));
wordAt += 2;
} else {
wordAt++;
}
}
result.setImmutable();
return result;
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 3rc}. See the instruction format spec
* for details.
*/
public final class Form3rc extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form3rc();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form3rc() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int size = regs.size();
StringBuilder sb = new StringBuilder(30);
sb.append("{");
switch (size) {
case 0: {
// Nothing to do.
break;
}
case 1: {
sb.append(regs.get(0).regString());
break;
}
default: {
RegisterSpec lastReg = regs.get(size - 1);
if (lastReg.getCategory() == 2) {
/*
* Add one to properly represent a list-final
* category-2 register.
*/
lastReg = lastReg.withOffset(1);
}
sb.append(regs.get(0).regString());
sb.append("..");
sb.append(lastReg.regString());
}
}
sb.append("}, ");
sb.append(cstString(insn));
return sb.toString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 3;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof CstInsn)) {
return false;
}
CstInsn ci = (CstInsn) insn;
int cpi = ci.getIndex();
if (! unsignedFitsInShort(cpi)) {
return false;
}
Constant cst = ci.getConstant();
if (!((cst instanceof CstMethodRef) ||
(cst instanceof CstType))) {
return false;
}
RegisterSpecList regs = ci.getRegisters();
int sz = regs.size();
if (sz == 0) {
return true;
}
int first = regs.get(0).getReg();
int next = first;
if (!unsignedFitsInShort(first)) {
return false;
}
for (int i = 0; i < sz; i++) {
RegisterSpec one = regs.get(i);
if (one.getReg() != next) {
return false;
}
next += one.getCategory();
}
return unsignedFitsInByte(next - first);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int sz = regs.size();
int cpi = ((CstInsn) insn).getIndex();
int firstReg;
int count;
if (sz == 0) {
firstReg = 0;
count = 0;
} else {
int lastReg = regs.get(sz - 1).getNextReg();
firstReg = regs.get(0).getReg();
count = lastReg - firstReg;
}
write(out,
opcodeUnit(insn, count),
(short) cpi,
(short) firstReg);
}
}

View File

@ -0,0 +1,193 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 45cc}. See the instruction format spec
* for details.
*/
public final class Form45cc extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form45cc();
/** Maximal number of operands */
private static final int MAX_NUM_OPS = 5;
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form45cc() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = explicitize(insn.getRegisters());
return regListString(regs) + ", " + cstString(insn);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 4;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof CstInsn)) {
return false;
}
CstInsn ci = (CstInsn) insn;
int cpi = ci.getIndex();
if (! unsignedFitsInShort(cpi)) {
return false;
}
Constant cst = ci.getConstant();
if (!((cst instanceof CstMethodRef) ||
(cst instanceof CstType))) {
return false;
}
RegisterSpecList regs = ci.getRegisters();
return (wordCount(regs) >= 0);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return Form4rcc.THE_ONE;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
int cpi = ((CstInsn) insn).getIndex();
RegisterSpecList regs = explicitize(insn.getRegisters());
int sz = regs.size();
int r0 = (sz > 0) ? regs.get(0).getReg() : 0;
int r1 = (sz > 1) ? regs.get(1).getReg() : 0;
int r2 = (sz > 2) ? regs.get(2).getReg() : 0;
int r3 = (sz > 3) ? regs.get(3).getReg() : 0;
int r4 = (sz > 4) ? regs.get(4).getReg() : 0;
write(out,
opcodeUnit(insn,
makeByte(r4, sz)), // encode the fifth operand here
(short) cpi,
codeUnit(r0, r1, r2, r3));
}
/**
* Gets the number of words required for the given register list, where
* category-2 values count as two words. Return {@code -1} if the
* list requires more than five words or contains registers that need
* more than a nibble to identify them.
*
* @param regs {@code non-null;} the register list in question
* @return {@code >= -1;} the number of words required, or {@code -1}
* if the list couldn't possibly fit in this format
*/
private static int wordCount(RegisterSpecList regs) {
int sz = regs.size();
if (sz > MAX_NUM_OPS) {
// It can't possibly fit.
return -1;
}
int result = 0;
for (int i = 0; i < sz; i++) {
RegisterSpec one = regs.get(i);
result += one.getCategory();
/*
* The check below adds (category - 1) to the register, to
* account for the fact that the second half of a
* category-2 register has to be represented explicitly in
* the result.
*/
if (!unsignedFitsInNibble(one.getReg() + one.getCategory() - 1)) {
return -1;
}
}
return (result <= MAX_NUM_OPS) ? result : -1;
}
/**
* Returns a register list which is equivalent to the given one,
* except that it splits category-2 registers into two explicit
* entries. This returns the original list if no modification is
* required
*
* @param orig {@code non-null;} the original list
* @return {@code non-null;} the list with the described transformation
*/
private static RegisterSpecList explicitize(RegisterSpecList orig) {
int wordCount = wordCount(orig);
int sz = orig.size();
if (wordCount == sz) {
return orig;
}
RegisterSpecList result = new RegisterSpecList(wordCount);
int wordAt = 0;
for (int i = 0; i < sz; i++) {
RegisterSpec one = orig.get(i);
result.set(wordAt, one);
if (one.getCategory() == 2) {
result.set(wordAt + 1,
RegisterSpec.make(one.getReg() + 1, Type.VOID));
wordAt += 2;
} else {
wordAt++;
}
}
result.setImmutable();
return result;
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 4rcc}. See the instruction format spec
* for details.
*/
public final class Form4rcc extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form4rcc();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form4rcc() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int size = regs.size();
StringBuilder sb = new StringBuilder(30);
sb.append("{");
switch (size) {
case 0: {
// Nothing to do.
break;
}
case 1: {
sb.append(regs.get(0).regString());
break;
}
default: {
RegisterSpec lastReg = regs.get(size - 1);
if (lastReg.getCategory() == 2) {
/*
* Add one to properly represent a list-final
* category-2 register.
*/
lastReg = lastReg.withOffset(1);
}
sb.append(regs.get(0).regString());
sb.append("..");
sb.append(lastReg.regString());
}
}
sb.append("}, ");
sb.append(cstString(insn));
return sb.toString();
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
if (noteIndices) {
return cstComment(insn);
} else {
return "";
}
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 4;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
if (!(insn instanceof CstInsn)) {
return false;
}
CstInsn ci = (CstInsn) insn;
int cpi = ci.getIndex();
if (! unsignedFitsInShort(cpi)) {
return false;
}
Constant cst = ci.getConstant();
if (!((cst instanceof CstMethodRef) ||
(cst instanceof CstType))) {
return false;
}
RegisterSpecList regs = ci.getRegisters();
int sz = regs.size();
if (sz == 0) {
return true;
}
int first = regs.get(0).getReg();
int next = first;
if (!unsignedFitsInShort(first)) {
return false;
}
for (int i = 0; i < sz; i++) {
RegisterSpec one = regs.get(i);
if (one.getReg() != next) {
return false;
}
next += one.getCategory();
}
return unsignedFitsInByte(next - first);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
int sz = regs.size();
int cpi = ((CstInsn) insn).getIndex();
int firstReg;
int count;
if (sz == 0) {
firstReg = 0;
count = 0;
} else {
int lastReg = regs.get(sz - 1).getNextReg();
firstReg = regs.get(0).getReg();
count = lastReg - firstReg;
}
write(out,
opcodeUnit(insn, count),
(short) cpi,
(short) firstReg);
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.rop.code.RegisterSpecList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstLiteral64;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format {@code 51l}. See the instruction format spec
* for details.
*/
public final class Form51l extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new Form51l();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private Form51l() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return regs.get(0).regString() + ", " + literalBitsString(value);
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
CstLiteralBits value = (CstLiteralBits) ((CstInsn) insn).getConstant();
return literalBitsComment(value, 64);
}
/** {@inheritDoc} */
@Override
public int codeSize() {
return 5;
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
if (!((insn instanceof CstInsn) &&
(regs.size() == 1) &&
unsignedFitsInByte(regs.get(0).getReg()))) {
return false;
}
CstInsn ci = (CstInsn) insn;
Constant cst = ci.getConstant();
return (cst instanceof CstLiteral64);
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
return null;
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
RegisterSpecList regs = insn.getRegisters();
long value =
((CstLiteral64) ((CstInsn) insn).getConstant()).getLongBits();
write(out,
opcodeUnit(insn, regs.get(0).getReg()),
(short) value,
(short) (value >> 16),
(short) (value >> 32),
(short) (value >> 48));
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.code.form;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.DalvOps;
import com.android.dexgen.dex.code.InsnFormat;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Instruction format for nonstandard format instructions, which aren't
* generally real instructions but do end up appearing in instruction
* lists. Most of the overridden methods on this class end up throwing
* exceptions, as code should know (implicitly or explicitly) to avoid
* using this class. The one exception is {@link #isCompatible}, which
* always returns {@code true}.
*/
public final class SpecialFormat extends InsnFormat {
/** {@code non-null;} unique instance of this class */
public static final InsnFormat THE_ONE = new SpecialFormat();
/**
* Constructs an instance. This class is not publicly
* instantiable. Use {@link #THE_ONE}.
*/
private SpecialFormat() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public String insnArgString(DalvInsn insn) {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
public String insnCommentString(DalvInsn insn, boolean noteIndices) {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
public int codeSize() {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
public boolean isCompatible(DalvInsn insn) {
return true;
}
/** {@inheritDoc} */
@Override
public InsnFormat nextUp() {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
public void writeTo(AnnotatedOutput out, DalvInsn insn) {
throw new RuntimeException("unsupported");
}
}

View File

@ -0,0 +1,220 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotation;
import com.android.dexgen.rop.annotation.AnnotationVisibility;
import com.android.dexgen.rop.annotation.NameValuePair;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstAnnotation;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ByteArrayAnnotatedOutput;
import java.util.Arrays;
import java.util.Comparator;
/**
* Single annotation, which consists of a type and a set of name-value
* element pairs.
*/
public final class AnnotationItem extends OffsettedItem {
/** annotation visibility constant: visible at build time only */
private static final int VISIBILITY_BUILD = 0;
/** annotation visibility constant: visible at runtime */
private static final int VISIBILITY_RUNTIME = 1;
/** annotation visibility constant: visible at runtime only to system */
private static final int VISIBILITY_SYSTEM = 2;
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 1;
/** {@code non-null;} unique instance of {@link #TypeIdSorter} */
private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter();
/** {@code non-null;} the annotation to represent */
private final Annotation annotation;
/**
* {@code null-ok;} type reference for the annotation type; set during
* {@link #addContents}
*/
private TypeIdItem type;
/**
* {@code null-ok;} encoded form, ready for writing to a file; set during
* {@link #place0}
*/
private byte[] encodedForm;
/**
* Comparator that sorts (outer) instances by type id index.
*/
private static class TypeIdSorter implements Comparator<AnnotationItem> {
/** {@inheritDoc} */
public int compare(AnnotationItem item1, AnnotationItem item2) {
int index1 = item1.type.getIndex();
int index2 = item2.type.getIndex();
if (index1 < index2) {
return -1;
} else if (index1 > index2) {
return 1;
}
return 0;
}
}
/**
* Sorts an array of instances, in place, by type id index,
* ignoring all other aspects of the elements. This is only valid
* to use after type id indices are known.
*
* @param array {@code non-null;} array to sort
*/
public static void sortByTypeIdIndex(AnnotationItem[] array) {
Arrays.sort(array, TYPE_ID_SORTER);
}
/**
* Constructs an instance.
*
* @param annotation {@code non-null;} annotation to represent
*/
public AnnotationItem(Annotation annotation) {
/*
* The write size isn't known up-front because (the variable-lengthed)
* leb128 type is used to represent some things.
*/
super(ALIGNMENT, -1);
if (annotation == null) {
throw new NullPointerException("annotation == null");
}
this.annotation = annotation;
this.type = null;
this.encodedForm = null;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_ANNOTATION_ITEM;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return annotation.hashCode();
}
/** {@inheritDoc} */
@Override
protected int compareTo0(OffsettedItem other) {
AnnotationItem otherAnnotation = (AnnotationItem) other;
return annotation.compareTo(otherAnnotation.annotation);
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return annotation.toHuman();
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
type = file.getTypeIds().intern(annotation.getType());
ValueEncoder.addContents(file, annotation);
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// Encode the data and note the size.
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
encoder.writeAnnotation(annotation, false);
encodedForm = out.toByteArray();
// Add one for the visibility byte in front of the encoded annotation.
setWriteSize(encodedForm.length + 1);
}
/**
* Write a (listing file) annotation for this instance to the given
* output, that consumes no bytes of output. This is for annotating
* a reference to this instance at the point of the reference.
*
* @param out {@code non-null;} where to output to
* @param prefix {@code non-null;} prefix for each line of output
*/
public void annotateTo(AnnotatedOutput out, String prefix) {
out.annotate(0, prefix + "visibility: " +
annotation.getVisibility().toHuman());
out.annotate(0, prefix + "type: " + annotation.getType().toHuman());
for (NameValuePair pair : annotation.getNameValuePairs()) {
CstUtf8 name = pair.getName();
Constant value = pair.getValue();
out.annotate(0, prefix + name.toHuman() + ": " +
ValueEncoder.constantToHuman(value));
}
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
AnnotationVisibility visibility = annotation.getVisibility();
if (annotates) {
out.annotate(0, offsetString() + " annotation");
out.annotate(1, " visibility: VISBILITY_" + visibility);
}
switch (visibility) {
case BUILD: out.writeByte(VISIBILITY_BUILD); break;
case RUNTIME: out.writeByte(VISIBILITY_RUNTIME); break;
case SYSTEM: out.writeByte(VISIBILITY_SYSTEM); break;
default: {
// EMBEDDED shouldn't appear at the top level.
throw new RuntimeException("shouldn't happen");
}
}
if (annotates) {
/*
* The output is to be annotated, so redo the work previously
* done by place0(), except this time annotations will actually
* get emitted.
*/
ValueEncoder encoder = new ValueEncoder(file, out);
encoder.writeAnnotation(annotation, true);
} else {
out.write(encodedForm);
}
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotation;
import com.android.dexgen.rop.annotation.Annotations;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
/**
* Set of annotations, where no annotation type appears more than once.
*/
public final class AnnotationSetItem extends OffsettedItem {
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 4;
/** the size of an entry int the set: one {@code uint} */
private static final int ENTRY_WRITE_SIZE = 4;
/** {@code non-null;} the set of annotations */
private final Annotations annotations;
/**
* {@code non-null;} set of annotations as individual items in an array.
* <b>Note:</b> The contents have to get sorted by type id before
* writing.
*/
private final AnnotationItem[] items;
/**
* Constructs an instance.
*
* @param annotations {@code non-null;} set of annotations
*/
public AnnotationSetItem(Annotations annotations) {
super(ALIGNMENT, writeSize(annotations));
this.annotations = annotations;
this.items = new AnnotationItem[annotations.size()];
int at = 0;
for (Annotation a : annotations.getAnnotations()) {
items[at] = new AnnotationItem(a);
at++;
}
}
/**
* Gets the write size for the given set.
*
* @param annotations {@code non-null;} the set
* @return {@code > 0;} the write size
*/
private static int writeSize(Annotations annotations) {
// This includes an int size at the start of the list.
try {
return (annotations.size() * ENTRY_WRITE_SIZE) + 4;
} catch (NullPointerException ex) {
// Elucidate the exception.
throw new NullPointerException("list == null");
}
}
/**
* Gets the underlying annotations of this instance
*
* @return {@code non-null;} the annotations
*/
public Annotations getAnnotations() {
return annotations;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return annotations.hashCode();
}
/** {@inheritDoc} */
@Override
protected int compareTo0(OffsettedItem other) {
AnnotationSetItem otherSet = (AnnotationSetItem) other;
return annotations.compareTo(otherSet.annotations);
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_ANNOTATION_SET_ITEM;
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return annotations.toString();
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
MixedItemSection byteData = file.getByteData();
int size = items.length;
for (int i = 0; i < size; i++) {
items[i] = byteData.intern(items[i]);
}
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// Sort the array to be in type id index order.
AnnotationItem.sortByTypeIdIndex(items);
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
int size = items.length;
if (annotates) {
out.annotate(0, offsetString() + " annotation set");
out.annotate(4, " size: " + Hex.u4(size));
}
out.writeInt(size);
for (int i = 0; i < size; i++) {
AnnotationItem item = items[i];
int offset = item.getAbsoluteOffset();
if (annotates) {
out.annotate(4, " entries[" + Integer.toHexString(i) + "]: " +
Hex.u4(offset));
items[i].annotateTo(out, " ");
}
out.writeInt(offset);
}
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
/**
* Indirect reference to an {@link AnnotationSetItem}.
*/
public final class AnnotationSetRefItem extends OffsettedItem {
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 4;
/** write size of this class, in bytes */
private static final int WRITE_SIZE = 4;
/** {@code non-null;} the annotation set to refer to */
private AnnotationSetItem annotations;
/**
* Constructs an instance.
*
* @param annotations {@code non-null;} the annotation set to refer to
*/
public AnnotationSetRefItem(AnnotationSetItem annotations) {
super(ALIGNMENT, WRITE_SIZE);
if (annotations == null) {
throw new NullPointerException("annotations == null");
}
this.annotations = annotations;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_ANNOTATION_SET_REF_ITEM;
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
MixedItemSection wordData = file.getWordData();
annotations = wordData.intern(annotations);
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return annotations.toHuman();
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
int annotationsOff = annotations.getAbsoluteOffset();
if (out.annotates()) {
out.annotate(4, " annotations_off: " + Hex.u4(annotationsOff));
}
out.writeInt(annotationsOff);
}
}

View File

@ -0,0 +1,254 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotation;
import com.android.dexgen.rop.annotation.NameValuePair;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstAnnotation;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstInteger;
import com.android.dexgen.rop.cst.CstKnownNull;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.rop.type.TypeList;
import java.util.ArrayList;
import static com.android.dexgen.rop.annotation.AnnotationVisibility.*;
/**
* Utility class for dealing with annotations.
*/
public final class AnnotationUtils {
/** {@code non-null;} type for {@code AnnotationDefault} annotations */
private static final CstType ANNOTATION_DEFAULT_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;"));
/** {@code non-null;} type for {@code EnclosingClass} annotations */
private static final CstType ENCLOSING_CLASS_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;"));
/** {@code non-null;} type for {@code EnclosingMethod} annotations */
private static final CstType ENCLOSING_METHOD_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;"));
/** {@code non-null;} type for {@code InnerClass} annotations */
private static final CstType INNER_CLASS_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;"));
/** {@code non-null;} type for {@code MemberClasses} annotations */
private static final CstType MEMBER_CLASSES_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;"));
/** {@code non-null;} type for {@code Signature} annotations */
private static final CstType SIGNATURE_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/Signature;"));
/** {@code non-null;} type for {@code Throws} annotations */
private static final CstType THROWS_TYPE =
CstType.intern(Type.intern("Ldalvik/annotation/Throws;"));
/** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */
private static final CstUtf8 ACCESS_FLAGS_UTF = new CstUtf8("accessFlags");
/** {@code non-null;} the UTF-8 constant {@code "name"} */
private static final CstUtf8 NAME_UTF = new CstUtf8("name");
/** {@code non-null;} the UTF-8 constant {@code "value"} */
private static final CstUtf8 VALUE_UTF = new CstUtf8("value");
/**
* This class is uninstantiable.
*/
private AnnotationUtils() {
// This space intentionally left blank.
}
/**
* Constructs a standard {@code AnnotationDefault} annotation.
*
* @param defaults {@code non-null;} the defaults, itself as an annotation
* @return {@code non-null;} the constructed annotation
*/
public static Annotation makeAnnotationDefault(Annotation defaults) {
Annotation result = new Annotation(ANNOTATION_DEFAULT_TYPE, SYSTEM);
result.put(new NameValuePair(VALUE_UTF, new CstAnnotation(defaults)));
result.setImmutable();
return result;
}
/**
* Constructs a standard {@code EnclosingClass} annotation.
*
* @param clazz {@code non-null;} the enclosing class
* @return {@code non-null;} the annotation
*/
public static Annotation makeEnclosingClass(CstType clazz) {
Annotation result = new Annotation(ENCLOSING_CLASS_TYPE, SYSTEM);
result.put(new NameValuePair(VALUE_UTF, clazz));
result.setImmutable();
return result;
}
/**
* Constructs a standard {@code EnclosingMethod} annotation.
*
* @param method {@code non-null;} the enclosing method
* @return {@code non-null;} the annotation
*/
public static Annotation makeEnclosingMethod(CstMethodRef method) {
Annotation result = new Annotation(ENCLOSING_METHOD_TYPE, SYSTEM);
result.put(new NameValuePair(VALUE_UTF, method));
result.setImmutable();
return result;
}
/**
* Constructs a standard {@code InnerClass} annotation.
*
* @param name {@code null-ok;} the original name of the class, or
* {@code null} to represent an anonymous class
* @param accessFlags the original access flags
* @return {@code non-null;} the annotation
*/
public static Annotation makeInnerClass(CstUtf8 name, int accessFlags) {
Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM);
Constant nameCst =
(name != null) ? new CstString(name) : CstKnownNull.THE_ONE;
result.put(new NameValuePair(NAME_UTF, nameCst));
result.put(new NameValuePair(ACCESS_FLAGS_UTF,
CstInteger.make(accessFlags)));
result.setImmutable();
return result;
}
/**
* Constructs a standard {@code MemberClasses} annotation.
*
* @param types {@code non-null;} the list of (the types of) the member classes
* @return {@code non-null;} the annotation
*/
public static Annotation makeMemberClasses(TypeList types) {
CstArray array = makeCstArray(types);
Annotation result = new Annotation(MEMBER_CLASSES_TYPE, SYSTEM);
result.put(new NameValuePair(VALUE_UTF, array));
result.setImmutable();
return result;
}
/**
* Constructs a standard {@code Signature} annotation.
*
* @param signature {@code non-null;} the signature string
* @return {@code non-null;} the annotation
*/
public static Annotation makeSignature(CstUtf8 signature) {
Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM);
/*
* Split the string into pieces that are likely to be common
* across many signatures and the rest of the file.
*/
String raw = signature.getString();
int rawLength = raw.length();
ArrayList<String> pieces = new ArrayList<String>(20);
for (int at = 0; at < rawLength; /*at*/) {
char c = raw.charAt(at);
int endAt = at + 1;
if (c == 'L') {
// Scan to ';' or '<'. Consume ';' but not '<'.
while (endAt < rawLength) {
c = raw.charAt(endAt);
if (c == ';') {
endAt++;
break;
} else if (c == '<') {
break;
}
endAt++;
}
} else {
// Scan to 'L' without consuming it.
while (endAt < rawLength) {
c = raw.charAt(endAt);
if (c == 'L') {
break;
}
endAt++;
}
}
pieces.add(raw.substring(at, endAt));
at = endAt;
}
int size = pieces.size();
CstArray.List list = new CstArray.List(size);
for (int i = 0; i < size; i++) {
list.set(i, new CstString(pieces.get(i)));
}
list.setImmutable();
result.put(new NameValuePair(VALUE_UTF, new CstArray(list)));
result.setImmutable();
return result;
}
/**
* Constructs a standard {@code Throws} annotation.
*
* @param types {@code non-null;} the list of thrown types
* @return {@code non-null;} the annotation
*/
public static Annotation makeThrows(TypeList types) {
CstArray array = makeCstArray(types);
Annotation result = new Annotation(THROWS_TYPE, SYSTEM);
result.put(new NameValuePair(VALUE_UTF, array));
result.setImmutable();
return result;
}
/**
* Converts a {@link TypeList} to a {@link CstArray}.
*
* @param types {@code non-null;} the type list
* @return {@code non-null;} the corresponding array constant
*/
private static CstArray makeCstArray(TypeList types) {
int size = types.size();
CstArray.List list = new CstArray.List(size);
for (int i = 0; i < size; i++) {
list.set(i, CstType.intern(types.getType(i)));
}
list.setImmutable();
return new CstArray(list);
}
}

View File

@ -0,0 +1,385 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotations;
import com.android.dexgen.rop.annotation.AnnotationsList;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
/**
* Per-class directory of annotations.
*/
public final class AnnotationsDirectoryItem extends OffsettedItem {
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 4;
/** write size of this class's header, in bytes */
private static final int HEADER_SIZE = 16;
/** write size of a list element, in bytes */
private static final int ELEMENT_SIZE = 8;
/** {@code null-ok;} the class-level annotations, if any */
private AnnotationSetItem classAnnotations;
/** {@code null-ok;} the annotated fields, if any */
private ArrayList<FieldAnnotationStruct> fieldAnnotations;
/** {@code null-ok;} the annotated methods, if any */
private ArrayList<MethodAnnotationStruct> methodAnnotations;
/** {@code null-ok;} the annotated parameters, if any */
private ArrayList<ParameterAnnotationStruct> parameterAnnotations;
/**
* Constructs an empty instance.
*/
public AnnotationsDirectoryItem() {
super(ALIGNMENT, -1);
classAnnotations = null;
fieldAnnotations = null;
methodAnnotations = null;
parameterAnnotations = null;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
}
/**
* Returns whether this item is empty (has no contents).
*
* @return {@code true} if this item is empty, or {@code false}
* if not
*/
public boolean isEmpty() {
return (classAnnotations == null) &&
(fieldAnnotations == null) &&
(methodAnnotations == null) &&
(parameterAnnotations == null);
}
/**
* Returns whether this item is a candidate for interning. The only
* interning candidates are ones that <i>only</i> have a non-null
* set of class annotations, with no other lists.
*
* @return {@code true} if this is an interning candidate, or
* {@code false} if not
*/
public boolean isInternable() {
return (classAnnotations != null) &&
(fieldAnnotations == null) &&
(methodAnnotations == null) &&
(parameterAnnotations == null);
}
/** {@inheritDoc} */
@Override
public int hashCode() {
if (classAnnotations == null) {
return 0;
}
return classAnnotations.hashCode();
}
/**
* {@inheritDoc}
*
* <p><b>Note:</b>: This throws an exception if this item is not
* internable.</p>
*
* @see #isInternable
*/
@Override
public int compareTo0(OffsettedItem other) {
if (! isInternable()) {
throw new UnsupportedOperationException("uninternable instance");
}
AnnotationsDirectoryItem otherDirectory =
(AnnotationsDirectoryItem) other;
return classAnnotations.compareTo(otherDirectory.classAnnotations);
}
/**
* Sets the direct annotations on this instance. These are annotations
* made on the class, per se, as opposed to on one of its members.
* It is only valid to call this method at most once per instance.
*
* @param annotations {@code non-null;} annotations to set for this class
*/
public void setClassAnnotations(Annotations annotations) {
if (annotations == null) {
throw new NullPointerException("annotations == null");
}
if (classAnnotations != null) {
throw new UnsupportedOperationException(
"class annotations already set");
}
classAnnotations = new AnnotationSetItem(annotations);
}
/**
* Adds a field annotations item to this instance.
*
* @param field {@code non-null;} field in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addFieldAnnotations(CstFieldRef field,
Annotations annotations) {
if (fieldAnnotations == null) {
fieldAnnotations = new ArrayList<FieldAnnotationStruct>();
}
fieldAnnotations.add(new FieldAnnotationStruct(field,
new AnnotationSetItem(annotations)));
}
/**
* Adds a method annotations item to this instance.
*
* @param method {@code non-null;} method in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addMethodAnnotations(CstMethodRef method,
Annotations annotations) {
if (methodAnnotations == null) {
methodAnnotations = new ArrayList<MethodAnnotationStruct>();
}
methodAnnotations.add(new MethodAnnotationStruct(method,
new AnnotationSetItem(annotations)));
}
/**
* Adds a parameter annotations item to this instance.
*
* @param method {@code non-null;} method in question
* @param list {@code non-null;} associated list of annotation sets to add
*/
public void addParameterAnnotations(CstMethodRef method,
AnnotationsList list) {
if (parameterAnnotations == null) {
parameterAnnotations = new ArrayList<ParameterAnnotationStruct>();
}
parameterAnnotations.add(new ParameterAnnotationStruct(method, list));
}
/**
* Gets the method annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the method annotations, if any
*/
public Annotations getMethodAnnotations(CstMethodRef method) {
if (methodAnnotations == null) {
return null;
}
for (MethodAnnotationStruct item : methodAnnotations) {
if (item.getMethod().equals(method)) {
return item.getAnnotations();
}
}
return null;
}
/**
* Gets the parameter annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the parameter annotations, if any
*/
public AnnotationsList getParameterAnnotations(CstMethodRef method) {
if (parameterAnnotations == null) {
return null;
}
for (ParameterAnnotationStruct item : parameterAnnotations) {
if (item.getMethod().equals(method)) {
return item.getAnnotationsList();
}
}
return null;
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
MixedItemSection wordData = file.getWordData();
if (classAnnotations != null) {
classAnnotations = wordData.intern(classAnnotations);
}
if (fieldAnnotations != null) {
for (FieldAnnotationStruct item : fieldAnnotations) {
item.addContents(file);
}
}
if (methodAnnotations != null) {
for (MethodAnnotationStruct item : methodAnnotations) {
item.addContents(file);
}
}
if (parameterAnnotations != null) {
for (ParameterAnnotationStruct item : parameterAnnotations) {
item.addContents(file);
}
}
}
/** {@inheritDoc} */
@Override
public String toHuman() {
throw new RuntimeException("unsupported");
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// We just need to set the write size here.
int elementCount = listSize(fieldAnnotations)
+ listSize(methodAnnotations) + listSize(parameterAnnotations);
setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE));
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations);
int fieldsSize = listSize(fieldAnnotations);
int methodsSize = listSize(methodAnnotations);
int parametersSize = listSize(parameterAnnotations);
if (annotates) {
out.annotate(0, offsetString() + " annotations directory");
out.annotate(4, " class_annotations_off: " + Hex.u4(classOff));
out.annotate(4, " fields_size: " +
Hex.u4(fieldsSize));
out.annotate(4, " methods_size: " +
Hex.u4(methodsSize));
out.annotate(4, " parameters_size: " +
Hex.u4(parametersSize));
}
out.writeInt(classOff);
out.writeInt(fieldsSize);
out.writeInt(methodsSize);
out.writeInt(parametersSize);
if (fieldsSize != 0) {
Collections.sort(fieldAnnotations);
if (annotates) {
out.annotate(0, " fields:");
}
for (FieldAnnotationStruct item : fieldAnnotations) {
item.writeTo(file, out);
}
}
if (methodsSize != 0) {
Collections.sort(methodAnnotations);
if (annotates) {
out.annotate(0, " methods:");
}
for (MethodAnnotationStruct item : methodAnnotations) {
item.writeTo(file, out);
}
}
if (parametersSize != 0) {
Collections.sort(parameterAnnotations);
if (annotates) {
out.annotate(0, " parameters:");
}
for (ParameterAnnotationStruct item : parameterAnnotations) {
item.writeTo(file, out);
}
}
}
/**
* Gets the list size of the given list, or {@code 0} if given
* {@code null}.
*
* @param list {@code null-ok;} the list in question
* @return {@code >= 0;} its size
*/
private static int listSize(ArrayList<?> list) {
if (list == null) {
return 0;
}
return list.size();
}
/**
* Prints out the contents of this instance, in a debugging-friendly
* way. This is meant to be called from {@link ClassDefItem#debugPrint}.
*
* @param out {@code non-null;} where to output to
*/
/*package*/ void debugPrint(PrintWriter out) {
if (classAnnotations != null) {
out.println(" class annotations: " + classAnnotations);
}
if (fieldAnnotations != null) {
out.println(" field annotations:");
for (FieldAnnotationStruct item : fieldAnnotations) {
out.println(" " + item.toHuman());
}
}
if (methodAnnotations != null) {
out.println(" method annotations:");
for (MethodAnnotationStruct item : methodAnnotations) {
out.println(" " + item.toHuman());
}
}
if (parameterAnnotations != null) {
out.println(" parameter annotations:");
for (ParameterAnnotationStruct item : parameterAnnotations) {
out.println(" " + item.toHuman());
}
}
}
}

View File

@ -0,0 +1,317 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.code.CatchHandlerList;
import com.android.dexgen.dex.code.CatchTable;
import com.android.dexgen.dex.code.DalvCode;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ByteArrayAnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.io.PrintWriter;
import java.util.Map;
import java.util.TreeMap;
/**
* List of exception handlers (tuples of covered range, catch type,
* handler address) for a particular piece of code. Instances of this
* class correspond to a {@code try_item[]} and a
* {@code catch_handler_item[]}.
*/
public final class CatchStructs {
/**
* the size of a {@code try_item}: a {@code uint}
* and two {@code ushort}s
*/
private static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2);
/** {@code non-null;} code that contains the catches */
private final DalvCode code;
/**
* {@code null-ok;} the underlying table; set in
* {@link #finishProcessingIfNecessary}
*/
private CatchTable table;
/**
* {@code null-ok;} the encoded handler list, if calculated; set in
* {@link #encode}
*/
private byte[] encodedHandlers;
/**
* length of the handlers header (encoded size), if known; used for
* annotation
*/
private int encodedHandlerHeaderSize;
/**
* {@code null-ok;} map from handler lists to byte offsets, if calculated; set in
* {@link #encode}
*/
private TreeMap<CatchHandlerList, Integer> handlerOffsets;
/**
* Constructs an instance.
*
* @param code {@code non-null;} code that contains the catches
*/
public CatchStructs(DalvCode code) {
this.code = code;
this.table = null;
this.encodedHandlers = null;
this.encodedHandlerHeaderSize = 0;
this.handlerOffsets = null;
}
/**
* Finish processing the catches, if necessary.
*/
private void finishProcessingIfNecessary() {
if (table == null) {
table = code.getCatches();
}
}
/**
* Gets the size of the tries list, in entries.
*
* @return {@code >= 0;} the tries list size
*/
public int triesSize() {
finishProcessingIfNecessary();
return table.size();
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
*/
public void debugPrint(PrintWriter out, String prefix) {
annotateEntries(prefix, out, null);
}
/**
* Encodes the handler lists.
*
* @param file {@code non-null;} file this instance is part of
*/
public void encode(DexFile file) {
finishProcessingIfNecessary();
TypeIdsSection typeIds = file.getTypeIds();
int size = table.size();
handlerOffsets = new TreeMap<CatchHandlerList, Integer>();
/*
* First add a map entry for each unique list. The tree structure
* will ensure they are sorted when we reiterate later.
*/
for (int i = 0; i < size; i++) {
handlerOffsets.put(table.get(i).getHandlers(), null);
}
if (handlerOffsets.size() > 65535) {
throw new UnsupportedOperationException(
"too many catch handlers");
}
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
// Write out the handlers "header" consisting of its size in entries.
encodedHandlerHeaderSize =
out.writeUnsignedLeb128(handlerOffsets.size());
// Now write the lists out in order, noting the offset of each.
for (Map.Entry<CatchHandlerList, Integer> mapping :
handlerOffsets.entrySet()) {
CatchHandlerList list = mapping.getKey();
int listSize = list.size();
boolean catchesAll = list.catchesAll();
// Set the offset before we do any writing.
mapping.setValue(out.getCursor());
if (catchesAll) {
// A size <= 0 means that the list ends with a catch-all.
out.writeSignedLeb128(-(listSize - 1));
listSize--;
} else {
out.writeSignedLeb128(listSize);
}
for (int i = 0; i < listSize; i++) {
CatchHandlerList.Entry entry = list.get(i);
out.writeUnsignedLeb128(
typeIds.indexOf(entry.getExceptionType()));
out.writeUnsignedLeb128(entry.getHandler());
}
if (catchesAll) {
out.writeUnsignedLeb128(list.get(listSize).getHandler());
}
}
encodedHandlers = out.toByteArray();
}
/**
* Gets the write size of this instance, in bytes.
*
* @return {@code >= 0;} the write size
*/
public int writeSize() {
return (triesSize() * TRY_ITEM_WRITE_SIZE) +
+ encodedHandlers.length;
}
/**
* Writes this instance to the given stream.
*
* @param file {@code non-null;} file this instance is part of
* @param out {@code non-null;} where to write to
*/
public void writeTo(DexFile file, AnnotatedOutput out) {
finishProcessingIfNecessary();
if (out.annotates()) {
annotateEntries(" ", null, out);
}
int tableSize = table.size();
for (int i = 0; i < tableSize; i++) {
CatchTable.Entry one = table.get(i);
int start = one.getStart();
int end = one.getEnd();
int insnCount = end - start;
if (insnCount >= 65536) {
throw new UnsupportedOperationException(
"bogus exception range: " + Hex.u4(start) + ".." +
Hex.u4(end));
}
out.writeInt(start);
out.writeShort(insnCount);
out.writeShort(handlerOffsets.get(one.getHandlers()));
}
out.write(encodedHandlers);
}
/**
* Helper method to annotate or simply print the exception handlers.
* Only one of {@code printTo} or {@code annotateTo} should
* be non-null.
*
* @param prefix {@code non-null;} prefix for each line
* @param printTo {@code null-ok;} where to print to
* @param annotateTo {@code null-ok;} where to consume bytes and annotate to
*/
private void annotateEntries(String prefix, PrintWriter printTo,
AnnotatedOutput annotateTo) {
finishProcessingIfNecessary();
boolean consume = (annotateTo != null);
int amt1 = consume ? 6 : 0;
int amt2 = consume ? 2 : 0;
int size = table.size();
String subPrefix = prefix + " ";
if (consume) {
annotateTo.annotate(0, prefix + "tries:");
} else {
printTo.println(prefix + "tries:");
}
for (int i = 0; i < size; i++) {
CatchTable.Entry entry = table.get(i);
CatchHandlerList handlers = entry.getHandlers();
String s1 = subPrefix + "try " + Hex.u2or4(entry.getStart())
+ ".." + Hex.u2or4(entry.getEnd());
String s2 = handlers.toHuman(subPrefix, "");
if (consume) {
annotateTo.annotate(amt1, s1);
annotateTo.annotate(amt2, s2);
} else {
printTo.println(s1);
printTo.println(s2);
}
}
if (! consume) {
// Only emit the handler lists if we are consuming bytes.
return;
}
annotateTo.annotate(0, prefix + "handlers:");
annotateTo.annotate(encodedHandlerHeaderSize,
subPrefix + "size: " + Hex.u2(handlerOffsets.size()));
int lastOffset = 0;
CatchHandlerList lastList = null;
for (Map.Entry<CatchHandlerList, Integer> mapping :
handlerOffsets.entrySet()) {
CatchHandlerList list = mapping.getKey();
int offset = mapping.getValue();
if (lastList != null) {
annotateAndConsumeHandlers(lastList, lastOffset,
offset - lastOffset, subPrefix, printTo, annotateTo);
}
lastList = list;
lastOffset = offset;
}
annotateAndConsumeHandlers(lastList, lastOffset,
encodedHandlers.length - lastOffset,
subPrefix, printTo, annotateTo);
}
/**
* Helper for {@link #annotateEntries} to annotate a catch handler list
* while consuming it.
*
* @param handlers {@code non-null;} handlers to annotate
* @param offset {@code >= 0;} the offset of this handler
* @param size {@code >= 1;} the number of bytes the handlers consume
* @param prefix {@code non-null;} prefix for each line
* @param printTo {@code null-ok;} where to print to
* @param annotateTo {@code non-null;} where to annotate to
*/
private static void annotateAndConsumeHandlers(CatchHandlerList handlers,
int offset, int size, String prefix, PrintWriter printTo,
AnnotatedOutput annotateTo) {
String s = handlers.toHuman(prefix, Hex.u2(offset) + ": ");
if (printTo != null) {
printTo.println(s);
}
annotateTo.annotate(size, s);
}
}

View File

@ -0,0 +1,429 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstLiteralBits;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.Zeroes;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ByteArrayAnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.Writers;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
/**
* Representation of all the parts of a Dalvik class that are generally
* "inflated" into an in-memory representation at runtime. Instances of
* this class are represented in a compact streamable form in a
* {@code dex} file, as opposed to a random-access form.
*/
public final class ClassDataItem extends OffsettedItem {
/** {@code non-null;} what class this data is for, just for listing generation */
private final CstType thisClass;
/** {@code non-null;} list of static fields */
private final ArrayList<EncodedField> staticFields;
/** {@code non-null;} list of initial values for static fields */
private final HashMap<EncodedField, Constant> staticValues;
/** {@code non-null;} list of instance fields */
private final ArrayList<EncodedField> instanceFields;
/** {@code non-null;} list of direct methods */
private final ArrayList<EncodedMethod> directMethods;
/** {@code non-null;} list of virtual methods */
private final ArrayList<EncodedMethod> virtualMethods;
/** {@code null-ok;} static initializer list; set in {@link #addContents} */
private CstArray staticValuesConstant;
/**
* {@code null-ok;} encoded form, ready for writing to a file; set during
* {@link #place0}
*/
private byte[] encodedForm;
/**
* Constructs an instance. Its sets of members are initially
* empty.
*
* @param thisClass {@code non-null;} what class this data is for, just
* for listing generation
*/
public ClassDataItem(CstType thisClass) {
super(1, -1);
if (thisClass == null) {
throw new NullPointerException("thisClass == null");
}
this.thisClass = thisClass;
this.staticFields = new ArrayList<EncodedField>(20);
this.staticValues = new HashMap<EncodedField, Constant>(40);
this.instanceFields = new ArrayList<EncodedField>(20);
this.directMethods = new ArrayList<EncodedMethod>(20);
this.virtualMethods = new ArrayList<EncodedMethod>(20);
this.staticValuesConstant = null;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_CLASS_DATA_ITEM;
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return toString();
}
/**
* Returns whether this instance is empty.
*
* @return {@code true} if this instance is empty or
* {@code false} if at least one element has been added to it
*/
public boolean isEmpty() {
return staticFields.isEmpty() && instanceFields.isEmpty()
&& directMethods.isEmpty() && virtualMethods.isEmpty();
}
/**
* Adds a static field.
*
* @param field {@code non-null;} the field to add
* @param value {@code null-ok;} initial value for the field, if any
*/
public void addStaticField(EncodedField field, Constant value) {
if (field == null) {
throw new NullPointerException("field == null");
}
if (staticValuesConstant != null) {
throw new UnsupportedOperationException(
"static fields already sorted");
}
staticFields.add(field);
staticValues.put(field, value);
}
/**
* Adds an instance field.
*
* @param field {@code non-null;} the field to add
*/
public void addInstanceField(EncodedField field) {
if (field == null) {
throw new NullPointerException("field == null");
}
instanceFields.add(field);
}
/**
* Adds a direct ({@code static} and/or {@code private}) method.
*
* @param method {@code non-null;} the method to add
*/
public void addDirectMethod(EncodedMethod method) {
if (method == null) {
throw new NullPointerException("method == null");
}
directMethods.add(method);
}
/**
* Adds a virtual method.
*
* @param method {@code non-null;} the method to add
*/
public void addVirtualMethod(EncodedMethod method) {
if (method == null) {
throw new NullPointerException("method == null");
}
virtualMethods.add(method);
}
/**
* Gets all the methods in this class. The returned list is not linked
* in any way to the underlying lists contained in this instance, but
* the objects contained in the list are shared.
*
* @return {@code non-null;} list of all methods
*/
public ArrayList<EncodedMethod> getMethods() {
int sz = directMethods.size() + virtualMethods.size();
ArrayList<EncodedMethod> result = new ArrayList<EncodedMethod>(sz);
result.addAll(directMethods);
result.addAll(virtualMethods);
return result;
}
/**
* Prints out the contents of this instance, in a debugging-friendly
* way.
*
* @param out {@code non-null;} where to output to
* @param verbose whether to be verbose with the output
*/
public void debugPrint(Writer out, boolean verbose) {
PrintWriter pw = Writers.printWriterFor(out);
int sz = staticFields.size();
for (int i = 0; i < sz; i++) {
pw.println(" sfields[" + i + "]: " + staticFields.get(i));
}
sz = instanceFields.size();
for (int i = 0; i < sz; i++) {
pw.println(" ifields[" + i + "]: " + instanceFields.get(i));
}
sz = directMethods.size();
for (int i = 0; i < sz; i++) {
pw.println(" dmeths[" + i + "]:");
directMethods.get(i).debugPrint(pw, verbose);
}
sz = virtualMethods.size();
for (int i = 0; i < sz; i++) {
pw.println(" vmeths[" + i + "]:");
virtualMethods.get(i).debugPrint(pw, verbose);
}
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
if (!staticFields.isEmpty()) {
getStaticValuesConstant(); // Force the fields to be sorted.
for (EncodedField field : staticFields) {
field.addContents(file);
}
}
if (!instanceFields.isEmpty()) {
Collections.sort(instanceFields);
for (EncodedField field : instanceFields) {
field.addContents(file);
}
}
if (!directMethods.isEmpty()) {
Collections.sort(directMethods);
for (EncodedMethod method : directMethods) {
method.addContents(file);
}
}
if (!virtualMethods.isEmpty()) {
Collections.sort(virtualMethods);
for (EncodedMethod method : virtualMethods) {
method.addContents(file);
}
}
}
/**
* Gets a {@link CstArray} corresponding to {@link #staticValues} if
* it contains any non-zero non-{@code null} values.
*
* @return {@code null-ok;} the corresponding constant or {@code null} if
* there are no values to encode
*/
public CstArray getStaticValuesConstant() {
if ((staticValuesConstant == null) && (staticFields.size() != 0)) {
staticValuesConstant = makeStaticValuesConstant();
}
return staticValuesConstant;
}
/**
* Gets a {@link CstArray} corresponding to {@link #staticValues} if
* it contains any non-zero non-{@code null} values.
*
* @return {@code null-ok;} the corresponding constant or {@code null} if
* there are no values to encode
*/
private CstArray makeStaticValuesConstant() {
// First sort the statics into their final order.
Collections.sort(staticFields);
/*
* Get the size of staticValues minus any trailing zeros/nulls (both
* nulls per se as well as instances of CstKnownNull).
*/
int size = staticFields.size();
while (size > 0) {
EncodedField field = staticFields.get(size - 1);
Constant cst = staticValues.get(field);
if (cst instanceof CstLiteralBits) {
// Note: CstKnownNull extends CstLiteralBits.
if (((CstLiteralBits) cst).getLongBits() != 0) {
break;
}
} else if (cst != null) {
break;
}
size--;
}
if (size == 0) {
return null;
}
// There is something worth encoding, so build up a result.
CstArray.List list = new CstArray.List(size);
for (int i = 0; i < size; i++) {
EncodedField field = staticFields.get(i);
Constant cst = staticValues.get(field);
if (cst == null) {
cst = Zeroes.zeroFor(field.getRef().getType());
}
list.set(i, cst);
}
list.setImmutable();
return new CstArray(list);
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// Encode the data and note the size.
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
encodeOutput(addedTo.getFile(), out);
encodedForm = out.toByteArray();
setWriteSize(encodedForm.length);
}
/**
* Writes out the encoded form of this instance.
*
* @param file {@code non-null;} file this instance is part of
* @param out {@code non-null;} where to write to
*/
private void encodeOutput(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
if (annotates) {
out.annotate(0, offsetString() + " class data for " +
thisClass.toHuman());
}
encodeSize(file, out, "static_fields", staticFields.size());
encodeSize(file, out, "instance_fields", instanceFields.size());
encodeSize(file, out, "direct_methods", directMethods.size());
encodeSize(file, out, "virtual_methods", virtualMethods.size());
encodeList(file, out, "static_fields", staticFields);
encodeList(file, out, "instance_fields", instanceFields);
encodeList(file, out, "direct_methods", directMethods);
encodeList(file, out, "virtual_methods", virtualMethods);
if (annotates) {
out.endAnnotation();
}
}
/**
* Helper for {@link #encodeOutput}, which writes out the given
* size value, annotating it as well (if annotations are enabled).
*
* @param file {@code non-null;} file this instance is part of
* @param out {@code non-null;} where to write to
* @param label {@code non-null;} the label for the purposes of annotation
* @param size {@code >= 0;} the size to write
*/
private static void encodeSize(DexFile file, AnnotatedOutput out,
String label, int size) {
if (out.annotates()) {
out.annotate(String.format(" %-21s %08x", label + "_size:",
size));
}
out.writeUnsignedLeb128(size);
}
/**
* Helper for {@link #encodeOutput}, which writes out the given
* list. It also annotates the items (if any and if annotations
* are enabled).
*
* @param file {@code non-null;} file this instance is part of
* @param out {@code non-null;} where to write to
* @param label {@code non-null;} the label for the purposes of annotation
* @param list {@code non-null;} the list in question
*/
private static void encodeList(DexFile file, AnnotatedOutput out,
String label, ArrayList<? extends EncodedMember> list) {
int size = list.size();
int lastIndex = 0;
if (size == 0) {
return;
}
if (out.annotates()) {
out.annotate(0, " " + label + ":");
}
for (int i = 0; i < size; i++) {
lastIndex = list.get(i).encode(file, out, lastIndex, i);
}
}
/** {@inheritDoc} */
@Override
public void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
if (annotates) {
/*
* The output is to be annotated, so redo the work previously
* done by place0(), except this time annotations will actually
* get emitted.
*/
encodeOutput(file, out);
} else {
out.write(encodedForm);
}
}
}

View File

@ -0,0 +1,410 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotations;
import com.android.dexgen.rop.annotation.AnnotationsList;
import com.android.dexgen.rop.code.AccessFlags;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.StdTypeList;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.Writers;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.TreeSet;
/**
* Representation of a Dalvik class, which is basically a set of
* members (fields or methods) along with a few more pieces of
* information.
*/
public final class ClassDefItem extends IndexedItem {
/** size of instances when written out to a file, in bytes */
public static final int WRITE_SIZE = 32;
/** {@code non-null;} type constant for this class */
private final CstType thisClass;
/** access flags */
private final int accessFlags;
/**
* {@code null-ok;} superclass or {@code null} if this class is a/the
* root class
*/
private final CstType superclass;
/** {@code null-ok;} list of implemented interfaces */
private TypeListItem interfaces;
/** {@code null-ok;} source file name or {@code null} if unknown */
private final CstUtf8 sourceFile;
/** {@code non-null;} associated class data object */
private final ClassDataItem classData;
/**
* {@code null-ok;} item wrapper for the static values, initialized
* in {@link #addContents}
*/
private EncodedArrayItem staticValuesItem;
/** {@code non-null;} annotations directory */
private AnnotationsDirectoryItem annotationsDirectory;
/**
* Constructs an instance. Its sets of members and annotations are
* initially empty.
*
* @param thisClass {@code non-null;} type constant for this class
* @param accessFlags access flags
* @param superclass {@code null-ok;} superclass or {@code null} if
* this class is a/the root class
* @param interfaces {@code non-null;} list of implemented interfaces
* @param sourceFile {@code null-ok;} source file name or
* {@code null} if unknown
*/
public ClassDefItem(CstType thisClass, int accessFlags,
CstType superclass, TypeList interfaces, CstUtf8 sourceFile) {
if (thisClass == null) {
throw new NullPointerException("thisClass == null");
}
/*
* TODO: Maybe check accessFlags and superclass, at
* least for easily-checked stuff?
*/
if (interfaces == null) {
throw new NullPointerException("interfaces == null");
}
this.thisClass = thisClass;
this.accessFlags = accessFlags;
this.superclass = superclass;
this.interfaces =
(interfaces.size() == 0) ? null : new TypeListItem(interfaces);
this.sourceFile = sourceFile;
this.classData = new ClassDataItem(thisClass);
this.staticValuesItem = null;
this.annotationsDirectory = new AnnotationsDirectoryItem();
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_CLASS_DEF_ITEM;
}
/** {@inheritDoc} */
@Override
public int writeSize() {
return WRITE_SIZE;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
TypeIdsSection typeIds = file.getTypeIds();
MixedItemSection byteData = file.getByteData();
MixedItemSection wordData = file.getWordData();
MixedItemSection typeLists = file.getTypeLists();
StringIdsSection stringIds = file.getStringIds();
typeIds.intern(thisClass);
if (!classData.isEmpty()) {
MixedItemSection classDataSection = file.getClassData();
classDataSection.add(classData);
CstArray staticValues = classData.getStaticValuesConstant();
if (staticValues != null) {
staticValuesItem =
byteData.intern(new EncodedArrayItem(staticValues));
}
}
if (superclass != null) {
typeIds.intern(superclass);
}
if (interfaces != null) {
interfaces = typeLists.intern(interfaces);
}
if (sourceFile != null) {
stringIds.intern(sourceFile);
}
if (! annotationsDirectory.isEmpty()) {
if (annotationsDirectory.isInternable()) {
annotationsDirectory = wordData.intern(annotationsDirectory);
} else {
wordData.add(annotationsDirectory);
}
}
}
/** {@inheritDoc} */
@Override
public void writeTo(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
TypeIdsSection typeIds = file.getTypeIds();
int classIdx = typeIds.indexOf(thisClass);
int superIdx = (superclass == null) ? -1 :
typeIds.indexOf(superclass);
int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces);
int annoOff = annotationsDirectory.isEmpty() ? 0 :
annotationsDirectory.getAbsoluteOffset();
int sourceFileIdx = (sourceFile == null) ? -1 :
file.getStringIds().indexOf(sourceFile);
int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset();
int staticValuesOff =
OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem);
if (annotates) {
out.annotate(0, indexString() + ' ' + thisClass.toHuman());
out.annotate(4, " class_idx: " + Hex.u4(classIdx));
out.annotate(4, " access_flags: " +
AccessFlags.classString(accessFlags));
out.annotate(4, " superclass_idx: " + Hex.u4(superIdx) +
" // " + ((superclass == null) ? "<none>" :
superclass.toHuman()));
out.annotate(4, " interfaces_off: " + Hex.u4(interOff));
if (interOff != 0) {
TypeList list = interfaces.getList();
int sz = list.size();
for (int i = 0; i < sz; i++) {
out.annotate(0, " " + list.getType(i).toHuman());
}
}
out.annotate(4, " source_file_idx: " + Hex.u4(sourceFileIdx) +
" // " + ((sourceFile == null) ? "<none>" :
sourceFile.toHuman()));
out.annotate(4, " annotations_off: " + Hex.u4(annoOff));
out.annotate(4, " class_data_off: " + Hex.u4(dataOff));
out.annotate(4, " static_values_off: " +
Hex.u4(staticValuesOff));
}
out.writeInt(classIdx);
out.writeInt(accessFlags);
out.writeInt(superIdx);
out.writeInt(interOff);
out.writeInt(sourceFileIdx);
out.writeInt(annoOff);
out.writeInt(dataOff);
out.writeInt(staticValuesOff);
}
/**
* Gets the constant corresponding to this class.
*
* @return {@code non-null;} the constant
*/
public CstType getThisClass() {
return thisClass;
}
/**
* Gets the access flags.
*
* @return the access flags
*/
public int getAccessFlags() {
return accessFlags;
}
/**
* Gets the superclass.
*
* @return {@code null-ok;} the superclass or {@code null} if
* this class is a/the root class
*/
public CstType getSuperclass() {
return superclass;
}
/**
* Gets the list of interfaces implemented.
*
* @return {@code non-null;} the interfaces list
*/
public TypeList getInterfaces() {
if (interfaces == null) {
return StdTypeList.EMPTY;
}
return interfaces.getList();
}
/**
* Gets the source file name.
*
* @return {@code null-ok;} the source file name or {@code null} if unknown
*/
public CstUtf8 getSourceFile() {
return sourceFile;
}
/**
* Adds a static field.
*
* @param field {@code non-null;} the field to add
* @param value {@code null-ok;} initial value for the field, if any
*/
public void addStaticField(EncodedField field, Constant value) {
classData.addStaticField(field, value);
}
/**
* Adds an instance field.
*
* @param field {@code non-null;} the field to add
*/
public void addInstanceField(EncodedField field) {
classData.addInstanceField(field);
}
/**
* Adds a direct ({@code static} and/or {@code private}) method.
*
* @param method {@code non-null;} the method to add
*/
public void addDirectMethod(EncodedMethod method) {
classData.addDirectMethod(method);
}
/**
* Adds a virtual method.
*
* @param method {@code non-null;} the method to add
*/
public void addVirtualMethod(EncodedMethod method) {
classData.addVirtualMethod(method);
}
/**
* Gets all the methods in this class. The returned list is not linked
* in any way to the underlying lists contained in this instance, but
* the objects contained in the list are shared.
*
* @return {@code non-null;} list of all methods
*/
public ArrayList<EncodedMethod> getMethods() {
return classData.getMethods();
}
/**
* Sets the direct annotations on this class. These are annotations
* made on the class, per se, as opposed to on one of its members.
* It is only valid to call this method at most once per instance.
*
* @param annotations {@code non-null;} annotations to set for this class
*/
public void setClassAnnotations(Annotations annotations) {
annotationsDirectory.setClassAnnotations(annotations);
}
/**
* Adds a field annotations item to this class.
*
* @param field {@code non-null;} field in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addFieldAnnotations(CstFieldRef field,
Annotations annotations) {
annotationsDirectory.addFieldAnnotations(field, annotations);
}
/**
* Adds a method annotations item to this class.
*
* @param method {@code non-null;} method in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addMethodAnnotations(CstMethodRef method,
Annotations annotations) {
annotationsDirectory.addMethodAnnotations(method, annotations);
}
/**
* Adds a parameter annotations item to this class.
*
* @param method {@code non-null;} method in question
* @param list {@code non-null;} associated list of annotation sets to add
*/
public void addParameterAnnotations(CstMethodRef method,
AnnotationsList list) {
annotationsDirectory.addParameterAnnotations(method, list);
}
/**
* Gets the method annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the method annotations, if any
*/
public Annotations getMethodAnnotations(CstMethodRef method) {
return annotationsDirectory.getMethodAnnotations(method);
}
/**
* Gets the parameter annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the parameter annotations, if any
*/
public AnnotationsList getParameterAnnotations(CstMethodRef method) {
return annotationsDirectory.getParameterAnnotations(method);
}
/**
* Prints out the contents of this instance, in a debugging-friendly
* way.
*
* @param out {@code non-null;} where to output to
* @param verbose whether to be verbose with the output
*/
public void debugPrint(Writer out, boolean verbose) {
PrintWriter pw = Writers.printWriterFor(out);
pw.println(getClass().getName() + " {");
pw.println(" accessFlags: " + Hex.u2(accessFlags));
pw.println(" superclass: " + superclass);
pw.println(" interfaces: " +
((interfaces == null) ? "<none>" : interfaces));
pw.println(" sourceFile: " +
((sourceFile == null) ? "<none>" : sourceFile.toQuoted()));
classData.debugPrint(out, verbose);
annotationsDirectory.debugPrint(pw);
pw.println("}");
}
}

View File

@ -0,0 +1,187 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.util.ArrayList;
import java.util.Collection;
import java.util.TreeMap;
/**
* Class definitions list section of a {@code .dex} file.
*/
public final class ClassDefsSection extends UniformItemSection {
/**
* {@code non-null;} map from type constants for classes to {@link
* ClassDefItem} instances that define those classes
*/
private final TreeMap<Type, ClassDefItem> classDefs;
/** {@code null-ok;} ordered list of classes; set in {@link #orderItems} */
private ArrayList<ClassDefItem> orderedDefs;
/**
* Constructs an instance. The file offset is initially unknown.
*
* @param file {@code non-null;} file that this instance is part of
*/
public ClassDefsSection(DexFile file) {
super("class_defs", file, 4);
classDefs = new TreeMap<Type, ClassDefItem>();
orderedDefs = null;
}
/** {@inheritDoc} */
@Override
public Collection<? extends Item> items() {
if (orderedDefs != null) {
return orderedDefs;
}
return classDefs.values();
}
/** {@inheritDoc} */
@Override
public IndexedItem get(Constant cst) {
if (cst == null) {
throw new NullPointerException("cst == null");
}
throwIfNotPrepared();
Type type = ((CstType) cst).getClassType();
IndexedItem result = classDefs.get(type);
if (result == null) {
throw new IllegalArgumentException("not found");
}
return result;
}
/**
* Writes the portion of the file header that refers to this instance.
*
* @param out {@code non-null;} where to write
*/
public void writeHeaderPart(AnnotatedOutput out) {
throwIfNotPrepared();
int sz = classDefs.size();
int offset = (sz == 0) ? 0 : getFileOffset();
if (out.annotates()) {
out.annotate(4, "class_defs_size: " + Hex.u4(sz));
out.annotate(4, "class_defs_off: " + Hex.u4(offset));
}
out.writeInt(sz);
out.writeInt(offset);
}
/**
* Adds an element to this instance. It is illegal to attempt to add more
* than one class with the same name.
*
* @param clazz {@code non-null;} the class def to add
*/
public void add(ClassDefItem clazz) {
Type type;
try {
type = clazz.getThisClass().getClassType();
} catch (NullPointerException ex) {
// Elucidate the exception.
throw new NullPointerException("clazz == null");
}
throwIfPrepared();
if (classDefs.get(type) != null) {
throw new IllegalArgumentException("already added: " + type);
}
classDefs.put(type, clazz);
}
/** {@inheritDoc} */
@Override
protected void orderItems() {
int sz = classDefs.size();
int idx = 0;
orderedDefs = new ArrayList<ClassDefItem>(sz);
/*
* Iterate over all the classes, recursively assigning an
* index to each, implicitly skipping the ones that have
* already been assigned by the time this (top-level)
* iteration reaches them.
*/
for (Type type : classDefs.keySet()) {
idx = orderItems0(type, idx, sz - idx);
}
}
/**
* Helper for {@link #orderItems}, which recursively assigns indices
* to classes.
*
* @param type {@code null-ok;} type ref to assign, if any
* @param idx {@code >= 0;} the next index to assign
* @param maxDepth maximum recursion depth; if negative, this will
* throw an exception indicating class definition circularity
* @return {@code >= 0;} the next index to assign
*/
private int orderItems0(Type type, int idx, int maxDepth) {
ClassDefItem c = classDefs.get(type);
if ((c == null) || (c.hasIndex())) {
return idx;
}
if (maxDepth < 0) {
throw new RuntimeException("class circularity with " + type);
}
maxDepth--;
CstType superclassCst = c.getSuperclass();
if (superclassCst != null) {
Type superclass = superclassCst.getClassType();
idx = orderItems0(superclass, idx, maxDepth);
}
TypeList interfaces = c.getInterfaces();
int sz = interfaces.size();
for (int i = 0; i < sz; i++) {
idx = orderItems0(interfaces.getType(i), idx, maxDepth);
}
c.setIndex(idx);
orderedDefs.add(c);
return idx + 1;
}
}

View File

@ -0,0 +1,334 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.code.CatchTable;
import com.android.dexgen.dex.code.CstInsn;
import com.android.dexgen.dex.code.DalvCode;
import com.android.dexgen.dex.code.DalvInsn;
import com.android.dexgen.dex.code.DalvInsnList;
import com.android.dexgen.dex.code.LocalList;
import com.android.dexgen.dex.code.PositionList;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstMemberRef;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.type.StdTypeList;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ExceptionWithContext;
import com.android.dexgen.util.Hex;
import java.io.PrintWriter;
import java.util.HashSet;
/**
* Representation of all the parts needed for concrete methods in a
* {@code dex} file.
*/
public final class CodeItem extends OffsettedItem {
/** file alignment of this class, in bytes */
private static final int ALIGNMENT = 4;
/** write size of the header of this class, in bytes */
private static final int HEADER_SIZE = 16;
/** {@code non-null;} method that this code implements */
private final CstMethodRef ref;
/** {@code non-null;} the bytecode instructions and associated data */
private final DalvCode code;
/** {@code null-ok;} the catches, if needed; set in {@link #addContents} */
private CatchStructs catches;
/** whether this instance is for a {@code static} method */
private final boolean isStatic;
/**
* {@code non-null;} list of possibly-thrown exceptions; just used in
* generating debugging output (listings)
*/
private final TypeList throwsList;
/**
* {@code null-ok;} the debug info or {@code null} if there is none;
* set in {@link #addContents}
*/
private DebugInfoItem debugInfo;
/**
* Constructs an instance.
*
* @param ref {@code non-null;} method that this code implements
* @param code {@code non-null;} the underlying code
* @param isStatic whether this instance is for a {@code static}
* method
* @param throwsList {@code non-null;} list of possibly-thrown exceptions,
* just used in generating debugging output (listings)
*/
public CodeItem(CstMethodRef ref, DalvCode code, boolean isStatic,
TypeList throwsList) {
super(ALIGNMENT, -1);
if (ref == null) {
throw new NullPointerException("ref == null");
}
if (code == null) {
throw new NullPointerException("code == null");
}
if (throwsList == null) {
throw new NullPointerException("throwsList == null");
}
this.ref = ref;
this.code = code;
this.isStatic = isStatic;
this.throwsList = throwsList;
this.catches = null;
this.debugInfo = null;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_CODE_ITEM;
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
MixedItemSection byteData = file.getByteData();
TypeIdsSection typeIds = file.getTypeIds();
if (code.hasPositions() || code.hasLocals()) {
debugInfo = new DebugInfoItem(code, isStatic, ref);
byteData.add(debugInfo);
}
if (code.hasAnyCatches()) {
for (Type type : code.getCatchTypes()) {
typeIds.intern(type);
}
catches = new CatchStructs(code);
}
for (Constant c : code.getInsnConstants()) {
file.internIfAppropriate(c);
}
}
/** {@inheritDoc} */
@Override
public String toString() {
return "CodeItem{" + toHuman() + "}";
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return ref.toHuman();
}
/**
* Gets the reference to the method this instance implements.
*
* @return {@code non-null;} the method reference
*/
public CstMethodRef getRef() {
return ref;
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} per-line prefix to use
* @param verbose whether to be verbose with the output
*/
public void debugPrint(PrintWriter out, String prefix, boolean verbose) {
out.println(ref.toHuman() + ":");
DalvInsnList insns = code.getInsns();
out.println("regs: " + Hex.u2(getRegistersSize()) +
"; ins: " + Hex.u2(getInsSize()) + "; outs: " +
Hex.u2(getOutsSize()));
insns.debugPrint(out, prefix, verbose);
String prefix2 = prefix + " ";
if (catches != null) {
out.print(prefix);
out.println("catches");
catches.debugPrint(out, prefix2);
}
if (debugInfo != null) {
out.print(prefix);
out.println("debug info");
debugInfo.debugPrint(out, prefix2);
}
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
final DexFile file = addedTo.getFile();
int catchesSize;
/*
* In order to get the catches and insns, all the code's
* constants need to be assigned indices.
*/
code.assignIndices(new DalvCode.AssignIndicesCallback() {
public int getIndex(Constant cst) {
IndexedItem item = file.findItemOrNull(cst);
if (item == null) {
return -1;
}
return item.getIndex();
}
});
if (catches != null) {
catches.encode(file);
catchesSize = catches.writeSize();
} else {
catchesSize = 0;
}
/*
* The write size includes the header, two bytes per code
* unit, post-code padding if necessary, and however much
* space the catches need.
*/
int insnsSize = code.getInsns().codeSize();
if ((insnsSize & 1) != 0) {
insnsSize++;
}
setWriteSize(HEADER_SIZE + (insnsSize * 2) + catchesSize);
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
int regSz = getRegistersSize();
int outsSz = getOutsSize();
int insSz = getInsSize();
int insnsSz = code.getInsns().codeSize();
boolean needPadding = (insnsSz & 1) != 0;
int triesSz = (catches == null) ? 0 : catches.triesSize();
int debugOff = (debugInfo == null) ? 0 : debugInfo.getAbsoluteOffset();
if (annotates) {
out.annotate(0, offsetString() + ' ' + ref.toHuman());
out.annotate(2, " registers_size: " + Hex.u2(regSz));
out.annotate(2, " ins_size: " + Hex.u2(insSz));
out.annotate(2, " outs_size: " + Hex.u2(outsSz));
out.annotate(2, " tries_size: " + Hex.u2(triesSz));
out.annotate(4, " debug_off: " + Hex.u4(debugOff));
out.annotate(4, " insns_size: " + Hex.u4(insnsSz));
// This isn't represented directly here, but it is useful to see.
int size = throwsList.size();
if (size != 0) {
out.annotate(0, " throws " + StdTypeList.toHuman(throwsList));
}
}
out.writeShort(regSz);
out.writeShort(insSz);
out.writeShort(outsSz);
out.writeShort(triesSz);
out.writeInt(debugOff);
out.writeInt(insnsSz);
writeCodes(file, out);
if (catches != null) {
if (needPadding) {
if (annotates) {
out.annotate(2, " padding: 0");
}
out.writeShort(0);
}
catches.writeTo(file, out);
}
if (annotates) {
/*
* These are pointed at in the code header (above), but it's less
* distracting to expand on them at the bottom of the code.
*/
if (debugInfo != null) {
out.annotate(0, " debug info");
debugInfo.annotateTo(file, out, " ");
}
}
}
/**
* Helper for {@link #writeTo0} which writes out the actual bytecode.
*
* @param file {@code non-null;} file we are part of
* @param out {@code non-null;} where to write to
*/
private void writeCodes(DexFile file, AnnotatedOutput out) {
DalvInsnList insns = code.getInsns();
try {
insns.writeTo(out);
} catch (RuntimeException ex) {
throw ExceptionWithContext.withContext(ex, "...while writing " +
"instructions for " + ref.toHuman());
}
}
/**
* Get the in registers count.
*
* @return the count
*/
private int getInsSize() {
return ref.getParameterWordCount(isStatic);
}
/**
* Get the out registers count.
*
* @return the count
*/
private int getOutsSize() {
return code.getInsns().getOutsSize();
}
/**
* Get the total registers count.
*
* @return the count
*/
private int getRegistersSize() {
return code.getInsns().getRegistersSize();
}
}

View File

@ -0,0 +1,154 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
/**
* Constants for the dex debug info state machine format.
*/
public interface DebugInfoConstants {
/*
* normal opcodes
*/
/**
* Terminates a debug info sequence for a method.<p>
* Args: none
*
*/
static final int DBG_END_SEQUENCE = 0x00;
/**
* Advances the program counter/address register without emitting
* a positions entry.<p>
*
* Args:
* <ol>
* <li>Unsigned LEB128 &mdash; amount to advance pc by
* </ol>
*/
static final int DBG_ADVANCE_PC = 0x01;
/**
* Advances the line register without emitting
* a positions entry.<p>
*
* Args:
* <ol>
* <li>Signed LEB128 &mdash; amount to change line register by.
* </ol>
*/
static final int DBG_ADVANCE_LINE = 0x02;
/**
* Introduces a local variable at the current address.<p>
*
* Args:
* <ol>
* <li>Unsigned LEB128 &mdash; register that will contain local.
* <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
* <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
* </ol>
*/
static final int DBG_START_LOCAL = 0x03;
/**
* Introduces a local variable at the current address with a type
* signature specified.<p>
*
* Args:
* <ol>
* <li>Unsigned LEB128 &mdash; register that will contain local.
* <li>Unsigned LEB128 &mdash; string index (shifted by 1) of local name.
* <li>Unsigned LEB128 &mdash; type index (shifted by 1) of type.
* <li>Unsigned LEB128 &mdash; string index (shifted by 1) of
* type signature.
* </ol>
*/
static final int DBG_START_LOCAL_EXTENDED = 0x04;
/**
* Marks a currently-live local variable as out of scope at the
* current address.<p>
*
* Args:
* <ol>
* <li>Unsigned LEB128 &mdash; register that contained local
* </ol>
*/
static final int DBG_END_LOCAL = 0x05;
/**
* Re-introduces a local variable at the current address. The name
* and type are the same as the last local that was live in the specified
* register.<p>
*
* Args:
* <ol>
* <li>Unsigned LEB128 &mdash; register to re-start.
* </ol>
*/
static final int DBG_RESTART_LOCAL = 0x06;
/**
* Sets the "prologue_end" state machine register, indicating that the
* next position entry that is added should be considered the end of
* a method prologue (an appropriate place for a method breakpoint).<p>
*
* The prologue_end register is cleared by any special
* ({@code >= OPCODE_BASE}) opcode.
*/
static final int DBG_SET_PROLOGUE_END = 0x07;
/**
* Sets the "epilogue_begin" state machine register, indicating that the
* next position entry that is added should be considered the beginning of
* a method epilogue (an appropriate place to suspend execution before
* method exit).<p>
*
* The epilogue_begin register is cleared by any special
* ({@code >= OPCODE_BASE}) opcode.
*/
static final int DBG_SET_EPILOGUE_BEGIN = 0x08;
/**
* Sets the current file that that line numbers refer to. All subsequent
* line number entries make reference to this source file name, instead
* of the default name specified in code_item.
*
* Args:
* <ol>
* <li>Unsigned LEB128 &mdash; string index (shifted by 1) of source
* file name.
* </ol>
*/
static final int DBG_SET_FILE = 0x09;
/* IF YOU ADD A NEW OPCODE, increase OPCODE_BASE */
/*
* "special opcode" configuration, essentially what's found in
* the line number program header in DWARFv3, Section 6.2.4
*/
/** the smallest value a special opcode can take */
static final int DBG_FIRST_SPECIAL = 0x0a;
static final int DBG_LINE_BASE = -4;
static final int DBG_LINE_RANGE = 15;
// MIN_INSN_LENGTH is always 1
}

View File

@ -0,0 +1,653 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.code.DalvCode;
import com.android.dexgen.dex.code.DalvInsnList;
import com.android.dexgen.dex.code.LocalList;
import com.android.dexgen.dex.code.PositionList;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Prototype;
import com.android.dexgen.rop.type.StdTypeList;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.ExceptionWithContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import static com.android.dexgen.dex.file.DebugInfoConstants.*;
/**
* A decoder for the dex debug info state machine format.
* This code exists mostly as a reference implementation and test for
* for the {@code DebugInfoEncoder}
*/
public class DebugInfoDecoder {
/** encoded debug info */
private final byte[] encoded;
/** positions decoded */
private final ArrayList<PositionEntry> positions;
/** locals decoded */
private final ArrayList<LocalEntry> locals;
/** size of code block in code units */
private final int codesize;
/** indexed by register, the last local variable live in a reg */
private final LocalEntry[] lastEntryForReg;
/** method descriptor of method this debug info is for */
private final Prototype desc;
/** true if method is static */
private final boolean isStatic;
/** dex file this debug info will be stored in */
private final DexFile file;
/**
* register size, in register units, of the register space
* used by this method
*/
private final int regSize;
/** current decoding state: line number */
private int line = 1;
/** current decoding state: bytecode address */
private int address = 0;
/** string index of the string "this" */
private final int thisStringIdx;
/**
* Constructs an instance.
*
* @param encoded encoded debug info
* @param codesize size of code block in code units
* @param regSize register size, in register units, of the register space
* used by this method
* @param isStatic true if method is static
* @param ref method descriptor of method this debug info is for
* @param file dex file this debug info will be stored in
*/
DebugInfoDecoder(byte[] encoded, int codesize, int regSize,
boolean isStatic, CstMethodRef ref, DexFile file) {
if (encoded == null) {
throw new NullPointerException("encoded == null");
}
this.encoded = encoded;
this.isStatic = isStatic;
this.desc = ref.getPrototype();
this.file = file;
this.regSize = regSize;
positions = new ArrayList<PositionEntry>();
locals = new ArrayList<LocalEntry>();
this.codesize = codesize;
lastEntryForReg = new LocalEntry[regSize];
int idx = -1;
try {
idx = file.getStringIds().indexOf(new CstUtf8("this"));
} catch (IllegalArgumentException ex) {
/*
* Silently tolerate not finding "this". It just means that
* no method has local variable info that looks like
* a standard instance method.
*/
}
thisStringIdx = idx;
}
/**
* An entry in the resulting postions table
*/
static private class PositionEntry {
/** bytecode address */
public int address;
/** line number */
public int line;
public PositionEntry(int address, int line) {
this.address = address;
this.line = line;
}
}
/**
* An entry in the resulting locals table
*/
static private class LocalEntry {
/** address of event */
public int address;
/** {@code true} iff it's a local start */
public boolean isStart;
/** register number */
public int reg;
/** index of name in strings table */
public int nameIndex;
/** index of type in types table */
public int typeIndex;
/** index of type signature in strings table */
public int signatureIndex;
public LocalEntry(int address, boolean isStart, int reg, int nameIndex,
int typeIndex, int signatureIndex) {
this.address = address;
this.isStart = isStart;
this.reg = reg;
this.nameIndex = nameIndex;
this.typeIndex = typeIndex;
this.signatureIndex = signatureIndex;
}
public String toString() {
return String.format("[%x %s v%d %04x %04x %04x]",
address, isStart ? "start" : "end", reg,
nameIndex, typeIndex, signatureIndex);
}
}
/**
* Gets the decoded positions list.
* Valid after calling {@code decode}.
*
* @return positions list in ascending address order.
*/
public List<PositionEntry> getPositionList() {
return positions;
}
/**
* Gets the decoded locals list, in ascending start-address order.
* Valid after calling {@code decode}.
*
* @return locals list in ascending address order.
*/
public List<LocalEntry> getLocals() {
return locals;
}
/**
* Decodes the debug info sequence.
*/
public void decode() {
try {
decode0();
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"...while decoding debug info");
}
}
/**
* Reads a string index. String indicies are offset by 1, and a 0 value
* in the stream (-1 as returned by this method) means "null"
*
* @param bs
* @return index into file's string ids table, -1 means null
* @throws IOException
*/
private int readStringIndex(InputStream bs) throws IOException {
int offsetIndex = readUnsignedLeb128(bs);
return offsetIndex - 1;
}
/**
* Gets the register that begins the method's parameter range (including
* the 'this' parameter for non-static methods). The range continues until
* {@code regSize}
*
* @return register as noted above.
*/
private int getParamBase() {
return regSize
- desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
}
private void decode0() throws IOException {
ByteArrayInputStream bs = new ByteArrayInputStream(encoded);
line = readUnsignedLeb128(bs);
int szParams = readUnsignedLeb128(bs);
StdTypeList params = desc.getParameterTypes();
int curReg = getParamBase();
if (szParams != params.size()) {
throw new RuntimeException(
"Mismatch between parameters_size and prototype");
}
if (!isStatic) {
// Start off with implicit 'this' entry
LocalEntry thisEntry =
new LocalEntry(0, true, curReg, thisStringIdx, 0, 0);
locals.add(thisEntry);
lastEntryForReg[curReg] = thisEntry;
curReg++;
}
for (int i = 0; i < szParams; i++) {
Type paramType = params.getType(i);
LocalEntry le;
int nameIdx = readStringIndex(bs);
if (nameIdx == -1) {
/*
* Unnamed parameter; often but not always filled in by an
* extended start op after the prologue
*/
le = new LocalEntry(0, true, curReg, -1, 0, 0);
} else {
// TODO: Final 0 should be idx of paramType.getDescriptor().
le = new LocalEntry(0, true, curReg, nameIdx, 0, 0);
}
locals.add(le);
lastEntryForReg[curReg] = le;
curReg += paramType.getCategory();
}
for (;;) {
int opcode = bs.read();
if (opcode < 0) {
throw new RuntimeException
("Reached end of debug stream without "
+ "encountering end marker");
}
switch (opcode) {
case DBG_START_LOCAL: {
int reg = readUnsignedLeb128(bs);
int nameIdx = readStringIndex(bs);
int typeIdx = readStringIndex(bs);
LocalEntry le = new LocalEntry(
address, true, reg, nameIdx, typeIdx, 0);
locals.add(le);
lastEntryForReg[reg] = le;
}
break;
case DBG_START_LOCAL_EXTENDED: {
int reg = readUnsignedLeb128(bs);
int nameIdx = readStringIndex(bs);
int typeIdx = readStringIndex(bs);
int sigIdx = readStringIndex(bs);
LocalEntry le = new LocalEntry(
address, true, reg, nameIdx, typeIdx, sigIdx);
locals.add(le);
lastEntryForReg[reg] = le;
}
break;
case DBG_RESTART_LOCAL: {
int reg = readUnsignedLeb128(bs);
LocalEntry prevle;
LocalEntry le;
try {
prevle = lastEntryForReg[reg];
if (prevle.isStart) {
throw new RuntimeException("nonsensical "
+ "RESTART_LOCAL on live register v"
+ reg);
}
le = new LocalEntry(address, true, reg,
prevle.nameIndex, prevle.typeIndex, 0);
} catch (NullPointerException ex) {
throw new RuntimeException(
"Encountered RESTART_LOCAL on new v" + reg);
}
locals.add(le);
lastEntryForReg[reg] = le;
}
break;
case DBG_END_LOCAL: {
int reg = readUnsignedLeb128(bs);
LocalEntry prevle;
LocalEntry le;
try {
prevle = lastEntryForReg[reg];
if (!prevle.isStart) {
throw new RuntimeException("nonsensical "
+ "END_LOCAL on dead register v" + reg);
}
le = new LocalEntry(address, false, reg,
prevle.nameIndex, prevle.typeIndex,
prevle.signatureIndex);
} catch (NullPointerException ex) {
throw new RuntimeException(
"Encountered END_LOCAL on new v" + reg);
}
locals.add(le);
lastEntryForReg[reg] = le;
}
break;
case DBG_END_SEQUENCE:
// all done
return;
case DBG_ADVANCE_PC:
address += readUnsignedLeb128(bs);
break;
case DBG_ADVANCE_LINE:
line += readSignedLeb128(bs);
break;
case DBG_SET_PROLOGUE_END:
//TODO do something with this.
break;
case DBG_SET_EPILOGUE_BEGIN:
//TODO do something with this.
break;
case DBG_SET_FILE:
//TODO do something with this.
break;
default:
if (opcode < DBG_FIRST_SPECIAL) {
throw new RuntimeException(
"Invalid extended opcode encountered "
+ opcode);
}
int adjopcode = opcode - DBG_FIRST_SPECIAL;
address += adjopcode / DBG_LINE_RANGE;
line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
positions.add(new PositionEntry(address, line));
break;
}
}
}
/**
* Validates an encoded debug info stream against data used to encode it,
* throwing an exception if they do not match. Used to validate the
* encoder.
*
* @param info encoded debug info
* @param file {@code non-null;} file to refer to during decoding
* @param ref {@code non-null;} method whose info is being decoded
* @param code {@code non-null;} original code object that was encoded
* @param isStatic whether the method is static
*/
public static void validateEncode(byte[] info, DexFile file,
CstMethodRef ref, DalvCode code, boolean isStatic) {
PositionList pl = code.getPositions();
LocalList ll = code.getLocals();
DalvInsnList insns = code.getInsns();
int codeSize = insns.codeSize();
int countRegisters = insns.getRegistersSize();
try {
validateEncode0(info, codeSize, countRegisters,
isStatic, ref, file, pl, ll);
} catch (RuntimeException ex) {
System.err.println("instructions:");
insns.debugPrint(System.err, " ", true);
System.err.println("local list:");
ll.debugPrint(System.err, " ");
throw ExceptionWithContext.withContext(ex,
"while processing " + ref.toHuman());
}
}
private static void validateEncode0(byte[] info, int codeSize,
int countRegisters, boolean isStatic, CstMethodRef ref,
DexFile file, PositionList pl, LocalList ll) {
DebugInfoDecoder decoder
= new DebugInfoDecoder(info, codeSize, countRegisters,
isStatic, ref, file);
decoder.decode();
/*
* Go through the decoded position entries, matching up
* with original entries.
*/
List<PositionEntry> decodedEntries = decoder.getPositionList();
if (decodedEntries.size() != pl.size()) {
throw new RuntimeException(
"Decoded positions table not same size was "
+ decodedEntries.size() + " expected " + pl.size());
}
for (PositionEntry entry : decodedEntries) {
boolean found = false;
for (int i = pl.size() - 1; i >= 0; i--) {
PositionList.Entry ple = pl.get(i);
if (entry.line == ple.getPosition().getLine()
&& entry.address == ple.getAddress()) {
found = true;
break;
}
}
if (!found) {
throw new RuntimeException ("Could not match position entry: "
+ entry.address + ", " + entry.line);
}
}
/*
* Go through the original local list, in order, matching up
* with decoded entries.
*/
List<LocalEntry> decodedLocals = decoder.getLocals();
int thisStringIdx = decoder.thisStringIdx;
int decodedSz = decodedLocals.size();
int paramBase = decoder.getParamBase();
/*
* Preflight to fill in any parameters that were skipped in
* the prologue (including an implied "this") but then
* identified by full signature.
*/
for (int i = 0; i < decodedSz; i++) {
LocalEntry entry = decodedLocals.get(i);
int idx = entry.nameIndex;
if ((idx < 0) || (idx == thisStringIdx)) {
for (int j = i + 1; j < decodedSz; j++) {
LocalEntry e2 = decodedLocals.get(j);
if (e2.address != 0) {
break;
}
if ((entry.reg == e2.reg) && e2.isStart) {
decodedLocals.set(i, e2);
decodedLocals.remove(j);
decodedSz--;
break;
}
}
}
}
int origSz = ll.size();
int decodeAt = 0;
boolean problem = false;
for (int i = 0; i < origSz; i++) {
LocalList.Entry origEntry = ll.get(i);
if (origEntry.getDisposition()
== LocalList.Disposition.END_REPLACED) {
/*
* The encoded list doesn't represent replacements, so
* ignore them for the sake of comparison.
*/
continue;
}
LocalEntry decodedEntry;
do {
decodedEntry = decodedLocals.get(decodeAt);
if (decodedEntry.nameIndex >= 0) {
break;
}
/*
* A negative name index means this is an anonymous
* parameter, and we shouldn't expect to see it in the
* original list. So, skip it.
*/
decodeAt++;
} while (decodeAt < decodedSz);
int decodedAddress = decodedEntry.address;
if (decodedEntry.reg != origEntry.getRegister()) {
System.err.println("local register mismatch at orig " + i +
" / decoded " + decodeAt);
problem = true;
break;
}
if (decodedEntry.isStart != origEntry.isStart()) {
System.err.println("local start/end mismatch at orig " + i +
" / decoded " + decodeAt);
problem = true;
break;
}
/*
* The secondary check here accounts for the fact that a
* parameter might not be marked as starting at 0 in the
* original list.
*/
if ((decodedAddress != origEntry.getAddress())
&& !((decodedAddress == 0)
&& (decodedEntry.reg >= paramBase))) {
System.err.println("local address mismatch at orig " + i +
" / decoded " + decodeAt);
problem = true;
break;
}
decodeAt++;
}
if (problem) {
System.err.println("decoded locals:");
for (LocalEntry e : decodedLocals) {
System.err.println(" " + e);
}
throw new RuntimeException("local table problem");
}
}
/**
* Reads a DWARFv3-style signed LEB128 integer to the specified stream.
* See DWARF v3 section 7.6. An invalid sequence produces an IOException.
*
* @param bs stream to input from
* @return read value
* @throws IOException on invalid sequence in addition to
* those caused by the InputStream
*/
public static int readSignedLeb128(InputStream bs) throws IOException {
int result = 0;
int cur;
int count = 0;
int signBits = -1;
do {
cur = bs.read();
result |= (cur & 0x7f) << (count * 7);
signBits <<= 7;
count++;
} while (((cur & 0x80) == 0x80) && count < 5);
if ((cur & 0x80) == 0x80) {
throw new IOException ("invalid LEB128 sequence");
}
// Sign extend if appropriate
if (((signBits >> 1) & result) != 0 ) {
result |= signBits;
}
return result;
}
/**
* Reads a DWARFv3-style unsigned LEB128 integer to the specified stream.
* See DWARF v3 section 7.6. An invalid sequence produces an IOException.
*
* @param bs stream to input from
* @return read value, which should be treated as an unsigned value.
* @throws IOException on invalid sequence in addition to
* those caused by the InputStream
*/
public static int readUnsignedLeb128(InputStream bs) throws IOException {
int result = 0;
int cur;
int count = 0;
do {
cur = bs.read();
result |= (cur & 0x7f) << (count * 7);
count++;
} while (((cur & 0x80) == 0x80) && count < 5);
if ((cur & 0x80) == 0x80) {
throw new IOException ("invalid LEB128 sequence");
}
return result;
}
}

View File

@ -0,0 +1,920 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.code.LocalList;
import com.android.dexgen.dex.code.PositionList;
import com.android.dexgen.rop.code.RegisterSpec;
import com.android.dexgen.rop.code.SourcePosition;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Prototype;
import com.android.dexgen.rop.type.StdTypeList;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ByteArrayAnnotatedOutput;
import com.android.dexgen.util.ExceptionWithContext;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.BitSet;
import static com.android.dexgen.dex.file.DebugInfoConstants.*;
/**
* An encoder for the dex debug info state machine format. The format
* for each method enrty is as follows:
* <ol>
* <li> signed LEB128: initial value for line register.
* <li> n instances of signed LEB128: string indicies (offset by 1)
* for each method argument in left-to-right order
* with {@code this} excluded. A value of '0' indicates "no name"
* <li> A sequence of special or normal opcodes as defined in
* {@code DebugInfoConstants}.
* <li> A single terminating {@code OP_END_SEQUENCE}
* </ol>
*/
public final class DebugInfoEncoder {
private static final boolean DEBUG = false;
/** {@code null-ok;} positions (line numbers) to encode */
private final PositionList positions;
/** {@code null-ok;} local variables to encode */
private final LocalList locals;
private final ByteArrayAnnotatedOutput output;
private final DexFile file;
private final int codeSize;
private final int regSize;
private final Prototype desc;
private final boolean isStatic;
/** current encoding state: bytecode address */
private int address = 0;
/** current encoding state: line number */
private int line = 1;
/**
* if non-null: the output to write annotations to. No normal
* output is written to this.
*/
private AnnotatedOutput annotateTo;
/** if non-null: another possible output for annotations */
private PrintWriter debugPrint;
/** if non-null: the prefix for each annotation or debugPrint line */
private String prefix;
/** true if output should be consumed during annotation */
private boolean shouldConsume;
/** indexed by register; last local alive in register */
private final LocalList.Entry[] lastEntryForReg;
/**
* Creates an instance.
*
* @param positions {@code null-ok;} positions (line numbers) to encode
* @param locals {@code null-ok;} local variables to encode
* @param file {@code null-ok;} may only be {@code null} if simply using
* this class to do a debug print
* @param codeSize
* @param regSize
* @param isStatic
* @param ref
*/
public DebugInfoEncoder(PositionList positions, LocalList locals,
DexFile file, int codeSize, int regSize,
boolean isStatic, CstMethodRef ref) {
this.positions = positions;
this.locals = locals;
this.file = file;
this.desc = ref.getPrototype();
this.isStatic = isStatic;
this.codeSize = codeSize;
this.regSize = regSize;
output = new ByteArrayAnnotatedOutput();
lastEntryForReg = new LocalList.Entry[regSize];
}
/**
* Annotates or writes a message to the {@code debugPrint} writer
* if applicable.
*
* @param length the number of bytes associated with this message
* @param message the message itself
*/
private void annotate(int length, String message) {
if (prefix != null) {
message = prefix + message;
}
if (annotateTo != null) {
annotateTo.annotate(shouldConsume ? length : 0, message);
}
if (debugPrint != null) {
debugPrint.println(message);
}
}
/**
* Converts this (PositionList, LocalList) pair into a state machine
* sequence.
*
* @return {@code non-null;} encoded byte sequence without padding and
* terminated with a {@code 0x00} byte
*/
public byte[] convert() {
try {
byte[] ret;
ret = convert0();
if (DEBUG) {
for (int i = 0 ; i < ret.length; i++) {
System.err.printf("byte %02x\n", (0xff & ret[i]));
}
}
return ret;
} catch (IOException ex) {
throw ExceptionWithContext
.withContext(ex, "...while encoding debug info");
}
}
/**
* Converts and produces annotations on a stream. Does not write
* actual bits to the {@code AnnotatedOutput}.
*
* @param prefix {@code null-ok;} prefix to attach to each line of output
* @param debugPrint {@code null-ok;} if specified, an alternate output for
* annotations
* @param out {@code null-ok;} if specified, where annotations should go
* @param consume whether to claim to have consumed output for
* {@code out}
* @return {@code non-null;} encoded output
*/
public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint,
AnnotatedOutput out, boolean consume) {
this.prefix = prefix;
this.debugPrint = debugPrint;
annotateTo = out;
shouldConsume = consume;
byte[] result = convert();
return result;
}
private byte[] convert0() throws IOException {
ArrayList<PositionList.Entry> sortedPositions = buildSortedPositions();
ArrayList<LocalList.Entry> methodArgs = extractMethodArguments();
emitHeader(sortedPositions, methodArgs);
// TODO: Make this mark be the actual prologue end.
output.writeByte(DBG_SET_PROLOGUE_END);
if (annotateTo != null || debugPrint != null) {
annotate(1, String.format("%04x: prologue end",address));
}
int positionsSz = sortedPositions.size();
int localsSz = locals.size();
// Current index in sortedPositions
int curPositionIdx = 0;
// Current index in locals
int curLocalIdx = 0;
for (;;) {
/*
* Emit any information for the current address.
*/
curLocalIdx = emitLocalsAtAddress(curLocalIdx);
curPositionIdx =
emitPositionsAtAddress(curPositionIdx, sortedPositions);
/*
* Figure out what the next important address is.
*/
int nextAddrL = Integer.MAX_VALUE; // local variable
int nextAddrP = Integer.MAX_VALUE; // position (line number)
if (curLocalIdx < localsSz) {
nextAddrL = locals.get(curLocalIdx).getAddress();
}
if (curPositionIdx < positionsSz) {
nextAddrP = sortedPositions.get(curPositionIdx).getAddress();
}
int next = Math.min(nextAddrP, nextAddrL);
// No next important address == done.
if (next == Integer.MAX_VALUE) {
break;
}
/*
* If the only work remaining are local ends at the end of the
* block, stop here. Those are implied anyway.
*/
if (next == codeSize
&& nextAddrL == Integer.MAX_VALUE
&& nextAddrP == Integer.MAX_VALUE) {
break;
}
if (next == nextAddrP) {
// Combined advance PC + position entry
emitPosition(sortedPositions.get(curPositionIdx++));
} else {
emitAdvancePc(next - address);
}
}
emitEndSequence();
return output.toByteArray();
}
/**
* Emits all local variable activity that occurs at the current
* {@link #address} starting at the given index into {@code
* locals} and including all subsequent activity at the same
* address.
*
* @param curLocalIdx Current index in locals
* @return new value for {@code curLocalIdx}
* @throws IOException
*/
private int emitLocalsAtAddress(int curLocalIdx)
throws IOException {
int sz = locals.size();
// TODO: Don't emit ends implied by starts.
while ((curLocalIdx < sz)
&& (locals.get(curLocalIdx).getAddress() == address)) {
LocalList.Entry entry = locals.get(curLocalIdx++);
int reg = entry.getRegister();
LocalList.Entry prevEntry = lastEntryForReg[reg];
if (entry == prevEntry) {
/*
* Here we ignore locals entries for parameters,
* which have already been represented and placed in the
* lastEntryForReg array.
*/
continue;
}
// At this point we have a new entry one way or another.
lastEntryForReg[reg] = entry;
if (entry.isStart()) {
if ((prevEntry != null) && entry.matches(prevEntry)) {
/*
* The previous local in this register has the same
* name and type as the one being introduced now, so
* use the more efficient "restart" form.
*/
if (prevEntry.isStart()) {
/*
* We should never be handed a start when a
* a matching local is already active.
*/
throw new RuntimeException("shouldn't happen");
}
emitLocalRestart(entry);
} else {
emitLocalStart(entry);
}
} else {
/*
* Only emit a local end if it is *not* due to a direct
* replacement. Direct replacements imply an end of the
* previous local in the same register.
*
* TODO: Make sure the runtime can deal with implied
* local ends from category-2 interactions, and when so,
* also stop emitting local ends for those cases.
*/
if (entry.getDisposition()
!= LocalList.Disposition.END_REPLACED) {
emitLocalEnd(entry);
}
}
}
return curLocalIdx;
}
/**
* Emits all positions that occur at the current {@code address}
*
* @param curPositionIdx Current index in sortedPositions
* @param sortedPositions positions, sorted by ascending address
* @return new value for {@code curPositionIdx}
* @throws IOException
*/
private int emitPositionsAtAddress(int curPositionIdx,
ArrayList<PositionList.Entry> sortedPositions)
throws IOException {
int positionsSz = sortedPositions.size();
while ((curPositionIdx < positionsSz)
&& (sortedPositions.get(curPositionIdx).getAddress()
== address)) {
emitPosition(sortedPositions.get(curPositionIdx++));
}
return curPositionIdx;
}
/**
* Emits the header sequence, which consists of LEB128-encoded initial
* line number and string indicies for names of all non-"this" arguments.
*
* @param sortedPositions positions, sorted by ascending address
* @param methodArgs local list entries for method argumens arguments,
* in left-to-right order omitting "this"
* @throws IOException
*/
private void emitHeader(ArrayList<PositionList.Entry> sortedPositions,
ArrayList<LocalList.Entry> methodArgs) throws IOException {
boolean annotate = (annotateTo != null) || (debugPrint != null);
int mark = output.getCursor();
// Start by initializing the line number register.
if (sortedPositions.size() > 0) {
PositionList.Entry entry = sortedPositions.get(0);
line = entry.getPosition().getLine();
}
output.writeUnsignedLeb128(line);
if (annotate) {
annotate(output.getCursor() - mark, "line_start: " + line);
}
int curParam = getParamBase();
// paramTypes will not include 'this'
StdTypeList paramTypes = desc.getParameterTypes();
int szParamTypes = paramTypes.size();
/*
* Initialize lastEntryForReg to have an initial
* entry for the 'this' pointer.
*/
if (!isStatic) {
for (LocalList.Entry arg : methodArgs) {
if (curParam == arg.getRegister()) {
lastEntryForReg[curParam] = arg;
break;
}
}
curParam++;
}
// Write out the number of parameter entries that will follow.
mark = output.getCursor();
output.writeUnsignedLeb128(szParamTypes);
if (annotate) {
annotate(output.getCursor() - mark,
String.format("parameters_size: %04x", szParamTypes));
}
/*
* Then emit the string indicies of all the method parameters.
* Note that 'this', if applicable, is excluded.
*/
for (int i = 0; i < szParamTypes; i++) {
Type pt = paramTypes.get(i);
LocalList.Entry found = null;
mark = output.getCursor();
for (LocalList.Entry arg : methodArgs) {
if (curParam == arg.getRegister()) {
found = arg;
if (arg.getSignature() != null) {
/*
* Parameters with signatures will be re-emitted
* in complete as LOCAL_START_EXTENDED's below.
*/
emitStringIndex(null);
} else {
emitStringIndex(arg.getName());
}
lastEntryForReg[curParam] = arg;
break;
}
}
if (found == null) {
/*
* Emit a null symbol for "unnamed." This is common
* for, e.g., synthesized methods and inner-class
* this$0 arguments.
*/
emitStringIndex(null);
}
if (annotate) {
String parameterName
= (found == null || found.getSignature() != null)
? "<unnamed>" : found.getName().toHuman();
annotate(output.getCursor() - mark,
"parameter " + parameterName + " "
+ RegisterSpec.PREFIX + curParam);
}
curParam += pt.getCategory();
}
/*
* If anything emitted above has a type signature, emit it again as
* a LOCAL_RESTART_EXTENDED
*/
for (LocalList.Entry arg : lastEntryForReg) {
if (arg == null) {
continue;
}
CstUtf8 signature = arg.getSignature();
if (signature != null) {
emitLocalStartExtended(arg);
}
}
}
/**
* Builds a list of position entries, sorted by ascending address.
*
* @return A sorted positions list
*/
private ArrayList<PositionList.Entry> buildSortedPositions() {
int sz = (positions == null) ? 0 : positions.size();
ArrayList<PositionList.Entry> result = new ArrayList(sz);
for (int i = 0; i < sz; i++) {
result.add(positions.get(i));
}
// Sort ascending by address.
Collections.sort (result, new Comparator<PositionList.Entry>() {
public int compare (PositionList.Entry a, PositionList.Entry b) {
return a.getAddress() - b.getAddress();
}
public boolean equals (Object obj) {
return obj == this;
}
});
return result;
}
/**
* Gets the register that begins the method's parameter range (including
* the 'this' parameter for non-static methods). The range continues until
* {@code regSize}
*
* @return register as noted above
*/
private int getParamBase() {
return regSize
- desc.getParameterTypes().getWordCount() - (isStatic? 0 : 1);
}
/**
* Extracts method arguments from a locals list. These will be collected
* from the input list and sorted by ascending register in the
* returned list.
*
* @return list of non-{@code this} method argument locals,
* sorted by ascending register
*/
private ArrayList<LocalList.Entry> extractMethodArguments() {
ArrayList<LocalList.Entry> result
= new ArrayList(desc.getParameterTypes().size());
int argBase = getParamBase();
BitSet seen = new BitSet(regSize - argBase);
int sz = locals.size();
for (int i = 0; i < sz; i++) {
LocalList.Entry e = locals.get(i);
int reg = e.getRegister();
if (reg < argBase) {
continue;
}
// only the lowest-start-address entry is included.
if (seen.get(reg - argBase)) {
continue;
}
seen.set(reg - argBase);
result.add(e);
}
// Sort by ascending register.
Collections.sort(result, new Comparator<LocalList.Entry>() {
public int compare(LocalList.Entry a, LocalList.Entry b) {
return a.getRegister() - b.getRegister();
}
public boolean equals(Object obj) {
return obj == this;
}
});
return result;
}
/**
* Returns a string representation of this LocalList entry that is
* appropriate for emitting as an annotation.
*
* @param e {@code non-null;} entry
* @return {@code non-null;} annotation string
*/
private String entryAnnotationString(LocalList.Entry e) {
StringBuilder sb = new StringBuilder();
sb.append(RegisterSpec.PREFIX);
sb.append(e.getRegister());
sb.append(' ');
CstUtf8 name = e.getName();
if (name == null) {
sb.append("null");
} else {
sb.append(name.toHuman());
}
sb.append(' ');
CstType type = e.getType();
if (type == null) {
sb.append("null");
} else {
sb.append(type.toHuman());
}
CstUtf8 signature = e.getSignature();
if (signature != null) {
sb.append(' ');
sb.append(signature.toHuman());
}
return sb.toString();
}
/**
* Emits a {@link DebugInfoConstants#DBG_RESTART_LOCAL DBG_RESTART_LOCAL}
* sequence.
*
* @param entry entry associated with this restart
* @throws IOException
*/
private void emitLocalRestart(LocalList.Entry entry)
throws IOException {
int mark = output.getCursor();
output.writeByte(DBG_RESTART_LOCAL);
emitUnsignedLeb128(entry.getRegister());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: +local restart %s",
address, entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local restart");
}
}
/**
* Emits a string index as an unsigned LEB128. The actual value written
* is shifted by 1, so that the '0' value is reserved for "null". The
* null symbol is used in some cases by the parameter name list
* at the beginning of the sequence.
*
* @param string {@code null-ok;} string to emit
* @throws IOException
*/
private void emitStringIndex(CstUtf8 string) throws IOException {
if ((string == null) || (file == null)) {
output.writeUnsignedLeb128(0);
} else {
output.writeUnsignedLeb128(
1 + file.getStringIds().indexOf(string));
}
if (DEBUG) {
System.err.printf("Emit string %s\n",
string == null ? "<null>" : string.toQuoted());
}
}
/**
* Emits a type index as an unsigned LEB128. The actual value written
* is shifted by 1, so that the '0' value is reserved for "null".
*
* @param type {@code null-ok;} type to emit
* @throws IOException
*/
private void emitTypeIndex(CstType type) throws IOException {
if ((type == null) || (file == null)) {
output.writeUnsignedLeb128(0);
} else {
output.writeUnsignedLeb128(
1 + file.getTypeIds().indexOf(type));
}
if (DEBUG) {
System.err.printf("Emit type %s\n",
type == null ? "<null>" : type.toHuman());
}
}
/**
* Emits a {@link DebugInfoConstants#DBG_START_LOCAL DBG_START_LOCAL} or
* {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
* DBG_START_LOCAL_EXTENDED} sequence.
*
* @param entry entry to emit
* @throws IOException
*/
private void emitLocalStart(LocalList.Entry entry)
throws IOException {
if (entry.getSignature() != null) {
emitLocalStartExtended(entry);
return;
}
int mark = output.getCursor();
output.writeByte(DBG_START_LOCAL);
emitUnsignedLeb128(entry.getRegister());
emitStringIndex(entry.getName());
emitTypeIndex(entry.getType());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: +local %s", address,
entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local start");
}
}
/**
* Emits a {@link DebugInfoConstants#DBG_START_LOCAL_EXTENDED
* DBG_START_LOCAL_EXTENDED} sequence.
*
* @param entry entry to emit
* @throws IOException
*/
private void emitLocalStartExtended(LocalList.Entry entry)
throws IOException {
int mark = output.getCursor();
output.writeByte(DBG_START_LOCAL_EXTENDED);
emitUnsignedLeb128(entry.getRegister());
emitStringIndex(entry.getName());
emitTypeIndex(entry.getType());
emitStringIndex(entry.getSignature());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: +localx %s", address,
entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local start");
}
}
/**
* Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence.
*
* @param entry {@code entry non-null;} entry associated with end.
* @throws IOException
*/
private void emitLocalEnd(LocalList.Entry entry)
throws IOException {
int mark = output.getCursor();
output.writeByte(DBG_END_LOCAL);
output.writeUnsignedLeb128(entry.getRegister());
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: -local %s", address,
entryAnnotationString(entry)));
}
if (DEBUG) {
System.err.println("emit local end");
}
}
/**
* Emits the necessary byte sequences to emit the given position table
* entry. This will typically be a single special opcode, although
* it may also require DBG_ADVANCE_PC or DBG_ADVANCE_LINE.
*
* @param entry position entry to emit.
* @throws IOException
*/
private void emitPosition(PositionList.Entry entry)
throws IOException {
SourcePosition pos = entry.getPosition();
int newLine = pos.getLine();
int newAddress = entry.getAddress();
int opcode;
int deltaLines = newLine - line;
int deltaAddress = newAddress - address;
if (deltaAddress < 0) {
throw new RuntimeException(
"Position entries must be in ascending address order");
}
if ((deltaLines < DBG_LINE_BASE)
|| (deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1))) {
emitAdvanceLine(deltaLines);
deltaLines = 0;
}
opcode = computeOpcode (deltaLines, deltaAddress);
if ((opcode & ~0xff) > 0) {
emitAdvancePc(deltaAddress);
deltaAddress = 0;
opcode = computeOpcode (deltaLines, deltaAddress);
if ((opcode & ~0xff) > 0) {
emitAdvanceLine(deltaLines);
deltaLines = 0;
opcode = computeOpcode (deltaLines, deltaAddress);
}
}
output.writeByte(opcode);
line += deltaLines;
address += deltaAddress;
if (annotateTo != null || debugPrint != null) {
annotate(1,
String.format("%04x: line %d", address, line));
}
}
/**
* Computes a special opcode that will encode the given position change.
* If the return value is > 0xff, then the request cannot be fulfilled.
* Essentially the same as described in "DWARF Debugging Format Version 3"
* section 6.2.5.1.
*
* @param deltaLines {@code >= DBG_LINE_BASE, <= DBG_LINE_BASE +
* DBG_LINE_RANGE;} the line change to encode
* @param deltaAddress {@code >= 0;} the address change to encode
* @return {@code <= 0xff} if in range, otherwise parameters are out
* of range
*/
private static int computeOpcode(int deltaLines, int deltaAddress) {
if (deltaLines < DBG_LINE_BASE
|| deltaLines > (DBG_LINE_BASE + DBG_LINE_RANGE -1)) {
throw new RuntimeException("Parameter out of range");
}
return (deltaLines - DBG_LINE_BASE)
+ (DBG_LINE_RANGE * deltaAddress) + DBG_FIRST_SPECIAL;
}
/**
* Emits an {@link DebugInfoConstants#DBG_ADVANCE_LINE DBG_ADVANCE_LINE}
* sequence.
*
* @param deltaLines amount to change line number register by
* @throws IOException
*/
private void emitAdvanceLine(int deltaLines) throws IOException {
int mark = output.getCursor();
output.writeByte(DBG_ADVANCE_LINE);
output.writeSignedLeb128(deltaLines);
line += deltaLines;
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("line = %d", line));
}
if (DEBUG) {
System.err.printf("Emitting advance_line for %d\n", deltaLines);
}
}
/**
* Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC}
* sequence.
*
* @param deltaAddress {@code >= 0;} amount to change program counter by
* @throws IOException
*/
private void emitAdvancePc(int deltaAddress) throws IOException {
int mark = output.getCursor();
output.writeByte(DBG_ADVANCE_PC);
output.writeUnsignedLeb128(deltaAddress);
address += deltaAddress;
if (annotateTo != null || debugPrint != null) {
annotate(output.getCursor() - mark,
String.format("%04x: advance pc", address));
}
if (DEBUG) {
System.err.printf("Emitting advance_pc for %d\n", deltaAddress);
}
}
/**
* Emits an unsigned LEB128 value.
*
* @param n {@code >= 0;} value to emit. Note that, although this can
* represent integers larger than Integer.MAX_VALUE, we currently don't
* allow that.
* @throws IOException
*/
private void emitUnsignedLeb128(int n) throws IOException {
// We'll never need the top end of the unsigned range anyway.
if (n < 0) {
throw new RuntimeException(
"Signed value where unsigned required: " + n);
}
output.writeUnsignedLeb128(n);
}
/**
* Emits the {@link DebugInfoConstants#DBG_END_SEQUENCE DBG_END_SEQUENCE}
* bytecode.
*/
private void emitEndSequence() {
output.writeByte(DBG_END_SEQUENCE);
if (annotateTo != null || debugPrint != null) {
annotate(1, "end sequence");
}
}
}

View File

@ -0,0 +1,196 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.code.DalvCode;
import com.android.dexgen.dex.code.DalvInsnList;
import com.android.dexgen.dex.code.LocalList;
import com.android.dexgen.dex.code.PositionList;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ExceptionWithContext;
import java.io.PrintWriter;
public class DebugInfoItem extends OffsettedItem {
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 1;
private static final boolean ENABLE_ENCODER_SELF_CHECK = false;
/** {@code non-null;} the code this item represents */
private final DalvCode code;
private byte[] encoded;
private final boolean isStatic;
private final CstMethodRef ref;
public DebugInfoItem(DalvCode code, boolean isStatic, CstMethodRef ref) {
// We don't know the write size yet.
super (ALIGNMENT, -1);
if (code == null) {
throw new NullPointerException("code == null");
}
this.code = code;
this.isStatic = isStatic;
this.ref = ref;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_DEBUG_INFO_ITEM;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
// No contents to add.
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// Encode the data and note the size.
try {
encoded = encode(addedTo.getFile(), null, null, null, false);
setWriteSize(encoded.length);
} catch (RuntimeException ex) {
throw ExceptionWithContext.withContext(ex,
"...while placing debug info for " + ref.toHuman());
}
}
/** {@inheritDoc} */
@Override
public String toHuman() {
throw new RuntimeException("unsupported");
}
/**
* Writes annotations for the elements of this list, as
* zero-length. This is meant to be used for dumping this instance
* directly after a code dump (with the real local list actually
* existing elsewhere in the output).
*
* @param file {@code non-null;} the file to use for referencing other sections
* @param out {@code non-null;} where to annotate to
* @param prefix {@code null-ok;} prefix to attach to each line of output
*/
public void annotateTo(DexFile file, AnnotatedOutput out, String prefix) {
encode(file, prefix, null, out, false);
}
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param prefix {@code non-null;} prefix to attach to each line of output
*/
public void debugPrint(PrintWriter out, String prefix) {
encode(null, prefix, out, null, false);
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
if (out.annotates()) {
/*
* Re-run the encoder to generate the annotations,
* but write the bits from the original encode
*/
out.annotate(offsetString() + " debug info");
encode(file, null, null, out, true);
}
out.write(encoded);
}
/**
* Performs debug info encoding.
*
* @param file {@code null-ok;} file to refer to during encoding
* @param prefix {@code null-ok;} prefix to attach to each line of output
* @param debugPrint {@code null-ok;} if specified, an alternate output for
* annotations
* @param out {@code null-ok;} if specified, where annotations should go
* @param consume whether to claim to have consumed output for
* {@code out}
* @return {@code non-null;} the encoded array
*/
private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint,
AnnotatedOutput out, boolean consume) {
byte[] result = encode0(file, prefix, debugPrint, out, consume);
if (ENABLE_ENCODER_SELF_CHECK && (file != null)) {
try {
DebugInfoDecoder.validateEncode(result, file, ref, code,
isStatic);
} catch (RuntimeException ex) {
// Reconvert, annotating to System.err.
encode0(file, "", new PrintWriter(System.err, true), null,
false);
throw ex;
}
}
return result;
}
/**
* Helper for {@link #encode} to do most of the work.
*
* @param file {@code null-ok;} file to refer to during encoding
* @param prefix {@code null-ok;} prefix to attach to each line of output
* @param debugPrint {@code null-ok;} if specified, an alternate output for
* annotations
* @param out {@code null-ok;} if specified, where annotations should go
* @param consume whether to claim to have consumed output for
* {@code out}
* @return {@code non-null;} the encoded array
*/
private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint,
AnnotatedOutput out, boolean consume) {
PositionList positions = code.getPositions();
LocalList locals = code.getLocals();
DalvInsnList insns = code.getInsns();
int codeSize = insns.codeSize();
int regSize = insns.getRegistersSize();
DebugInfoEncoder encoder =
new DebugInfoEncoder(positions, locals,
file, codeSize, regSize, isStatic, ref);
byte[] result;
if ((debugPrint == null) && (out == null)) {
result = encoder.convert();
} else {
result = encoder.convertAndAnnotate(prefix, debugPrint, out,
consume);
}
return result;
}
}

View File

@ -0,0 +1,646 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.file.MixedItemSection.SortType;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstBaseMethodRef;
import com.android.dexgen.rop.cst.CstEnumRef;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstString;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.Type;
import com.android.dexgen.util.ByteArrayAnnotatedOutput;
import com.android.dexgen.util.ExceptionWithContext;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.Adler32;
/**
* Representation of an entire {@code .dex} (Dalvik EXecutable)
* file, which itself consists of a set of Dalvik classes.
*/
public final class DexFile {
/** {@code non-null;} word data section */
private final MixedItemSection wordData;
/**
* {@code non-null;} type lists section. This is word data, but separating
* it from {@link #wordData} helps break what would otherwise be a
* circular dependency between the that and {@link #protoIds}.
*/
private final MixedItemSection typeLists;
/**
* {@code non-null;} map section. The map needs to be in a section by itself
* for the self-reference mechanics to work in a reasonably
* straightforward way. See {@link MapItem#addMap} for more detail.
*/
private final MixedItemSection map;
/** {@code non-null;} string data section */
private final MixedItemSection stringData;
/** {@code non-null;} string identifiers section */
private final StringIdsSection stringIds;
/** {@code non-null;} type identifiers section */
private final TypeIdsSection typeIds;
/** {@code non-null;} prototype identifiers section */
private final ProtoIdsSection protoIds;
/** {@code non-null;} field identifiers section */
private final FieldIdsSection fieldIds;
/** {@code non-null;} method identifiers section */
private final MethodIdsSection methodIds;
/** {@code non-null;} class definitions section */
private final ClassDefsSection classDefs;
/** {@code non-null;} class data section */
private final MixedItemSection classData;
/** {@code non-null;} byte data section */
private final MixedItemSection byteData;
/** {@code non-null;} file header */
private final HeaderSection header;
/**
* {@code non-null;} array of sections in the order they will appear in the
* final output file
*/
private final Section[] sections;
/** {@code >= -1;} total file size or {@code -1} if unknown */
private int fileSize;
/** {@code >= 40;} maximum width of the file dump */
private int dumpWidth;
/**
* Constructs an instance. It is initially empty.
*/
public DexFile() {
header = new HeaderSection(this);
typeLists = new MixedItemSection(null, this, 4, SortType.NONE);
wordData = new MixedItemSection("word_data", this, 4, SortType.TYPE);
stringData =
new MixedItemSection("string_data", this, 1, SortType.INSTANCE);
classData = new MixedItemSection(null, this, 1, SortType.NONE);
byteData = new MixedItemSection("byte_data", this, 1, SortType.TYPE);
stringIds = new StringIdsSection(this);
typeIds = new TypeIdsSection(this);
protoIds = new ProtoIdsSection(this);
fieldIds = new FieldIdsSection(this);
methodIds = new MethodIdsSection(this);
classDefs = new ClassDefsSection(this);
map = new MixedItemSection("map", this, 4, SortType.NONE);
/*
* This is the list of sections in the order they appear in
* the final output.
*/
sections = new Section[] {
header, stringIds, typeIds, protoIds, fieldIds, methodIds,
classDefs, wordData, typeLists, stringData, byteData,
classData, map };
fileSize = -1;
dumpWidth = 79;
}
/**
* Adds a class to this instance. It is illegal to attempt to add more
* than one class with the same name.
*
* @param clazz {@code non-null;} the class to add
*/
public void add(ClassDefItem clazz) {
classDefs.add(clazz);
}
/**
* Gets the class definition with the given name, if any.
*
* @param name {@code non-null;} the class name to look for
* @return {@code null-ok;} the class with the given name, or {@code null}
* if there is no such class
*/
public ClassDefItem getClassOrNull(String name) {
try {
Type type = Type.internClassName(name);
return (ClassDefItem) classDefs.get(new CstType(type));
} catch (IllegalArgumentException ex) {
// Translate exception, per contract.
return null;
}
}
/**
* Writes the contents of this instance as either a binary or a
* human-readable form, or both.
*
* @param out {@code null-ok;} where to write to
* @param humanOut {@code null-ok;} where to write human-oriented output to
* @param verbose whether to be verbose when writing human-oriented output
*/
public void writeTo(OutputStream out, Writer humanOut, boolean verbose)
throws IOException {
boolean annotate = (humanOut != null);
ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
if (out != null) {
out.write(result.getArray());
}
if (annotate) {
result.writeAnnotationsTo(humanOut);
}
}
/**
* Returns the contents of this instance as a {@code .dex} file,
* in {@code byte[]} form.
*
* @param humanOut {@code null-ok;} where to write human-oriented output to
* @param verbose whether to be verbose when writing human-oriented output
* @return {@code non-null;} a {@code .dex} file for this instance
*/
public byte[] toDex(Writer humanOut, boolean verbose)
throws IOException {
boolean annotate = (humanOut != null);
ByteArrayAnnotatedOutput result = toDex0(annotate, verbose);
if (annotate) {
result.writeAnnotationsTo(humanOut);
}
return result.getArray();
}
/**
* Sets the maximum width of the human-oriented dump of the instance.
*
* @param dumpWidth {@code >= 40;} the width
*/
public void setDumpWidth(int dumpWidth) {
if (dumpWidth < 40) {
throw new IllegalArgumentException("dumpWidth < 40");
}
this.dumpWidth = dumpWidth;
}
/**
* Gets the total file size, if known.
*
* <p>This is package-scope in order to allow
* the {@link HeaderSection} to set itself up properly.</p>
*
* @return {@code >= 0;} the total file size
* @throws RuntimeException thrown if the file size is not yet known
*/
/*package*/ int getFileSize() {
if (fileSize < 0) {
throw new RuntimeException("file size not yet known");
}
return fileSize;
}
/**
* Gets the string data section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the string data section
*/
/*package*/ MixedItemSection getStringData() {
return stringData;
}
/**
* Gets the word data section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the word data section
*/
/*package*/ MixedItemSection getWordData() {
return wordData;
}
/**
* Gets the type lists section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the word data section
*/
/*package*/ MixedItemSection getTypeLists() {
return typeLists;
}
/**
* Gets the map section.
*
* <p>This is package-scope in order to allow the header section
* to query it.</p>
*
* @return {@code non-null;} the map section
*/
/*package*/ MixedItemSection getMap() {
return map;
}
/**
* Gets the string identifiers section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the string identifiers section
*/
/*package*/ StringIdsSection getStringIds() {
return stringIds;
}
/**
* Gets the class definitions section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the class definitions section
*/
/*package*/ ClassDefsSection getClassDefs() {
return classDefs;
}
/**
* Gets the class data section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the class data section
*/
/*package*/ MixedItemSection getClassData() {
return classData;
}
/**
* Gets the type identifiers section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the class identifiers section
*/
/*package*/ TypeIdsSection getTypeIds() {
return typeIds;
}
/**
* Gets the prototype identifiers section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the prototype identifiers section
*/
/*package*/ ProtoIdsSection getProtoIds() {
return protoIds;
}
/**
* Gets the field identifiers section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the field identifiers section
*/
/*package*/ FieldIdsSection getFieldIds() {
return fieldIds;
}
/**
* Gets the method identifiers section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the method identifiers section
*/
/*package*/ MethodIdsSection getMethodIds() {
return methodIds;
}
/**
* Gets the byte data section.
*
* <p>This is package-scope in order to allow
* the various {@link Item} instances to add items to the
* instance.</p>
*
* @return {@code non-null;} the byte data section
*/
/*package*/ MixedItemSection getByteData() {
return byteData;
}
/**
* Gets the first section of the file that is to be considered
* part of the data section.
*
* <p>This is package-scope in order to allow the header section
* to query it.</p>
*
* @return {@code non-null;} the section
*/
/*package*/ Section getFirstDataSection() {
return wordData;
}
/**
* Gets the last section of the file that is to be considered
* part of the data section.
*
* <p>This is package-scope in order to allow the header section
* to query it.</p>
*
* @return {@code non-null;} the section
*/
/*package*/ Section getLastDataSection() {
return map;
}
/**
* Interns the given constant in the appropriate section of this
* instance, or do nothing if the given constant isn't the sort
* that should be interned.
*
* @param cst {@code non-null;} constant to possibly intern
*/
/*package*/ void internIfAppropriate(Constant cst) {
if (cst instanceof CstString) {
stringIds.intern((CstString) cst);
} else if (cst instanceof CstUtf8) {
stringIds.intern((CstUtf8) cst);
} else if (cst instanceof CstType) {
typeIds.intern((CstType) cst);
} else if (cst instanceof CstBaseMethodRef) {
methodIds.intern((CstBaseMethodRef) cst);
} else if (cst instanceof CstFieldRef) {
fieldIds.intern((CstFieldRef) cst);
} else if (cst instanceof CstEnumRef) {
fieldIds.intern(((CstEnumRef) cst).getFieldRef());
} else if (cst == null) {
throw new NullPointerException("cst == null");
}
}
/**
* Gets the {@link IndexedItem} corresponding to the given constant,
* if it is a constant that has such a correspondence, or return
* {@code null} if it isn't such a constant. This will throw
* an exception if the given constant <i>should</i> have been found
* but wasn't.
*
* @param cst {@code non-null;} the constant to look up
* @return {@code null-ok;} its corresponding item, if it has a corresponding
* item, or {@code null} if it's not that sort of constant
*/
/*package*/ IndexedItem findItemOrNull(Constant cst) {
IndexedItem item;
if (cst instanceof CstString) {
return stringIds.get(cst);
} else if (cst instanceof CstType) {
return typeIds.get(cst);
} else if (cst instanceof CstBaseMethodRef) {
return methodIds.get(cst);
} else if (cst instanceof CstFieldRef) {
return fieldIds.get(cst);
} else {
return null;
}
}
/**
* Returns the contents of this instance as a {@code .dex} file,
* in a {@link ByteArrayAnnotatedOutput} instance.
*
* @param annotate whether or not to keep annotations
* @param verbose if annotating, whether to be verbose
* @return {@code non-null;} a {@code .dex} file for this instance
*/
private ByteArrayAnnotatedOutput toDex0(boolean annotate,
boolean verbose) {
/*
* The following is ordered so that the prepare() calls which
* add items happen before the calls to the sections that get
* added to.
*/
classDefs.prepare();
classData.prepare();
wordData.prepare();
byteData.prepare();
methodIds.prepare();
fieldIds.prepare();
protoIds.prepare();
typeLists.prepare();
typeIds.prepare();
stringIds.prepare();
stringData.prepare();
header.prepare();
// Place the sections within the file.
int count = sections.length;
int offset = 0;
for (int i = 0; i < count; i++) {
Section one = sections[i];
int placedAt = one.setFileOffset(offset);
if (placedAt < offset) {
throw new RuntimeException("bogus placement for section " + i);
}
try {
if (one == map) {
/*
* Inform the map of all the sections, and add it
* to the file. This can only be done after all
* the other items have been sorted and placed.
*/
MapItem.addMap(sections, map);
map.prepare();
}
if (one instanceof MixedItemSection) {
/*
* Place the items of a MixedItemSection that just
* got placed.
*/
((MixedItemSection) one).placeItems();
}
offset = placedAt + one.writeSize();
} catch (RuntimeException ex) {
throw ExceptionWithContext.withContext(ex,
"...while writing section " + i);
}
}
// Write out all the sections.
fileSize = offset;
byte[] barr = new byte[fileSize];
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(barr);
if (annotate) {
out.enableAnnotations(dumpWidth, verbose);
}
for (int i = 0; i < count; i++) {
try {
Section one = sections[i];
int zeroCount = one.getFileOffset() - out.getCursor();
if (zeroCount < 0) {
throw new ExceptionWithContext("excess write of " +
(-zeroCount));
}
out.writeZeroes(one.getFileOffset() - out.getCursor());
one.writeTo(out);
} catch (RuntimeException ex) {
ExceptionWithContext ec;
if (ex instanceof ExceptionWithContext) {
ec = (ExceptionWithContext) ex;
} else {
ec = new ExceptionWithContext(ex);
}
ec.addContext("...while writing section " + i);
throw ec;
}
}
if (out.getCursor() != fileSize) {
throw new RuntimeException("foreshortened write");
}
// Perform final bookkeeping.
calcSignature(barr);
calcChecksum(barr);
if (annotate) {
wordData.writeIndexAnnotation(out, ItemType.TYPE_CODE_ITEM,
"\nmethod code index:\n\n");
getStatistics().writeAnnotation(out);
out.finishAnnotating();
}
return out;
}
/**
* Generates and returns statistics for all the items in the file.
*
* @return {@code non-null;} the statistics
*/
public Statistics getStatistics() {
Statistics stats = new Statistics();
for (Section s : sections) {
stats.addAll(s);
}
return stats;
}
/**
* Calculates the signature for the {@code .dex} file in the
* given array, and modify the array to contain it.
*
* @param bytes {@code non-null;} the bytes of the file
*/
private static void calcSignature(byte[] bytes) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException(ex);
}
md.update(bytes, 32, bytes.length - 32);
try {
int amt = md.digest(bytes, 12, 20);
if (amt != 20) {
throw new RuntimeException("unexpected digest write: " + amt +
" bytes");
}
} catch (DigestException ex) {
throw new RuntimeException(ex);
}
}
/**
* Calculates the checksum for the {@code .dex} file in the
* given array, and modify the array to contain it.
*
* @param bytes {@code non-null;} the bytes of the file
*/
private static void calcChecksum(byte[] bytes) {
Adler32 a32 = new Adler32();
a32.update(bytes, 12, bytes.length - 12);
int sum = (int) a32.getValue();
bytes[8] = (byte) sum;
bytes[9] = (byte) (sum >> 8);
bytes[10] = (byte) (sum >> 16);
bytes[11] = (byte) (sum >> 24);
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotation;
import com.android.dexgen.rop.annotation.AnnotationVisibility;
import com.android.dexgen.rop.annotation.NameValuePair;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstAnnotation;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ByteArrayAnnotatedOutput;
import java.util.Arrays;
import java.util.Comparator;
/**
* Encoded array of constant values.
*/
public final class EncodedArrayItem extends OffsettedItem {
/** the required alignment for instances of this class */
private static final int ALIGNMENT = 1;
/** {@code non-null;} the array to represent */
private final CstArray array;
/**
* {@code null-ok;} encoded form, ready for writing to a file; set during
* {@link #place0}
*/
private byte[] encodedForm;
/**
* Constructs an instance.
*
* @param array {@code non-null;} array to represent
*/
public EncodedArrayItem(CstArray array) {
/*
* The write size isn't known up-front because (the variable-lengthed)
* leb128 type is used to represent some things.
*/
super(ALIGNMENT, -1);
if (array == null) {
throw new NullPointerException("array == null");
}
this.array = array;
this.encodedForm = null;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_ENCODED_ARRAY_ITEM;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return array.hashCode();
}
/** {@inheritDoc} */
@Override
protected int compareTo0(OffsettedItem other) {
EncodedArrayItem otherArray = (EncodedArrayItem) other;
return array.compareTo(otherArray.array);
}
/** {@inheritDoc} */
@Override
public String toHuman() {
return array.toHuman();
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
ValueEncoder.addContents(file, array);
}
/** {@inheritDoc} */
@Override
protected void place0(Section addedTo, int offset) {
// Encode the data and note the size.
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
ValueEncoder encoder = new ValueEncoder(addedTo.getFile(), out);
encoder.writeArray(array, false);
encodedForm = out.toByteArray();
setWriteSize(encodedForm.length);
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
if (annotates) {
out.annotate(0, offsetString() + " encoded array");
/*
* The output is to be annotated, so redo the work previously
* done by place0(), except this time annotations will actually
* get emitted.
*/
ValueEncoder encoder = new ValueEncoder(file, out);
encoder.writeArray(array, true);
} else {
out.write(encodedForm);
}
}
}

View File

@ -0,0 +1,154 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.code.AccessFlags;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.Leb128Utils;
import java.io.PrintWriter;
/**
* Representation of a field of a class, of any sort.
*/
public final class EncodedField extends EncodedMember
implements Comparable<EncodedField> {
/** {@code non-null;} constant for the field */
private final CstFieldRef field;
/**
* Constructs an instance.
*
* @param field {@code non-null;} constant for the field
* @param accessFlags access flags
*/
public EncodedField(CstFieldRef field, int accessFlags) {
super(accessFlags);
if (field == null) {
throw new NullPointerException("field == null");
}
/*
* TODO: Maybe check accessFlags, at least for
* easily-checked stuff?
*/
this.field = field;
}
/** {@inheritDoc} */
public int hashCode() {
return field.hashCode();
}
/** {@inheritDoc} */
public boolean equals(Object other) {
if (! (other instanceof EncodedField)) {
return false;
}
return compareTo((EncodedField) other) == 0;
}
/**
* {@inheritDoc}
*
* <p><b>Note:</b> This compares the method constants only,
* ignoring any associated code, because it should never be the
* case that two different items with the same method constant
* ever appear in the same list (or same file, even).</p>
*/
public int compareTo(EncodedField other) {
return field.compareTo(other.field);
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuffer sb = new StringBuffer(100);
sb.append(getClass().getName());
sb.append('{');
sb.append(Hex.u2(getAccessFlags()));
sb.append(' ');
sb.append(field);
sb.append('}');
return sb.toString();
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
FieldIdsSection fieldIds = file.getFieldIds();
fieldIds.intern(field);
}
/** {@inheritDoc} */
@Override
public CstUtf8 getName() {
return field.getNat().getName();
}
/** {@inheritDoc} */
public String toHuman() {
return field.toHuman();
}
/** {@inheritDoc} */
@Override
public void debugPrint(PrintWriter out, boolean verbose) {
// TODO: Maybe put something better here?
out.println(toString());
}
/**
* Gets the constant for the field.
*
* @return {@code non-null;} the constant
*/
public CstFieldRef getRef() {
return field;
}
/** {@inheritDoc} */
@Override
public int encode(DexFile file, AnnotatedOutput out,
int lastIndex, int dumpSeq) {
int fieldIdx = file.getFieldIds().indexOf(field);
int diff = fieldIdx - lastIndex;
int accessFlags = getAccessFlags();
if (out.annotates()) {
out.annotate(0, String.format(" [%x] %s", dumpSeq,
field.toHuman()));
out.annotate(Leb128Utils.unsignedLeb128Size(diff),
" field_idx: " + Hex.u4(fieldIdx));
out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
" access_flags: " +
AccessFlags.fieldString(accessFlags));
}
out.writeUnsignedLeb128(diff);
out.writeUnsignedLeb128(accessFlags);
return fieldIdx;
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ToHuman;
import java.io.PrintWriter;
/**
* Representation of a member (field or method) of a class, for the
* purposes of encoding it inside a {@link ClassDataItem}.
*/
public abstract class EncodedMember implements ToHuman {
/** access flags */
private final int accessFlags;
/**
* Constructs an instance.
*
* @param accessFlags access flags for the member
*/
public EncodedMember(int accessFlags) {
this.accessFlags = accessFlags;
}
/**
* Gets the access flags.
*
* @return the access flags
*/
public final int getAccessFlags() {
return accessFlags;
}
/**
* Gets the name.
*
* @return {@code non-null;} the name
*/
public abstract CstUtf8 getName();
/**
* Does a human-friendly dump of this instance.
*
* @param out {@code non-null;} where to dump
* @param verbose whether to be verbose with the output
*/
public abstract void debugPrint(PrintWriter out, boolean verbose);
/**
* Populates a {@link DexFile} with items from within this instance.
*
* @param file {@code non-null;} the file to populate
*/
public abstract void addContents(DexFile file);
/**
* Encodes this instance to the given output.
*
* @param file {@code non-null;} file this instance is part of
* @param out {@code non-null;} where to write to
* @param lastIndex {@code >= 0;} the previous member index value encoded, or
* {@code 0} if this is the first element to encode
* @param dumpSeq {@code >= 0;} sequence number of this instance for
* annotation purposes
* @return {@code >= 0;} the member index value that was encoded
*/
public abstract int encode(DexFile file, AnnotatedOutput out,
int lastIndex, int dumpSeq);
}

View File

@ -0,0 +1,196 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.dex.code.DalvCode;
import com.android.dexgen.rop.code.AccessFlags;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.Leb128Utils;
import java.io.PrintWriter;
/**
* Class that representats a method of a class.
*/
public final class EncodedMethod extends EncodedMember
implements Comparable<EncodedMethod> {
/** {@code non-null;} constant for the method */
private final CstMethodRef method;
/**
* {@code null-ok;} code for the method, if the method is neither
* {@code abstract} nor {@code native}
*/
private final CodeItem code;
/**
* Constructs an instance.
*
* @param method {@code non-null;} constant for the method
* @param accessFlags access flags
* @param code {@code null-ok;} code for the method, if it is neither
* {@code abstract} nor {@code native}
* @param throwsList {@code non-null;} list of possibly-thrown exceptions,
* just used in generating debugging output (listings)
*/
public EncodedMethod(CstMethodRef method, int accessFlags,
DalvCode code, TypeList throwsList) {
super(accessFlags);
if (method == null) {
throw new NullPointerException("method == null");
}
this.method = method;
if (code == null) {
this.code = null;
} else {
boolean isStatic = (accessFlags & AccessFlags.ACC_STATIC) != 0;
this.code = new CodeItem(method, code, isStatic, throwsList);
}
}
/** {@inheritDoc} */
public boolean equals(Object other) {
if (! (other instanceof EncodedMethod)) {
return false;
}
return compareTo((EncodedMethod) other) == 0;
}
/**
* {@inheritDoc}
*
* <p><b>Note:</b> This compares the method constants only,
* ignoring any associated code, because it should never be the
* case that two different items with the same method constant
* ever appear in the same list (or same file, even).</p>
*/
public int compareTo(EncodedMethod other) {
return method.compareTo(other.method);
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuffer sb = new StringBuffer(100);
sb.append(getClass().getName());
sb.append('{');
sb.append(Hex.u2(getAccessFlags()));
sb.append(' ');
sb.append(method);
if (code != null) {
sb.append(' ');
sb.append(code);
}
sb.append('}');
return sb.toString();
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
MethodIdsSection methodIds = file.getMethodIds();
MixedItemSection wordData = file.getWordData();
methodIds.intern(method);
if (code != null) {
wordData.add(code);
}
}
/** {@inheritDoc} */
public final String toHuman() {
return method.toHuman();
}
/** {@inheritDoc} */
@Override
public final CstUtf8 getName() {
return method.getNat().getName();
}
/** {@inheritDoc} */
@Override
public void debugPrint(PrintWriter out, boolean verbose) {
if (code == null) {
out.println(getRef().toHuman() + ": abstract or native");
} else {
code.debugPrint(out, " ", verbose);
}
}
/**
* Gets the constant for the method.
*
* @return {@code non-null;} the constant
*/
public final CstMethodRef getRef() {
return method;
}
/** {@inheritDoc} */
@Override
public int encode(DexFile file, AnnotatedOutput out,
int lastIndex, int dumpSeq) {
int methodIdx = file.getMethodIds().indexOf(method);
int diff = methodIdx - lastIndex;
int accessFlags = getAccessFlags();
int codeOff = OffsettedItem.getAbsoluteOffsetOr0(code);
boolean hasCode = (codeOff != 0);
boolean shouldHaveCode = (accessFlags &
(AccessFlags.ACC_ABSTRACT | AccessFlags.ACC_NATIVE)) == 0;
/*
* Verify that code appears if and only if a method is
* declared to have it.
*/
if (hasCode != shouldHaveCode) {
throw new UnsupportedOperationException(
"code vs. access_flags mismatch");
}
if (out.annotates()) {
out.annotate(0, String.format(" [%x] %s", dumpSeq,
method.toHuman()));
out.annotate(Leb128Utils.unsignedLeb128Size(diff),
" method_idx: " + Hex.u4(methodIdx));
out.annotate(Leb128Utils.unsignedLeb128Size(accessFlags),
" access_flags: " +
AccessFlags.methodString(accessFlags));
out.annotate(Leb128Utils.unsignedLeb128Size(codeOff),
" code_off: " + Hex.u4(codeOff));
}
out.writeUnsignedLeb128(diff);
out.writeUnsignedLeb128(accessFlags);
out.writeUnsignedLeb128(codeOff);
return methodIdx;
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotations;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.ToHuman;
/**
* Association of a field and its annotations.
*/
public final class FieldAnnotationStruct
implements ToHuman, Comparable<FieldAnnotationStruct> {
/** {@code non-null;} the field in question */
private final CstFieldRef field;
/** {@code non-null;} the associated annotations */
private AnnotationSetItem annotations;
/**
* Constructs an instance.
*
* @param field {@code non-null;} the field in question
* @param annotations {@code non-null;} the associated annotations
*/
public FieldAnnotationStruct(CstFieldRef field,
AnnotationSetItem annotations) {
if (field == null) {
throw new NullPointerException("field == null");
}
if (annotations == null) {
throw new NullPointerException("annotations == null");
}
this.field = field;
this.annotations = annotations;
}
/** {@inheritDoc} */
public int hashCode() {
return field.hashCode();
}
/** {@inheritDoc} */
public boolean equals(Object other) {
if (! (other instanceof FieldAnnotationStruct)) {
return false;
}
return field.equals(((FieldAnnotationStruct) other).field);
}
/** {@inheritDoc} */
public int compareTo(FieldAnnotationStruct other) {
return field.compareTo(other.field);
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
FieldIdsSection fieldIds = file.getFieldIds();
MixedItemSection wordData = file.getWordData();
fieldIds.intern(field);
annotations = wordData.intern(annotations);
}
/** {@inheritDoc} */
public void writeTo(DexFile file, AnnotatedOutput out) {
int fieldIdx = file.getFieldIds().indexOf(field);
int annotationsOff = annotations.getAbsoluteOffset();
if (out.annotates()) {
out.annotate(0, " " + field.toHuman());
out.annotate(4, " field_idx: " + Hex.u4(fieldIdx));
out.annotate(4, " annotations_off: " +
Hex.u4(annotationsOff));
}
out.writeInt(fieldIdx);
out.writeInt(annotationsOff);
}
/** {@inheritDoc} */
public String toHuman() {
return field.toHuman() + ": " + annotations;
}
/**
* Gets the field this item is for.
*
* @return {@code non-null;} the field
*/
public CstFieldRef getField() {
return field;
}
/**
* Gets the associated annotations.
*
* @return {@code non-null;} the annotations
*/
public Annotations getAnnotations() {
return annotations.getAnnotations();
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.CstFieldRef;
/**
* Representation of a field reference inside a Dalvik file.
*/
public final class FieldIdItem extends MemberIdItem {
/**
* Constructs an instance.
*
* @param field {@code non-null;} the constant for the field
*/
public FieldIdItem(CstFieldRef field) {
super(field);
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_FIELD_ID_ITEM;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
super.addContents(file);
TypeIdsSection typeIds = file.getTypeIds();
typeIds.intern(getFieldRef().getType());
}
/**
* Gets the field constant.
*
* @return {@code non-null;} the constant
*/
public CstFieldRef getFieldRef() {
return (CstFieldRef) getRef();
}
/** {@inheritDoc} */
@Override
protected int getTypoidIdx(DexFile file) {
TypeIdsSection typeIds = file.getTypeIds();
return typeIds.indexOf(getFieldRef().getType());
}
/** {@inheritDoc} */
@Override
protected String getTypoidName() {
return "type_idx";
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.util.Collection;
import java.util.TreeMap;
/**
* Field refs list section of a {@code .dex} file.
*/
public final class FieldIdsSection extends MemberIdsSection {
/**
* {@code non-null;} map from field constants to {@link
* FieldIdItem} instances
*/
private final TreeMap<CstFieldRef, FieldIdItem> fieldIds;
/**
* Constructs an instance. The file offset is initially unknown.
*
* @param file {@code non-null;} file that this instance is part of
*/
public FieldIdsSection(DexFile file) {
super("field_ids", file);
fieldIds = new TreeMap<CstFieldRef, FieldIdItem>();
}
/** {@inheritDoc} */
@Override
public Collection<? extends Item> items() {
return fieldIds.values();
}
/** {@inheritDoc} */
@Override
public IndexedItem get(Constant cst) {
if (cst == null) {
throw new NullPointerException("cst == null");
}
throwIfNotPrepared();
IndexedItem result = fieldIds.get((CstFieldRef) cst);
if (result == null) {
throw new IllegalArgumentException("not found");
}
return result;
}
/**
* Writes the portion of the file header that refers to this instance.
*
* @param out {@code non-null;} where to write
*/
public void writeHeaderPart(AnnotatedOutput out) {
throwIfNotPrepared();
int sz = fieldIds.size();
int offset = (sz == 0) ? 0 : getFileOffset();
if (out.annotates()) {
out.annotate(4, "field_ids_size: " + Hex.u4(sz));
out.annotate(4, "field_ids_off: " + Hex.u4(offset));
}
out.writeInt(sz);
out.writeInt(offset);
}
/**
* Interns an element into this instance.
*
* @param field {@code non-null;} the reference to intern
* @return {@code non-null;} the interned reference
*/
public FieldIdItem intern(CstFieldRef field) {
if (field == null) {
throw new NullPointerException("field == null");
}
throwIfPrepared();
FieldIdItem result = fieldIds.get(field);
if (result == null) {
result = new FieldIdItem(field);
fieldIds.put(field, result);
}
return result;
}
/**
* Gets the index of the given reference, which must have been added
* to this instance.
*
* @param ref {@code non-null;} the reference to look up
* @return {@code >= 0;} the reference's index
*/
public int indexOf(CstFieldRef ref) {
if (ref == null) {
throw new NullPointerException("ref == null");
}
throwIfNotPrepared();
FieldIdItem item = fieldIds.get(ref);
if (item == null) {
throw new IllegalArgumentException("not found");
}
return item.getIndex();
}
}

View File

@ -0,0 +1,123 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
/**
* File header section of a {@code .dex} file.
*/
public final class HeaderItem extends IndexedItem {
/**
* {@code non-null;} the file format magic number, represented as the
* low-order bytes of a string
*/
private static final String MAGIC = "dex\n035\0";
/** size of this section, in bytes */
private static final int HEADER_SIZE = 0x70;
/** the endianness tag */
private static final int ENDIAN_TAG = 0x12345678;
/**
* Constructs an instance.
*/
public HeaderItem() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_HEADER_ITEM;
}
/** {@inheritDoc} */
@Override
public int writeSize() {
return HEADER_SIZE;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
// Nothing to do here.
}
/** {@inheritDoc} */
@Override
public void writeTo(DexFile file, AnnotatedOutput out) {
int mapOff = file.getMap().getFileOffset();
Section firstDataSection = file.getFirstDataSection();
Section lastDataSection = file.getLastDataSection();
int dataOff = firstDataSection.getFileOffset();
int dataSize = lastDataSection.getFileOffset() +
lastDataSection.writeSize() - dataOff;
if (out.annotates()) {
out.annotate(8, "magic: " + new CstUtf8(MAGIC).toQuoted());
out.annotate(4, "checksum");
out.annotate(20, "signature");
out.annotate(4, "file_size: " +
Hex.u4(file.getFileSize()));
out.annotate(4, "header_size: " + Hex.u4(HEADER_SIZE));
out.annotate(4, "endian_tag: " + Hex.u4(ENDIAN_TAG));
out.annotate(4, "link_size: 0");
out.annotate(4, "link_off: 0");
out.annotate(4, "map_off: " + Hex.u4(mapOff));
}
// Write the magic number.
for (int i = 0; i < 8; i++) {
out.writeByte(MAGIC.charAt(i));
}
// Leave space for the checksum and signature.
out.writeZeroes(24);
out.writeInt(file.getFileSize());
out.writeInt(HEADER_SIZE);
out.writeInt(ENDIAN_TAG);
/*
* Write zeroes for the link size and data, as the output
* isn't a staticly linked file.
*/
out.writeZeroes(8);
out.writeInt(mapOff);
// Write out each section's respective header part.
file.getStringIds().writeHeaderPart(out);
file.getTypeIds().writeHeaderPart(out);
file.getProtoIds().writeHeaderPart(out);
file.getFieldIds().writeHeaderPart(out);
file.getMethodIds().writeHeaderPart(out);
file.getClassDefs().writeHeaderPart(out);
if (out.annotates()) {
out.annotate(4, "data_size: " + Hex.u4(dataSize));
out.annotate(4, "data_off: " + Hex.u4(dataOff));
}
out.writeInt(dataSize);
out.writeInt(dataOff);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.Constant;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* File header section of a {@code .dex} file.
*/
public final class HeaderSection extends UniformItemSection {
/** {@code non-null;} the list of the one item in the section */
private final List<HeaderItem> list;
/**
* Constructs an instance. The file offset is initially unknown.
*
* @param file {@code non-null;} file that this instance is part of
*/
public HeaderSection(DexFile file) {
super(null, file, 4);
HeaderItem item = new HeaderItem();
item.setIndex(0);
this.list = Collections.singletonList(item);
}
/** {@inheritDoc} */
@Override
public IndexedItem get(Constant cst) {
return null;
}
/** {@inheritDoc} */
@Override
public Collection<? extends Item> items() {
return list;
}
/** {@inheritDoc} */
@Override
protected void orderItems() {
// Nothing to do here.
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.CstType;
/**
* Representation of a reference to an item inside a Dalvik file.
*/
public abstract class IdItem extends IndexedItem {
/**
* {@code non-null;} the type constant for the defining class of
* the reference
*/
private final CstType type;
/**
* Constructs an instance.
*
* @param type {@code non-null;} the type constant for the defining
* class of the reference
*/
public IdItem(CstType type) {
if (type == null) {
throw new NullPointerException("type == null");
}
this.type = type;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
TypeIdsSection typeIds = file.getTypeIds();
typeIds.intern(type);
}
/**
* Gets the type constant for the defining class of the
* reference.
*
* @return {@code non-null;} the type constant
*/
public final CstType getDefiningClass() {
return type;
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
/**
* An item in a Dalvik file which is referenced by index.
*/
public abstract class IndexedItem extends Item {
/** {@code >= -1;} assigned index of the item, or {@code -1} if not
* yet assigned */
private int index;
/**
* Constructs an instance. The index is initially unassigned.
*/
public IndexedItem() {
index = -1;
}
/**
* Gets whether or not this instance has been assigned an index.
*
* @return {@code true} iff this instance has been assigned an index
*/
public final boolean hasIndex() {
return (index >= 0);
}
/**
* Gets the item index.
*
* @return {@code >= 0;} the index
* @throws RuntimeException thrown if the item index is not yet assigned
*/
public final int getIndex() {
if (index < 0) {
throw new RuntimeException("index not yet set");
}
return index;
}
/**
* Sets the item index. This method may only ever be called once
* per instance, and this will throw a {@code RuntimeException} if
* called a second (or subsequent) time.
*
* @param index {@code >= 0;} the item index
*/
public final void setIndex(int index) {
if (this.index != -1) {
throw new RuntimeException("index already set");
}
this.index = index;
}
/**
* Gets the index of this item as a string, suitable for including in
* annotations.
*
* @return {@code non-null;} the index string
*/
public final String indexString() {
return '[' + Integer.toHexString(index) + ']';
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.util.AnnotatedOutput;
/**
* Base class for any structurally-significant and (potentially)
* repeated piece of a Dalvik file.
*/
public abstract class Item {
/**
* Constructs an instance.
*/
public Item() {
// This space intentionally left blank.
}
/**
* Returns the item type for this instance.
*
* @return {@code non-null;} the item type
*/
public abstract ItemType itemType();
/**
* Returns the human name for the particular type of item this
* instance is.
*
* @return {@code non-null;} the name
*/
public final String typeName() {
return itemType().toHuman();
}
/**
* Gets the size of this instance when written, in bytes.
*
* @return {@code >= 0;} the write size
*/
public abstract int writeSize();
/**
* Populates a {@link DexFile} with items from within this instance.
* This will <i>not</i> add an item to the file for this instance itself
* (which should have been done by whatever refers to this instance).
*
* <p><b>Note:</b> Subclasses must override this to do something
* appropriate.</p>
*
* @param file {@code non-null;} the file to populate
*/
public abstract void addContents(DexFile file);
/**
* Writes the representation of this instance to the given data section,
* using the given {@link DexFile} to look things up as needed.
* If this instance keeps track of its offset, then this method will
* note the written offset and will also throw an exception if this
* instance has already been written.
*
* @param file {@code non-null;} the file to use for reference
* @param out {@code non-null;} where to write to
*/
public abstract void writeTo(DexFile file, AnnotatedOutput out);
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.util.ToHuman;
/**
* Enumeration of all the top-level item types.
*/
public enum ItemType implements ToHuman {
TYPE_HEADER_ITEM( 0x0000, "header_item"),
TYPE_STRING_ID_ITEM( 0x0001, "string_id_item"),
TYPE_TYPE_ID_ITEM( 0x0002, "type_id_item"),
TYPE_PROTO_ID_ITEM( 0x0003, "proto_id_item"),
TYPE_FIELD_ID_ITEM( 0x0004, "field_id_item"),
TYPE_METHOD_ID_ITEM( 0x0005, "method_id_item"),
TYPE_CLASS_DEF_ITEM( 0x0006, "class_def_item"),
TYPE_MAP_LIST( 0x1000, "map_list"),
TYPE_TYPE_LIST( 0x1001, "type_list"),
TYPE_ANNOTATION_SET_REF_LIST( 0x1002, "annotation_set_ref_list"),
TYPE_ANNOTATION_SET_ITEM( 0x1003, "annotation_set_item"),
TYPE_CLASS_DATA_ITEM( 0x2000, "class_data_item"),
TYPE_CODE_ITEM( 0x2001, "code_item"),
TYPE_STRING_DATA_ITEM( 0x2002, "string_data_item"),
TYPE_DEBUG_INFO_ITEM( 0x2003, "debug_info_item"),
TYPE_ANNOTATION_ITEM( 0x2004, "annotation_item"),
TYPE_ENCODED_ARRAY_ITEM( 0x2005, "encoded_array_item"),
TYPE_ANNOTATIONS_DIRECTORY_ITEM(0x2006, "annotations_directory_item"),
TYPE_MAP_ITEM( -1, "map_item"),
TYPE_TYPE_ITEM( -1, "type_item"),
TYPE_EXCEPTION_HANDLER_ITEM( -1, "exception_handler_item"),
TYPE_ANNOTATION_SET_REF_ITEM( -1, "annotation_set_ref_item");
/** value when represented in a {@link MapItem} */
private final int mapValue;
/** {@code non-null;} name of the type */
private final String typeName;
/** {@code non-null;} the short human name */
private final String humanName;
/**
* Constructs an instance.
*
* @param mapValue value when represented in a {@link MapItem}
* @param typeName {@code non-null;} name of the type
*/
private ItemType(int mapValue, String typeName) {
this.mapValue = mapValue;
this.typeName = typeName;
// Make the human name.
String human = typeName;
if (human.endsWith("_item")) {
human = human.substring(0, human.length() - 5);
}
this.humanName = human.replace('_', ' ');
}
/**
* Gets the map value.
*
* @return the map value
*/
public int getMapValue() {
return mapValue;
}
/**
* Gets the type name.
*
* @return {@code non-null;} the type name
*/
public String getTypeName() {
return typeName;
}
/** {@inheritDoc} */
public String toHuman() {
return humanName;
}
}

View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.util.ArrayList;
/**
* Class that represents a map item.
*/
public final class MapItem extends OffsettedItem {
/** file alignment of this class, in bytes */
private static final int ALIGNMENT = 4;
/** write size of this class, in bytes: three {@code uint}s */
private static final int WRITE_SIZE = (4 * 3);
/** {@code non-null;} item type this instance covers */
private final ItemType type;
/** {@code non-null;} section this instance covers */
private final Section section;
/**
* {@code null-ok;} first item covered or {@code null} if this is
* a self-reference
*/
private final Item firstItem;
/**
* {@code null-ok;} last item covered or {@code null} if this is
* a self-reference
*/
private final Item lastItem;
/**
* {@code > 0;} count of items covered; {@code 1} if this
* is a self-reference
*/
private final int itemCount;
/**
* Constructs a list item with instances of this class representing
* the contents of the given array of sections, adding it to the
* given map section.
*
* @param sections {@code non-null;} the sections
* @param mapSection {@code non-null;} the section that the resulting map
* should be added to; it should be empty on entry to this method
*/
public static void addMap(Section[] sections,
MixedItemSection mapSection) {
if (sections == null) {
throw new NullPointerException("sections == null");
}
if (mapSection.items().size() != 0) {
throw new IllegalArgumentException(
"mapSection.items().size() != 0");
}
ArrayList<MapItem> items = new ArrayList<MapItem>(50);
for (Section section : sections) {
ItemType currentType = null;
Item firstItem = null;
Item lastItem = null;
int count = 0;
for (Item item : section.items()) {
ItemType type = item.itemType();
if (type != currentType) {
if (count != 0) {
items.add(new MapItem(currentType, section,
firstItem, lastItem, count));
}
currentType = type;
firstItem = item;
count = 0;
}
lastItem = item;
count++;
}
if (count != 0) {
// Add a MapItem for the final items in the section.
items.add(new MapItem(currentType, section,
firstItem, lastItem, count));
} else if (section == mapSection) {
// Add a MapItem for the self-referential section.
items.add(new MapItem(mapSection));
}
}
mapSection.add(
new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items));
}
/**
* Constructs an instance.
*
* @param type {@code non-null;} item type this instance covers
* @param section {@code non-null;} section this instance covers
* @param firstItem {@code non-null;} first item covered
* @param lastItem {@code non-null;} last item covered
* @param itemCount {@code > 0;} count of items covered
*/
private MapItem(ItemType type, Section section, Item firstItem,
Item lastItem, int itemCount) {
super(ALIGNMENT, WRITE_SIZE);
if (type == null) {
throw new NullPointerException("type == null");
}
if (section == null) {
throw new NullPointerException("section == null");
}
if (firstItem == null) {
throw new NullPointerException("firstItem == null");
}
if (lastItem == null) {
throw new NullPointerException("lastItem == null");
}
if (itemCount <= 0) {
throw new IllegalArgumentException("itemCount <= 0");
}
this.type = type;
this.section = section;
this.firstItem = firstItem;
this.lastItem = lastItem;
this.itemCount = itemCount;
}
/**
* Constructs a self-referential instance. This instance is meant to
* represent the section containing the {@code map_list}.
*
* @param section {@code non-null;} section this instance covers
*/
private MapItem(Section section) {
super(ALIGNMENT, WRITE_SIZE);
if (section == null) {
throw new NullPointerException("section == null");
}
this.type = ItemType.TYPE_MAP_LIST;
this.section = section;
this.firstItem = null;
this.lastItem = null;
this.itemCount = 1;
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_MAP_ITEM;
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuffer sb = new StringBuffer(100);
sb.append(getClass().getName());
sb.append('{');
sb.append(section.toString());
sb.append(' ');
sb.append(type.toHuman());
sb.append('}');
return sb.toString();
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
// We have nothing to add.
}
/** {@inheritDoc} */
@Override
public final String toHuman() {
return toString();
}
/** {@inheritDoc} */
@Override
protected void writeTo0(DexFile file, AnnotatedOutput out) {
int value = type.getMapValue();
int offset;
if (firstItem == null) {
offset = section.getFileOffset();
} else {
offset = section.getAbsoluteItemOffset(firstItem);
}
if (out.annotates()) {
out.annotate(0, offsetString() + ' ' + type.getTypeName() +
" map");
out.annotate(2, " type: " + Hex.u2(value) + " // " +
type.toString());
out.annotate(2, " unused: 0");
out.annotate(4, " size: " + Hex.u4(itemCount));
out.annotate(4, " offset: " + Hex.u4(offset));
}
out.writeShort(value);
out.writeShort(0); // unused
out.writeInt(itemCount);
out.writeInt(offset);
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.CstMemberRef;
import com.android.dexgen.rop.cst.CstNat;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
/**
* Representation of a member (field or method) reference inside a
* Dalvik file.
*/
public abstract class MemberIdItem extends IdItem {
/** size of instances when written out to a file, in bytes */
public static final int WRITE_SIZE = 8;
/** {@code non-null;} the constant for the member */
private final CstMemberRef cst;
/**
* Constructs an instance.
*
* @param cst {@code non-null;} the constant for the member
*/
public MemberIdItem(CstMemberRef cst) {
super(cst.getDefiningClass());
this.cst = cst;
}
/** {@inheritDoc} */
@Override
public int writeSize() {
return WRITE_SIZE;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
super.addContents(file);
StringIdsSection stringIds = file.getStringIds();
stringIds.intern(getRef().getNat().getName());
}
/** {@inheritDoc} */
@Override
public final void writeTo(DexFile file, AnnotatedOutput out) {
TypeIdsSection typeIds = file.getTypeIds();
StringIdsSection stringIds = file.getStringIds();
CstNat nat = cst.getNat();
int classIdx = typeIds.indexOf(getDefiningClass());
int nameIdx = stringIds.indexOf(nat.getName());
int typoidIdx = getTypoidIdx(file);
if (out.annotates()) {
out.annotate(0, indexString() + ' ' + cst.toHuman());
out.annotate(2, " class_idx: " + Hex.u2(classIdx));
out.annotate(2, String.format(" %-10s %s", getTypoidName() + ':',
Hex.u2(typoidIdx)));
out.annotate(4, " name_idx: " + Hex.u4(nameIdx));
}
out.writeShort(classIdx);
out.writeShort(typoidIdx);
out.writeInt(nameIdx);
}
/**
* Returns the index of the type-like thing associated with
* this item, in order that it may be written out. Subclasses must
* override this to get whatever it is they need to store.
*
* @param file {@code non-null;} the file being written
* @return the index in question
*/
protected abstract int getTypoidIdx(DexFile file);
/**
* Returns the field name of the type-like thing associated with
* this item, for listing-generating purposes. Subclasses must override
* this.
*
* @return {@code non-null;} the name in question
*/
protected abstract String getTypoidName();
/**
* Gets the member constant.
*
* @return {@code non-null;} the constant
*/
public final CstMemberRef getRef() {
return cst;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
/**
* Member (field or method) refs list section of a {@code .dex} file.
*/
public abstract class MemberIdsSection extends UniformItemSection {
/**
* Constructs an instance. The file offset is initially unknown.
*
* @param name {@code null-ok;} the name of this instance, for annotation
* purposes
* @param file {@code non-null;} file that this instance is part of
*/
public MemberIdsSection(String name, DexFile file) {
super(name, file, 4);
}
/** {@inheritDoc} */
@Override
protected void orderItems() {
int idx = 0;
for (Object i : items()) {
((MemberIdItem) i).setIndex(idx);
idx++;
}
}
}

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotations;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.ToHuman;
/**
* Association of a method and its annotations.
*/
public final class MethodAnnotationStruct
implements ToHuman, Comparable<MethodAnnotationStruct> {
/** {@code non-null;} the method in question */
private final CstMethodRef method;
/** {@code non-null;} the associated annotations */
private AnnotationSetItem annotations;
/**
* Constructs an instance.
*
* @param method {@code non-null;} the method in question
* @param annotations {@code non-null;} the associated annotations
*/
public MethodAnnotationStruct(CstMethodRef method,
AnnotationSetItem annotations) {
if (method == null) {
throw new NullPointerException("method == null");
}
if (annotations == null) {
throw new NullPointerException("annotations == null");
}
this.method = method;
this.annotations = annotations;
}
/** {@inheritDoc} */
public int hashCode() {
return method.hashCode();
}
/** {@inheritDoc} */
public boolean equals(Object other) {
if (! (other instanceof MethodAnnotationStruct)) {
return false;
}
return method.equals(((MethodAnnotationStruct) other).method);
}
/** {@inheritDoc} */
public int compareTo(MethodAnnotationStruct other) {
return method.compareTo(other.method);
}
/** {@inheritDoc} */
public void addContents(DexFile file) {
MethodIdsSection methodIds = file.getMethodIds();
MixedItemSection wordData = file.getWordData();
methodIds.intern(method);
annotations = wordData.intern(annotations);
}
/** {@inheritDoc} */
public void writeTo(DexFile file, AnnotatedOutput out) {
int methodIdx = file.getMethodIds().indexOf(method);
int annotationsOff = annotations.getAbsoluteOffset();
if (out.annotates()) {
out.annotate(0, " " + method.toHuman());
out.annotate(4, " method_idx: " + Hex.u4(methodIdx));
out.annotate(4, " annotations_off: " +
Hex.u4(annotationsOff));
}
out.writeInt(methodIdx);
out.writeInt(annotationsOff);
}
/** {@inheritDoc} */
public String toHuman() {
return method.toHuman() + ": " + annotations;
}
/**
* Gets the method this item is for.
*
* @return {@code non-null;} the method
*/
public CstMethodRef getMethod() {
return method;
}
/**
* Gets the associated annotations.
*
* @return {@code non-null;} the annotations
*/
public Annotations getAnnotations() {
return annotations.getAnnotations();
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.CstBaseMethodRef;
/**
* Representation of a method reference inside a Dalvik file.
*/
public final class MethodIdItem extends MemberIdItem {
/**
* Constructs an instance.
*
* @param method {@code non-null;} the constant for the method
*/
public MethodIdItem(CstBaseMethodRef method) {
super(method);
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_METHOD_ID_ITEM;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
super.addContents(file);
ProtoIdsSection protoIds = file.getProtoIds();
protoIds.intern(getMethodRef().getPrototype());
}
/**
* Gets the method constant.
*
* @return {@code non-null;} the constant
*/
public CstBaseMethodRef getMethodRef() {
return (CstBaseMethodRef) getRef();
}
/** {@inheritDoc} */
@Override
protected int getTypoidIdx(DexFile file) {
ProtoIdsSection protoIds = file.getProtoIds();
return protoIds.indexOf(getMethodRef().getPrototype());
}
/** {@inheritDoc} */
@Override
protected String getTypoidName() {
return "proto_idx";
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstBaseMethodRef;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import java.util.Collection;
import java.util.TreeMap;
/**
* Method refs list section of a {@code .dex} file.
*/
public final class MethodIdsSection extends MemberIdsSection {
/**
* {@code non-null;} map from method constants to {@link
* MethodIdItem} instances
*/
private final TreeMap<CstBaseMethodRef, MethodIdItem> methodIds;
/**
* Constructs an instance. The file offset is initially unknown.
*
* @param file {@code non-null;} file that this instance is part of
*/
public MethodIdsSection(DexFile file) {
super("method_ids", file);
methodIds = new TreeMap<CstBaseMethodRef, MethodIdItem>();
}
/** {@inheritDoc} */
@Override
public Collection<? extends Item> items() {
return methodIds.values();
}
/** {@inheritDoc} */
@Override
public IndexedItem get(Constant cst) {
if (cst == null) {
throw new NullPointerException("cst == null");
}
throwIfNotPrepared();
IndexedItem result = methodIds.get((CstBaseMethodRef) cst);
if (result == null) {
throw new IllegalArgumentException("not found");
}
return result;
}
/**
* Writes the portion of the file header that refers to this instance.
*
* @param out {@code non-null;} where to write
*/
public void writeHeaderPart(AnnotatedOutput out) {
throwIfNotPrepared();
int sz = methodIds.size();
int offset = (sz == 0) ? 0 : getFileOffset();
if (out.annotates()) {
out.annotate(4, "method_ids_size: " + Hex.u4(sz));
out.annotate(4, "method_ids_off: " + Hex.u4(offset));
}
out.writeInt(sz);
out.writeInt(offset);
}
/**
* Interns an element into this instance.
*
* @param method {@code non-null;} the reference to intern
* @return {@code non-null;} the interned reference
*/
public MethodIdItem intern(CstBaseMethodRef method) {
if (method == null) {
throw new NullPointerException("method == null");
}
throwIfPrepared();
MethodIdItem result = methodIds.get(method);
if (result == null) {
result = new MethodIdItem(method);
methodIds.put(method, result);
}
return result;
}
/**
* Gets the index of the given reference, which must have been added
* to this instance.
*
* @param ref {@code non-null;} the reference to look up
* @return {@code >= 0;} the reference's index
*/
public int indexOf(CstBaseMethodRef ref) {
if (ref == null) {
throw new NullPointerException("ref == null");
}
throwIfNotPrepared();
MethodIdItem item = methodIds.get(ref);
if (item == null) {
throw new IllegalArgumentException("not found");
}
return item.getIndex();
}
}

View File

@ -0,0 +1,362 @@
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed 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 com.android.dexgen.dex.file;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.ExceptionWithContext;
import com.android.dexgen.util.Hex;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;
/**
* A section of a {@code .dex} file which consists of a sequence of
* {@link OffsettedItem} objects, which may each be of a different concrete
* class and/or size.
*
* <b>Note:</b> It is invalid for an item in an instance of this class to
* have a larger alignment requirement than the alignment of this instance.
*/
public final class MixedItemSection extends Section {
static enum SortType {
/** no sorting */
NONE,
/** sort by type only */
TYPE,
/** sort in class-major order, with instances sorted per-class */
INSTANCE;
};
/** {@code non-null;} sorter which sorts instances by type */
private static final Comparator<OffsettedItem> TYPE_SORTER =
new Comparator<OffsettedItem>() {
public int compare(OffsettedItem item1, OffsettedItem item2) {
ItemType type1 = item1.itemType();
ItemType type2 = item2.itemType();
return type1.compareTo(type2);
}
};
/** {@code non-null;} the items in this part */
private final ArrayList<OffsettedItem> items;
/** {@code non-null;} items that have been explicitly interned */
private final HashMap<OffsettedItem, OffsettedItem> interns;
/** {@code non-null;} how to sort the items */
private final SortType sort;
/**
* {@code >= -1;} the current size of this part, in bytes, or {@code -1}
* if not yet calculated
*/
private int writeSize;
/**
* Constructs an instance. The file offset is initially unknown.
*
* @param name {@code null-ok;} the name of this instance, for annotation
* purposes
* @param file {@code non-null;} file that this instance is part of
* @param alignment {@code > 0;} alignment requirement for the final output;
* must be a power of 2
* @param sort how the items should be sorted in the final output
*/
public MixedItemSection(String name, DexFile file, int alignment,
SortType sort) {
super(name, file, alignment);
this.items = new ArrayList<OffsettedItem>(100);
this.interns = new HashMap<OffsettedItem, OffsettedItem>(100);
this.sort = sort;
this.writeSize = -1;
}
/** {@inheritDoc} */
@Override
public Collection<? extends Item> items() {
return items;
}
/** {@inheritDoc} */
@Override
public int writeSize() {
throwIfNotPrepared();
return writeSize;
}
/** {@inheritDoc} */
@Override
public int getAbsoluteItemOffset(Item item) {
OffsettedItem oi = (OffsettedItem) item;
return oi.getAbsoluteOffset();
}
/**
* Gets the size of this instance, in items.
*
* @return {@code >= 0;} the size
*/
public int size() {
return items.size();
}
/**
* Writes the portion of the file header that refers to this instance.
*
* @param out {@code non-null;} where to write
*/
public void writeHeaderPart(AnnotatedOutput out) {
throwIfNotPrepared();
if (writeSize == -1) {
throw new RuntimeException("write size not yet set");
}
int sz = writeSize;
int offset = (sz == 0) ? 0 : getFileOffset();
String name = getName();
if (name == null) {
name = "<unnamed>";
}
int spaceCount = 15 - name.length();
char[] spaceArr = new char[spaceCount];
Arrays.fill(spaceArr, ' ');
String spaces = new String(spaceArr);
if (out.annotates()) {
out.annotate(4, name + "_size:" + spaces + Hex.u4(sz));
out.annotate(4, name + "_off: " + spaces + Hex.u4(offset));
}
out.writeInt(sz);
out.writeInt(offset);
}
/**
* Adds an item to this instance. This will in turn tell the given item
* that it has been added to this instance. It is invalid to add the
* same item to more than one instance, nor to add the same items
* multiple times to a single instance.
*
* @param item {@code non-null;} the item to add
*/
public void add(OffsettedItem item) {
throwIfPrepared();
try {
if (item.getAlignment() > getAlignment()) {
throw new IllegalArgumentException(
"incompatible item alignment");
}
} catch (NullPointerException ex) {
// Elucidate the exception.
throw new NullPointerException("item == null");
}
items.add(item);
}
/**
* Interns an item in this instance, returning the interned instance
* (which may not be the one passed in). This will add the item if no
* equal item has been added.
*
* @param item {@code non-null;} the item to intern
* @return {@code non-null;} the equivalent interned instance
*/
public <T extends OffsettedItem> T intern(T item) {
throwIfPrepared();
OffsettedItem result = interns.get(item);
if (result != null) {
return (T) result;
}
add(item);
interns.put(item, item);
return item;
}
/**
* Gets an item which was previously interned.
*
* @param item {@code non-null;} the item to look for
* @return {@code non-null;} the equivalent already-interned instance
*/
public <T extends OffsettedItem> T get(T item) {
throwIfNotPrepared();
OffsettedItem result = interns.get(item);
if (result != null) {
return (T) result;
}
throw new NoSuchElementException(item.toString());
}
/**
* Writes an index of contents of the items in this instance of the
* given type. If there are none, this writes nothing. If there are any,
* then the index is preceded by the given intro string.
*
* @param out {@code non-null;} where to write to
* @param itemType {@code non-null;} the item type of interest
* @param intro {@code non-null;} the introductory string for non-empty indices
*/
public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType,
String intro) {
throwIfNotPrepared();
TreeMap<String, OffsettedItem> index =
new TreeMap<String, OffsettedItem>();
for (OffsettedItem item : items) {
if (item.itemType() == itemType) {
String label = item.toHuman();
index.put(label, item);
}
}
if (index.size() == 0) {
return;
}
out.annotate(0, intro);
for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) {
String label = entry.getKey();
OffsettedItem item = entry.getValue();
out.annotate(0, item.offsetString() + ' ' + label + '\n');
}
}
/** {@inheritDoc} */
@Override
protected void prepare0() {
DexFile file = getFile();
/*
* It's okay for new items to be added as a result of an
* addContents() call; we just have to deal with the possibility.
*/
int i = 0;
for (;;) {
int sz = items.size();
if (i >= sz) {
break;
}
for (/*i*/; i < sz; i++) {
OffsettedItem one = items.get(i);
one.addContents(file);
}
}
}
/**
* Places all the items in this instance at particular offsets. This
* will call {@link OffsettedItem#place} on each item. If an item
* does not know its write size before the call to {@code place},
* it is that call which is responsible for setting the write size.
* This method may only be called once per instance; subsequent calls
* will throw an exception.
*/
public void placeItems() {
throwIfNotPrepared();
switch (sort) {
case INSTANCE: {
Collections.sort(items);
break;
}
case TYPE: {
Collections.sort(items, TYPE_SORTER);
break;
}
}
int sz = items.size();
int outAt = 0;
for (int i = 0; i < sz; i++) {
OffsettedItem one = items.get(i);
try {
int placedAt = one.place(this, outAt);
if (placedAt < outAt) {
throw new RuntimeException("bogus place() result for " +
one);
}
outAt = placedAt + one.writeSize();
} catch (RuntimeException ex) {
throw ExceptionWithContext.withContext(ex,
"...while placing " + one);
}
}
writeSize = outAt;
}
/** {@inheritDoc} */
@Override
protected void writeTo0(AnnotatedOutput out) {
boolean annotates = out.annotates();
boolean first = true;
DexFile file = getFile();
int at = 0;
for (OffsettedItem one : items) {
if (annotates) {
if (first) {
first = false;
} else {
out.annotate(0, "\n");
}
}
int alignMask = one.getAlignment() - 1;
int writeAt = (at + alignMask) & ~alignMask;
if (at != writeAt) {
out.writeZeroes(writeAt - at);
at = writeAt;
}
one.writeTo(file, out);
at += one.writeSize();
}
if (at != writeSize) {
throw new RuntimeException("output size mismatch");
}
}
}

Some files were not shown because too many files have changed in this diff Show More