mirror of
https://github.com/revanced/smali.git
synced 2025-05-13 20:57:07 +02:00
Nearly full switch over to dexlib2 in baksmali
No odex handling/register analysis yet, and there are still a few minor features that haven't been reimplemented yet. Still lots of bugs :)
This commit is contained in:
parent
ba114e7211
commit
754b3c4dc0
@ -34,13 +34,13 @@ import org.jf.dexlib.TypeIdItem;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class CatchMethodItem extends MethodItem {
|
public class CatchMethodItem extends MethodItem {
|
||||||
private final TypeIdItem exceptionType;
|
private final String exceptionType;
|
||||||
|
|
||||||
private final LabelMethodItem tryStartLabel;
|
private final LabelMethodItem tryStartLabel;
|
||||||
private final LabelMethodItem tryEndLabel;
|
private final LabelMethodItem tryEndLabel;
|
||||||
private final LabelMethodItem handlerLabel;
|
private final LabelMethodItem handlerLabel;
|
||||||
|
|
||||||
public CatchMethodItem(MethodDefinition.LabelCache labelCache, int codeAddress, TypeIdItem exceptionType,
|
public CatchMethodItem(MethodDefinition.LabelCache labelCache, int codeAddress, String exceptionType,
|
||||||
int startAddress, int endAddress, int handlerAddress) {
|
int startAddress, int endAddress, int handlerAddress) {
|
||||||
super(codeAddress);
|
super(codeAddress);
|
||||||
this.exceptionType = exceptionType;
|
this.exceptionType = exceptionType;
|
||||||
@ -81,7 +81,7 @@ public class CatchMethodItem extends MethodItem {
|
|||||||
writer.write(".catchall");
|
writer.write(".catchall");
|
||||||
} else {
|
} else {
|
||||||
writer.write(".catch ");
|
writer.write(".catch ");
|
||||||
ReferenceFormatter.writeTypeReference(writer, exceptionType);
|
writer.write(exceptionType);
|
||||||
}
|
}
|
||||||
writer.write(" {");
|
writer.write(" {");
|
||||||
tryStartLabel.writeTo(writer);
|
tryStartLabel.writeTo(writer);
|
||||||
|
@ -41,7 +41,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ClassDefinition {
|
public class ClassDefinition {
|
||||||
@Nonnull private final ClassDef classDef;
|
@Nonnull public final ClassDef classDef;
|
||||||
@Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
|
@Nonnull private final HashSet<String> fieldsSetInStaticConstructor;
|
||||||
|
|
||||||
protected boolean validationErrors;
|
protected boolean validationErrors;
|
||||||
@ -95,8 +95,8 @@ public class ClassDefinition {
|
|||||||
writeAnnotations(writer);
|
writeAnnotations(writer);
|
||||||
writeStaticFields(writer);
|
writeStaticFields(writer);
|
||||||
writeInstanceFields(writer);
|
writeInstanceFields(writer);
|
||||||
/*writeDirectMethods(writer);
|
writeDirectMethods(writer);
|
||||||
writeVirtualMethods(writer);*/
|
writeVirtualMethods(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeClass(IndentingWriter writer) throws IOException {
|
private void writeClass(IndentingWriter writer) throws IOException {
|
||||||
@ -189,6 +189,62 @@ public class ClassDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeDirectMethods(IndentingWriter writer) throws IOException {
|
||||||
|
boolean wroteHeader = false;
|
||||||
|
for (Method method: classDef.getMethods()) {
|
||||||
|
int accessFlags = method.getAccessFlags();
|
||||||
|
|
||||||
|
if (AccessFlags.STATIC.isSet(accessFlags) ||
|
||||||
|
AccessFlags.PRIVATE.isSet(accessFlags) ||
|
||||||
|
AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||||
|
if (!wroteHeader) {
|
||||||
|
writer.write("\n\n");
|
||||||
|
writer.write("# direct methods");
|
||||||
|
wroteHeader = true;
|
||||||
|
}
|
||||||
|
writer.write('\n');
|
||||||
|
// TODO: detect duplicate methods.
|
||||||
|
// TODO: check for method validation errors
|
||||||
|
|
||||||
|
MethodImplementation methodImpl = method.getImplementation();
|
||||||
|
if (methodImpl == null) {
|
||||||
|
MethodDefinition.writeEmptyMethodTo(writer, method);
|
||||||
|
} else {
|
||||||
|
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
|
||||||
|
methodDefinition.writeTo(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeVirtualMethods(IndentingWriter writer) throws IOException {
|
||||||
|
boolean wroteHeader = false;
|
||||||
|
for (Method method: classDef.getMethods()) {
|
||||||
|
int accessFlags = method.getAccessFlags();
|
||||||
|
|
||||||
|
if (!AccessFlags.STATIC.isSet(accessFlags) &&
|
||||||
|
!AccessFlags.PRIVATE.isSet(accessFlags) &&
|
||||||
|
!AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||||
|
if (!wroteHeader) {
|
||||||
|
writer.write("\n\n");
|
||||||
|
writer.write("# virtual methods");
|
||||||
|
wroteHeader = true;
|
||||||
|
}
|
||||||
|
writer.write('\n');
|
||||||
|
// TODO: detect duplicate methods.
|
||||||
|
// TODO: check for method validation errors
|
||||||
|
|
||||||
|
MethodImplementation methodImpl = method.getImplementation();
|
||||||
|
if (methodImpl == null) {
|
||||||
|
MethodDefinition.writeEmptyMethodTo(writer, method);
|
||||||
|
} else {
|
||||||
|
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
|
||||||
|
methodDefinition.writeTo(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: uncomment
|
//TODO: uncomment
|
||||||
/*private void writeDirectMethods(IndentingWriter writer) throws IOException {
|
/*private void writeDirectMethods(IndentingWriter writer) throws IOException {
|
||||||
if (classDataItem == null) {
|
if (classDataItem == null) {
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class BeginEpilogueMethodItem extends DebugMethodItem {
|
||||||
|
public BeginEpilogueMethodItem(int codeAddress, int sortOrder) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
writer.write(".prologue");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.MethodItem;
|
||||||
|
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||||
|
import org.jf.dexlib2.DebugItemType;
|
||||||
|
import org.jf.dexlib2.iface.debug.*;
|
||||||
|
import org.jf.util.ExceptionWithContext;
|
||||||
|
|
||||||
|
public abstract class DebugMethodItem extends MethodItem {
|
||||||
|
private final int sortOrder;
|
||||||
|
|
||||||
|
protected DebugMethodItem(int codeAddress, int sortOrder) {
|
||||||
|
super(codeAddress);
|
||||||
|
this.sortOrder = sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public double getSortOrder() { return sortOrder; }
|
||||||
|
|
||||||
|
public static DebugMethodItem build(RegisterFormatter registerFormatter, DebugItem debugItem) {
|
||||||
|
int codeAddress = debugItem.getCodeAddress();
|
||||||
|
switch (debugItem.getDebugItemType()) {
|
||||||
|
case DebugItemType.START_LOCAL:
|
||||||
|
return new StartLocalMethodItem(codeAddress, -1, registerFormatter, (StartLocal)debugItem);
|
||||||
|
case DebugItemType.END_LOCAL:
|
||||||
|
return new EndLocalMethodItem(codeAddress, -1, registerFormatter, (EndLocal)debugItem);
|
||||||
|
case DebugItemType.RESTART_LOCAL:
|
||||||
|
return new RestartLocalMethodItem(codeAddress, -1, registerFormatter, (RestartLocal)debugItem);
|
||||||
|
case DebugItemType.EPILOGUE_BEGIN:
|
||||||
|
return new BeginEpilogueMethodItem(codeAddress, -4);
|
||||||
|
case DebugItemType.PROLOGUE_END:
|
||||||
|
return new EndPrologueMethodItem(codeAddress, -4);
|
||||||
|
case DebugItemType.SET_SOURCE_FILE:
|
||||||
|
return new SetSourceFileMethodItem(codeAddress, -3, (SetSourceFile)debugItem);
|
||||||
|
case DebugItemType.LINE_NUMBER:
|
||||||
|
return new LineNumberMethodItem(codeAddress, -2, (LineNumber)debugItem);
|
||||||
|
default:
|
||||||
|
throw new ExceptionWithContext("Invalid debug item type: %d", debugItem.getDebugItemType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||||
|
import org.jf.dexlib2.iface.debug.EndLocal;
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EndLocalMethodItem extends DebugMethodItem {
|
||||||
|
@Nonnull private final EndLocal endLocal;
|
||||||
|
@Nonnull private final RegisterFormatter registerFormatter;
|
||||||
|
|
||||||
|
public EndLocalMethodItem(int codeAddress, int sortOrder, @Nonnull RegisterFormatter registerFormatter,
|
||||||
|
@Nonnull EndLocal endLocal) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
this.endLocal = endLocal;
|
||||||
|
this.registerFormatter = registerFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
writer.write(".end local ");
|
||||||
|
registerFormatter.writeTo(writer, endLocal.getRegister());
|
||||||
|
|
||||||
|
//TODO: what if name is null, but there is a type?
|
||||||
|
String name = endLocal.getName();
|
||||||
|
if (name != null) {
|
||||||
|
writer.write(" #");
|
||||||
|
writer.write(name);
|
||||||
|
writer.write(':');
|
||||||
|
writer.write(endLocal.getType());
|
||||||
|
String signature = endLocal.getSignature();
|
||||||
|
if (signature != null) {
|
||||||
|
writer.write(",\"");
|
||||||
|
writer.write(signature);
|
||||||
|
writer.write('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class EndPrologueMethodItem extends DebugMethodItem {
|
||||||
|
public EndPrologueMethodItem(int codeAddress, int sortOrder) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
writer.write(".prologue");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.dexlib2.iface.debug.LineNumber;
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class LineNumberMethodItem extends DebugMethodItem {
|
||||||
|
private final int lineNumber;
|
||||||
|
|
||||||
|
public LineNumberMethodItem(int codeAddress, int sortOrder, @Nonnull LineNumber lineNumber) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
this.lineNumber = lineNumber.getLineNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
writer.write(".line ");
|
||||||
|
writer.printSignedIntAsDec(lineNumber);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||||
|
import org.jf.dexlib2.iface.debug.RestartLocal;
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class RestartLocalMethodItem extends DebugMethodItem {
|
||||||
|
@Nonnull private final RestartLocal restartLocal;
|
||||||
|
@Nonnull private final RegisterFormatter registerFormatter;
|
||||||
|
|
||||||
|
public RestartLocalMethodItem(int codeAddress, int sortOrder, @Nonnull RegisterFormatter registerFormatter,
|
||||||
|
@Nonnull RestartLocal restartLocal) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
this.restartLocal = restartLocal;
|
||||||
|
this.registerFormatter = registerFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
writer.write(".restart local ");
|
||||||
|
registerFormatter.writeTo(writer, restartLocal.getRegister());
|
||||||
|
|
||||||
|
//TODO: what if name is null, but there is a type?
|
||||||
|
String name = restartLocal.getName();
|
||||||
|
if (name != null) {
|
||||||
|
writer.write(" #");
|
||||||
|
writer.write(name);
|
||||||
|
writer.write(':');
|
||||||
|
writer.write(restartLocal.getType());
|
||||||
|
String signature = restartLocal.getSignature();
|
||||||
|
if (signature != null) {
|
||||||
|
writer.write(",\"");
|
||||||
|
writer.write(signature);
|
||||||
|
writer.write('"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.dexlib2.iface.debug.SetSourceFile;
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
import org.jf.util.StringUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class SetSourceFileMethodItem extends DebugMethodItem {
|
||||||
|
@Nullable private final String sourceFile;
|
||||||
|
|
||||||
|
public SetSourceFileMethodItem(int codeAddress, int sortOrder, @Nonnull SetSourceFile setSourceFile) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
this.sourceFile = setSourceFile.getSourceFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
//TODO: make sure smali can handle an empty .source directive
|
||||||
|
writer.write(".source");
|
||||||
|
|
||||||
|
if (sourceFile != null) {
|
||||||
|
writer.write(" \"");
|
||||||
|
StringUtils.writeEscapedString(writer, sourceFile);
|
||||||
|
writer.write('"');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.baksmali.Adaptors.Debug;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
||||||
|
import org.jf.dexlib2.iface.debug.StartLocal;
|
||||||
|
import org.jf.util.IndentingWriter;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class StartLocalMethodItem extends DebugMethodItem {
|
||||||
|
@Nonnull private final StartLocal startLocal;
|
||||||
|
@Nonnull private final RegisterFormatter registerFormatter;
|
||||||
|
|
||||||
|
public StartLocalMethodItem(int codeAddress, int sortOrder, @Nonnull RegisterFormatter registerFormatter,
|
||||||
|
@Nonnull StartLocal startLocal) {
|
||||||
|
super(codeAddress, sortOrder);
|
||||||
|
this.startLocal = startLocal;
|
||||||
|
this.registerFormatter = registerFormatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
writer.write(".local ");
|
||||||
|
registerFormatter.writeTo(writer, startLocal.getRegister());
|
||||||
|
writer.write(", ");
|
||||||
|
//TODO: what about when name or type is null?
|
||||||
|
writer.write(startLocal.getName());
|
||||||
|
writer.write(':');
|
||||||
|
writer.write(startLocal.getType());
|
||||||
|
String signature = startLocal.getSignature();
|
||||||
|
if (signature != null) {
|
||||||
|
writer.write(",\"");
|
||||||
|
//TODO: does dalvik require this be in any format? Does it need to be escaped?
|
||||||
|
writer.write(signature);
|
||||||
|
writer.write('"');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,123 +0,0 @@
|
|||||||
/*
|
|
||||||
* [The "BSD licence"]
|
|
||||||
* Copyright (c) 2010 Ben Gruver (JesusFreke)
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. The name of the author may not be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
||||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
||||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
||||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
||||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
|
||||||
|
|
||||||
import org.jf.util.StringUtils;
|
|
||||||
import org.jf.util.IndentingWriter;
|
|
||||||
import org.jf.dexlib.CodeItem;
|
|
||||||
import org.jf.dexlib.StringIdItem;
|
|
||||||
import org.jf.dexlib.TypeIdItem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public abstract class DebugMethodItem extends MethodItem {
|
|
||||||
private final double sortOrder;
|
|
||||||
|
|
||||||
public DebugMethodItem(int codeAddress, double sortOrder) {
|
|
||||||
super(codeAddress);
|
|
||||||
this.sortOrder = sortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getSortOrder() {
|
|
||||||
return sortOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void writeLine(IndentingWriter writer, int line) throws IOException {
|
|
||||||
writer.write(".line ");
|
|
||||||
writer.printSignedIntAsDec(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void writeEndPrologue(IndentingWriter writer) throws IOException {
|
|
||||||
writer.write(".prologue");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void writeBeginEpilogue(IndentingWriter writer) throws IOException {
|
|
||||||
writer.write(".epilogue");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void writeStartLocal(IndentingWriter writer, CodeItem codeItem, int register,
|
|
||||||
StringIdItem name, TypeIdItem type, StringIdItem signature)
|
|
||||||
throws IOException {
|
|
||||||
writer.write(".local ");
|
|
||||||
RegisterFormatter.writeTo(writer, codeItem, register);
|
|
||||||
writer.write(", ");
|
|
||||||
writer.write(name.getStringValue());
|
|
||||||
writer.write(':');
|
|
||||||
writer.write(type.getTypeDescriptor());
|
|
||||||
if (signature != null) {
|
|
||||||
writer.write(",\"");
|
|
||||||
writer.write(signature.getStringValue());
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void writeEndLocal(IndentingWriter writer, CodeItem codeItem, int register, StringIdItem name,
|
|
||||||
TypeIdItem type, StringIdItem signature) throws IOException {
|
|
||||||
writer.write(".end local ");
|
|
||||||
RegisterFormatter.writeTo(writer, codeItem, register);
|
|
||||||
|
|
||||||
if (name != null) {
|
|
||||||
writer.write(" #");
|
|
||||||
writer.write(name.getStringValue());
|
|
||||||
writer.write(':');
|
|
||||||
writer.write(type.getTypeDescriptor());
|
|
||||||
if (signature != null) {
|
|
||||||
writer.write(",\"");
|
|
||||||
writer.write(signature.getStringValue());
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static void writeRestartLocal(IndentingWriter writer, CodeItem codeItem, int register,
|
|
||||||
StringIdItem name, TypeIdItem type, StringIdItem signature)
|
|
||||||
throws IOException {
|
|
||||||
writer.write(".restart local ");
|
|
||||||
RegisterFormatter.writeTo(writer, codeItem, register);
|
|
||||||
|
|
||||||
if (name != null) {
|
|
||||||
writer.write(" #");
|
|
||||||
writer.write(name.getStringValue());
|
|
||||||
writer.write(':');
|
|
||||||
writer.write(type.getTypeDescriptor());
|
|
||||||
if (signature != null) {
|
|
||||||
writer.write(",\"");
|
|
||||||
writer.write(signature.getStringValue());
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void writeSetFile(IndentingWriter writer, String fileName) throws IOException {
|
|
||||||
writer.write(".source \"");
|
|
||||||
StringUtils.writeEscapedString(writer, fileName);
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,36 +28,44 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors.Format;
|
package org.jf.baksmali.Adaptors.Format;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
|
import org.jf.baksmali.Renderers.LongRenderer;
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.ArrayPayload;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.baksmali.Renderers.ByteRenderer;
|
|
||||||
import org.jf.dexlib.Code.Format.ArrayDataPseudoInstruction;
|
|
||||||
import org.jf.dexlib.CodeItem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.List;
|
||||||
|
|
||||||
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayDataPseudoInstruction> {
|
public class ArrayDataMethodItem extends InstructionMethodItem<ArrayPayload> {
|
||||||
public ArrayDataMethodItem(CodeItem codeItem, int codeAddress, ArrayDataPseudoInstruction instruction) {
|
public ArrayDataMethodItem(MethodDefinition methodDef, int codeAddress, ArrayPayload instruction) {
|
||||||
super(codeItem, codeAddress, instruction);
|
super(methodDef, codeAddress, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
writer.write(".array-data 0x");
|
int elementWidth = instruction.getElementWidth();
|
||||||
writer.printUnsignedLongAsHex(instruction.getElementWidth());
|
|
||||||
|
writer.write(".array-data ");
|
||||||
|
writer.printSignedIntAsDec(instruction.getElementWidth());
|
||||||
writer.write('\n');
|
writer.write('\n');
|
||||||
|
|
||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
Iterator<ArrayDataPseudoInstruction.ArrayElement> iterator = instruction.getElements();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
ArrayDataPseudoInstruction.ArrayElement element = iterator.next();
|
|
||||||
|
|
||||||
for (int i=0; i<element.elementWidth; i++) {
|
List<Number> elements = instruction.getArrayElements();
|
||||||
if (i!=0) {
|
|
||||||
writer.write(' ');
|
String suffix = "";
|
||||||
}
|
switch (elementWidth) {
|
||||||
ByteRenderer.writeUnsignedTo(writer, element.buffer[element.bufferIndex+i]);
|
case 1:
|
||||||
}
|
suffix = "t";
|
||||||
writer.write('\n');
|
break;
|
||||||
|
case 2:
|
||||||
|
suffix = "s";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Number number: instruction.getArrayElements()) {
|
||||||
|
LongRenderer.writeSignedIntOrLongTo(writer, number.longValue());
|
||||||
|
writer.write(suffix);
|
||||||
|
writer.write("\n");
|
||||||
}
|
}
|
||||||
writer.deindent(4);
|
writer.deindent(4);
|
||||||
writer.write(".end array-data");
|
writer.write(".end array-data");
|
||||||
|
@ -28,26 +28,25 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors.Format;
|
package org.jf.baksmali.Adaptors.Format;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
import org.jf.baksmali.Adaptors.MethodItem;
|
import org.jf.baksmali.Adaptors.MethodItem;
|
||||||
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
import org.jf.baksmali.Adaptors.ReferenceFormatter;
|
||||||
import org.jf.baksmali.Adaptors.RegisterFormatter;
|
|
||||||
import org.jf.dexlib.Code.Format.Instruction20bc;
|
|
||||||
import org.jf.dexlib.Code.Format.UnknownInstruction;
|
import org.jf.dexlib.Code.Format.UnknownInstruction;
|
||||||
|
import org.jf.dexlib2.ReferenceType;
|
||||||
|
import org.jf.dexlib2.iface.instruction.*;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.baksmali.Renderers.LongRenderer;
|
import org.jf.baksmali.Renderers.LongRenderer;
|
||||||
import org.jf.dexlib.Code.*;
|
|
||||||
import org.jf.dexlib.CodeItem;
|
|
||||||
import org.jf.dexlib.Item;
|
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
||||||
protected final CodeItem codeItem;
|
@Nonnull protected final MethodDefinition methodDef;
|
||||||
protected final T instruction;
|
@Nonnull protected final T instruction;
|
||||||
|
|
||||||
public InstructionMethodItem(CodeItem codeItem, int codeAddress, T instruction) {
|
public InstructionMethodItem(@Nonnull MethodDefinition methodDef, int codeAddress, @Nonnull T instruction) {
|
||||||
super(codeAddress);
|
super(codeAddress);
|
||||||
this.codeItem = codeItem;
|
this.methodDef = methodDef;
|
||||||
this.instruction = instruction;
|
this.instruction = instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
switch (instruction.getFormat()) {
|
switch (instruction.getOpcode().format) {
|
||||||
case Format10t:
|
case Format10t:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
@ -91,13 +90,14 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeSecondRegister(writer);
|
writeSecondRegister(writer);
|
||||||
return true;
|
return true;
|
||||||
case Format20bc:
|
//TODO: uncomment
|
||||||
|
/*case Format20bc:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
writeVerificationErrorType(writer);
|
writeVerificationErrorType(writer);
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeReference(writer);
|
writeReference(writer);
|
||||||
return true;
|
return true;*/
|
||||||
case Format20t:
|
case Format20t:
|
||||||
case Format30t:
|
case Format30t:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
@ -112,7 +112,8 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeReference(writer);
|
writeReference(writer);
|
||||||
return true;
|
return true;
|
||||||
case Format21h:
|
case Format21ih:
|
||||||
|
case Format21lh:
|
||||||
case Format21s:
|
case Format21s:
|
||||||
case Format31i:
|
case Format31i:
|
||||||
case Format51l:
|
case Format51l:
|
||||||
@ -149,7 +150,8 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeReference(writer);
|
writeReference(writer);
|
||||||
return true;
|
return true;
|
||||||
case Format22cs:
|
//TODO: uncomment
|
||||||
|
/*case Format22cs:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
writeFirstRegister(writer);
|
writeFirstRegister(writer);
|
||||||
@ -157,7 +159,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writeSecondRegister(writer);
|
writeSecondRegister(writer);
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeFieldOffset(writer);
|
writeFieldOffset(writer);
|
||||||
return true;
|
return true;*/
|
||||||
case Format22t:
|
case Format22t:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
@ -191,7 +193,8 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeReference(writer);
|
writeReference(writer);
|
||||||
return true;
|
return true;
|
||||||
case Format35mi:
|
//TODO: uncomment
|
||||||
|
/*case Format35mi:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
writeInvokeRegisters(writer);
|
writeInvokeRegisters(writer);
|
||||||
@ -204,7 +207,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writeInvokeRegisters(writer);
|
writeInvokeRegisters(writer);
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeVtableIndex(writer);
|
writeVtableIndex(writer);
|
||||||
return true;
|
return true;*/
|
||||||
case Format3rc:
|
case Format3rc:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
@ -212,7 +215,8 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeReference(writer);
|
writeReference(writer);
|
||||||
return true;
|
return true;
|
||||||
case Format3rmi:
|
//TODO: uncomment
|
||||||
|
/*case Format3rmi:
|
||||||
writeOpcode(writer);
|
writeOpcode(writer);
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
writeInvokeRangeRegisters(writer);
|
writeInvokeRangeRegisters(writer);
|
||||||
@ -225,14 +229,14 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writeInvokeRangeRegisters(writer);
|
writeInvokeRangeRegisters(writer);
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeVtableIndex(writer);
|
writeVtableIndex(writer);
|
||||||
return true;
|
return true;*/
|
||||||
}
|
}
|
||||||
assert false;
|
assert false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeOpcode(IndentingWriter writer) throws IOException {
|
protected void writeOpcode(IndentingWriter writer) throws IOException {
|
||||||
writer.write(instruction.opcode.name);
|
writer.write(instruction.getOpcode().name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeTargetLabel(IndentingWriter writer) throws IOException {
|
protected void writeTargetLabel(IndentingWriter writer) throws IOException {
|
||||||
@ -242,11 +246,11 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
|
protected void writeRegister(IndentingWriter writer, int registerNumber) throws IOException {
|
||||||
RegisterFormatter.writeTo(writer, codeItem, registerNumber);
|
methodDef.registerFormatter.writeTo(writer, registerNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeFirstRegister(IndentingWriter writer) throws IOException {
|
protected void writeFirstRegister(IndentingWriter writer) throws IOException {
|
||||||
writeRegister(writer, ((SingleRegisterInstruction)instruction).getRegisterA());
|
writeRegister(writer, ((OneRegisterInstruction)instruction).getRegisterA());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeSecondRegister(IndentingWriter writer) throws IOException {
|
protected void writeSecondRegister(IndentingWriter writer) throws IOException {
|
||||||
@ -254,40 +258,42 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void writeThirdRegister(IndentingWriter writer) throws IOException {
|
protected void writeThirdRegister(IndentingWriter writer) throws IOException {
|
||||||
writeRegister(writer, ((ThreeRegisterInstruction)instruction).getRegisterC());
|
writeRegister(writer, ((ThreeRegisterInstruction) instruction).getRegisterC());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
|
protected void writeInvokeRegisters(IndentingWriter writer) throws IOException {
|
||||||
FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
|
FiveRegisterInstruction instruction = (FiveRegisterInstruction)this.instruction;
|
||||||
final int regCount = instruction.getRegCount();
|
final int regCount = instruction.getRegisterCount();
|
||||||
|
|
||||||
writer.write('{');
|
writer.write('{');
|
||||||
switch (regCount) {
|
switch (regCount) {
|
||||||
case 1:
|
case 1:
|
||||||
writeRegister(writer, instruction.getRegisterD());
|
writeRegister(writer, instruction.getRegisterC());
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
writeRegister(writer, instruction.getRegisterD());
|
writeRegister(writer, instruction.getRegisterC());
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterE());
|
writeRegister(writer, instruction.getRegisterD());
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
writeRegister(writer, instruction.getRegisterC());
|
||||||
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterD());
|
writeRegister(writer, instruction.getRegisterD());
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterE());
|
writeRegister(writer, instruction.getRegisterE());
|
||||||
writer.write(", ");
|
|
||||||
writeRegister(writer, instruction.getRegisterF());
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
|
writeRegister(writer, instruction.getRegisterC());
|
||||||
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterD());
|
writeRegister(writer, instruction.getRegisterD());
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterE());
|
writeRegister(writer, instruction.getRegisterE());
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterF());
|
writeRegister(writer, instruction.getRegisterF());
|
||||||
writer.write(", ");
|
|
||||||
writeRegister(writer, instruction.getRegisterG());
|
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
|
writeRegister(writer, instruction.getRegisterC());
|
||||||
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterD());
|
writeRegister(writer, instruction.getRegisterD());
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterE());
|
writeRegister(writer, instruction.getRegisterE());
|
||||||
@ -295,8 +301,6 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
writeRegister(writer, instruction.getRegisterF());
|
writeRegister(writer, instruction.getRegisterF());
|
||||||
writer.write(", ");
|
writer.write(", ");
|
||||||
writeRegister(writer, instruction.getRegisterG());
|
writeRegister(writer, instruction.getRegisterG());
|
||||||
writer.write(", ");
|
|
||||||
writeRegister(writer, instruction.getRegisterA());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
writer.write('}');
|
writer.write('}');
|
||||||
@ -305,20 +309,21 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
|
protected void writeInvokeRangeRegisters(IndentingWriter writer) throws IOException {
|
||||||
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
|
RegisterRangeInstruction instruction = (RegisterRangeInstruction)this.instruction;
|
||||||
|
|
||||||
int regCount = instruction.getRegCount();
|
int regCount = instruction.getRegisterCount();
|
||||||
if (regCount == 0) {
|
if (regCount == 0) {
|
||||||
writer.write("{}");
|
writer.write("{}");
|
||||||
} else {
|
} else {
|
||||||
int startRegister = instruction.getStartRegister();
|
int startRegister = instruction.getStartRegister();
|
||||||
RegisterFormatter.writeRegisterRange(writer, codeItem, startRegister, startRegister+regCount-1);
|
methodDef.registerFormatter.writeRegisterRange(writer, startRegister, startRegister+regCount-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeLiteral(IndentingWriter writer) throws IOException {
|
protected void writeLiteral(IndentingWriter writer) throws IOException {
|
||||||
LongRenderer.writeSignedIntOrLongTo(writer, ((LiteralInstruction)instruction).getLiteral());
|
LongRenderer.writeSignedIntOrLongTo(writer, ((WideLiteralInstruction)instruction).getWideLiteral());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeFieldOffset(IndentingWriter writer) throws IOException {
|
//TODO: uncomment
|
||||||
|
/*protected void writeFieldOffset(IndentingWriter writer) throws IOException {
|
||||||
writer.write("field@0x");
|
writer.write("field@0x");
|
||||||
writer.printUnsignedLongAsHex(((OdexedFieldAccess) instruction).getFieldOffset());
|
writer.printUnsignedLongAsHex(((OdexedFieldAccess) instruction).getFieldOffset());
|
||||||
}
|
}
|
||||||
@ -331,15 +336,20 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
|
|||||||
protected void writeVtableIndex(IndentingWriter writer) throws IOException {
|
protected void writeVtableIndex(IndentingWriter writer) throws IOException {
|
||||||
writer.write("vtable@0x");
|
writer.write("vtable@0x");
|
||||||
writer.printUnsignedLongAsHex(((OdexedInvokeVirtual) instruction).getVtableIndex());
|
writer.printUnsignedLongAsHex(((OdexedInvokeVirtual) instruction).getVtableIndex());
|
||||||
}
|
}*/
|
||||||
|
|
||||||
protected void writeReference(IndentingWriter writer) throws IOException {
|
protected void writeReference(IndentingWriter writer) throws IOException {
|
||||||
Item item = ((InstructionWithReference)instruction).getReferencedItem();
|
String reference = ((ReferenceInstruction)instruction).getReference();
|
||||||
ReferenceFormatter.writeReference(writer, item);
|
if (instruction.getOpcode().referenceType == ReferenceType.STRING) {
|
||||||
|
ReferenceFormatter.writeStringReference(writer, reference);
|
||||||
|
} else {
|
||||||
|
writer.write(reference);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {
|
//TODO: uncomment
|
||||||
|
/*protected void writeVerificationErrorType(IndentingWriter writer) throws IOException {
|
||||||
VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType();
|
VerificationErrorType validationErrorType = ((Instruction20bc)instruction).getValidationErrorType();
|
||||||
writer.write(validationErrorType.getName());
|
writer.write(validationErrorType.getName());
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
@ -29,37 +29,36 @@
|
|||||||
package org.jf.baksmali.Adaptors.Format;
|
package org.jf.baksmali.Adaptors.Format;
|
||||||
|
|
||||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
import org.jf.dexlib.Code.Format.*;
|
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||||
import org.jf.dexlib.Code.Instruction;
|
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||||
import org.jf.dexlib.Code.OffsetInstruction;
|
import org.jf.dexlib2.iface.instruction.formats.ArrayPayload;
|
||||||
import org.jf.dexlib.CodeItem;
|
import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload;
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload;
|
||||||
|
|
||||||
public class InstructionMethodItemFactory {
|
public class InstructionMethodItemFactory {
|
||||||
private InstructionMethodItemFactory() {
|
private InstructionMethodItemFactory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstructionMethodItem makeInstructionFormatMethodItem(
|
public static InstructionMethodItem makeInstructionFormatMethodItem(
|
||||||
MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress, Instruction instruction) {
|
MethodDefinition methodDef, int codeAddress, Instruction instruction) {
|
||||||
|
|
||||||
if (instruction instanceof OffsetInstruction) {
|
if (instruction instanceof OffsetInstruction) {
|
||||||
return new OffsetInstructionFormatMethodItem(methodDefinition.getLabelCache(), codeItem,
|
return new OffsetInstructionFormatMethodItem(methodDef, codeAddress, (OffsetInstruction)instruction);
|
||||||
codeAddress, (OffsetInstruction)instruction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (instruction.getFormat()) {
|
switch (instruction.getOpcode().format) {
|
||||||
case ArrayData:
|
case ArrayPayload:
|
||||||
return new ArrayDataMethodItem(codeItem, codeAddress,
|
return new ArrayDataMethodItem(methodDef, codeAddress, (ArrayPayload)instruction);
|
||||||
(ArrayDataPseudoInstruction)instruction);
|
case PackedSwitchPayload:
|
||||||
case PackedSwitchData:
|
return new PackedSwitchMethodItem(methodDef, codeAddress, (PackedSwitchPayload)instruction);
|
||||||
return new PackedSwitchMethodItem(methodDefinition, codeItem, codeAddress,
|
case SparseSwitchPayload:
|
||||||
(PackedSwitchDataPseudoInstruction)instruction);
|
return new SparseSwitchMethodItem(methodDef, codeAddress, (SparseSwitchPayload)instruction);
|
||||||
case SparseSwitchData:
|
//TODO: uncomment
|
||||||
return new SparseSwitchMethodItem(methodDefinition, codeItem, codeAddress,
|
/*case UnresolvedOdexInstruction:
|
||||||
(SparseSwitchDataPseudoInstruction)instruction);
|
|
||||||
case UnresolvedOdexInstruction:
|
|
||||||
return new UnresolvedOdexInstructionMethodItem(codeItem, codeAddress,
|
return new UnresolvedOdexInstructionMethodItem(codeItem, codeAddress,
|
||||||
(UnresolvedOdexInstruction)instruction);
|
(UnresolvedOdexInstruction)instruction);*/
|
||||||
default:
|
default:
|
||||||
return new InstructionMethodItem<Instruction>(codeItem, codeAddress, instruction);
|
return new InstructionMethodItem<Instruction>(methodDef, codeAddress, instruction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,22 +30,21 @@ package org.jf.baksmali.Adaptors.Format;
|
|||||||
|
|
||||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
|
import org.jf.dexlib2.Opcode;
|
||||||
|
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.dexlib.Code.OffsetInstruction;
|
|
||||||
import org.jf.dexlib.Code.Opcode;
|
|
||||||
import org.jf.dexlib.CodeItem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<OffsetInstruction> {
|
public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<OffsetInstruction> {
|
||||||
protected LabelMethodItem label;
|
protected LabelMethodItem label;
|
||||||
|
|
||||||
public OffsetInstructionFormatMethodItem(MethodDefinition.LabelCache labelCache, CodeItem codeItem, int codeAddress,
|
public OffsetInstructionFormatMethodItem(MethodDefinition methodDef, int codeAddress,
|
||||||
OffsetInstruction instruction) {
|
OffsetInstruction instruction) {
|
||||||
super(codeItem, codeAddress, instruction);
|
super(methodDef, codeAddress, instruction);
|
||||||
|
|
||||||
label = new LabelMethodItem(codeAddress + instruction.getTargetAddressOffset(), getLabelPrefix());
|
label = new LabelMethodItem(codeAddress + instruction.getCodeOffset(), getLabelPrefix());
|
||||||
label = labelCache.internLabel(label);
|
label = methodDef.getLabelCache().internLabel(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -58,7 +57,8 @@ public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<Off
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getLabelPrefix() {
|
private String getLabelPrefix() {
|
||||||
switch (instruction.getFormat()) {
|
Opcode opcode = instruction.getOpcode();
|
||||||
|
switch (opcode.format) {
|
||||||
case Format10t:
|
case Format10t:
|
||||||
case Format20t:
|
case Format20t:
|
||||||
case Format30t:
|
case Format30t:
|
||||||
@ -67,13 +67,13 @@ public class OffsetInstructionFormatMethodItem extends InstructionMethodItem<Off
|
|||||||
case Format22t:
|
case Format22t:
|
||||||
return "cond_";
|
return "cond_";
|
||||||
case Format31t:
|
case Format31t:
|
||||||
if (instruction.opcode == Opcode.FILL_ARRAY_DATA) {
|
if (opcode == Opcode.FILL_ARRAY_DATA) {
|
||||||
return "array_";
|
return "array_";
|
||||||
}
|
}
|
||||||
if (instruction.opcode == Opcode.PACKED_SWITCH) {
|
if (opcode == Opcode.PACKED_SWITCH) {
|
||||||
return "pswitch_data_";
|
return "pswitch_data_";
|
||||||
}
|
}
|
||||||
assert instruction.opcode == Opcode.SPARSE_SWITCH;
|
// Opcode.SPARSE_SWITCH;
|
||||||
return "sswitch_data_";
|
return "sswitch_data_";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,55 +30,54 @@ package org.jf.baksmali.Adaptors.Format;
|
|||||||
|
|
||||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
|
import org.jf.dexlib2.iface.instruction.SwitchElement;
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.baksmali.Renderers.IntegerRenderer;
|
import org.jf.baksmali.Renderers.IntegerRenderer;
|
||||||
import org.jf.dexlib.Code.Format.PackedSwitchDataPseudoInstruction;
|
|
||||||
import org.jf.dexlib.CodeItem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDataPseudoInstruction> {
|
public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchPayload> {
|
||||||
private final List<PackedSwitchTarget> targets;
|
private final List<PackedSwitchTarget> targets;
|
||||||
|
private final int firstKey;
|
||||||
|
|
||||||
public PackedSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress,
|
public PackedSwitchMethodItem(MethodDefinition methodDef, int codeAddress, PackedSwitchPayload instruction) {
|
||||||
PackedSwitchDataPseudoInstruction instruction) {
|
super(methodDef, codeAddress, instruction);
|
||||||
super(codeItem, codeAddress, instruction);
|
|
||||||
|
|
||||||
int baseCodeAddress = methodDefinition.getPackedSwitchBaseAddress(codeAddress);
|
int baseCodeAddress = methodDef.getPackedSwitchBaseAddress(codeAddress);
|
||||||
|
|
||||||
targets = new ArrayList<PackedSwitchTarget>();
|
targets = new ArrayList<PackedSwitchTarget>();
|
||||||
Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator = instruction.iterateKeysAndTargets();
|
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
//TODO: does dalvik allow switc payloads with no cases?
|
||||||
|
int firstKey = 0;
|
||||||
if (baseCodeAddress >= 0) {
|
if (baseCodeAddress >= 0) {
|
||||||
while (iterator.hasNext()) {
|
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||||
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
|
if (first) {
|
||||||
PackedSwitchLabelTarget packedSwitchLabelTarget = new PackedSwitchLabelTarget();
|
firstKey = switchElement.getKey();
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "pswitch_");
|
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + switchElement.getOffset(), "pswitch_");
|
||||||
label = methodDefinition.getLabelCache().internLabel(label);
|
targets.add(new PackedSwitchLabelTarget(label));
|
||||||
packedSwitchLabelTarget.Target = label;
|
|
||||||
targets.add(packedSwitchLabelTarget);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (iterator.hasNext()) {
|
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||||
PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
|
if (first) {
|
||||||
PackedSwitchOffsetTarget packedSwitchOffsetTarget = new PackedSwitchOffsetTarget();
|
firstKey = switchElement.getKey();
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
packedSwitchOffsetTarget.Target = target.targetAddressOffset;
|
targets.add(new PackedSwitchOffsetTarget(switchElement.getOffset()));
|
||||||
targets.add(packedSwitchOffsetTarget);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.firstKey = firstKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
writer.write(".packed-switch ");
|
writer.write(".packed-switch ");
|
||||||
IntegerRenderer.writeTo(writer, instruction.getFirstKey());
|
IntegerRenderer.writeTo(writer, firstKey);
|
||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
writer.write('\n');
|
writer.write('\n');
|
||||||
for (PackedSwitchTarget target: targets) {
|
for (PackedSwitchTarget target: targets) {
|
||||||
@ -95,19 +94,25 @@ public class PackedSwitchMethodItem extends InstructionMethodItem<PackedSwitchDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class PackedSwitchLabelTarget extends PackedSwitchTarget {
|
private static class PackedSwitchLabelTarget extends PackedSwitchTarget {
|
||||||
public LabelMethodItem Target;
|
private final LabelMethodItem target;
|
||||||
|
public PackedSwitchLabelTarget(LabelMethodItem target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||||
Target.writeTo(writer);
|
target.writeTo(writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PackedSwitchOffsetTarget extends PackedSwitchTarget {
|
private static class PackedSwitchOffsetTarget extends PackedSwitchTarget {
|
||||||
public int Target;
|
private final int target;
|
||||||
|
public PackedSwitchOffsetTarget(int target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||||
if (Target >= 0) {
|
if (target >= 0) {
|
||||||
writer.write('+');
|
writer.write('+');
|
||||||
}
|
}
|
||||||
writer.printSignedIntAsDec(Target);
|
writer.printSignedIntAsDec(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,48 +30,33 @@ package org.jf.baksmali.Adaptors.Format;
|
|||||||
|
|
||||||
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
import org.jf.baksmali.Adaptors.LabelMethodItem;
|
||||||
import org.jf.baksmali.Adaptors.MethodDefinition;
|
import org.jf.baksmali.Adaptors.MethodDefinition;
|
||||||
|
import org.jf.dexlib2.iface.instruction.SwitchElement;
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.baksmali.Renderers.IntegerRenderer;
|
import org.jf.baksmali.Renderers.IntegerRenderer;
|
||||||
import org.jf.dexlib.Code.Format.SparseSwitchDataPseudoInstruction;
|
|
||||||
import org.jf.dexlib.CodeItem;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDataPseudoInstruction> {
|
public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchPayload> {
|
||||||
private final List<SparseSwitchTarget> targets;
|
private final List<SparseSwitchTarget> targets;
|
||||||
|
|
||||||
public SparseSwitchMethodItem(MethodDefinition methodDefinition, CodeItem codeItem, int codeAddress,
|
public SparseSwitchMethodItem(MethodDefinition methodDef, int codeAddress, SparseSwitchPayload instruction) {
|
||||||
SparseSwitchDataPseudoInstruction instruction) {
|
super(methodDef, codeAddress, instruction);
|
||||||
super(codeItem, codeAddress, instruction);
|
|
||||||
|
|
||||||
int baseCodeAddress = methodDefinition.getSparseSwitchBaseAddress(codeAddress);
|
int baseCodeAddress = methodDef.getSparseSwitchBaseAddress(codeAddress);
|
||||||
|
|
||||||
targets = new ArrayList<SparseSwitchTarget>();
|
targets = new ArrayList<SparseSwitchTarget>();
|
||||||
Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator = instruction.iterateKeysAndTargets();
|
|
||||||
if (baseCodeAddress >= 0) {
|
if (baseCodeAddress >= 0) {
|
||||||
while (iterator.hasNext()) {
|
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||||
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
|
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + switchElement.getOffset(), "sswitch_");
|
||||||
SparseSwitchLabelTarget sparseSwitchLabelTarget = new SparseSwitchLabelTarget();
|
targets.add(new SparseSwitchLabelTarget(switchElement.getKey(), label));
|
||||||
sparseSwitchLabelTarget.Key = target.key;
|
|
||||||
|
|
||||||
LabelMethodItem label = new LabelMethodItem(baseCodeAddress + target.targetAddressOffset, "sswitch_");
|
|
||||||
label = methodDefinition.getLabelCache().internLabel(label);
|
|
||||||
sparseSwitchLabelTarget.Target = label;
|
|
||||||
|
|
||||||
targets.add(sparseSwitchLabelTarget);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//if we couldn't determine a base address, just use relative offsets rather than labels
|
//if we couldn't determine a base address, just use relative offsets rather than labels
|
||||||
while (iterator.hasNext()) {
|
for (SwitchElement switchElement: instruction.getSwitchElements()) {
|
||||||
SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
|
targets.add(new SparseSwitchOffsetTarget(switchElement.getKey(), switchElement.getOffset()));
|
||||||
SparseSwitchOffsetTarget sparseSwitchOffsetTarget = new SparseSwitchOffsetTarget();
|
|
||||||
sparseSwitchOffsetTarget.Key = target.key;
|
|
||||||
|
|
||||||
sparseSwitchOffsetTarget.Target = target.targetAddressOffset;
|
|
||||||
targets.add(sparseSwitchOffsetTarget);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -81,7 +66,7 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDa
|
|||||||
writer.write(".sparse-switch\n");
|
writer.write(".sparse-switch\n");
|
||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
for (SparseSwitchTarget target: targets) {
|
for (SparseSwitchTarget target: targets) {
|
||||||
IntegerRenderer.writeTo(writer, target.Key);
|
IntegerRenderer.writeTo(writer, target.getKey());
|
||||||
writer.write(" -> ");
|
writer.write(" -> ");
|
||||||
target.writeTargetTo(writer);
|
target.writeTargetTo(writer);
|
||||||
writer.write('\n');
|
writer.write('\n');
|
||||||
@ -92,24 +77,38 @@ public class SparseSwitchMethodItem extends InstructionMethodItem<SparseSwitchDa
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static abstract class SparseSwitchTarget {
|
private static abstract class SparseSwitchTarget {
|
||||||
public int Key;
|
private final int key;
|
||||||
|
public SparseSwitchTarget(int key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
public int getKey() { return key; }
|
||||||
public abstract void writeTargetTo(IndentingWriter writer) throws IOException;
|
public abstract void writeTargetTo(IndentingWriter writer) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SparseSwitchLabelTarget extends SparseSwitchTarget {
|
private static class SparseSwitchLabelTarget extends SparseSwitchTarget {
|
||||||
public LabelMethodItem Target;
|
private final LabelMethodItem target;
|
||||||
|
public SparseSwitchLabelTarget(int key, LabelMethodItem target) {
|
||||||
|
super(key);
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||||
Target.writeTo(writer);
|
target.writeTo(writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SparseSwitchOffsetTarget extends SparseSwitchTarget {
|
private static class SparseSwitchOffsetTarget extends SparseSwitchTarget {
|
||||||
public int Target;
|
private final int target;
|
||||||
|
public SparseSwitchOffsetTarget(int key, int target) {
|
||||||
|
super(key);
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
public void writeTargetTo(IndentingWriter writer) throws IOException {
|
||||||
if (Target >= 0) {
|
if (target >= 0) {
|
||||||
writer.write('+');
|
writer.write('+');
|
||||||
}
|
}
|
||||||
writer.printSignedIntAsDec(Target);
|
writer.printSignedIntAsDec(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ import org.jf.dexlib.CodeItem;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<UnresolvedOdexInstruction> {
|
//TODO: uncomment
|
||||||
|
/*public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<UnresolvedOdexInstruction> {
|
||||||
public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) {
|
public UnresolvedOdexInstructionMethodItem(CodeItem codeItem, int codeAddress, UnresolvedOdexInstruction instruction) {
|
||||||
super(codeItem, codeAddress, instruction);
|
super(codeItem, codeAddress, instruction);
|
||||||
}
|
}
|
||||||
@ -49,4 +50,4 @@ public class UnresolvedOdexInstructionMethodItem extends InstructionMethodItem<U
|
|||||||
writer.write("throw ");
|
writer.write("throw ");
|
||||||
writeRegister(writer, instruction.ObjectRegisterNum);
|
writeRegister(writer, instruction.ObjectRegisterNum);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
@ -28,293 +28,228 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
|
import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
|
||||||
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
|
import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
|
||||||
import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver;
|
import org.jf.dexlib2.AccessFlags;
|
||||||
import org.jf.dexlib.Code.InstructionWithReference;
|
import org.jf.dexlib2.Opcode;
|
||||||
|
import org.jf.dexlib2.iface.*;
|
||||||
|
import org.jf.dexlib2.iface.debug.DebugItem;
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||||
|
import org.jf.dexlib2.iface.instruction.OffsetInstruction;
|
||||||
|
import org.jf.dexlib2.util.InstructionOffsetMap;
|
||||||
|
import org.jf.dexlib2.util.MethodUtil;
|
||||||
|
import org.jf.dexlib2.util.TypeUtils;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.baksmali.baksmali;
|
import org.jf.baksmali.baksmali;
|
||||||
import org.jf.dexlib.*;
|
|
||||||
import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
|
|
||||||
import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
|
|
||||||
import org.jf.dexlib.Code.Analysis.ValidationException;
|
|
||||||
import org.jf.dexlib.Code.Format.Format;
|
|
||||||
import org.jf.dexlib.Code.Instruction;
|
|
||||||
import org.jf.dexlib.Code.OffsetInstruction;
|
|
||||||
import org.jf.dexlib.Code.Opcode;
|
|
||||||
import org.jf.dexlib.Debug.DebugInstructionIterator;
|
|
||||||
import org.jf.dexlib.Util.AccessFlags;
|
|
||||||
import org.jf.util.ExceptionWithContext;
|
import org.jf.util.ExceptionWithContext;
|
||||||
import org.jf.dexlib.Util.SparseIntArray;
|
import org.jf.dexlib.Util.SparseIntArray;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class MethodDefinition {
|
public class MethodDefinition {
|
||||||
private final ClassDataItem.EncodedMethod encodedMethod;
|
@Nonnull public final ClassDefinition classDef;
|
||||||
private MethodAnalyzer methodAnalyzer;
|
@Nonnull public final Method method;
|
||||||
|
@Nonnull public final MethodImplementation methodImpl;
|
||||||
|
public RegisterFormatter registerFormatter;
|
||||||
|
|
||||||
private final LabelCache labelCache = new LabelCache();
|
@Nonnull private final String methodString;
|
||||||
|
|
||||||
private final SparseIntArray packedSwitchMap;
|
@Nonnull private final LabelCache labelCache = new LabelCache();
|
||||||
private final SparseIntArray sparseSwitchMap;
|
|
||||||
private final SparseIntArray instructionMap;
|
|
||||||
|
|
||||||
public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod) {
|
@Nonnull private final SparseIntArray packedSwitchMap;
|
||||||
|
@Nonnull private final SparseIntArray sparseSwitchMap;
|
||||||
|
@Nonnull private final InstructionOffsetMap instructionOffsetMap;
|
||||||
|
|
||||||
|
public MethodDefinition(@Nonnull ClassDefinition classDef, @Nonnull Method method,
|
||||||
|
@Nonnull MethodImplementation methodImpl) {
|
||||||
|
this.classDef = classDef;
|
||||||
|
this.method = method;
|
||||||
|
this.methodImpl = methodImpl;
|
||||||
|
|
||||||
|
this.methodString = MethodUtil.buildFullMethodString(classDef.classDef, method);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.encodedMethod = encodedMethod;
|
|
||||||
|
|
||||||
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
|
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
|
||||||
|
|
||||||
if (encodedMethod.codeItem != null) {
|
List<? extends Instruction> instructions = methodImpl.getInstructions();
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
|
||||||
|
|
||||||
packedSwitchMap = new SparseIntArray(1);
|
packedSwitchMap = new SparseIntArray(0);
|
||||||
sparseSwitchMap = new SparseIntArray(1);
|
sparseSwitchMap = new SparseIntArray(0);
|
||||||
instructionMap = new SparseIntArray(instructions.length);
|
instructionOffsetMap = new InstructionOffsetMap(methodImpl);
|
||||||
|
|
||||||
int currentCodeAddress = 0;
|
for (int i=0; i<instructions.size(); i++) {
|
||||||
for (int i=0; i<instructions.length; i++) {
|
Instruction instruction = instructions.get(i);
|
||||||
Instruction instruction = instructions[i];
|
|
||||||
if (instruction.opcode == Opcode.PACKED_SWITCH) {
|
Opcode opcode = instruction.getOpcode();
|
||||||
packedSwitchMap.append(
|
if (opcode == Opcode.PACKED_SWITCH) {
|
||||||
currentCodeAddress +
|
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
|
||||||
((OffsetInstruction)instruction).getTargetAddressOffset(),
|
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
|
||||||
currentCodeAddress);
|
targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
|
||||||
} else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
|
packedSwitchMap.append(codeOffset, targetOffset);
|
||||||
sparseSwitchMap.append(
|
} else if (opcode == Opcode.SPARSE_SWITCH) {
|
||||||
currentCodeAddress +
|
int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
|
||||||
((OffsetInstruction)instruction).getTargetAddressOffset(),
|
int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
|
||||||
currentCodeAddress);
|
targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
|
||||||
}
|
sparseSwitchMap.append(codeOffset, targetOffset);
|
||||||
instructionMap.append(currentCodeAddress, i);
|
|
||||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
packedSwitchMap = null;
|
|
||||||
sparseSwitchMap = null;
|
|
||||||
instructionMap = null;
|
|
||||||
methodAnalyzer = null;
|
|
||||||
}
|
}
|
||||||
}catch (Exception ex) {
|
}catch (Exception ex) {
|
||||||
throw ExceptionWithContext.withContext(ex, String.format("Error while processing method %s",
|
throw ExceptionWithContext.withContext(ex, "Error while processing method %s", methodString);
|
||||||
encodedMethod.method.getMethodString()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTo(IndentingWriter writer, AnnotationSetItem annotationSet,
|
public static void writeEmptyMethodTo(IndentingWriter writer, Method method) throws IOException {
|
||||||
AnnotationSetRefList parameterAnnotations) throws IOException {
|
|
||||||
final CodeItem codeItem = encodedMethod.codeItem;
|
|
||||||
|
|
||||||
writer.write(".method ");
|
writer.write(".method ");
|
||||||
writeAccessFlags(writer, encodedMethod);
|
writeAccessFlags(writer, method.getAccessFlags());
|
||||||
writer.write(encodedMethod.method.getMethodName().getStringValue());
|
writer.write(method.getName());
|
||||||
writer.write(encodedMethod.method.getPrototype().getPrototypeString());
|
writer.write("(");
|
||||||
|
for (MethodParameter parameter: method.getParameters()) {
|
||||||
|
writer.write(parameter.getType());
|
||||||
|
}
|
||||||
|
writer.write(")");
|
||||||
|
writer.write(method.getReturnType());
|
||||||
writer.write('\n');
|
writer.write('\n');
|
||||||
|
|
||||||
writer.indent(4);
|
writer.indent(4);
|
||||||
if (codeItem != null) {
|
writeParameters(writer, method.getParameters());
|
||||||
if (baksmali.useLocalsDirective) {
|
AnnotationFormatter.writeTo(writer, method.getAnnotations());
|
||||||
writer.write(".locals ");
|
writer.deindent(4);
|
||||||
} else {
|
writer.write(".end method\n");
|
||||||
writer.write(".registers ");
|
}
|
||||||
}
|
|
||||||
writer.printSignedIntAsDec(getRegisterCount(encodedMethod));
|
|
||||||
writer.write('\n');
|
|
||||||
writeParameters(writer, codeItem, parameterAnnotations);
|
|
||||||
//TODO: uncomment
|
|
||||||
/*if (annotationSet != null) {
|
|
||||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
writer.write('\n');
|
public void writeTo(IndentingWriter writer) throws IOException {
|
||||||
|
int parameterRegisterCount = 0;
|
||||||
|
if (!AccessFlags.STATIC.isSet(method.getAccessFlags())) {
|
||||||
|
parameterRegisterCount++;
|
||||||
|
}
|
||||||
|
|
||||||
for (MethodItem methodItem: getMethodItems()) {
|
writer.write(".method ");
|
||||||
if (methodItem.writeTo(writer)) {
|
writeAccessFlags(writer, method.getAccessFlags());
|
||||||
writer.write('\n');
|
writer.write(method.getName());
|
||||||
}
|
writer.write("(");
|
||||||
|
for (MethodParameter parameter: method.getParameters()) {
|
||||||
|
String type = parameter.getType();
|
||||||
|
writer.write(type);
|
||||||
|
parameterRegisterCount++;
|
||||||
|
if (TypeUtils.isWideType(type)) {
|
||||||
|
parameterRegisterCount++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
writer.write(")");
|
||||||
|
writer.write(method.getReturnType());
|
||||||
|
writer.write('\n');
|
||||||
|
|
||||||
|
writer.indent(4);
|
||||||
|
if (baksmali.useLocalsDirective) {
|
||||||
|
writer.write(".locals ");
|
||||||
|
writer.printSignedIntAsDec(methodImpl.getRegisterCount() - parameterRegisterCount);
|
||||||
} else {
|
} else {
|
||||||
writeParameters(writer, codeItem, parameterAnnotations);
|
writer.write(".registers ");
|
||||||
//TODO: uncomment
|
writer.printSignedIntAsDec(methodImpl.getRegisterCount());
|
||||||
/*if (annotationSet != null) {
|
}
|
||||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
writer.write('\n');
|
||||||
}*/
|
writeParameters(writer, method.getParameters());
|
||||||
|
|
||||||
|
if (registerFormatter == null) {
|
||||||
|
registerFormatter = new RegisterFormatter(methodImpl.getRegisterCount(), parameterRegisterCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnnotationFormatter.writeTo(writer, method.getAnnotations());
|
||||||
|
|
||||||
|
writer.write('\n');
|
||||||
|
|
||||||
|
for (MethodItem methodItem: getMethodItems()) {
|
||||||
|
if (methodItem.writeTo(writer)) {
|
||||||
|
writer.write('\n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
writer.deindent(4);
|
writer.deindent(4);
|
||||||
writer.write(".end method\n");
|
writer.write(".end method\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getRegisterCount(ClassDataItem.EncodedMethod encodedMethod)
|
private int findSwitchPayload(int targetOffset, Opcode type) {
|
||||||
{
|
int targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
|
||||||
int totalRegisters = encodedMethod.codeItem.getRegisterCount();
|
|
||||||
if (baksmali.useLocalsDirective) {
|
//TODO: does dalvik let you pad with multiple nops?
|
||||||
int parameterRegisters = encodedMethod.method.getPrototype().getParameterRegisterCount();
|
//TODO: does dalvik let a switch instruction point to a non-payload instruction?
|
||||||
if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
|
|
||||||
parameterRegisters++;
|
List<? extends Instruction> instructions = methodImpl.getInstructions();
|
||||||
|
Instruction instruction = instructions.get(targetIndex);
|
||||||
|
if (instruction.getOpcode() != type) {
|
||||||
|
// maybe it's pointing to a NOP padding instruction. Look at the next instruction
|
||||||
|
if (instruction.getOpcode() == Opcode.NOP) {
|
||||||
|
targetIndex += 1;
|
||||||
|
if (targetIndex < instructions.size()) {
|
||||||
|
instruction = instructions.get(targetIndex);
|
||||||
|
if (instruction.getOpcode() == type) {
|
||||||
|
return instructionOffsetMap.getInstructionCodeOffset(targetIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return totalRegisters - parameterRegisters;
|
throw new ExceptionWithContext("No switch payload at offset 0x%x", targetOffset);
|
||||||
|
} else {
|
||||||
|
return targetOffset;
|
||||||
}
|
}
|
||||||
return totalRegisters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeAccessFlags(IndentingWriter writer, ClassDataItem.EncodedMethod encodedMethod)
|
private static void writeAccessFlags(IndentingWriter writer, int accessFlags)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
|
for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(accessFlags)) {
|
||||||
writer.write(accessFlag.toString());
|
writer.write(accessFlag.toString());
|
||||||
writer.write(' ');
|
writer.write(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeParameters(IndentingWriter writer, CodeItem codeItem,
|
private static void writeParameters(IndentingWriter writer,
|
||||||
AnnotationSetRefList parameterAnnotations) throws IOException {
|
List<? extends MethodParameter> parameters) throws IOException {
|
||||||
DebugInfoItem debugInfoItem = null;
|
int registerNumber = 0;
|
||||||
if (baksmali.outputDebugInfo && codeItem != null) {
|
for (MethodParameter parameter: parameters) {
|
||||||
debugInfoItem = codeItem.getDebugInfo();
|
String parameterType = parameter.getType();
|
||||||
}
|
String parameterName = parameter.getName();
|
||||||
|
List<? extends Annotation> annotations = parameter.getAnnotations();
|
||||||
int parameterCount = 0;
|
if (parameterName != null || annotations.size() != 0) {
|
||||||
AnnotationSetItem[] annotations;
|
writer.write(".parameter p");
|
||||||
StringIdItem[] parameterNames = null;
|
writer.printSignedIntAsDec(registerNumber);
|
||||||
|
if (parameterName != null) {
|
||||||
if (parameterAnnotations != null) {
|
writer.write(" ");
|
||||||
annotations = parameterAnnotations.getAnnotationSets();
|
writer.write(parameterName);
|
||||||
parameterCount = annotations.length;
|
}
|
||||||
} else {
|
if (annotations.size() > 0) {
|
||||||
annotations = new AnnotationSetItem[0];
|
writer.indent(4);
|
||||||
}
|
AnnotationFormatter.writeTo(writer, annotations);
|
||||||
|
writer.deindent(4);
|
||||||
if (debugInfoItem != null) {
|
writer.write(".end parameter\n");
|
||||||
parameterNames = debugInfoItem.getParameterNames();
|
} else {
|
||||||
}
|
writer.write("\n");
|
||||||
if (parameterNames == null) {
|
}
|
||||||
parameterNames = new StringIdItem[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameterCount < parameterNames.length) {
|
|
||||||
parameterCount = parameterNames.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<parameterCount; i++) {
|
|
||||||
AnnotationSetItem annotationSet = null;
|
|
||||||
if (i < annotations.length) {
|
|
||||||
annotationSet = annotations[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringIdItem parameterName = null;
|
registerNumber++;
|
||||||
if (i < parameterNames.length) {
|
if (TypeUtils.isWideType(parameterType)) {
|
||||||
parameterName = parameterNames[i];
|
registerNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.write(".parameter");
|
|
||||||
|
|
||||||
if (parameterName != null) {
|
|
||||||
writer.write(" \"");
|
|
||||||
writer.write(parameterName.getStringValue());
|
|
||||||
writer.write('"');
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.write('\n');
|
|
||||||
//TODO: uncomment
|
|
||||||
/*if (annotationSet != null) {
|
|
||||||
writer.indent(4);
|
|
||||||
AnnotationFormatter.writeTo(writer, annotationSet);
|
|
||||||
writer.deindent(4);
|
|
||||||
|
|
||||||
writer.write(".end parameter\n");
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LabelCache getLabelCache() {
|
@Nonnull public LabelCache getLabelCache() {
|
||||||
return labelCache;
|
return labelCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationException getValidationException() {
|
public int getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset) {
|
||||||
if (methodAnalyzer == null) {
|
return packedSwitchMap.get(packedSwitchPayloadCodeOffset, -1);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return methodAnalyzer.getValidationException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
|
public int getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset) {
|
||||||
int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
|
return sparseSwitchMap.get(sparseSwitchPayloadCodeOffset, -1);
|
||||||
|
|
||||||
if (packedSwitchBaseAddress == -1) {
|
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
|
||||||
int index = instructionMap.get(packedSwitchDataAddress);
|
|
||||||
|
|
||||||
if (instructions[index].opcode == Opcode.NOP) {
|
|
||||||
packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress+2, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return packedSwitchBaseAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSparseSwitchBaseAddress(int sparseSwitchDataAddress) {
|
|
||||||
int sparseSwitchBaseAddress = this.sparseSwitchMap.get(sparseSwitchDataAddress, -1);
|
|
||||||
|
|
||||||
if (sparseSwitchBaseAddress == -1) {
|
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
|
||||||
int index = instructionMap.get(sparseSwitchDataAddress);
|
|
||||||
|
|
||||||
if (instructions[index].opcode == Opcode.NOP) {
|
|
||||||
sparseSwitchBaseAddress = this.packedSwitchMap.get(sparseSwitchDataAddress+2, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sparseSwitchBaseAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param instructions The instructions array for this method
|
|
||||||
* @param instruction The instruction
|
|
||||||
* @return true if the specified instruction is a NOP, and the next instruction is one of the variable sized
|
|
||||||
* switch/array data structures
|
|
||||||
*/
|
|
||||||
private boolean isInstructionPaddingNop(List<AnalyzedInstruction> instructions, AnalyzedInstruction instruction) {
|
|
||||||
if (instruction.getInstruction().opcode != Opcode.NOP ||
|
|
||||||
instruction.getInstruction().getFormat().variableSizeFormat) {
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instruction.getInstructionIndex() == instructions.size()-1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnalyzedInstruction nextInstruction = instructions.get(instruction.getInstructionIndex()+1);
|
|
||||||
if (nextInstruction.getInstruction().getFormat().variableSizeFormat) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean needsAnalyzed() {
|
|
||||||
for (Instruction instruction: encodedMethod.codeItem.getInstructions()) {
|
|
||||||
if (instruction.opcode.odexOnly()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MethodItem> getMethodItems() {
|
private List<MethodItem> getMethodItems() {
|
||||||
ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
|
ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
|
||||||
|
|
||||||
if (encodedMethod.codeItem == null) {
|
//TODO: addAnalyzedInstructionMethodItems
|
||||||
return methodItems;
|
addInstructionMethodItems(methodItems);
|
||||||
}
|
|
||||||
|
|
||||||
if ((baksmali.registerInfo != 0) || baksmali.verify ||
|
|
||||||
(baksmali.deodex && needsAnalyzed())) {
|
|
||||||
addAnalyzedInstructionMethodItems(methodItems);
|
|
||||||
} else {
|
|
||||||
addInstructionMethodItems(methodItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
addTries(methodItems);
|
addTries(methodItems);
|
||||||
if (baksmali.outputDebugInfo) {
|
if (baksmali.outputDebugInfo) {
|
||||||
@ -335,18 +270,18 @@ public class MethodDefinition {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addInstructionMethodItems(List<MethodItem> methodItems) {
|
private void addInstructionMethodItems(List<MethodItem> methodItems) {
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
List<? extends Instruction> instructions = methodImpl.getInstructions();
|
||||||
|
|
||||||
int currentCodeAddress = 0;
|
int currentCodeAddress = 0;
|
||||||
for (int i=0; i<instructions.length; i++) {
|
for (int i=0; i<instructions.size(); i++) {
|
||||||
Instruction instruction = instructions[i];
|
Instruction instruction = instructions.get(i);
|
||||||
|
|
||||||
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
|
||||||
encodedMethod.codeItem, currentCodeAddress, instruction);
|
currentCodeAddress, instruction);
|
||||||
|
|
||||||
methodItems.add(methodItem);
|
methodItems.add(methodItem);
|
||||||
|
|
||||||
if (i != instructions.length - 1) {
|
if (i != instructions.size() - 1) {
|
||||||
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
methodItems.add(new BlankMethodItem(currentCodeAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,14 +296,16 @@ public class MethodDefinition {
|
|||||||
@Override
|
@Override
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
public boolean writeTo(IndentingWriter writer) throws IOException {
|
||||||
writer.write("#@");
|
writer.write("#@");
|
||||||
writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFF);
|
writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!baksmali.noAccessorComments && (instruction instanceof InstructionWithReference)) {
|
//TODO: uncomment
|
||||||
if (instruction.opcode == Opcode.INVOKE_STATIC || instruction.opcode == Opcode.INVOKE_STATIC_RANGE) {
|
/*if (!baksmali.noAccessorComments && (instruction instanceof InstructionWithReference)) {
|
||||||
|
Opcode opcode = instruction.getOpcode();
|
||||||
|
if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) {
|
||||||
MethodIdItem methodIdItem =
|
MethodIdItem methodIdItem =
|
||||||
(MethodIdItem)((InstructionWithReference) instruction).getReferencedItem();
|
(MethodIdItem)((InstructionWithReference) instruction).getReferencedItem();
|
||||||
|
|
||||||
@ -380,13 +317,14 @@ public class MethodDefinition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
currentCodeAddress += instruction.getSize(currentCodeAddress);
|
currentCodeAddress += instruction.getCodeUnits();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
|
//TODO: uncomment
|
||||||
|
/*private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
|
||||||
methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex, baksmali.inlineResolver);
|
methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex, baksmali.inlineResolver);
|
||||||
|
|
||||||
methodAnalyzer.analyze();
|
methodAnalyzer.analyze();
|
||||||
@ -455,20 +393,21 @@ public class MethodDefinition {
|
|||||||
|
|
||||||
currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
|
currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private void addTries(List<MethodItem> methodItems) {
|
private void addTries(List<MethodItem> methodItems) {
|
||||||
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
|
List<? extends TryBlock> tryBlocks = methodImpl.getTryBlocks();
|
||||||
|
if (tryBlocks.size() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction[] instructions = encodedMethod.codeItem.getInstructions();
|
List<? extends Instruction> instructions = methodImpl.getInstructions();
|
||||||
int lastInstructionAddress = instructionMap.keyAt(instructionMap.size()-1);
|
int lastInstructionAddress = instructionOffsetMap.getInstructionCodeOffset(instructions.size() - 1);
|
||||||
int codeSize = lastInstructionAddress + instructions[instructions.length - 1].getSize(lastInstructionAddress);
|
int codeSize = lastInstructionAddress + instructions.get(instructions.size() - 1).getCodeUnits();
|
||||||
|
|
||||||
for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
|
for (TryBlock tryBlock: tryBlocks) {
|
||||||
int startAddress = tryItem.getStartCodeAddress();
|
int startAddress = tryBlock.getStartCodeOffset();
|
||||||
int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength();
|
int endAddress = startAddress + tryBlock.getCodeUnitCount();
|
||||||
|
|
||||||
if (startAddress >= codeSize) {
|
if (startAddress >= codeSize) {
|
||||||
throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.",
|
throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.",
|
||||||
@ -487,142 +426,28 @@ public class MethodDefinition {
|
|||||||
* the address for that instruction
|
* the address for that instruction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int lastCoveredIndex = instructionMap.getClosestSmaller(endAddress-1);
|
int lastCoveredIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(endAddress - 1, false);
|
||||||
int lastCoveredAddress = instructionMap.keyAt(lastCoveredIndex);
|
int lastCoveredAddress = instructionOffsetMap.getInstructionCodeOffset(lastCoveredIndex);
|
||||||
|
|
||||||
//add the catch all handler if it exists
|
for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
|
||||||
int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
|
int handlerOffset = handler.getHandlerCodeOffset();
|
||||||
if (catchAllAddress != -1) {
|
if (handlerOffset >= codeSize) {
|
||||||
if (catchAllAddress >= codeSize) {
|
throw new ExceptionWithContext(
|
||||||
throw new RuntimeException(String.format(
|
"Exception handler offset %d is past the end of the code block.", handlerOffset);
|
||||||
"Catch all handler offset %d is past the end of the code block.", catchAllAddress));
|
|
||||||
}
|
|
||||||
|
|
||||||
CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress, null,
|
|
||||||
startAddress, endAddress, catchAllAddress);
|
|
||||||
methodItems.add(catchAllMethodItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
//add the rest of the handlers
|
|
||||||
for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
|
|
||||||
if (handler.getHandlerAddress() >= codeSize) {
|
|
||||||
throw new RuntimeException(String.format(
|
|
||||||
"Exception handler offset %d is past the end of the code block.", catchAllAddress));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//use the address from the last covered instruction
|
//use the address from the last covered instruction
|
||||||
CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress,
|
CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress,
|
||||||
handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
|
handler.getExceptionType(), startAddress, endAddress, handlerOffset);
|
||||||
methodItems.add(catchMethodItem);
|
methodItems.add(catchMethodItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDebugInfo(final List<MethodItem> methodItems) {
|
private void addDebugInfo(final List<MethodItem> methodItems) {
|
||||||
if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) {
|
for (DebugItem debugItem: methodImpl.getDebugItems()) {
|
||||||
return;
|
methodItems.add(DebugMethodItem.build(registerFormatter, debugItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
final CodeItem codeItem = encodedMethod.codeItem;
|
|
||||||
DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
|
|
||||||
|
|
||||||
DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
|
|
||||||
new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
|
|
||||||
@Override
|
|
||||||
public void ProcessStartLocal(final int codeAddress, final int length, final int registerNum,
|
|
||||||
final StringIdItem name, final TypeIdItem type) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeStartLocal(writer, codeItem, registerNum, name, type, null);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessStartLocalExtended(final int codeAddress, final int length,
|
|
||||||
final int registerNum, final StringIdItem name,
|
|
||||||
final TypeIdItem type, final StringIdItem signature) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeStartLocal(writer, codeItem, registerNum, name, type, signature);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessEndLocal(final int codeAddress, final int length, final int registerNum,
|
|
||||||
final StringIdItem name, final TypeIdItem type,
|
|
||||||
final StringIdItem signature) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeEndLocal(writer, codeItem, registerNum, name, type, signature);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessRestartLocal(final int codeAddress, final int length, final int registerNum,
|
|
||||||
final StringIdItem name, final TypeIdItem type,
|
|
||||||
final StringIdItem signature) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -1) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeRestartLocal(writer, codeItem, registerNum, name, type, signature);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessSetPrologueEnd(int codeAddress) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -4) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeEndPrologue(writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessSetEpilogueBegin(int codeAddress) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -4) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeBeginEpilogue(writer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -3) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeSetFile(writer, name.getStringValue());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ProcessLineEmit(int codeAddress, final int line) {
|
|
||||||
methodItems.add(new DebugMethodItem(codeAddress, -2) {
|
|
||||||
@Override
|
|
||||||
public boolean writeTo(IndentingWriter writer) throws IOException {
|
|
||||||
writeLine(writer, line);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLabelSequentialNumbers() {
|
private void setLabelSequentialNumbers() {
|
||||||
|
@ -39,7 +39,8 @@ import org.jf.dexlib.Code.Analysis.RegisterType;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
//TODO: uncomment
|
||||||
|
/*public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
||||||
private final AnalyzedInstruction analyzedInstruction;
|
private final AnalyzedInstruction analyzedInstruction;
|
||||||
private final MethodAnalyzer methodAnalyzer;
|
private final MethodAnalyzer methodAnalyzer;
|
||||||
|
|
||||||
@ -108,4 +109,4 @@ public class PostInstructionRegisterInfoMethodItem extends MethodItem {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
@ -41,7 +41,8 @@ import org.jf.dexlib.Util.AccessFlags;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
//TODO: uncomment
|
||||||
|
/*public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
||||||
private final AnalyzedInstruction analyzedInstruction;
|
private final AnalyzedInstruction analyzedInstruction;
|
||||||
private final MethodAnalyzer methodAnalyzer;
|
private final MethodAnalyzer methodAnalyzer;
|
||||||
|
|
||||||
@ -262,4 +263,4 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
@ -35,45 +35,9 @@ import org.jf.util.StringUtils;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ReferenceFormatter {
|
public class ReferenceFormatter {
|
||||||
public static void writeReference(IndentingWriter writer, Item item) throws IOException {
|
|
||||||
switch (item.getItemType()) {
|
|
||||||
case TYPE_METHOD_ID_ITEM:
|
|
||||||
writeMethodReference(writer, (MethodIdItem)item);
|
|
||||||
return;
|
|
||||||
case TYPE_FIELD_ID_ITEM:
|
|
||||||
writeFieldReference(writer, (FieldIdItem)item);
|
|
||||||
return;
|
|
||||||
case TYPE_STRING_ID_ITEM:
|
|
||||||
writeStringReference(writer, ((StringIdItem)item).getStringValue());
|
|
||||||
return;
|
|
||||||
case TYPE_TYPE_ID_ITEM:
|
|
||||||
writeTypeReference(writer, (TypeIdItem)item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeMethodReference(IndentingWriter writer, MethodIdItem item) throws IOException {
|
|
||||||
writer.write(item.getContainingClass().getTypeDescriptor());
|
|
||||||
writer.write("->");
|
|
||||||
writer.write(item.getMethodName().getStringValue());
|
|
||||||
writer.write(item.getPrototype().getPrototypeString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeFieldReference(IndentingWriter writer, FieldIdItem item) throws IOException {
|
|
||||||
writer.write(item.getContainingClass().getTypeDescriptor());
|
|
||||||
writer.write("->");
|
|
||||||
writer.write(item.getFieldName().getStringValue());
|
|
||||||
writer.write(':');
|
|
||||||
writer.write(item.getFieldType().getTypeDescriptor());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeStringReference(IndentingWriter writer, String item) throws IOException {
|
public static void writeStringReference(IndentingWriter writer, String item) throws IOException {
|
||||||
writer.write('"');
|
writer.write('"');
|
||||||
StringUtils.writeEscapedString(writer, item);
|
StringUtils.writeEscapedString(writer, item);
|
||||||
writer.write('"');
|
writer.write('"');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeTypeReference(IndentingWriter writer, TypeIdItem item) throws IOException {
|
|
||||||
writer.write(item.getTypeDescriptor());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
package org.jf.baksmali.Adaptors;
|
package org.jf.baksmali.Adaptors;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import org.jf.util.IndentingWriter;
|
import org.jf.util.IndentingWriter;
|
||||||
import org.jf.baksmali.baksmali;
|
import org.jf.baksmali.baksmali;
|
||||||
import org.jf.dexlib.CodeItem;
|
import org.jf.dexlib.CodeItem;
|
||||||
@ -39,6 +40,13 @@ import java.io.IOException;
|
|||||||
* This class contains the logic used for formatting registers
|
* This class contains the logic used for formatting registers
|
||||||
*/
|
*/
|
||||||
public class RegisterFormatter {
|
public class RegisterFormatter {
|
||||||
|
public final int registerCount;
|
||||||
|
public final int parameterRegisterCount;
|
||||||
|
|
||||||
|
public RegisterFormatter(int registerCount, int parameterRegisterCount) {
|
||||||
|
this.registerCount = registerCount;
|
||||||
|
this.parameterRegisterCount = parameterRegisterCount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write out the register range value used by Format3rc. If baksmali.noParameterRegisters is true, it will always
|
* Write out the register range value used by Format3rc. If baksmali.noParameterRegisters is true, it will always
|
||||||
@ -46,19 +54,11 @@ public class RegisterFormatter {
|
|||||||
* registers, and if so, use the p<n> format for both. If only the last register is a parameter register, it will
|
* registers, and if so, use the p<n> format for both. If only the last register is a parameter register, it will
|
||||||
* use the v<n> format for both, otherwise it would be confusing to have something like {v20 .. p1}
|
* use the v<n> format for both, otherwise it would be confusing to have something like {v20 .. p1}
|
||||||
* @param writer the <code>IndentingWriter</code> to write to
|
* @param writer the <code>IndentingWriter</code> to write to
|
||||||
* @param codeItem the <code>CodeItem</code> that the register is from
|
|
||||||
* @param startRegister the first register in the range
|
* @param startRegister the first register in the range
|
||||||
* @param lastRegister the last register in the range
|
* @param lastRegister the last register in the range
|
||||||
*/
|
*/
|
||||||
public static void writeRegisterRange(IndentingWriter writer, CodeItem codeItem, int startRegister,
|
public void writeRegisterRange(IndentingWriter writer, int startRegister, int lastRegister) throws IOException {
|
||||||
int lastRegister) throws IOException {
|
|
||||||
assert lastRegister >= startRegister;
|
|
||||||
|
|
||||||
if (!baksmali.noParameterRegisters) {
|
if (!baksmali.noParameterRegisters) {
|
||||||
int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount()
|
|
||||||
+ (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue())==0)?1:0);
|
|
||||||
int registerCount = codeItem.getRegisterCount();
|
|
||||||
|
|
||||||
assert startRegister <= lastRegister;
|
assert startRegister <= lastRegister;
|
||||||
|
|
||||||
if (startRegister >= registerCount - parameterRegisterCount) {
|
if (startRegister >= registerCount - parameterRegisterCount) {
|
||||||
@ -83,14 +83,10 @@ public class RegisterFormatter {
|
|||||||
* and if so, formats it in the p<n> format instead.
|
* and if so, formats it in the p<n> format instead.
|
||||||
*
|
*
|
||||||
* @param writer the <code>IndentingWriter</code> to write to
|
* @param writer the <code>IndentingWriter</code> to write to
|
||||||
* @param codeItem the <code>CodeItem</code> that the register is from
|
|
||||||
* @param register the register number
|
* @param register the register number
|
||||||
*/
|
*/
|
||||||
public static void writeTo(IndentingWriter writer, CodeItem codeItem, int register) throws IOException {
|
public void writeTo(IndentingWriter writer, int register) throws IOException {
|
||||||
if (!baksmali.noParameterRegisters) {
|
if (!baksmali.noParameterRegisters) {
|
||||||
int parameterRegisterCount = codeItem.getParent().method.getPrototype().getParameterRegisterCount()
|
|
||||||
+ (((codeItem.getParent().accessFlags & AccessFlags.STATIC.getValue())==0)?1:0);
|
|
||||||
int registerCount = codeItem.getRegisterCount();
|
|
||||||
if (register >= registerCount - parameterRegisterCount) {
|
if (register >= registerCount - parameterRegisterCount) {
|
||||||
writer.write('p');
|
writer.write('p');
|
||||||
writer.printSignedIntAsDec((register - (registerCount - parameterRegisterCount)));
|
writer.printSignedIntAsDec((register - (registerCount - parameterRegisterCount)));
|
||||||
|
@ -34,7 +34,8 @@ import org.jf.util.IndentingWriter;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class SyntheticAccessCommentMethodItem extends MethodItem {
|
//TODO: uncomment
|
||||||
|
/*public class SyntheticAccessCommentMethodItem extends MethodItem {
|
||||||
private final AccessedMember accessedMember;
|
private final AccessedMember accessedMember;
|
||||||
|
|
||||||
public SyntheticAccessCommentMethodItem(AccessedMember accessedMember, int codeAddress) {
|
public SyntheticAccessCommentMethodItem(AccessedMember accessedMember, int codeAddress) {
|
||||||
@ -59,4 +60,4 @@ public class SyntheticAccessCommentMethodItem extends MethodItem {
|
|||||||
ReferenceFormatter.writeReference(writer, accessedMember.accessedMember);
|
ReferenceFormatter.writeReference(writer, accessedMember.accessedMember);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
53
dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
Normal file
53
dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.dexlib2.util;
|
||||||
|
|
||||||
|
import org.jf.dexlib2.iface.ClassDef;
|
||||||
|
import org.jf.dexlib2.iface.Method;
|
||||||
|
import org.jf.dexlib2.iface.MethodParameter;
|
||||||
|
|
||||||
|
public class MethodUtil {
|
||||||
|
public static String buildFullMethodString(ClassDef classDef, Method method) {
|
||||||
|
//TODO: consider using a cached thread-local StringBuilder
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(classDef.getName());
|
||||||
|
sb.append("->");
|
||||||
|
sb.append(method.getName());
|
||||||
|
sb.append("(");
|
||||||
|
for (MethodParameter methodParameter: method.getParameters()) {
|
||||||
|
sb.append(methodParameter.getType());
|
||||||
|
}
|
||||||
|
sb.append(")");
|
||||||
|
sb.append(method.getReturnType());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
39
dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
Normal file
39
dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012, Google Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following disclaimer
|
||||||
|
* in the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
* * Neither the name of Google Inc. nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jf.dexlib2.util;
|
||||||
|
|
||||||
|
public class TypeUtils {
|
||||||
|
public static boolean isWideType(String type) {
|
||||||
|
char c = type.charAt(0);
|
||||||
|
return c == 'J' || c == 'D';
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user