mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2026-04-19 09:07:06 -04:00
Skeleton part of v3: remove AWT, PojavDX
This commit is contained in:
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "net.kdt.pojavlaunch"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 26
|
||||
versionCode 156235
|
||||
versionName "2.5.0_preview1_6397b_20200417"
|
||||
versionCode 1
|
||||
versionName "3.0.0_preview1_1b_20200426"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -21,18 +21,12 @@ android {
|
||||
debuggable = true
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the following configuration in order to might make Minecraft 1.12 support.
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
implementation 'commons-codec:commons-codec:1.14'
|
||||
implementation 'com.wu-man:android-bsf-api:3.1.3'
|
||||
// implementation 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
// implementation 'commons-codec:commons-codec:1.14'
|
||||
// implementation 'com.wu-man:android-bsf-api:3.1.3'
|
||||
implementation 'com.android.support:support-v4:26.0.0'
|
||||
implementation 'com.android.support:preference-v7:26.0.0'
|
||||
implementation 'com.android.support:appcompat-v7:26.0.0'
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.graphics;
|
||||
import java.lang.reflect.*;
|
||||
/**
|
||||
* PixelXorXfermode implements a simple pixel xor (op ^ src ^ dst).
|
||||
* This transformation does not follow premultiplied conventions, therefore
|
||||
* this mode *always* returns an opaque color (alpha == 255). Thus it is
|
||||
* not really usefull for operating on blended colors.
|
||||
*/
|
||||
@Deprecated
|
||||
public class PixelXorXfermode extends Xfermode {
|
||||
public PixelXorXfermode(int opColor) {
|
||||
try {
|
||||
Field field_nativeInstance = getClass().getDeclaredField("native_instance");
|
||||
field_nativeInstance.setAccessible(true);
|
||||
field_nativeInstance.set(null, nativeCreate(opColor));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private static native int nativeCreate(int opColor);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import com.android.internal.awt.AndroidGraphics2D;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.VolatileImage;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
||||
public class AndroidGraphicsConfiguration extends GraphicsConfiguration {
|
||||
|
||||
@Override
|
||||
public BufferedImage createCompatibleImage(int width, int height) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage createCompatibleImage(int width, int height,
|
||||
int transparency) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VolatileImage createCompatibleVolatileImage(int width, int height) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VolatileImage createCompatibleVolatileImage(int width, int height,
|
||||
int transparency) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
Canvas c = AndroidGraphics2D.getAndroidCanvas();
|
||||
if(c != null)
|
||||
return new Rectangle(0, 0, c.getWidth(), c.getHeight());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorModel getColorModel() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorModel getColorModel(int transparency) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AffineTransform getDefaultTransform() {
|
||||
return new AffineTransform();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsDevice getDevice() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AffineTransform getNormalizingTransform() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.peer.FontPeer;
|
||||
|
||||
import org.apache.harmony.awt.gl.MultiRectArea;
|
||||
import org.apache.harmony.awt.gl.font.AndroidFont;
|
||||
import org.apache.harmony.awt.gl.font.FontManager;
|
||||
import org.apache.harmony.awt.gl.font.FontMetricsImpl;
|
||||
import org.apache.harmony.awt.gl.font.AndroidFontManager;
|
||||
import org.apache.harmony.awt.wtk.NativeWindow;
|
||||
import org.apache.harmony.awt.wtk.WindowFactory;
|
||||
import org.apache.harmony.awt.gl.CommonGraphics2DFactory;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.content.Context;
|
||||
|
||||
public class AndroidGraphicsFactory extends CommonGraphics2DFactory {
|
||||
|
||||
public GraphicsEnvironment createGraphicsEnvironment(WindowFactory wf) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Font embedFont(String fontFilePath) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public FontManager getFontManager() {
|
||||
return AndroidFontManager.inst;
|
||||
}
|
||||
|
||||
public FontMetrics getFontMetrics(Font font) {
|
||||
return new FontMetricsImpl(font);
|
||||
}
|
||||
|
||||
public FontPeer getFontPeer(Font font) {
|
||||
//return getFontManager().getFontPeer(font.getName(), font.getStyle(), font.getSize());
|
||||
return new AndroidFont(font.getName(), font.getStyle(), font.getSize());
|
||||
}
|
||||
|
||||
public Graphics2D getGraphics2D(NativeWindow win, int translateX,
|
||||
int translateY, MultiRectArea clip) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Graphics2D getGraphics2D(NativeWindow win, int translateX,
|
||||
int translateY, int width, int height) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Graphics2D getGraphics2D(Context ctx, Canvas c, Paint p) {
|
||||
return AndroidGraphics2D.getInstance(ctx, c, p);
|
||||
}
|
||||
|
||||
public Graphics2D getGraphics2D(Canvas c, Paint p) {
|
||||
throw new RuntimeException("Not supported!");
|
||||
}
|
||||
|
||||
public Graphics2D getGraphics2D() {
|
||||
return AndroidGraphics2D.getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.internal.awt;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import java.awt.Transparency;
|
||||
import java.awt.color.ColorSpace;
|
||||
//import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.ComponentColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.ImageConsumer;
|
||||
import java.awt.image.IndexColorModel;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.harmony.awt.gl.image.DecodingImageSource;
|
||||
import org.apache.harmony.awt.gl.image.ImageDecoder;
|
||||
import org.apache.harmony.awt.internal.nls.Messages;
|
||||
|
||||
public class AndroidImageDecoder extends ImageDecoder {
|
||||
|
||||
private static final int hintflags =
|
||||
ImageConsumer.SINGLEFRAME | // PNG is a static image
|
||||
ImageConsumer.TOPDOWNLEFTRIGHT | // This order is only one possible
|
||||
ImageConsumer.COMPLETESCANLINES; // Don't deliver incomplete scanlines
|
||||
|
||||
// Each pixel is a grayscale sample.
|
||||
private static final int PNG_COLOR_TYPE_GRAY = 0;
|
||||
// Each pixel is an R,G,B triple.
|
||||
private static final int PNG_COLOR_TYPE_RGB = 2;
|
||||
// Each pixel is a palette index, a PLTE chunk must appear.
|
||||
private static final int PNG_COLOR_TYPE_PLTE = 3;
|
||||
// Each pixel is a grayscale sample, followed by an alpha sample.
|
||||
private static final int PNG_COLOR_TYPE_GRAY_ALPHA = 4;
|
||||
// Each pixel is an R,G,B triple, followed by an alpha sample.
|
||||
private static final int PNG_COLOR_TYPE_RGBA = 6;
|
||||
|
||||
private static final int NB_OF_LINES_PER_CHUNK = 1; // 0 = full image
|
||||
|
||||
Bitmap bm; // The image as decoded by Android
|
||||
|
||||
// Header information
|
||||
int imageWidth; // Image size
|
||||
int imageHeight;
|
||||
int colorType; // One of the PNG_ constants from above
|
||||
int bitDepth; // Number of bits per color
|
||||
byte cmap[]; // The color palette for index color models
|
||||
ColorModel model; // The corresponding AWT color model
|
||||
|
||||
boolean transferInts; // Is transfer of type int or byte?
|
||||
int dataElementsPerPixel;
|
||||
|
||||
// Buffers for decoded image data
|
||||
byte byteOut[];
|
||||
int intOut[];
|
||||
|
||||
|
||||
public AndroidImageDecoder(DecodingImageSource src, InputStream is) {
|
||||
super(src, is);
|
||||
dataElementsPerPixel = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* All the decoding is done in Android
|
||||
*
|
||||
* AWT???: Method returns only once the image is completly
|
||||
* decoded; decoding is not done asynchronously
|
||||
*/
|
||||
public void decodeImage() throws IOException {
|
||||
try {
|
||||
bm = BitmapFactory.decodeStream(inputStream);
|
||||
if (bm == null) {
|
||||
throw new IOException("Input stream empty and no image cached");
|
||||
}
|
||||
|
||||
// Check size
|
||||
imageWidth = bm.getWidth();
|
||||
imageHeight = bm.getHeight();
|
||||
if (imageWidth < 0 || imageHeight < 0 ) {
|
||||
throw new RuntimeException("Illegal image size: "
|
||||
+ imageWidth + ", " + imageHeight);
|
||||
}
|
||||
|
||||
// We got the image fully decoded; now send all image data to AWT
|
||||
setDimensions(imageWidth, imageHeight);
|
||||
model = createColorModel();
|
||||
setColorModel(model);
|
||||
setHints(hintflags);
|
||||
setProperties(new Hashtable<Object, Object>()); // Empty
|
||||
sendPixels(NB_OF_LINES_PER_CHUNK != 0 ? NB_OF_LINES_PER_CHUNK : imageHeight);
|
||||
imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
imageComplete(ImageConsumer.IMAGEERROR);
|
||||
throw e;
|
||||
} finally {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the AWT color model
|
||||
*
|
||||
* ???AWT: Android Bitmaps are always of type: ARGB-8888-Direct color model
|
||||
*
|
||||
* However, we leave the code here for a more powerfull decoder
|
||||
* that returns a native model, and the conversion is then handled
|
||||
* in AWT. With such a decoder, we would need to get the colorType,
|
||||
* the bitDepth, (and the color palette for an index color model)
|
||||
* from the image and construct the correct color model here.
|
||||
*/
|
||||
private ColorModel createColorModel() {
|
||||
ColorModel cm = null;
|
||||
int bmModel = 5; // TODO This doesn't exist: bm.getColorModel();
|
||||
cmap = null;
|
||||
|
||||
switch (bmModel) {
|
||||
// A1_MODEL
|
||||
case 1:
|
||||
colorType = PNG_COLOR_TYPE_GRAY;
|
||||
bitDepth = 1;
|
||||
break;
|
||||
|
||||
// A8_MODEL
|
||||
case 2:
|
||||
colorType = PNG_COLOR_TYPE_GRAY_ALPHA;
|
||||
bitDepth = 8;
|
||||
break;
|
||||
|
||||
// INDEX8_MODEL
|
||||
// RGB_565_MODEL
|
||||
// ARGB_8888_MODEL
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
colorType = bm.hasAlpha() ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB;
|
||||
bitDepth = 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
switch (colorType) {
|
||||
|
||||
case PNG_COLOR_TYPE_GRAY: {
|
||||
if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) {
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
// Create gray color model
|
||||
int numEntries = 1 << bitDepth;
|
||||
int scaleFactor = 255 / (numEntries-1);
|
||||
byte comps[] = new byte[numEntries];
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
comps[i] = (byte) (i * scaleFactor);
|
||||
}
|
||||
cm = new IndexColorModel(bitDepth, numEntries, comps, comps, comps);
|
||||
|
||||
transferInts = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNG_COLOR_TYPE_RGB: {
|
||||
if (bitDepth != 8) {
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
cm = new DirectColorModel(24, 0xff0000, 0xFF00, 0xFF);
|
||||
|
||||
transferInts = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNG_COLOR_TYPE_PLTE: {
|
||||
if (bitDepth != 8 && bitDepth != 4 && bitDepth != 2 && bitDepth != 1) {
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
if (cmap == null) {
|
||||
throw new IllegalStateException("Palette color type is not supported");
|
||||
}
|
||||
|
||||
cm = new IndexColorModel(bitDepth, cmap.length / 3, cmap, 0, false);
|
||||
|
||||
transferInts = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNG_COLOR_TYPE_GRAY_ALPHA: {
|
||||
if (bitDepth != 8) {
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
|
||||
true, false,
|
||||
Transparency.TRANSLUCENT,
|
||||
DataBuffer.TYPE_BYTE);
|
||||
|
||||
transferInts = false;
|
||||
dataElementsPerPixel = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNG_COLOR_TYPE_RGBA: {
|
||||
if (bitDepth != 8) {
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
cm = ColorModel.getRGBdefault();
|
||||
|
||||
transferInts = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// awt.3C=Unknown PNG color type
|
||||
throw new IllegalArgumentException(Messages.getString("awt.3C")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
return cm;
|
||||
}
|
||||
|
||||
private void sendPixels(int nbOfLinesPerChunk) {
|
||||
int w = imageWidth;
|
||||
int h = imageHeight;
|
||||
int n = 1;
|
||||
if (nbOfLinesPerChunk > 0 && nbOfLinesPerChunk <= h) {
|
||||
n = nbOfLinesPerChunk;
|
||||
}
|
||||
|
||||
if (transferInts) {
|
||||
// Create output buffer
|
||||
intOut = new int[w * n];
|
||||
for (int yi = 0; yi < h; yi += n) {
|
||||
// Last chunk might contain less liness
|
||||
if (n > 1 && h - yi < n ) {
|
||||
n = h - yi;
|
||||
}
|
||||
bm.getPixels(intOut, 0, w, 0, yi, w, n);
|
||||
setPixels(0, yi, w, n, model, intOut, 0, w);
|
||||
}
|
||||
} else {
|
||||
// Android bitmaps always store ints (ARGB-8888 direct model)
|
||||
throw new RuntimeException("Byte transfer not supported");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,536 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import org.apache.harmony.awt.gl.MultiRectArea;
|
||||
import org.apache.harmony.awt.gl.Surface;
|
||||
import org.apache.harmony.awt.gl.XORComposite;
|
||||
import org.apache.harmony.awt.gl.render.Blitter;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
public class AndroidJavaBlitter implements Blitter {
|
||||
|
||||
private Canvas canvas;
|
||||
private Paint paint;
|
||||
private int colorCache;
|
||||
|
||||
public AndroidJavaBlitter(Canvas c) {
|
||||
this.canvas = c;
|
||||
this.paint = new Paint();
|
||||
this.paint.setStrokeWidth(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instead of multiplication and division we are using values from
|
||||
* Lookup tables.
|
||||
*/
|
||||
static byte mulLUT[][]; // Lookup table for multiplication
|
||||
static byte divLUT[][]; // Lookup table for division
|
||||
|
||||
static{
|
||||
mulLUT = new byte[256][256];
|
||||
for(int i = 0; i < 256; i++){
|
||||
for(int j = 0; j < 256; j++){
|
||||
mulLUT[i][j] = (byte)((float)(i * j)/255 + 0.5f);
|
||||
}
|
||||
}
|
||||
divLUT = new byte[256][256];
|
||||
for(int i = 1; i < 256; i++){
|
||||
for(int j = 0; j < i; j++){
|
||||
divLUT[i][j] = (byte)(((float)j / 255) / ((float)i/ 255) * 255 + 0.5f);
|
||||
}
|
||||
for(int j = i; j < 256; j++){
|
||||
divLUT[i][j] = (byte)255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final static int AlphaCompositeMode = 1;
|
||||
final static int XORMode = 2;
|
||||
|
||||
public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
|
||||
Surface dstSurf, int width, int height, AffineTransform sysxform,
|
||||
AffineTransform xform, Composite comp, Color bgcolor,
|
||||
MultiRectArea clip) {
|
||||
|
||||
if(xform == null){
|
||||
blit(srcX, srcY, srcSurf, dstX, dstY, dstSurf, width, height,
|
||||
sysxform, comp, bgcolor, clip);
|
||||
}else{
|
||||
double scaleX = xform.getScaleX();
|
||||
double scaleY = xform.getScaleY();
|
||||
double scaledX = dstX / scaleX;
|
||||
double scaledY = dstY / scaleY;
|
||||
AffineTransform at = new AffineTransform();
|
||||
at.setToTranslation(scaledX, scaledY);
|
||||
xform.concatenate(at);
|
||||
sysxform.concatenate(xform);
|
||||
blit(srcX, srcY, srcSurf, 0, 0, dstSurf, width, height,
|
||||
sysxform, comp, bgcolor, clip);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
|
||||
Surface dstSurf, int width, int height, AffineTransform sysxform,
|
||||
Composite comp, Color bgcolor, MultiRectArea clip) {
|
||||
|
||||
if(sysxform == null) {
|
||||
sysxform = new AffineTransform();
|
||||
}
|
||||
int type = sysxform.getType();
|
||||
switch(type){
|
||||
case AffineTransform.TYPE_TRANSLATION:
|
||||
dstX += sysxform.getTranslateX();
|
||||
dstY += sysxform.getTranslateY();
|
||||
case AffineTransform.TYPE_IDENTITY:
|
||||
simpleBlit(srcX, srcY, srcSurf, dstX, dstY, dstSurf,
|
||||
width, height, comp, bgcolor, clip);
|
||||
break;
|
||||
default:
|
||||
int srcW = srcSurf.getWidth();
|
||||
int srcH = srcSurf.getHeight();
|
||||
|
||||
int w = srcX + width < srcW ? width : srcW - srcX;
|
||||
int h = srcY + height < srcH ? height : srcH - srcY;
|
||||
|
||||
ColorModel srcCM = srcSurf.getColorModel();
|
||||
Raster srcR = srcSurf.getRaster().createChild(srcX, srcY,
|
||||
w, h, 0, 0, null);
|
||||
|
||||
ColorModel dstCM = dstSurf.getColorModel();
|
||||
WritableRaster dstR = dstSurf.getRaster();
|
||||
|
||||
transformedBlit(srcCM, srcR, 0, 0, dstCM, dstR, dstX, dstY, w, h,
|
||||
sysxform, comp, bgcolor, clip);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void simpleBlit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
|
||||
Surface dstSurf, int width, int height, Composite comp,
|
||||
Color bgcolor, MultiRectArea clip) {
|
||||
|
||||
// TODO It's possible, though unlikely that we might encounter non-int[]
|
||||
// data buffers. In this case the following code needs to have several
|
||||
// branches that take this into account.
|
||||
data = (DataBufferInt)srcSurf.getRaster().getDataBuffer();
|
||||
int[] pixels = data.getData();
|
||||
if (!srcSurf.getColorModel().hasAlpha()) {
|
||||
// This wouldn't be necessary if Android supported RGB_888.
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
pixels[i] = pixels[i] | 0xff000000;
|
||||
}
|
||||
}
|
||||
bmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
|
||||
canvas.drawBitmap(bmap, dstX, dstY, paint);
|
||||
}
|
||||
|
||||
public void blit(int srcX, int srcY, Surface srcSurf, int dstX, int dstY,
|
||||
Surface dstSurf, int width, int height, Composite comp,
|
||||
Color bgcolor, MultiRectArea clip) {
|
||||
|
||||
javaBlt(srcX, srcY, srcSurf.getWidth(), srcSurf.getHeight(),
|
||||
srcSurf.getColorModel(), srcSurf.getRaster(), dstX, dstY,
|
||||
dstSurf.getWidth(), dstSurf.getHeight(),
|
||||
dstSurf.getColorModel(), dstSurf.getRaster(),
|
||||
width, height, comp, bgcolor, clip);
|
||||
|
||||
}
|
||||
|
||||
public void javaBlt(int srcX, int srcY, int srcW, int srcH,
|
||||
ColorModel srcCM, Raster srcRast, int dstX, int dstY,
|
||||
int dstW, int dstH, ColorModel dstCM, WritableRaster dstRast,
|
||||
int width, int height, Composite comp, Color bgcolor,
|
||||
MultiRectArea clip){
|
||||
|
||||
int srcX2 = srcW - 1;
|
||||
int srcY2 = srcH - 1;
|
||||
int dstX2 = dstW - 1;
|
||||
int dstY2 = dstH - 1;
|
||||
|
||||
if(srcX < 0){
|
||||
width += srcX;
|
||||
srcX = 0;
|
||||
}
|
||||
if(srcY < 0){
|
||||
height += srcY;
|
||||
srcY = 0;
|
||||
}
|
||||
|
||||
if(dstX < 0){
|
||||
width += dstX;
|
||||
srcX -= dstX;
|
||||
dstX = 0;
|
||||
}
|
||||
if(dstY < 0){
|
||||
height += dstY;
|
||||
srcY -= dstY;
|
||||
dstY = 0;
|
||||
}
|
||||
|
||||
if(srcX > srcX2 || srcY > srcY2) {
|
||||
return;
|
||||
}
|
||||
if(dstX > dstX2 || dstY > dstY2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(srcX + width > srcX2) {
|
||||
width = srcX2 - srcX + 1;
|
||||
}
|
||||
if(srcY + height > srcY2) {
|
||||
height = srcY2 - srcY + 1;
|
||||
}
|
||||
if(dstX + width > dstX2) {
|
||||
width = dstX2 - dstX + 1;
|
||||
}
|
||||
if(dstY + height > dstY2) {
|
||||
height = dstY2 - dstY + 1;
|
||||
}
|
||||
|
||||
if(width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int clipRects[];
|
||||
if(clip != null) {
|
||||
clipRects = clip.rect;
|
||||
} else {
|
||||
clipRects = new int[]{5, 0, 0, dstW - 1, dstH - 1};
|
||||
}
|
||||
|
||||
boolean isAlphaComp = false;
|
||||
int rule = 0;
|
||||
float alpha = 0;
|
||||
boolean isXORComp = false;
|
||||
Color xorcolor = null;
|
||||
CompositeContext cont = null;
|
||||
|
||||
if(comp instanceof AlphaComposite){
|
||||
isAlphaComp = true;
|
||||
AlphaComposite ac = (AlphaComposite) comp;
|
||||
rule = ac.getRule();
|
||||
alpha = ac.getAlpha();
|
||||
}else if(comp instanceof XORComposite){
|
||||
isXORComp = true;
|
||||
XORComposite xcomp = (XORComposite) comp;
|
||||
xorcolor = xcomp.getXORColor();
|
||||
}else{
|
||||
cont = comp.createContext(srcCM, dstCM, null);
|
||||
}
|
||||
|
||||
for(int i = 1; i < clipRects[0]; i += 4){
|
||||
int _sx = srcX;
|
||||
int _sy = srcY;
|
||||
|
||||
int _dx = dstX;
|
||||
int _dy = dstY;
|
||||
|
||||
int _w = width;
|
||||
int _h = height;
|
||||
|
||||
int cx = clipRects[i]; // Clipping left top X
|
||||
int cy = clipRects[i + 1]; // Clipping left top Y
|
||||
int cx2 = clipRects[i + 2]; // Clipping right bottom X
|
||||
int cy2 = clipRects[i + 3]; // Clipping right bottom Y
|
||||
|
||||
if(_dx > cx2 || _dy > cy2 || dstX2 < cx || dstY2 < cy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(cx > _dx){
|
||||
int shx = cx - _dx;
|
||||
_w -= shx;
|
||||
_dx = cx;
|
||||
_sx += shx;
|
||||
}
|
||||
|
||||
if(cy > _dy){
|
||||
int shy = cy - _dy;
|
||||
_h -= shy;
|
||||
_dy = cy;
|
||||
_sy += shy;
|
||||
}
|
||||
|
||||
if(_dx + _w > cx2 + 1){
|
||||
_w = cx2 - _dx + 1;
|
||||
}
|
||||
|
||||
if(_dy + _h > cy2 + 1){
|
||||
_h = cy2 - _dy + 1;
|
||||
}
|
||||
|
||||
if(_sx > srcX2 || _sy > srcY2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isAlphaComp){
|
||||
alphaCompose(_sx, _sy, srcCM, srcRast, _dx, _dy,
|
||||
dstCM, dstRast, _w, _h, rule, alpha, bgcolor);
|
||||
}else if(isXORComp){
|
||||
xorCompose(_sx, _sy, srcCM, srcRast, _dx, _dy,
|
||||
dstCM, dstRast, _w, _h, xorcolor);
|
||||
}else{
|
||||
Raster sr = srcRast.createChild(_sx, _sy, _w, _h, 0, 0, null);
|
||||
WritableRaster dr = dstRast.createWritableChild(_dx, _dy,
|
||||
_w, _h, 0, 0, null);
|
||||
cont.compose(sr, dr, dr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DataBufferInt data;
|
||||
Bitmap bmap, bmp;
|
||||
|
||||
void alphaCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast,
|
||||
int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast,
|
||||
int width, int height, int rule, float alpha, Color bgcolor){
|
||||
|
||||
Object srcPixel = getTransferArray(srcRast, 1);
|
||||
data = (DataBufferInt)srcRast.getDataBuffer();
|
||||
int pix[] = data.getData();
|
||||
bmap = Bitmap.createBitmap(pix, width, height, Bitmap.Config.RGB_565);
|
||||
canvas.drawBitmap(bmap, dstX, dstY, paint);
|
||||
}
|
||||
|
||||
void render(int[] img, int x, int y, int width, int height) {
|
||||
canvas.drawBitmap(Bitmap.createBitmap(img, width, height, Bitmap.Config.ARGB_8888), x, y, paint);
|
||||
}
|
||||
|
||||
void xorCompose(int srcX, int srcY, ColorModel srcCM, Raster srcRast,
|
||||
int dstX, int dstY, ColorModel dstCM, WritableRaster dstRast,
|
||||
int width, int height, Color xorcolor){
|
||||
|
||||
data = (DataBufferInt)srcRast.getDataBuffer();
|
||||
int pix[] = data.getData();
|
||||
bmap = Bitmap.createBitmap(pix, width, height, Bitmap.Config.RGB_565);
|
||||
canvas.drawBitmap(bmap, dstX, dstY, paint);
|
||||
}
|
||||
|
||||
private void transformedBlit(ColorModel srcCM, Raster srcR, int srcX, int srcY,
|
||||
ColorModel dstCM, WritableRaster dstR, int dstX, int dstY,
|
||||
int width, int height, AffineTransform at, Composite comp,
|
||||
Color bgcolor, MultiRectArea clip) {
|
||||
|
||||
data = (DataBufferInt)srcR.getDataBuffer();
|
||||
int[] pixels = data.getData();
|
||||
if (!srcCM.hasAlpha()) {
|
||||
// This wouldn't be necessary if Android supported RGB_888.
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
pixels[i] = pixels[i] | 0xff000000;
|
||||
}
|
||||
}
|
||||
bmap = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
Matrix tm = new Matrix();
|
||||
tm.setConcat(canvas.getMatrix(), AndroidGraphics2D.createMatrixObj(at));
|
||||
if(at.getType() > 1) {
|
||||
bmp = Bitmap.createBitmap(bmap, 0, 0, width, height, tm, true);
|
||||
} else {
|
||||
bmp = Bitmap.createBitmap(bmap, 0, 0, width, height, tm, false);
|
||||
}
|
||||
canvas.drawBitmap(bmp, dstX + (float)at.getTranslateX(), dstY + (float)at.getTranslateY(), paint);
|
||||
}
|
||||
|
||||
private Rectangle2D getBounds2D(AffineTransform at, Rectangle r) {
|
||||
int x = r.x;
|
||||
int y = r.y;
|
||||
int width = r.width;
|
||||
int height = r.height;
|
||||
|
||||
float[] corners = {
|
||||
x, y,
|
||||
x + width, y,
|
||||
x + width, y + height,
|
||||
x, y + height
|
||||
};
|
||||
|
||||
at.transform(corners, 0, corners, 0, 4);
|
||||
|
||||
Rectangle2D.Float bounds = new Rectangle2D.Float(corners[0], corners[1], 0 , 0);
|
||||
bounds.add(corners[2], corners[3]);
|
||||
bounds.add(corners[4], corners[5]);
|
||||
bounds.add(corners[6], corners[7]);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private int compose(int srcRGB, boolean isSrcAlphaPre,
|
||||
int dstRGB, boolean dstHasAlpha, boolean isDstAlphaPre,
|
||||
int rule, int srcConstAlpha){
|
||||
|
||||
int sa, sr, sg, sb, da, dr, dg, db;
|
||||
|
||||
sa = (srcRGB >> 24) & 0xff;
|
||||
sr = (srcRGB >> 16) & 0xff;
|
||||
sg = (srcRGB >> 8) & 0xff;
|
||||
sb = srcRGB & 0xff;
|
||||
|
||||
if(isSrcAlphaPre){
|
||||
sa = mulLUT[srcConstAlpha][sa] & 0xff;
|
||||
sr = mulLUT[srcConstAlpha][sr] & 0xff;
|
||||
sg = mulLUT[srcConstAlpha][sg] & 0xff;
|
||||
sb = mulLUT[srcConstAlpha][sb] & 0xff;
|
||||
}else{
|
||||
sa = mulLUT[srcConstAlpha][sa] & 0xff;
|
||||
sr = mulLUT[sa][sr] & 0xff;
|
||||
sg = mulLUT[sa][sg] & 0xff;
|
||||
sb = mulLUT[sa][sb] & 0xff;
|
||||
}
|
||||
|
||||
da = (dstRGB >> 24) & 0xff;
|
||||
dr = (dstRGB >> 16) & 0xff;
|
||||
dg = (dstRGB >> 8) & 0xff;
|
||||
db = dstRGB & 0xff;
|
||||
|
||||
if(!isDstAlphaPre){
|
||||
dr = mulLUT[da][dr] & 0xff;
|
||||
dg = mulLUT[da][dg] & 0xff;
|
||||
db = mulLUT[da][db] & 0xff;
|
||||
}
|
||||
|
||||
int Fs = 0;
|
||||
int Fd = 0;
|
||||
switch(rule){
|
||||
case AlphaComposite.CLEAR:
|
||||
break;
|
||||
|
||||
case AlphaComposite.DST:
|
||||
Fd = 255;
|
||||
break;
|
||||
|
||||
case AlphaComposite.DST_ATOP:
|
||||
Fs = 255 - da;
|
||||
Fd = sa;
|
||||
break;
|
||||
|
||||
case AlphaComposite.DST_IN:
|
||||
Fd = sa;
|
||||
break;
|
||||
|
||||
case AlphaComposite.DST_OUT:
|
||||
Fd = 255 - sa;
|
||||
break;
|
||||
|
||||
case AlphaComposite.DST_OVER:
|
||||
Fs = 255 - da;
|
||||
Fd = 255;
|
||||
break;
|
||||
|
||||
case AlphaComposite.SRC:
|
||||
Fs = 255;
|
||||
break;
|
||||
|
||||
case AlphaComposite.SRC_ATOP:
|
||||
Fs = da;
|
||||
Fd = 255 - sa;
|
||||
break;
|
||||
|
||||
case AlphaComposite.SRC_IN:
|
||||
Fs = da;
|
||||
break;
|
||||
|
||||
case AlphaComposite.SRC_OUT:
|
||||
Fs = 255 - da;
|
||||
break;
|
||||
|
||||
case AlphaComposite.SRC_OVER:
|
||||
Fs = 255;
|
||||
Fd = 255 - sa;
|
||||
break;
|
||||
|
||||
case AlphaComposite.XOR:
|
||||
Fs = 255 - da;
|
||||
Fd = 255 - sa;
|
||||
break;
|
||||
}
|
||||
dr = (mulLUT[sr][Fs] & 0xff) + (mulLUT[dr][Fd] & 0xff);
|
||||
dg = (mulLUT[sg][Fs] & 0xff) + (mulLUT[dg][Fd] & 0xff);
|
||||
db = (mulLUT[sb][Fs] & 0xff) + (mulLUT[db][Fd] & 0xff);
|
||||
|
||||
da = (mulLUT[sa][Fs] & 0xff) + (mulLUT[da][Fd] & 0xff);
|
||||
|
||||
if(!isDstAlphaPre){
|
||||
if(da != 255){
|
||||
dr = divLUT[da][dr] & 0xff;
|
||||
dg = divLUT[da][dg] & 0xff;
|
||||
db = divLUT[da][db] & 0xff;
|
||||
}
|
||||
}
|
||||
if(!dstHasAlpha) {
|
||||
da = 0xff;
|
||||
}
|
||||
dstRGB = (da << 24) | (dr << 16) | (dg << 8) | db;
|
||||
|
||||
return dstRGB;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate an array that can be use to store the result for a
|
||||
* Raster.getDataElements call.
|
||||
* @param raster Raster (type) where the getDataElements call will be made.
|
||||
* @param nbPixels How many pixels to store in the array at most
|
||||
* @return the result array or null
|
||||
*/
|
||||
private Object getTransferArray(Raster raster, int nbPixels) {
|
||||
int transferType = raster.getTransferType();
|
||||
int nbDataElements = raster.getSampleModel().getNumDataElements();
|
||||
int n = nbDataElements * nbPixels;
|
||||
switch (transferType) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return new byte[n];
|
||||
case DataBuffer.TYPE_SHORT:
|
||||
case DataBuffer.TYPE_USHORT:
|
||||
return new short[n];
|
||||
case DataBuffer.TYPE_INT:
|
||||
return new int[n];
|
||||
case DataBuffer.TYPE_FLOAT:
|
||||
return new float[n];
|
||||
case DataBuffer.TYPE_DOUBLE:
|
||||
return new double[n];
|
||||
case DataBuffer.TYPE_UNDEFINED:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a pixel
|
||||
*/
|
||||
private void dot(int x, int y, int clr) {
|
||||
if (colorCache != clr) {
|
||||
paint.setColor(clr);
|
||||
colorCache = clr;
|
||||
}
|
||||
canvas.drawLine(x, y, x + 1, y + 1, paint);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import org.apache.harmony.awt.wtk.NativeEventQueue;
|
||||
|
||||
public class AndroidNativeEventQueue extends NativeEventQueue {
|
||||
|
||||
private Object eventMonitor;
|
||||
|
||||
public AndroidNativeEventQueue() {
|
||||
super();
|
||||
eventMonitor = getEventMonitor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awake() {
|
||||
synchronized (eventMonitor) {
|
||||
eventMonitor.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchEvent() {
|
||||
//???AWT
|
||||
System.out.println(getClass()+": empty method called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getJavaWindow() {
|
||||
//???AWT
|
||||
System.out.println(getClass()+": empty method called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performLater(Task task) {
|
||||
//???AWT
|
||||
System.out.println(getClass()+": empty method called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performTask(Task task) {
|
||||
//???AWT
|
||||
System.out.println(getClass()+": empty method called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean waitEvent() {
|
||||
while (isEmpty() ) {
|
||||
synchronized (eventMonitor) {
|
||||
try {
|
||||
eventMonitor.wait(1000);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import java.awt.GraphicsDevice;
|
||||
|
||||
import org.apache.harmony.awt.wtk.CursorFactory;
|
||||
import org.apache.harmony.awt.wtk.GraphicsFactory;
|
||||
import org.apache.harmony.awt.wtk.NativeEventQueue;
|
||||
import org.apache.harmony.awt.wtk.NativeIM;
|
||||
import org.apache.harmony.awt.wtk.NativeMouseInfo;
|
||||
import org.apache.harmony.awt.wtk.NativeRobot;
|
||||
import org.apache.harmony.awt.wtk.SystemProperties;
|
||||
import org.apache.harmony.awt.wtk.WTK;
|
||||
import org.apache.harmony.awt.wtk.WindowFactory;
|
||||
|
||||
public class AndroidWTK extends WTK {
|
||||
|
||||
private AndroidGraphicsFactory mAgf;
|
||||
private AndroidNativeEventQueue mAneq;
|
||||
|
||||
@Override
|
||||
public CursorFactory getCursorFactory() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsFactory getGraphicsFactory() {
|
||||
if(mAgf == null) {
|
||||
mAgf = new AndroidGraphicsFactory();
|
||||
}
|
||||
return mAgf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeEventQueue getNativeEventQueue() {
|
||||
if(mAneq == null) {
|
||||
mAneq = new AndroidNativeEventQueue();
|
||||
}
|
||||
return mAneq;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeIM getNativeIM() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeMouseInfo getNativeMouseInfo() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NativeRobot getNativeRobot(GraphicsDevice screen) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemProperties getSystemProperties() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowFactory getWindowFactory() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Toolkit;
|
||||
|
||||
import org.apache.harmony.awt.wtk.GraphicsFactory;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
|
||||
public class AwtFactory {
|
||||
|
||||
private static GraphicsFactory gf;
|
||||
|
||||
/**
|
||||
* Use this method to get acces to AWT drawing primitives and to
|
||||
* render into the surface area of a Android widget. Origin and
|
||||
* clip of the returned graphics object are the same as in the
|
||||
* corresponding Android widget.
|
||||
*
|
||||
* @param c Canvas of the android widget to draw into
|
||||
* @param p The default drawing parameters such as font,
|
||||
* stroke, foreground and background colors, etc.
|
||||
* @return The AWT Graphics object that makes all AWT
|
||||
* drawing primitives available in the androind world.
|
||||
*/
|
||||
public static Graphics2D getAwtGraphics(Canvas c, Paint p) {
|
||||
// AWT?? TODO: test it!
|
||||
if (null == gf) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
gf = tk.getGraphicsFactory();
|
||||
}
|
||||
return gf.getGraphics2D(c, p);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2007, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.awt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.imageio.stream.ImageOutputStream;
|
||||
|
||||
public class ImageOutputStreamWrapper extends OutputStream {
|
||||
|
||||
protected ImageOutputStream mIos;
|
||||
|
||||
private byte[] mBuff;
|
||||
|
||||
public ImageOutputStreamWrapper(ImageOutputStream ios) {
|
||||
if (null == ios) {
|
||||
throw new IllegalArgumentException("ImageOutputStream must not be null");
|
||||
}
|
||||
this.mIos = ios;
|
||||
this.mBuff = new byte[1];
|
||||
}
|
||||
|
||||
public ImageOutputStream getImageOutputStream() {
|
||||
return mIos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) throws IOException {
|
||||
mBuff[0] = (byte)oneByte;
|
||||
mIos.write(mBuff, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
mIos.write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
mIos.write(b, off, len);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
mIos.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (mIos == null) {
|
||||
throw new IOException("Stream already closed");
|
||||
}
|
||||
mIos = null;
|
||||
}
|
||||
}
|
||||
@@ -1,456 +0,0 @@
|
||||
package com.android.internal.awt;
|
||||
|
||||
import java.awt.*;
|
||||
import java.text.*;
|
||||
import java.awt.image.renderable.*;
|
||||
import java.awt.font.*;
|
||||
import java.awt.image.*;
|
||||
import java.awt.RenderingHints.*;
|
||||
import java.util.*;
|
||||
import java.awt.geom.*;
|
||||
|
||||
public class NullGraphics2D extends Graphics2D
|
||||
{
|
||||
|
||||
@Override
|
||||
public void clearRect(int x, int y, int width, int height)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clipRect(int x, int y, int width, int height)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyArea(int sx, int sy, int width, int height, int dx, int dy)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics create()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawArc(int x, int y, int width, int height, int sa, int ea)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, int x, int y, ImageObserver observer)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOval(int x, int y, int width, int height)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPolygon(int[] xpoints, int[] ypoints, int npoints)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPolyline(int[] xpoints, int[] ypoints, int npoints)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillArc(int x, int y, int width, int height, int sa, int ea)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillOval(int x, int y, int width, int height)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillPolygon(int[] xpoints, int[] ypoints, int npoints)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillRect(int x, int y, int width, int height)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public Shape getClip()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getClipBounds()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor()
|
||||
{
|
||||
return Color.BLACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font getFont()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return Font.decode(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontMetrics getFontMetrics(Font font)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return new FontMetrics(getFont()){};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClip(int x, int y, int width, int height)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClip(Shape clip)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(Color c)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFont(Font font)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPaintMode()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setXORMode(Color color)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRenderingHints(Map<?, ?> hints)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clip(Shape s)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Shape s)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGlyphVector(GlyphVector g, float x, float y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRenderableImage(RenderableImage img, AffineTransform xform)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRenderedImage(RenderedImage img, AffineTransform xform)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(AttributedCharacterIterator iterator, float x, float y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(AttributedCharacterIterator iterator, int x, int y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(String s, float x, float y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawString(String str, int x, int y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(Shape s)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getBackground()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Composite getComposite()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration getDeviceConfiguration()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontRenderContext getFontRenderContext()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Paint getPaint()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRenderingHint(RenderingHints.Key key)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenderingHints getRenderingHints()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stroke getStroke()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AffineTransform getTransform()
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hit(Rectangle rect, Shape s, boolean onStroke)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rotate(double theta)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rotate(double theta, double x, double y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scale(double sx, double sy)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(Color color)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComposite(Composite comp)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPaint(Paint paint)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderingHint(RenderingHints.Key key, Object value)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderingHints(Map<?, ?> hints)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStroke(Stroke s)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransform(AffineTransform Tx)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shear(double shx, double shy)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(AffineTransform Tx)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translate(double tx, double ty)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translate(int x, int y)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
|
||||
}
|
||||
@@ -171,10 +171,14 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe
|
||||
} else {
|
||||
properties.lwjglKeycode = AndroidLWJGLKeycode.getKeyIndex(spinnerKeycode.getSelectedItemPosition()) - 2;
|
||||
properties.name = editName.getText().toString();
|
||||
if (properties.lwjglKeycode < 0) {
|
||||
properties.name = ControlButton.getSpecialButtons()[properties.lwjglKeycode + 2].name;
|
||||
}
|
||||
properties.hidden = checkHidden.isChecked();
|
||||
|
||||
mHandleView.mView.updateProperties();
|
||||
|
||||
mHandleView.mView.setModified(true);
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
@@ -192,6 +196,8 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe
|
||||
{
|
||||
ControlsLayout layout = ((ControlsLayout) mHandleView.mView.getParent());
|
||||
layout.removeControlButton(mHandleView.mView);
|
||||
|
||||
mHandleView.mView.setModified(true);
|
||||
}
|
||||
});
|
||||
alert.setNegativeButton(android.R.string.cancel, null);
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import static com.pojavdx.dex.EncodedValueReader.ENCODED_ANNOTATION;
|
||||
|
||||
/**
|
||||
* An annotation.
|
||||
*/
|
||||
public final class Annotation implements Comparable<Annotation> {
|
||||
private final Dex dex;
|
||||
private final byte visibility;
|
||||
private final EncodedValue encodedAnnotation;
|
||||
|
||||
public Annotation(Dex dex, byte visibility, EncodedValue encodedAnnotation) {
|
||||
this.dex = dex;
|
||||
this.visibility = visibility;
|
||||
this.encodedAnnotation = encodedAnnotation;
|
||||
}
|
||||
|
||||
public byte getVisibility() {
|
||||
return visibility;
|
||||
}
|
||||
|
||||
public EncodedValueReader getReader() {
|
||||
return new EncodedValueReader(encodedAnnotation, ENCODED_ANNOTATION);
|
||||
}
|
||||
|
||||
public int getTypeIndex() {
|
||||
EncodedValueReader reader = getReader();
|
||||
reader.readAnnotation();
|
||||
return reader.getAnnotationType();
|
||||
}
|
||||
|
||||
public void writeTo(Dex.Section out) {
|
||||
out.writeByte(visibility);
|
||||
encodedAnnotation.writeTo(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Annotation other) {
|
||||
return encodedAnnotation.compareTo(other.encodedAnnotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return dex == null
|
||||
? visibility + " " + getTypeIndex()
|
||||
: visibility + " " + dex.typeNames().get(getTypeIndex());
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.Dex.Section;
|
||||
import com.pojavdx.dex.util.Unsigned;
|
||||
|
||||
/**
|
||||
* A call_site_id_item: https://source.android.com/devices/tech/dalvik/dex-format#call-site-id-item
|
||||
*/
|
||||
public class CallSiteId implements Comparable<CallSiteId> {
|
||||
|
||||
private final Dex dex;
|
||||
private final int offset;
|
||||
|
||||
public CallSiteId(Dex dex, int offset) {
|
||||
this.dex = dex;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CallSiteId o) {
|
||||
return Unsigned.compare(offset, o.offset);
|
||||
}
|
||||
|
||||
public int getCallSiteOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void writeTo(Section out) {
|
||||
out.writeInt(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (dex == null) {
|
||||
return String.valueOf(offset);
|
||||
}
|
||||
return dex.protoIds().get(offset).toString();
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
public final class ClassData {
|
||||
private final Field[] staticFields;
|
||||
private final Field[] instanceFields;
|
||||
private final Method[] directMethods;
|
||||
private final Method[] virtualMethods;
|
||||
|
||||
public ClassData(Field[] staticFields, Field[] instanceFields,
|
||||
Method[] directMethods, Method[] virtualMethods) {
|
||||
this.staticFields = staticFields;
|
||||
this.instanceFields = instanceFields;
|
||||
this.directMethods = directMethods;
|
||||
this.virtualMethods = virtualMethods;
|
||||
}
|
||||
|
||||
public Field[] getStaticFields() {
|
||||
return staticFields;
|
||||
}
|
||||
|
||||
public Field[] getInstanceFields() {
|
||||
return instanceFields;
|
||||
}
|
||||
|
||||
public Method[] getDirectMethods() {
|
||||
return directMethods;
|
||||
}
|
||||
|
||||
public Method[] getVirtualMethods() {
|
||||
return virtualMethods;
|
||||
}
|
||||
|
||||
public Field[] allFields() {
|
||||
Field[] result = new Field[staticFields.length + instanceFields.length];
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(staticFields, 0, result, 0, staticFields.length);
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(instanceFields, 0, result, staticFields.length, instanceFields.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Method[] allMethods() {
|
||||
Method[] result = new Method[directMethods.length + virtualMethods.length];
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(directMethods, 0, result, 0, directMethods.length);
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(virtualMethods, 0, result, directMethods.length, virtualMethods.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Field {
|
||||
private final int fieldIndex;
|
||||
private final int accessFlags;
|
||||
|
||||
public Field(int fieldIndex, int accessFlags) {
|
||||
this.fieldIndex = fieldIndex;
|
||||
this.accessFlags = accessFlags;
|
||||
}
|
||||
|
||||
public int getFieldIndex() {
|
||||
return fieldIndex;
|
||||
}
|
||||
|
||||
public int getAccessFlags() {
|
||||
return accessFlags;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Method {
|
||||
private final int methodIndex;
|
||||
private final int accessFlags;
|
||||
private final int codeOffset;
|
||||
|
||||
public Method(int methodIndex, int accessFlags, int codeOffset) {
|
||||
this.methodIndex = methodIndex;
|
||||
this.accessFlags = accessFlags;
|
||||
this.codeOffset = codeOffset;
|
||||
}
|
||||
|
||||
public int getMethodIndex() {
|
||||
return methodIndex;
|
||||
}
|
||||
|
||||
public int getAccessFlags() {
|
||||
return accessFlags;
|
||||
}
|
||||
|
||||
public int getCodeOffset() {
|
||||
return codeOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
/**
|
||||
* A type definition.
|
||||
*/
|
||||
public final class ClassDef {
|
||||
public static final int NO_INDEX = -1;
|
||||
private final Dex buffer;
|
||||
private final int offset;
|
||||
private final int typeIndex;
|
||||
private final int accessFlags;
|
||||
private final int supertypeIndex;
|
||||
private final int interfacesOffset;
|
||||
private final int sourceFileIndex;
|
||||
private final int annotationsOffset;
|
||||
private final int classDataOffset;
|
||||
private final int staticValuesOffset;
|
||||
|
||||
public ClassDef(Dex buffer, int offset, int typeIndex, int accessFlags,
|
||||
int supertypeIndex, int interfacesOffset, int sourceFileIndex,
|
||||
int annotationsOffset, int classDataOffset, int staticValuesOffset) {
|
||||
this.buffer = buffer;
|
||||
this.offset = offset;
|
||||
this.typeIndex = typeIndex;
|
||||
this.accessFlags = accessFlags;
|
||||
this.supertypeIndex = supertypeIndex;
|
||||
this.interfacesOffset = interfacesOffset;
|
||||
this.sourceFileIndex = sourceFileIndex;
|
||||
this.annotationsOffset = annotationsOffset;
|
||||
this.classDataOffset = classDataOffset;
|
||||
this.staticValuesOffset = staticValuesOffset;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public int getTypeIndex() {
|
||||
return typeIndex;
|
||||
}
|
||||
|
||||
public int getSupertypeIndex() {
|
||||
return supertypeIndex;
|
||||
}
|
||||
|
||||
public int getInterfacesOffset() {
|
||||
return interfacesOffset;
|
||||
}
|
||||
|
||||
public short[] getInterfaces() {
|
||||
return buffer.readTypeList(interfacesOffset).getTypes();
|
||||
}
|
||||
|
||||
public int getAccessFlags() {
|
||||
return accessFlags;
|
||||
}
|
||||
|
||||
public int getSourceFileIndex() {
|
||||
return sourceFileIndex;
|
||||
}
|
||||
|
||||
public int getAnnotationsOffset() {
|
||||
return annotationsOffset;
|
||||
}
|
||||
|
||||
public int getClassDataOffset() {
|
||||
return classDataOffset;
|
||||
}
|
||||
|
||||
public int getStaticValuesOffset() {
|
||||
return staticValuesOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (buffer == null) {
|
||||
return typeIndex + " " + supertypeIndex;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(buffer.typeNames().get(typeIndex));
|
||||
if (supertypeIndex != NO_INDEX) {
|
||||
result.append(" extends ").append(buffer.typeNames().get(supertypeIndex));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
public final class Code {
|
||||
private final int registersSize;
|
||||
private final int insSize;
|
||||
private final int outsSize;
|
||||
private final int debugInfoOffset;
|
||||
private final short[] instructions;
|
||||
private final Try[] tries;
|
||||
private final CatchHandler[] catchHandlers;
|
||||
|
||||
public Code(int registersSize, int insSize, int outsSize, int debugInfoOffset,
|
||||
short[] instructions, Try[] tries, CatchHandler[] catchHandlers) {
|
||||
this.registersSize = registersSize;
|
||||
this.insSize = insSize;
|
||||
this.outsSize = outsSize;
|
||||
this.debugInfoOffset = debugInfoOffset;
|
||||
this.instructions = instructions;
|
||||
this.tries = tries;
|
||||
this.catchHandlers = catchHandlers;
|
||||
}
|
||||
|
||||
public int getRegistersSize() {
|
||||
return registersSize;
|
||||
}
|
||||
|
||||
public int getInsSize() {
|
||||
return insSize;
|
||||
}
|
||||
|
||||
public int getOutsSize() {
|
||||
return outsSize;
|
||||
}
|
||||
|
||||
public int getDebugInfoOffset() {
|
||||
return debugInfoOffset;
|
||||
}
|
||||
|
||||
public short[] getInstructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public Try[] getTries() {
|
||||
return tries;
|
||||
}
|
||||
|
||||
public CatchHandler[] getCatchHandlers() {
|
||||
return catchHandlers;
|
||||
}
|
||||
|
||||
public static class Try {
|
||||
final int startAddress;
|
||||
final int instructionCount;
|
||||
final int catchHandlerIndex;
|
||||
|
||||
Try(int startAddress, int instructionCount, int catchHandlerIndex) {
|
||||
this.startAddress = startAddress;
|
||||
this.instructionCount = instructionCount;
|
||||
this.catchHandlerIndex = catchHandlerIndex;
|
||||
}
|
||||
|
||||
public int getStartAddress() {
|
||||
return startAddress;
|
||||
}
|
||||
|
||||
public int getInstructionCount() {
|
||||
return instructionCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this try's catch handler <strong>index</strong>. Note that
|
||||
* this is distinct from the its catch handler <strong>offset</strong>.
|
||||
*/
|
||||
public int getCatchHandlerIndex() {
|
||||
return catchHandlerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CatchHandler {
|
||||
final int[] typeIndexes;
|
||||
final int[] addresses;
|
||||
final int catchAllAddress;
|
||||
final int offset;
|
||||
|
||||
public CatchHandler(int[] typeIndexes, int[] addresses, int catchAllAddress, int offset) {
|
||||
this.typeIndexes = typeIndexes;
|
||||
this.addresses = addresses;
|
||||
this.catchAllAddress = catchAllAddress;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public int[] getTypeIndexes() {
|
||||
return typeIndexes;
|
||||
}
|
||||
|
||||
public int[] getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
public int getCatchAllAddress() {
|
||||
return catchAllAddress;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,819 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.Code.CatchHandler;
|
||||
import com.pojavdx.dex.Code.Try;
|
||||
import com.pojavdx.dex.MethodHandle.MethodHandleType;
|
||||
import com.pojavdx.dex.util.ByteInput;
|
||||
import com.pojavdx.dex.util.ByteOutput;
|
||||
import com.pojavdx.dex.util.FileUtils;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UTFDataFormatException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.RandomAccess;
|
||||
import java.util.zip.Adler32;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* The bytes of a dex file in memory for reading and writing. All int offsets
|
||||
* are unsigned.
|
||||
*/
|
||||
public final class Dex {
|
||||
private static final int CHECKSUM_OFFSET = 8;
|
||||
private static final int CHECKSUM_SIZE = 4;
|
||||
private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + CHECKSUM_SIZE;
|
||||
private static final int SIGNATURE_SIZE = 20;
|
||||
// Provided as a convenience to avoid a memory allocation to benefit Dalvik.
|
||||
// Note: libcore.util.EmptyArray cannot be accessed when this code isn't run on Dalvik.
|
||||
static final short[] EMPTY_SHORT_ARRAY = new short[0];
|
||||
|
||||
private ByteBuffer data;
|
||||
private final TableOfContents tableOfContents = new TableOfContents();
|
||||
private int nextSectionStart = 0;
|
||||
private final StringTable strings = new StringTable();
|
||||
private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();
|
||||
private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();
|
||||
private final ProtoIdTable protoIds = new ProtoIdTable();
|
||||
private final FieldIdTable fieldIds = new FieldIdTable();
|
||||
private final MethodIdTable methodIds = new MethodIdTable();
|
||||
|
||||
/**
|
||||
* Creates a new dex that reads from {@code data}. It is an error to modify
|
||||
* {@code data} after using it to create a dex buffer.
|
||||
*/
|
||||
public Dex(byte[] data) throws IOException {
|
||||
this(ByteBuffer.wrap(data));
|
||||
}
|
||||
|
||||
private Dex(ByteBuffer data) throws IOException {
|
||||
this.data = data;
|
||||
this.data.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.tableOfContents.readFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new empty dex of the specified size.
|
||||
*/
|
||||
public Dex(int byteCount) throws IOException {
|
||||
this.data = ByteBuffer.wrap(new byte[byteCount]);
|
||||
this.data.order(ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dex buffer of the dex in {@code in}, and closes {@code in}.
|
||||
*/
|
||||
public Dex(InputStream in) throws IOException {
|
||||
try {
|
||||
loadFrom(in);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dex buffer from the dex file {@code file}.
|
||||
*/
|
||||
public Dex(File file) throws IOException {
|
||||
if (FileUtils.hasArchiveSuffix(file.getName())) {
|
||||
ZipFile zipFile = new ZipFile(file);
|
||||
ZipEntry entry = zipFile.getEntry(DexFormat.DEX_IN_JAR_NAME);
|
||||
if (entry != null) {
|
||||
try (InputStream inputStream = zipFile.getInputStream(entry)) {
|
||||
loadFrom(inputStream);
|
||||
}
|
||||
zipFile.close();
|
||||
} else {
|
||||
throw new DexException("Expected " + DexFormat.DEX_IN_JAR_NAME + " in " + file);
|
||||
}
|
||||
} else if (file.getName().endsWith(".dex")) {
|
||||
try (InputStream inputStream = new FileInputStream(file)) {
|
||||
loadFrom(inputStream);
|
||||
}
|
||||
} else {
|
||||
throw new DexException("unknown output extension: " + file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It is the caller's responsibility to close {@code in}.
|
||||
*/
|
||||
private void loadFrom(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[8192];
|
||||
|
||||
int count;
|
||||
while ((count = in.read(buffer)) != -1) {
|
||||
bytesOut.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
this.data = ByteBuffer.wrap(bytesOut.toByteArray());
|
||||
this.data.order(ByteOrder.LITTLE_ENDIAN);
|
||||
this.tableOfContents.readFrom(this);
|
||||
}
|
||||
|
||||
private static void checkBounds(int index, int length) {
|
||||
if (index < 0 || index >= length) {
|
||||
throw new IndexOutOfBoundsException("index:" + index + ", length=" + length);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
byte[] buffer = new byte[8192];
|
||||
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
|
||||
data.clear();
|
||||
while (data.hasRemaining()) {
|
||||
int count = Math.min(buffer.length, data.remaining());
|
||||
data.get(buffer, 0, count);
|
||||
out.write(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTo(File dexOut) throws IOException {
|
||||
try (OutputStream out = new FileOutputStream(dexOut)) {
|
||||
writeTo(out);
|
||||
}
|
||||
}
|
||||
|
||||
public TableOfContents getTableOfContents() {
|
||||
return tableOfContents;
|
||||
}
|
||||
|
||||
public Section open(int position) {
|
||||
if (position < 0 || position >= data.capacity()) {
|
||||
throw new IllegalArgumentException("position=" + position
|
||||
+ " length=" + data.capacity());
|
||||
}
|
||||
ByteBuffer sectionData = data.duplicate();
|
||||
sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
|
||||
sectionData.position(position);
|
||||
sectionData.limit(data.capacity());
|
||||
return new Section("section", sectionData);
|
||||
}
|
||||
|
||||
public Section appendSection(int maxByteCount, String name) {
|
||||
if ((maxByteCount & 3) != 0) {
|
||||
throw new IllegalStateException("Not four byte aligned!");
|
||||
}
|
||||
int limit = nextSectionStart + maxByteCount;
|
||||
ByteBuffer sectionData = data.duplicate();
|
||||
sectionData.order(ByteOrder.LITTLE_ENDIAN); // necessary?
|
||||
sectionData.position(nextSectionStart);
|
||||
sectionData.limit(limit);
|
||||
Section result = new Section(name, sectionData);
|
||||
nextSectionStart = limit;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return data.capacity();
|
||||
}
|
||||
|
||||
public int getNextSectionStart() {
|
||||
return nextSectionStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the the bytes of this dex.
|
||||
*/
|
||||
public byte[] getBytes() {
|
||||
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
|
||||
byte[] result = new byte[data.capacity()];
|
||||
data.position(0);
|
||||
data.get(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<String> strings() {
|
||||
return strings;
|
||||
}
|
||||
|
||||
public List<Integer> typeIds() {
|
||||
return typeIds;
|
||||
}
|
||||
|
||||
public List<String> typeNames() {
|
||||
return typeNames;
|
||||
}
|
||||
|
||||
public List<ProtoId> protoIds() {
|
||||
return protoIds;
|
||||
}
|
||||
|
||||
public List<FieldId> fieldIds() {
|
||||
return fieldIds;
|
||||
}
|
||||
|
||||
public List<MethodId> methodIds() {
|
||||
return methodIds;
|
||||
}
|
||||
|
||||
public Iterable<ClassDef> classDefs() {
|
||||
return new ClassDefIterable();
|
||||
}
|
||||
|
||||
public TypeList readTypeList(int offset) {
|
||||
if (offset == 0) {
|
||||
return TypeList.EMPTY;
|
||||
}
|
||||
return open(offset).readTypeList();
|
||||
}
|
||||
|
||||
public ClassData readClassData(ClassDef classDef) {
|
||||
int offset = classDef.getClassDataOffset();
|
||||
if (offset == 0) {
|
||||
throw new IllegalArgumentException("offset == 0");
|
||||
}
|
||||
return open(offset).readClassData();
|
||||
}
|
||||
|
||||
public Code readCode(ClassData.Method method) {
|
||||
int offset = method.getCodeOffset();
|
||||
if (offset == 0) {
|
||||
throw new IllegalArgumentException("offset == 0");
|
||||
}
|
||||
return open(offset).readCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signature of all but the first 32 bytes of this dex. The
|
||||
* first 32 bytes of dex files are not specified to be included in the
|
||||
* signature.
|
||||
*/
|
||||
public byte[] computeSignature() throws IOException {
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
byte[] buffer = new byte[8192];
|
||||
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
|
||||
data.limit(data.capacity());
|
||||
data.position(SIGNATURE_OFFSET + SIGNATURE_SIZE);
|
||||
while (data.hasRemaining()) {
|
||||
int count = Math.min(buffer.length, data.remaining());
|
||||
data.get(buffer, 0, count);
|
||||
digest.update(buffer, 0, count);
|
||||
}
|
||||
return digest.digest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the checksum of all but the first 12 bytes of {@code dex}.
|
||||
*/
|
||||
public int computeChecksum() throws IOException {
|
||||
Adler32 adler32 = new Adler32();
|
||||
byte[] buffer = new byte[8192];
|
||||
ByteBuffer data = this.data.duplicate(); // positioned ByteBuffers aren't thread safe
|
||||
data.limit(data.capacity());
|
||||
data.position(CHECKSUM_OFFSET + CHECKSUM_SIZE);
|
||||
while (data.hasRemaining()) {
|
||||
int count = Math.min(buffer.length, data.remaining());
|
||||
data.get(buffer, 0, count);
|
||||
adler32.update(buffer, 0, count);
|
||||
}
|
||||
return (int) adler32.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the signature and checksum of the dex file {@code out} and
|
||||
* writes them to the file.
|
||||
*/
|
||||
public void writeHashes() throws IOException {
|
||||
open(SIGNATURE_OFFSET).write(computeSignature());
|
||||
open(CHECKSUM_OFFSET).writeInt(computeChecksum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a descriptor index from a type index. Cheaper than:
|
||||
* {@code open(tableOfContents.typeIds.off + (index * SizeOf.TYPE_ID_ITEM)).readInt();}
|
||||
*/
|
||||
public int descriptorIndexFromTypeIndex(int typeIndex) {
|
||||
checkBounds(typeIndex, tableOfContents.typeIds.size);
|
||||
int position = tableOfContents.typeIds.off + (SizeOf.TYPE_ID_ITEM * typeIndex);
|
||||
return data.getInt(position);
|
||||
}
|
||||
|
||||
|
||||
public final class Section implements ByteInput, ByteOutput {
|
||||
private final String name;
|
||||
private final ByteBuffer data;
|
||||
private final int initialPosition;
|
||||
|
||||
private Section(String name, ByteBuffer data) {
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
this.initialPosition = data.position();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return data.position();
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
return data.getInt();
|
||||
}
|
||||
|
||||
public short readShort() {
|
||||
return data.getShort();
|
||||
}
|
||||
|
||||
public int readUnsignedShort() {
|
||||
return readShort() & 0xffff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() {
|
||||
return data.get();
|
||||
}
|
||||
|
||||
public byte[] readByteArray(int length) {
|
||||
byte[] result = new byte[length];
|
||||
data.get(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public short[] readShortArray(int length) {
|
||||
if (length == 0) {
|
||||
return EMPTY_SHORT_ARRAY;
|
||||
}
|
||||
short[] result = new short[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = readShort();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readUleb128() {
|
||||
return Leb128.readUnsignedLeb128(this);
|
||||
}
|
||||
|
||||
public int readUleb128p1() {
|
||||
return Leb128.readUnsignedLeb128(this) - 1;
|
||||
}
|
||||
|
||||
public int readSleb128() {
|
||||
return Leb128.readSignedLeb128(this);
|
||||
}
|
||||
|
||||
public void writeUleb128p1(int i) {
|
||||
writeUleb128(i + 1);
|
||||
}
|
||||
|
||||
public TypeList readTypeList() {
|
||||
int size = readInt();
|
||||
short[] types = readShortArray(size);
|
||||
alignToFourBytes();
|
||||
return new TypeList(Dex.this, types);
|
||||
}
|
||||
|
||||
public String readString() {
|
||||
int offset = readInt();
|
||||
int savedPosition = data.position();
|
||||
int savedLimit = data.limit();
|
||||
data.position(offset);
|
||||
data.limit(data.capacity());
|
||||
try {
|
||||
int expectedLength = readUleb128();
|
||||
String result = Mutf8.decode(this, new char[expectedLength]);
|
||||
if (result.length() != expectedLength) {
|
||||
throw new DexException("Declared length " + expectedLength
|
||||
+ " doesn't match decoded length of " + result.length());
|
||||
}
|
||||
return result;
|
||||
} catch (UTFDataFormatException e) {
|
||||
throw new DexException(e);
|
||||
} finally {
|
||||
data.position(savedPosition);
|
||||
data.limit(savedLimit);
|
||||
}
|
||||
}
|
||||
|
||||
public FieldId readFieldId() {
|
||||
int declaringClassIndex = readUnsignedShort();
|
||||
int typeIndex = readUnsignedShort();
|
||||
int nameIndex = readInt();
|
||||
return new FieldId(Dex.this, declaringClassIndex, typeIndex, nameIndex);
|
||||
}
|
||||
|
||||
public MethodId readMethodId() {
|
||||
int declaringClassIndex = readUnsignedShort();
|
||||
int protoIndex = readUnsignedShort();
|
||||
int nameIndex = readInt();
|
||||
return new MethodId(Dex.this, declaringClassIndex, protoIndex, nameIndex);
|
||||
}
|
||||
|
||||
public ProtoId readProtoId() {
|
||||
int shortyIndex = readInt();
|
||||
int returnTypeIndex = readInt();
|
||||
int parametersOffset = readInt();
|
||||
return new ProtoId(Dex.this, shortyIndex, returnTypeIndex, parametersOffset);
|
||||
}
|
||||
|
||||
public CallSiteId readCallSiteId() {
|
||||
int offset = readInt();
|
||||
return new CallSiteId(Dex.this, offset);
|
||||
}
|
||||
|
||||
public MethodHandle readMethodHandle() {
|
||||
MethodHandleType methodHandleType = MethodHandleType.fromValue(readUnsignedShort());
|
||||
int unused1 = readUnsignedShort();
|
||||
int fieldOrMethodId = readUnsignedShort();
|
||||
int unused2 = readUnsignedShort();
|
||||
return new MethodHandle(Dex.this, methodHandleType, unused1, fieldOrMethodId, unused2);
|
||||
}
|
||||
|
||||
public ClassDef readClassDef() {
|
||||
int offset = getPosition();
|
||||
int type = readInt();
|
||||
int accessFlags = readInt();
|
||||
int supertype = readInt();
|
||||
int interfacesOffset = readInt();
|
||||
int sourceFileIndex = readInt();
|
||||
int annotationsOffset = readInt();
|
||||
int classDataOffset = readInt();
|
||||
int staticValuesOffset = readInt();
|
||||
return new ClassDef(Dex.this, offset, type, accessFlags, supertype,
|
||||
interfacesOffset, sourceFileIndex, annotationsOffset, classDataOffset,
|
||||
staticValuesOffset);
|
||||
}
|
||||
|
||||
private Code readCode() {
|
||||
int registersSize = readUnsignedShort();
|
||||
int insSize = readUnsignedShort();
|
||||
int outsSize = readUnsignedShort();
|
||||
int triesSize = readUnsignedShort();
|
||||
int debugInfoOffset = readInt();
|
||||
int instructionsSize = readInt();
|
||||
short[] instructions = readShortArray(instructionsSize);
|
||||
Try[] tries;
|
||||
CatchHandler[] catchHandlers;
|
||||
if (triesSize > 0) {
|
||||
if (instructions.length % 2 == 1) {
|
||||
readShort(); // padding
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't read the tries until we've read the catch handlers.
|
||||
* Unfortunately they're in the opposite order in the dex file
|
||||
* so we need to read them out-of-order.
|
||||
*/
|
||||
Section triesSection = open(data.position());
|
||||
skip(triesSize * SizeOf.TRY_ITEM);
|
||||
catchHandlers = readCatchHandlers();
|
||||
tries = triesSection.readTries(triesSize, catchHandlers);
|
||||
} else {
|
||||
tries = new Try[0];
|
||||
catchHandlers = new CatchHandler[0];
|
||||
}
|
||||
return new Code(registersSize, insSize, outsSize, debugInfoOffset, instructions,
|
||||
tries, catchHandlers);
|
||||
}
|
||||
|
||||
private CatchHandler[] readCatchHandlers() {
|
||||
int baseOffset = data.position();
|
||||
int catchHandlersSize = readUleb128();
|
||||
CatchHandler[] result = new CatchHandler[catchHandlersSize];
|
||||
for (int i = 0; i < catchHandlersSize; i++) {
|
||||
int offset = data.position() - baseOffset;
|
||||
result[i] = readCatchHandler(offset);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Try[] readTries(int triesSize, CatchHandler[] catchHandlers) {
|
||||
Try[] result = new Try[triesSize];
|
||||
for (int i = 0; i < triesSize; i++) {
|
||||
int startAddress = readInt();
|
||||
int instructionCount = readUnsignedShort();
|
||||
int handlerOffset = readUnsignedShort();
|
||||
int catchHandlerIndex = findCatchHandlerIndex(catchHandlers, handlerOffset);
|
||||
result[i] = new Try(startAddress, instructionCount, catchHandlerIndex);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int findCatchHandlerIndex(CatchHandler[] catchHandlers, int offset) {
|
||||
for (int i = 0; i < catchHandlers.length; i++) {
|
||||
CatchHandler catchHandler = catchHandlers[i];
|
||||
if (catchHandler.getOffset() == offset) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private CatchHandler readCatchHandler(int offset) {
|
||||
int size = readSleb128();
|
||||
int handlersCount = Math.abs(size);
|
||||
int[] typeIndexes = new int[handlersCount];
|
||||
int[] addresses = new int[handlersCount];
|
||||
for (int i = 0; i < handlersCount; i++) {
|
||||
typeIndexes[i] = readUleb128();
|
||||
addresses[i] = readUleb128();
|
||||
}
|
||||
int catchAllAddress = size <= 0 ? readUleb128() : -1;
|
||||
return new CatchHandler(typeIndexes, addresses, catchAllAddress, offset);
|
||||
}
|
||||
|
||||
private ClassData readClassData() {
|
||||
int staticFieldsSize = readUleb128();
|
||||
int instanceFieldsSize = readUleb128();
|
||||
int directMethodsSize = readUleb128();
|
||||
int virtualMethodsSize = readUleb128();
|
||||
ClassData.Field[] staticFields = readFields(staticFieldsSize);
|
||||
ClassData.Field[] instanceFields = readFields(instanceFieldsSize);
|
||||
ClassData.Method[] directMethods = readMethods(directMethodsSize);
|
||||
ClassData.Method[] virtualMethods = readMethods(virtualMethodsSize);
|
||||
return new ClassData(staticFields, instanceFields, directMethods, virtualMethods);
|
||||
}
|
||||
|
||||
private ClassData.Field[] readFields(int count) {
|
||||
ClassData.Field[] result = new ClassData.Field[count];
|
||||
int fieldIndex = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
fieldIndex += readUleb128(); // field index diff
|
||||
int accessFlags = readUleb128();
|
||||
result[i] = new ClassData.Field(fieldIndex, accessFlags);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ClassData.Method[] readMethods(int count) {
|
||||
ClassData.Method[] result = new ClassData.Method[count];
|
||||
int methodIndex = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
methodIndex += readUleb128(); // method index diff
|
||||
int accessFlags = readUleb128();
|
||||
int codeOff = readUleb128();
|
||||
result[i] = new ClassData.Method(methodIndex, accessFlags, codeOff);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the bytes from {@code start} to this
|
||||
* section's current position.
|
||||
*/
|
||||
private byte[] getBytesFrom(int start) {
|
||||
int end = data.position();
|
||||
byte[] result = new byte[end - start];
|
||||
data.position(start);
|
||||
data.get(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Annotation readAnnotation() {
|
||||
byte visibility = readByte();
|
||||
int start = data.position();
|
||||
new EncodedValueReader(this, EncodedValueReader.ENCODED_ANNOTATION).skipValue();
|
||||
return new Annotation(Dex.this, visibility, new EncodedValue(getBytesFrom(start)));
|
||||
}
|
||||
|
||||
public EncodedValue readEncodedArray() {
|
||||
int start = data.position();
|
||||
new EncodedValueReader(this, EncodedValueReader.ENCODED_ARRAY).skipValue();
|
||||
return new EncodedValue(getBytesFrom(start));
|
||||
}
|
||||
|
||||
public void skip(int count) {
|
||||
if (count < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
data.position(data.position() + count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips bytes until the position is aligned to a multiple of 4.
|
||||
*/
|
||||
public void alignToFourBytes() {
|
||||
data.position((data.position() + 3) & ~3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes 0x00 until the position is aligned to a multiple of 4.
|
||||
*/
|
||||
public void alignToFourBytesWithZeroFill() {
|
||||
while ((data.position() & 3) != 0) {
|
||||
data.put((byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertFourByteAligned() {
|
||||
if ((data.position() & 3) != 0) {
|
||||
throw new IllegalStateException("Not four byte aligned!");
|
||||
}
|
||||
}
|
||||
|
||||
public void write(byte[] bytes) {
|
||||
this.data.put(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(int b) {
|
||||
data.put((byte) b);
|
||||
}
|
||||
|
||||
public void writeShort(short i) {
|
||||
data.putShort(i);
|
||||
}
|
||||
|
||||
public void writeUnsignedShort(int i) {
|
||||
short s = (short) i;
|
||||
if (i != (s & 0xffff)) {
|
||||
throw new IllegalArgumentException("Expected an unsigned short: " + i);
|
||||
}
|
||||
writeShort(s);
|
||||
}
|
||||
|
||||
public void write(short[] shorts) {
|
||||
for (short s : shorts) {
|
||||
writeShort(s);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeInt(int i) {
|
||||
data.putInt(i);
|
||||
}
|
||||
|
||||
public void writeUleb128(int i) {
|
||||
try {
|
||||
Leb128.writeUnsignedLeb128(this, i);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeSleb128(int i) {
|
||||
try {
|
||||
Leb128.writeSignedLeb128(this, i);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
throw new DexException("Section limit " + data.limit() + " exceeded by " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeStringData(String value) {
|
||||
try {
|
||||
int length = value.length();
|
||||
writeUleb128(length);
|
||||
write(Mutf8.encode(value));
|
||||
writeByte(0);
|
||||
} catch (UTFDataFormatException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTypeList(TypeList typeList) {
|
||||
short[] types = typeList.getTypes();
|
||||
writeInt(types.length);
|
||||
for (short type : types) {
|
||||
writeShort(type);
|
||||
}
|
||||
alignToFourBytesWithZeroFill();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes used by this section.
|
||||
*/
|
||||
public int used() {
|
||||
return data.position() - initialPosition;
|
||||
}
|
||||
}
|
||||
|
||||
private final class StringTable extends AbstractList<String> implements RandomAccess {
|
||||
@Override
|
||||
public String get(int index) {
|
||||
checkBounds(index, tableOfContents.stringIds.size);
|
||||
return open(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM))
|
||||
.readString();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return tableOfContents.stringIds.size;
|
||||
}
|
||||
}
|
||||
|
||||
private final class TypeIndexToDescriptorIndexTable extends AbstractList<Integer>
|
||||
implements RandomAccess {
|
||||
@Override
|
||||
public Integer get(int index) {
|
||||
return descriptorIndexFromTypeIndex(index);
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return tableOfContents.typeIds.size;
|
||||
}
|
||||
}
|
||||
|
||||
private final class TypeIndexToDescriptorTable extends AbstractList<String>
|
||||
implements RandomAccess {
|
||||
@Override
|
||||
public String get(int index) {
|
||||
return strings.get(descriptorIndexFromTypeIndex(index));
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return tableOfContents.typeIds.size;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ProtoIdTable extends AbstractList<ProtoId> implements RandomAccess {
|
||||
@Override
|
||||
public ProtoId get(int index) {
|
||||
checkBounds(index, tableOfContents.protoIds.size);
|
||||
return open(tableOfContents.protoIds.off + (SizeOf.PROTO_ID_ITEM * index))
|
||||
.readProtoId();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return tableOfContents.protoIds.size;
|
||||
}
|
||||
}
|
||||
|
||||
private final class FieldIdTable extends AbstractList<FieldId> implements RandomAccess {
|
||||
@Override
|
||||
public FieldId get(int index) {
|
||||
checkBounds(index, tableOfContents.fieldIds.size);
|
||||
return open(tableOfContents.fieldIds.off + (SizeOf.MEMBER_ID_ITEM * index))
|
||||
.readFieldId();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return tableOfContents.fieldIds.size;
|
||||
}
|
||||
}
|
||||
|
||||
private final class MethodIdTable extends AbstractList<MethodId> implements RandomAccess {
|
||||
@Override
|
||||
public MethodId get(int index) {
|
||||
checkBounds(index, tableOfContents.methodIds.size);
|
||||
return open(tableOfContents.methodIds.off + (SizeOf.MEMBER_ID_ITEM * index))
|
||||
.readMethodId();
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return tableOfContents.methodIds.size;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ClassDefIterator implements Iterator<ClassDef> {
|
||||
private final Dex.Section in = open(tableOfContents.classDefs.off);
|
||||
private int count = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return count < tableOfContents.classDefs.size;
|
||||
}
|
||||
@Override
|
||||
public ClassDef next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
count++;
|
||||
return in.readClassDef();
|
||||
}
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private final class ClassDefIterable implements Iterable<ClassDef> {
|
||||
@Override
|
||||
public Iterator<ClassDef> iterator() {
|
||||
return !tableOfContents.classDefs.exists()
|
||||
? Collections.<ClassDef>emptySet().iterator()
|
||||
: new ClassDefIterator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
|
||||
/**
|
||||
* Thrown when there's a format problem reading, writing, or generally
|
||||
* processing a dex file.
|
||||
*/
|
||||
public class DexException extends ExceptionWithContext {
|
||||
public DexException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DexException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
/**
|
||||
* Constants that show up in and are otherwise related to {@code .dex}
|
||||
* files, and helper methods for same.
|
||||
*/
|
||||
public final class DexFormat {
|
||||
private DexFormat() {}
|
||||
|
||||
/** API level to target in order to allow spaces in SimpleName */
|
||||
public static final int API_SPACES_IN_SIMPLE_NAME = 10000;
|
||||
|
||||
/** API level to target in order to generate const-method-handle and const-method-type */
|
||||
public static final int API_CONST_METHOD_HANDLE = 28;
|
||||
|
||||
/** API level to target in order to generate invoke-polymorphic and invoke-custom */
|
||||
public static final int API_METHOD_HANDLES = 26;
|
||||
|
||||
/** API level to target in order to define default and static interface methods */
|
||||
public static final int API_DEFINE_INTERFACE_METHODS = 24;
|
||||
|
||||
/** API level to target in order to invoke default and static interface methods */
|
||||
public static final int API_INVOKE_INTERFACE_METHODS = 24;
|
||||
|
||||
/** API level at which the invocation of static interface methods is permitted by dx.
|
||||
* This value has been determined experimentally by testing on different VM versions. */
|
||||
public static final int API_INVOKE_STATIC_INTERFACE_METHODS = 21;
|
||||
|
||||
/** API level to target in order to suppress extended opcode usage */
|
||||
public static final int API_NO_EXTENDED_OPCODES = 13;
|
||||
|
||||
/**
|
||||
* API level to target in order to produce the most modern file
|
||||
* format
|
||||
*/
|
||||
public static final int API_CURRENT = API_CONST_METHOD_HANDLE;
|
||||
|
||||
/** dex file version number for API level 10000 and earlier */
|
||||
public static final String VERSION_FOR_API_10000 = "040";
|
||||
|
||||
/** dex file version number for API level 28 and earlier */
|
||||
public static final String VERSION_FOR_API_28 = "039";
|
||||
|
||||
/** dex file version number for API level 26 and earlier */
|
||||
public static final String VERSION_FOR_API_26 = "038";
|
||||
|
||||
/** dex file version number for API level 24 and earlier */
|
||||
public static final String VERSION_FOR_API_24 = "037";
|
||||
|
||||
/** dex file version number for API level 13 and earlier */
|
||||
public static final String VERSION_FOR_API_13 = "035";
|
||||
|
||||
/**
|
||||
* Dex file version number for dalvik.
|
||||
* <p>
|
||||
* Note: Dex version 36 was loadable in some versions of Dalvik but was never fully supported or
|
||||
* completed and is not considered a valid dex file format.
|
||||
* </p>
|
||||
*/
|
||||
public static final String VERSION_CURRENT = VERSION_FOR_API_28;
|
||||
|
||||
/**
|
||||
* file name of the primary {@code .dex} file inside an
|
||||
* application or library {@code .jar} file
|
||||
*/
|
||||
public static final String DEX_IN_JAR_NAME = "classes.dex";
|
||||
|
||||
/** common prefix for all dex file "magic numbers" */
|
||||
public static final String MAGIC_PREFIX = "dex\n";
|
||||
|
||||
/** common suffix for all dex file "magic numbers" */
|
||||
public static final String MAGIC_SUFFIX = "\0";
|
||||
|
||||
/**
|
||||
* value used to indicate endianness of file contents
|
||||
*/
|
||||
public static final int ENDIAN_TAG = 0x12345678;
|
||||
|
||||
/**
|
||||
* Maximum addressable field or method index.
|
||||
* The largest addressable member is 0xffff, in the "instruction formats" spec as field@CCCC or
|
||||
* meth@CCCC.
|
||||
*/
|
||||
public static /* final */ int MAX_MEMBER_IDX = 0x2222; // 0xFFFF
|
||||
|
||||
/**
|
||||
* Maximum addressable type index.
|
||||
* The largest addressable type is 0xffff, in the "instruction formats" spec as type@CCCC.
|
||||
*/
|
||||
public static /* final */ int MAX_TYPE_IDX = 0x2222; // 65535
|
||||
|
||||
/**
|
||||
* Returns the API level corresponding to the given magic number,
|
||||
* or {@code -1} if the given array is not a well-formed dex file
|
||||
* magic number.
|
||||
*
|
||||
* @param magic array of bytes containing DEX file magic string
|
||||
* @return API level corresponding to magic string if valid, -1 otherwise.
|
||||
*/
|
||||
public static int magicToApi(byte[] magic) {
|
||||
if (magic.length != 8) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((magic[0] != 'd') || (magic[1] != 'e') || (magic[2] != 'x') || (magic[3] != '\n') ||
|
||||
(magic[7] != '\0')) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
String version = "" + ((char) magic[4]) + ((char) magic[5]) +((char) magic[6]);
|
||||
|
||||
if (version.equals(VERSION_FOR_API_13)) {
|
||||
return API_NO_EXTENDED_OPCODES;
|
||||
} else if (version.equals(VERSION_FOR_API_24)) {
|
||||
return API_DEFINE_INTERFACE_METHODS;
|
||||
} else if (version.equals(VERSION_FOR_API_26)) {
|
||||
return API_METHOD_HANDLES;
|
||||
} else if (version.equals(VERSION_FOR_API_28)) {
|
||||
return API_CONST_METHOD_HANDLE;
|
||||
} else if (version.equals(VERSION_FOR_API_10000)) {
|
||||
return API_SPACES_IN_SIMPLE_NAME;
|
||||
} else if (version.equals(VERSION_CURRENT)) {
|
||||
return API_CURRENT;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the magic number corresponding to the given target API level.
|
||||
*
|
||||
* @param targetApiLevel level of API (minimum supported value 13).
|
||||
* @return Magic string corresponding to API level supplied.
|
||||
*/
|
||||
public static String apiToMagic(int targetApiLevel) {
|
||||
String version;
|
||||
|
||||
if (targetApiLevel >= API_CURRENT) {
|
||||
version = VERSION_CURRENT;
|
||||
} else if (targetApiLevel >= API_SPACES_IN_SIMPLE_NAME) {
|
||||
version = VERSION_FOR_API_10000;
|
||||
} else if (targetApiLevel >= API_CONST_METHOD_HANDLE) {
|
||||
version = VERSION_FOR_API_28;
|
||||
} else if (targetApiLevel >= API_METHOD_HANDLES) {
|
||||
version = VERSION_FOR_API_26;
|
||||
} else if (targetApiLevel >= API_DEFINE_INTERFACE_METHODS) {
|
||||
version = VERSION_FOR_API_24;
|
||||
} else {
|
||||
version = VERSION_FOR_API_13;
|
||||
}
|
||||
|
||||
return MAGIC_PREFIX + version + MAGIC_SUFFIX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a DEX file magic string is supported.
|
||||
* @param magic string from DEX file
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSupportedDexMagic(byte[] magic) {
|
||||
int api = magicToApi(magic);
|
||||
return api > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
/**
|
||||
* Thrown when there's an index overflow writing a dex file.
|
||||
*/
|
||||
public final class DexIndexOverflowException extends DexException {
|
||||
public DexIndexOverflowException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DexIndexOverflowException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.ByteArrayByteInput;
|
||||
import com.pojavdx.dex.util.ByteInput;
|
||||
|
||||
/**
|
||||
* An encoded value or array.
|
||||
*/
|
||||
public final class EncodedValue implements Comparable<EncodedValue> {
|
||||
private final byte[] data;
|
||||
|
||||
public EncodedValue(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public ByteInput asByteInput() {
|
||||
return new ByteArrayByteInput(data);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void writeTo(Dex.Section out) {
|
||||
out.write(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(EncodedValue other) {
|
||||
int size = Math.min(data.length, other.data.length);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (data[i] != other.data[i]) {
|
||||
return (data[i] & 0xff) - (other.data[i] & 0xff);
|
||||
}
|
||||
}
|
||||
return data.length - other.data.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Integer.toHexString(data[0] & 0xff) + "...(" + data.length + ")";
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.ByteInput;
|
||||
import com.pojavdx.dex.util.ByteOutput;
|
||||
|
||||
/**
|
||||
* Read and write {@code encoded_value} primitives.
|
||||
*/
|
||||
public final class EncodedValueCodec {
|
||||
private EncodedValueCodec() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a signed integral to {@code out}.
|
||||
*/
|
||||
public static void writeSignedIntegralValue(ByteOutput out, int type, long value) {
|
||||
/*
|
||||
* Figure out how many bits are needed to represent the value,
|
||||
* including a sign bit: The bit count is subtracted from 65
|
||||
* and not 64 to account for the sign bit. The xor operation
|
||||
* has the effect of leaving non-negative values alone and
|
||||
* unary complementing negative values (so that a leading zero
|
||||
* count always returns a useful number for our present
|
||||
* purpose).
|
||||
*/
|
||||
int requiredBits = 65 - Long.numberOfLeadingZeros(value ^ (value >> 63));
|
||||
|
||||
// Round up the requiredBits to a number of bytes.
|
||||
int requiredBytes = (requiredBits + 0x07) >> 3;
|
||||
|
||||
/*
|
||||
* Write the header byte, which includes the type and
|
||||
* requiredBytes - 1.
|
||||
*/
|
||||
out.writeByte(type | ((requiredBytes - 1) << 5));
|
||||
|
||||
// Write the value, per se.
|
||||
while (requiredBytes > 0) {
|
||||
out.writeByte((byte) value);
|
||||
value >>= 8;
|
||||
requiredBytes--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an unsigned integral to {@code out}.
|
||||
*/
|
||||
public static void writeUnsignedIntegralValue(ByteOutput out, int type, long value) {
|
||||
// Figure out how many bits are needed to represent the value.
|
||||
int requiredBits = 64 - Long.numberOfLeadingZeros(value);
|
||||
if (requiredBits == 0) {
|
||||
requiredBits = 1;
|
||||
}
|
||||
|
||||
// Round up the requiredBits to a number of bytes.
|
||||
int requiredBytes = (requiredBits + 0x07) >> 3;
|
||||
|
||||
/*
|
||||
* Write the header byte, which includes the type and
|
||||
* requiredBytes - 1.
|
||||
*/
|
||||
out.writeByte(type | ((requiredBytes - 1) << 5));
|
||||
|
||||
// Write the value, per se.
|
||||
while (requiredBytes > 0) {
|
||||
out.writeByte((byte) value);
|
||||
value >>= 8;
|
||||
requiredBytes--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a right-zero-extended value to {@code out}.
|
||||
*/
|
||||
public static void writeRightZeroExtendedValue(ByteOutput out, int type, long value) {
|
||||
// Figure out how many bits are needed to represent the value.
|
||||
int requiredBits = 64 - Long.numberOfTrailingZeros(value);
|
||||
if (requiredBits == 0) {
|
||||
requiredBits = 1;
|
||||
}
|
||||
|
||||
// Round up the requiredBits to a number of bytes.
|
||||
int requiredBytes = (requiredBits + 0x07) >> 3;
|
||||
|
||||
// Scootch the first bits to be written down to the low-order bits.
|
||||
value >>= 64 - (requiredBytes * 8);
|
||||
|
||||
/*
|
||||
* Write the header byte, which includes the type and
|
||||
* requiredBytes - 1.
|
||||
*/
|
||||
out.writeByte(type | ((requiredBytes - 1) << 5));
|
||||
|
||||
// Write the value, per se.
|
||||
while (requiredBytes > 0) {
|
||||
out.writeByte((byte) value);
|
||||
value >>= 8;
|
||||
requiredBytes--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed integer.
|
||||
*
|
||||
* @param zwidth byte count minus one
|
||||
*/
|
||||
public static int readSignedInt(ByteInput in, int zwidth) {
|
||||
int result = 0;
|
||||
for (int i = zwidth; i >= 0; i--) {
|
||||
result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
|
||||
}
|
||||
result >>= (3 - zwidth) * 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned integer.
|
||||
*
|
||||
* @param zwidth byte count minus one
|
||||
* @param fillOnRight true to zero fill on the right; false on the left
|
||||
*/
|
||||
public static int readUnsignedInt(ByteInput in, int zwidth, boolean fillOnRight) {
|
||||
int result = 0;
|
||||
if (!fillOnRight) {
|
||||
for (int i = zwidth; i >= 0; i--) {
|
||||
result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
|
||||
}
|
||||
result >>>= (3 - zwidth) * 8;
|
||||
} else {
|
||||
for (int i = zwidth; i >= 0; i--) {
|
||||
result = (result >>> 8) | ((in.readByte() & 0xff) << 24);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a signed long.
|
||||
*
|
||||
* @param zwidth byte count minus one
|
||||
*/
|
||||
public static long readSignedLong(ByteInput in, int zwidth) {
|
||||
long result = 0;
|
||||
for (int i = zwidth; i >= 0; i--) {
|
||||
result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
|
||||
}
|
||||
result >>= (7 - zwidth) * 8;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an unsigned long.
|
||||
*
|
||||
* @param zwidth byte count minus one
|
||||
* @param fillOnRight true to zero fill on the right; false on the left
|
||||
*/
|
||||
public static long readUnsignedLong(ByteInput in, int zwidth, boolean fillOnRight) {
|
||||
long result = 0;
|
||||
if (!fillOnRight) {
|
||||
for (int i = zwidth; i >= 0; i--) {
|
||||
result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
|
||||
}
|
||||
result >>>= (7 - zwidth) * 8;
|
||||
} else {
|
||||
for (int i = zwidth; i >= 0; i--) {
|
||||
result = (result >>> 8) | ((in.readByte() & 0xffL) << 56);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.ByteInput;
|
||||
|
||||
/**
|
||||
* Pull parser for encoded values.
|
||||
*/
|
||||
public final class EncodedValueReader {
|
||||
public static final int ENCODED_BYTE = 0x00;
|
||||
public static final int ENCODED_SHORT = 0x02;
|
||||
public static final int ENCODED_CHAR = 0x03;
|
||||
public static final int ENCODED_INT = 0x04;
|
||||
public static final int ENCODED_LONG = 0x06;
|
||||
public static final int ENCODED_FLOAT = 0x10;
|
||||
public static final int ENCODED_DOUBLE = 0x11;
|
||||
public static final int ENCODED_METHOD_TYPE = 0x15;
|
||||
public static final int ENCODED_METHOD_HANDLE = 0x16;
|
||||
public static final int ENCODED_STRING = 0x17;
|
||||
public static final int ENCODED_TYPE = 0x18;
|
||||
public static final int ENCODED_FIELD = 0x19;
|
||||
public static final int ENCODED_ENUM = 0x1b;
|
||||
public static final int ENCODED_METHOD = 0x1a;
|
||||
public static final int ENCODED_ARRAY = 0x1c;
|
||||
public static final int ENCODED_ANNOTATION = 0x1d;
|
||||
public static final int ENCODED_NULL = 0x1e;
|
||||
public static final int ENCODED_BOOLEAN = 0x1f;
|
||||
|
||||
/** placeholder type if the type is not yet known */
|
||||
private static final int MUST_READ = -1;
|
||||
|
||||
protected final ByteInput in;
|
||||
private int type = MUST_READ;
|
||||
private int annotationType;
|
||||
private int arg;
|
||||
|
||||
public EncodedValueReader(ByteInput in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
public EncodedValueReader(EncodedValue in) {
|
||||
this(in.asByteInput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new encoded value reader whose only value is the specified
|
||||
* known type. This is useful for encoded values without a type prefix,
|
||||
* such as class_def_item's encoded_array or annotation_item's
|
||||
* encoded_annotation.
|
||||
*/
|
||||
public EncodedValueReader(ByteInput in, int knownType) {
|
||||
this.in = in;
|
||||
this.type = knownType;
|
||||
}
|
||||
|
||||
public EncodedValueReader(EncodedValue in, int knownType) {
|
||||
this(in.asByteInput(), knownType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the next value to read.
|
||||
*/
|
||||
public int peek() {
|
||||
if (type == MUST_READ) {
|
||||
int argAndType = in.readByte() & 0xff;
|
||||
type = argAndType & 0x1f;
|
||||
arg = (argAndType & 0xe0) >> 5;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins reading the elements of an array, returning the array's size. The
|
||||
* caller must follow up by calling a read method for each element in the
|
||||
* array. For example, this reads a byte array: <pre> {@code
|
||||
* int arraySize = readArray();
|
||||
* for (int i = 0, i < arraySize; i++) {
|
||||
* readByte();
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public int readArray() {
|
||||
checkType(ENCODED_ARRAY);
|
||||
type = MUST_READ;
|
||||
return Leb128.readUnsignedLeb128(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins reading the fields of an annotation, returning the number of
|
||||
* fields. The caller must follow up by making alternating calls to {@link
|
||||
* #readAnnotationName()} and another read method. For example, this reads
|
||||
* an annotation whose fields are all bytes: <pre> {@code
|
||||
* int fieldCount = readAnnotation();
|
||||
* int annotationType = getAnnotationType();
|
||||
* for (int i = 0; i < fieldCount; i++) {
|
||||
* readAnnotationName();
|
||||
* readByte();
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public int readAnnotation() {
|
||||
checkType(ENCODED_ANNOTATION);
|
||||
type = MUST_READ;
|
||||
annotationType = Leb128.readUnsignedLeb128(in);
|
||||
return Leb128.readUnsignedLeb128(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the annotation just returned by {@link
|
||||
* #readAnnotation()}. This method's value is undefined unless the most
|
||||
* recent call was to {@link #readAnnotation()}.
|
||||
*/
|
||||
public int getAnnotationType() {
|
||||
return annotationType;
|
||||
}
|
||||
|
||||
public int readAnnotationName() {
|
||||
return Leb128.readUnsignedLeb128(in);
|
||||
}
|
||||
|
||||
public byte readByte() {
|
||||
checkType(ENCODED_BYTE);
|
||||
type = MUST_READ;
|
||||
return (byte) EncodedValueCodec.readSignedInt(in, arg);
|
||||
}
|
||||
|
||||
public short readShort() {
|
||||
checkType(ENCODED_SHORT);
|
||||
type = MUST_READ;
|
||||
return (short) EncodedValueCodec.readSignedInt(in, arg);
|
||||
}
|
||||
|
||||
public char readChar() {
|
||||
checkType(ENCODED_CHAR);
|
||||
type = MUST_READ;
|
||||
return (char) EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readInt() {
|
||||
checkType(ENCODED_INT);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readSignedInt(in, arg);
|
||||
}
|
||||
|
||||
public long readLong() {
|
||||
checkType(ENCODED_LONG);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readSignedLong(in, arg);
|
||||
}
|
||||
|
||||
public float readFloat() {
|
||||
checkType(ENCODED_FLOAT);
|
||||
type = MUST_READ;
|
||||
return Float.intBitsToFloat(EncodedValueCodec.readUnsignedInt(in, arg, true));
|
||||
}
|
||||
|
||||
public double readDouble() {
|
||||
checkType(ENCODED_DOUBLE);
|
||||
type = MUST_READ;
|
||||
return Double.longBitsToDouble(EncodedValueCodec.readUnsignedLong(in, arg, true));
|
||||
}
|
||||
|
||||
public int readMethodType() {
|
||||
checkType(ENCODED_METHOD_TYPE);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readMethodHandle() {
|
||||
checkType(ENCODED_METHOD_HANDLE);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readString() {
|
||||
checkType(ENCODED_STRING);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readType() {
|
||||
checkType(ENCODED_TYPE);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readField() {
|
||||
checkType(ENCODED_FIELD);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readEnum() {
|
||||
checkType(ENCODED_ENUM);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public int readMethod() {
|
||||
checkType(ENCODED_METHOD);
|
||||
type = MUST_READ;
|
||||
return EncodedValueCodec.readUnsignedInt(in, arg, false);
|
||||
}
|
||||
|
||||
public void readNull() {
|
||||
checkType(ENCODED_NULL);
|
||||
type = MUST_READ;
|
||||
}
|
||||
|
||||
public boolean readBoolean() {
|
||||
checkType(ENCODED_BOOLEAN);
|
||||
type = MUST_READ;
|
||||
return arg != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips a single value, including its nested values if it is an array or
|
||||
* annotation.
|
||||
*/
|
||||
public void skipValue() {
|
||||
switch (peek()) {
|
||||
case ENCODED_BYTE:
|
||||
readByte();
|
||||
break;
|
||||
case ENCODED_SHORT:
|
||||
readShort();
|
||||
break;
|
||||
case ENCODED_CHAR:
|
||||
readChar();
|
||||
break;
|
||||
case ENCODED_INT:
|
||||
readInt();
|
||||
break;
|
||||
case ENCODED_LONG:
|
||||
readLong();
|
||||
break;
|
||||
case ENCODED_FLOAT:
|
||||
readFloat();
|
||||
break;
|
||||
case ENCODED_DOUBLE:
|
||||
readDouble();
|
||||
break;
|
||||
case ENCODED_METHOD_TYPE:
|
||||
readMethodType();
|
||||
break;
|
||||
case ENCODED_METHOD_HANDLE:
|
||||
readMethodHandle();
|
||||
break;
|
||||
case ENCODED_STRING:
|
||||
readString();
|
||||
break;
|
||||
case ENCODED_TYPE:
|
||||
readType();
|
||||
break;
|
||||
case ENCODED_FIELD:
|
||||
readField();
|
||||
break;
|
||||
case ENCODED_ENUM:
|
||||
readEnum();
|
||||
break;
|
||||
case ENCODED_METHOD:
|
||||
readMethod();
|
||||
break;
|
||||
case ENCODED_ARRAY:
|
||||
for (int i = 0, size = readArray(); i < size; i++) {
|
||||
skipValue();
|
||||
}
|
||||
break;
|
||||
case ENCODED_ANNOTATION:
|
||||
for (int i = 0, size = readAnnotation(); i < size; i++) {
|
||||
readAnnotationName();
|
||||
skipValue();
|
||||
}
|
||||
break;
|
||||
case ENCODED_NULL:
|
||||
readNull();
|
||||
break;
|
||||
case ENCODED_BOOLEAN:
|
||||
readBoolean();
|
||||
break;
|
||||
default:
|
||||
throw new DexException("Unexpected type: " + Integer.toHexString(type));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkType(int expected) {
|
||||
if (peek() != expected) {
|
||||
throw new IllegalStateException(
|
||||
String.format("Expected %x but was %x", expected, peek()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.Unsigned;
|
||||
|
||||
public final class FieldId implements Comparable<FieldId> {
|
||||
private final Dex dex;
|
||||
private final int declaringClassIndex;
|
||||
private final int typeIndex;
|
||||
private final int nameIndex;
|
||||
|
||||
public FieldId(Dex dex, int declaringClassIndex, int typeIndex, int nameIndex) {
|
||||
this.dex = dex;
|
||||
this.declaringClassIndex = declaringClassIndex;
|
||||
this.typeIndex = typeIndex;
|
||||
this.nameIndex = nameIndex;
|
||||
}
|
||||
|
||||
public int getDeclaringClassIndex() {
|
||||
return declaringClassIndex;
|
||||
}
|
||||
|
||||
public int getTypeIndex() {
|
||||
return typeIndex;
|
||||
}
|
||||
|
||||
public int getNameIndex() {
|
||||
return nameIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(FieldId other) {
|
||||
if (declaringClassIndex != other.declaringClassIndex) {
|
||||
return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
|
||||
}
|
||||
if (nameIndex != other.nameIndex) {
|
||||
return Unsigned.compare(nameIndex, other.nameIndex);
|
||||
}
|
||||
return Unsigned.compare(typeIndex, other.typeIndex); // should always be 0
|
||||
}
|
||||
|
||||
public void writeTo(Dex.Section out) {
|
||||
out.writeUnsignedShort(declaringClassIndex);
|
||||
out.writeUnsignedShort(typeIndex);
|
||||
out.writeInt(nameIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (dex == null) {
|
||||
return declaringClassIndex + " " + typeIndex + " " + nameIndex;
|
||||
}
|
||||
return dex.typeNames().get(typeIndex) + "." + dex.strings().get(nameIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.ByteInput;
|
||||
import com.pojavdx.dex.util.ByteOutput;
|
||||
|
||||
/**
|
||||
* Reads and writes DWARFv3 LEB 128 signed and unsigned integers. See DWARF v3
|
||||
* section 7.6.
|
||||
*/
|
||||
public final class Leb128 {
|
||||
private Leb128() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of bytes in the unsigned LEB128 encoding of the
|
||||
* given value.
|
||||
*
|
||||
* @param value the value in question
|
||||
* @return its write size, in bytes
|
||||
*/
|
||||
public static int unsignedLeb128Size(int value) {
|
||||
// TODO: This could be much cleverer.
|
||||
|
||||
int remaining = value >> 7;
|
||||
int count = 0;
|
||||
|
||||
while (remaining != 0) {
|
||||
remaining >>= 7;
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an signed integer from {@code in}.
|
||||
*/
|
||||
public static int readSignedLeb128(ByteInput in) {
|
||||
int result = 0;
|
||||
int cur;
|
||||
int count = 0;
|
||||
int signBits = -1;
|
||||
|
||||
do {
|
||||
cur = in.readByte() & 0xff;
|
||||
result |= (cur & 0x7f) << (count * 7);
|
||||
signBits <<= 7;
|
||||
count++;
|
||||
} while (((cur & 0x80) == 0x80) && count < 5);
|
||||
|
||||
if ((cur & 0x80) == 0x80) {
|
||||
throw new DexException("invalid LEB128 sequence");
|
||||
}
|
||||
|
||||
// Sign extend if appropriate
|
||||
if (((signBits >> 1) & result) != 0 ) {
|
||||
result |= signBits;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned integer from {@code in}.
|
||||
*/
|
||||
public static int readUnsignedLeb128(ByteInput in) {
|
||||
int result = 0;
|
||||
int cur;
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
cur = in.readByte() & 0xff;
|
||||
result |= (cur & 0x7f) << (count * 7);
|
||||
count++;
|
||||
} while (((cur & 0x80) == 0x80) && count < 5);
|
||||
|
||||
if ((cur & 0x80) == 0x80) {
|
||||
throw new DexException("invalid LEB128 sequence");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code value} as an unsigned integer to {@code out}, starting at
|
||||
* {@code offset}. Returns the number of bytes written.
|
||||
*/
|
||||
public static void writeUnsignedLeb128(ByteOutput out, int value) {
|
||||
int remaining = value >>> 7;
|
||||
|
||||
while (remaining != 0) {
|
||||
out.writeByte((byte) ((value & 0x7f) | 0x80));
|
||||
value = remaining;
|
||||
remaining >>>= 7;
|
||||
}
|
||||
|
||||
out.writeByte((byte) (value & 0x7f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes {@code value} as a signed integer to {@code out}, starting at
|
||||
* {@code offset}. Returns the number of bytes written.
|
||||
*/
|
||||
public static void writeSignedLeb128(ByteOutput out, int value) {
|
||||
int remaining = value >> 7;
|
||||
boolean hasMore = true;
|
||||
int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1;
|
||||
|
||||
while (hasMore) {
|
||||
hasMore = (remaining != end)
|
||||
|| ((remaining & 1) != ((value >> 6) & 1));
|
||||
|
||||
out.writeByte((byte) ((value & 0x7f) | (hasMore ? 0x80 : 0)));
|
||||
value = remaining;
|
||||
remaining >>= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.Dex.Section;
|
||||
import com.pojavdx.dex.util.Unsigned;
|
||||
|
||||
/**
|
||||
* A method_handle_item:
|
||||
* https://source.android.com/devices/tech/dalvik/dex-format#method-handle-item
|
||||
*/
|
||||
public class MethodHandle implements Comparable<MethodHandle> {
|
||||
|
||||
/**
|
||||
* A method handle type code:
|
||||
* https://source.android.com/devices/tech/dalvik/dex-format#method-handle-type-codes
|
||||
*/
|
||||
public enum MethodHandleType {
|
||||
METHOD_HANDLE_TYPE_STATIC_PUT(0x00),
|
||||
METHOD_HANDLE_TYPE_STATIC_GET(0x01),
|
||||
METHOD_HANDLE_TYPE_INSTANCE_PUT(0x02),
|
||||
METHOD_HANDLE_TYPE_INSTANCE_GET(0x03),
|
||||
METHOD_HANDLE_TYPE_INVOKE_STATIC(0x04),
|
||||
METHOD_HANDLE_TYPE_INVOKE_INSTANCE(0x05),
|
||||
METHOD_HANDLE_TYPE_INVOKE_DIRECT(0x06),
|
||||
METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR(0x07),
|
||||
METHOD_HANDLE_TYPE_INVOKE_INTERFACE(0x08);
|
||||
|
||||
private final int value;
|
||||
|
||||
MethodHandleType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static MethodHandleType fromValue(int value) {
|
||||
for (MethodHandleType methodHandleType : values()) {
|
||||
if (methodHandleType.value == value) {
|
||||
return methodHandleType;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.valueOf(value));
|
||||
}
|
||||
|
||||
public boolean isField() {
|
||||
switch (this) {
|
||||
case METHOD_HANDLE_TYPE_STATIC_PUT:
|
||||
case METHOD_HANDLE_TYPE_STATIC_GET:
|
||||
case METHOD_HANDLE_TYPE_INSTANCE_PUT:
|
||||
case METHOD_HANDLE_TYPE_INSTANCE_GET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Dex dex;
|
||||
private final MethodHandleType methodHandleType;
|
||||
private final int unused1;
|
||||
private final int fieldOrMethodId;
|
||||
private final int unused2;
|
||||
|
||||
public MethodHandle(
|
||||
Dex dex,
|
||||
MethodHandleType methodHandleType,
|
||||
int unused1,
|
||||
int fieldOrMethodId,
|
||||
int unused2) {
|
||||
this.dex = dex;
|
||||
this.methodHandleType = methodHandleType;
|
||||
this.unused1 = unused1;
|
||||
this.fieldOrMethodId = fieldOrMethodId;
|
||||
this.unused2 = unused2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MethodHandle o) {
|
||||
if (methodHandleType != o.methodHandleType) {
|
||||
return methodHandleType.compareTo(o.methodHandleType);
|
||||
}
|
||||
return Unsigned.compare(fieldOrMethodId, o.fieldOrMethodId);
|
||||
}
|
||||
|
||||
public MethodHandleType getMethodHandleType() {
|
||||
return methodHandleType;
|
||||
}
|
||||
|
||||
public int getUnused1() {
|
||||
return unused1;
|
||||
}
|
||||
|
||||
public int getFieldOrMethodId() {
|
||||
return fieldOrMethodId;
|
||||
}
|
||||
|
||||
public int getUnused2() {
|
||||
return unused2;
|
||||
}
|
||||
|
||||
public void writeTo(Section out) {
|
||||
out.writeUnsignedShort(methodHandleType.value);
|
||||
out.writeUnsignedShort(unused1);
|
||||
out.writeUnsignedShort(fieldOrMethodId);
|
||||
out.writeUnsignedShort(unused2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (dex == null) {
|
||||
return methodHandleType + " " + fieldOrMethodId;
|
||||
}
|
||||
return methodHandleType
|
||||
+ " "
|
||||
+ (methodHandleType.isField()
|
||||
? dex.fieldIds().get(fieldOrMethodId)
|
||||
: dex.methodIds().get(fieldOrMethodId));
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.Unsigned;
|
||||
|
||||
public final class MethodId implements Comparable<MethodId> {
|
||||
private final Dex dex;
|
||||
private final int declaringClassIndex;
|
||||
private final int protoIndex;
|
||||
private final int nameIndex;
|
||||
|
||||
public MethodId(Dex dex, int declaringClassIndex, int protoIndex, int nameIndex) {
|
||||
this.dex = dex;
|
||||
this.declaringClassIndex = declaringClassIndex;
|
||||
this.protoIndex = protoIndex;
|
||||
this.nameIndex = nameIndex;
|
||||
}
|
||||
|
||||
public int getDeclaringClassIndex() {
|
||||
return declaringClassIndex;
|
||||
}
|
||||
|
||||
public int getProtoIndex() {
|
||||
return protoIndex;
|
||||
}
|
||||
|
||||
public int getNameIndex() {
|
||||
return nameIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(MethodId other) {
|
||||
if (declaringClassIndex != other.declaringClassIndex) {
|
||||
return Unsigned.compare(declaringClassIndex, other.declaringClassIndex);
|
||||
}
|
||||
if (nameIndex != other.nameIndex) {
|
||||
return Unsigned.compare(nameIndex, other.nameIndex);
|
||||
}
|
||||
return Unsigned.compare(protoIndex, other.protoIndex);
|
||||
}
|
||||
|
||||
public void writeTo(Dex.Section out) {
|
||||
out.writeUnsignedShort(declaringClassIndex);
|
||||
out.writeUnsignedShort(protoIndex);
|
||||
out.writeInt(nameIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (dex == null) {
|
||||
return declaringClassIndex + " " + protoIndex + " " + nameIndex;
|
||||
}
|
||||
return dex.typeNames().get(declaringClassIndex)
|
||||
+ "." + dex.strings().get(nameIndex)
|
||||
+ dex.readTypeList(dex.protoIds().get(protoIndex).getParametersOffset());
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.ByteInput;
|
||||
import java.io.UTFDataFormatException;
|
||||
|
||||
/**
|
||||
* Modified UTF-8 as described in the dex file format spec.
|
||||
*/
|
||||
public final class Mutf8 {
|
||||
private Mutf8() {}
|
||||
|
||||
/**
|
||||
* Decodes bytes from {@code in} into {@code out} until a delimiter 0x00 is
|
||||
* encountered. Returns a new string containing the decoded characters.
|
||||
*/
|
||||
public static String decode(ByteInput in, char[] out) throws UTFDataFormatException {
|
||||
int s = 0;
|
||||
while (true) {
|
||||
char a = (char) (in.readByte() & 0xff);
|
||||
if (a == 0) {
|
||||
return new String(out, 0, s);
|
||||
}
|
||||
out[s] = a;
|
||||
if (a < '\u0080') {
|
||||
s++;
|
||||
} else if ((a & 0xe0) == 0xc0) {
|
||||
int b = in.readByte() & 0xff;
|
||||
if ((b & 0xC0) != 0x80) {
|
||||
throw new UTFDataFormatException("bad second byte");
|
||||
}
|
||||
out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
|
||||
} else if ((a & 0xf0) == 0xe0) {
|
||||
int b = in.readByte() & 0xff;
|
||||
int c = in.readByte() & 0xff;
|
||||
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
|
||||
throw new UTFDataFormatException("bad second or third byte");
|
||||
}
|
||||
out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
|
||||
} else {
|
||||
throw new UTFDataFormatException("bad byte");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes the modified UTF8 representation of 's' would take.
|
||||
*/
|
||||
private static long countBytes(String s, boolean shortLength) throws UTFDataFormatException {
|
||||
long result = 0;
|
||||
final int length = s.length();
|
||||
for (int i = 0; i < length; ++i) {
|
||||
char ch = s.charAt(i);
|
||||
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
|
||||
++result;
|
||||
} else if (ch <= 2047) {
|
||||
result += 2;
|
||||
} else {
|
||||
result += 3;
|
||||
}
|
||||
if (shortLength && result > 65535) {
|
||||
throw new UTFDataFormatException("String more than 65535 UTF bytes long");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the modified UTF-8 bytes corresponding to {@code s} into {@code
|
||||
* dst}, starting at {@code offset}.
|
||||
*/
|
||||
public static void encode(byte[] dst, int offset, String s) {
|
||||
final int length = s.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
char ch = s.charAt(i);
|
||||
if (ch != 0 && ch <= 127) { // U+0000 uses two bytes.
|
||||
dst[offset++] = (byte) ch;
|
||||
} else if (ch <= 2047) {
|
||||
dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
|
||||
dst[offset++] = (byte) (0x80 | (0x3f & ch));
|
||||
} else {
|
||||
dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
|
||||
dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6)));
|
||||
dst[offset++] = (byte) (0x80 | (0x3f & ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the <i>modified UTF-8</i> form of {@code s}.
|
||||
*/
|
||||
public static byte[] encode(String s) throws UTFDataFormatException {
|
||||
int utfCount = (int) countBytes(s, true);
|
||||
byte[] result = new byte[utfCount];
|
||||
encode(result, 0, s);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.Unsigned;
|
||||
|
||||
public final class ProtoId implements Comparable<ProtoId> {
|
||||
private final Dex dex;
|
||||
private final int shortyIndex;
|
||||
private final int returnTypeIndex;
|
||||
private final int parametersOffset;
|
||||
|
||||
public ProtoId(Dex dex, int shortyIndex, int returnTypeIndex, int parametersOffset) {
|
||||
this.dex = dex;
|
||||
this.shortyIndex = shortyIndex;
|
||||
this.returnTypeIndex = returnTypeIndex;
|
||||
this.parametersOffset = parametersOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ProtoId other) {
|
||||
if (returnTypeIndex != other.returnTypeIndex) {
|
||||
return Unsigned.compare(returnTypeIndex, other.returnTypeIndex);
|
||||
}
|
||||
return Unsigned.compare(parametersOffset, other.parametersOffset);
|
||||
}
|
||||
|
||||
public int getShortyIndex() {
|
||||
return shortyIndex;
|
||||
}
|
||||
|
||||
public int getReturnTypeIndex() {
|
||||
return returnTypeIndex;
|
||||
}
|
||||
|
||||
public int getParametersOffset() {
|
||||
return parametersOffset;
|
||||
}
|
||||
|
||||
public void writeTo(Dex.Section out) {
|
||||
out.writeInt(shortyIndex);
|
||||
out.writeInt(returnTypeIndex);
|
||||
out.writeInt(parametersOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (dex == null) {
|
||||
return shortyIndex + " " + returnTypeIndex + " " + parametersOffset;
|
||||
}
|
||||
|
||||
return dex.strings().get(shortyIndex)
|
||||
+ ": " + dex.typeNames().get(returnTypeIndex)
|
||||
+ " " + dex.readTypeList(parametersOffset);
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
public final class SizeOf {
|
||||
private SizeOf() {}
|
||||
|
||||
public static final int UBYTE = 1;
|
||||
public static final int USHORT = 2;
|
||||
public static final int UINT = 4;
|
||||
|
||||
public static final int SIGNATURE = UBYTE * 20;
|
||||
|
||||
/**
|
||||
* magic ubyte[8]
|
||||
* checksum uint
|
||||
* signature ubyte[20]
|
||||
* file_size uint
|
||||
* header_size uint
|
||||
* endian_tag uint
|
||||
* link_size uint
|
||||
* link_off uint
|
||||
* map_off uint
|
||||
* string_ids_size uint
|
||||
* string_ids_off uint
|
||||
* type_ids_size uint
|
||||
* type_ids_off uint
|
||||
* proto_ids_size uint
|
||||
* proto_ids_off uint
|
||||
* field_ids_size uint
|
||||
* field_ids_off uint
|
||||
* method_ids_size uint
|
||||
* method_ids_off uint
|
||||
* class_defs_size uint
|
||||
* class_defs_off uint
|
||||
* data_size uint
|
||||
* data_off uint
|
||||
*/
|
||||
public static final int HEADER_ITEM = (8 * UBYTE) + UINT + SIGNATURE + (20 * UINT); // 0x70
|
||||
|
||||
/**
|
||||
* string_data_off uint
|
||||
*/
|
||||
public static final int STRING_ID_ITEM = UINT;
|
||||
|
||||
/**
|
||||
* descriptor_idx uint
|
||||
*/
|
||||
public static final int TYPE_ID_ITEM = UINT;
|
||||
|
||||
/**
|
||||
* type_idx ushort
|
||||
*/
|
||||
public static final int TYPE_ITEM = USHORT;
|
||||
|
||||
/**
|
||||
* shorty_idx uint
|
||||
* return_type_idx uint
|
||||
* return_type_idx uint
|
||||
*/
|
||||
public static final int PROTO_ID_ITEM = UINT + UINT + UINT;
|
||||
|
||||
/**
|
||||
* class_idx ushort
|
||||
* type_idx/proto_idx ushort
|
||||
* name_idx uint
|
||||
*/
|
||||
public static final int MEMBER_ID_ITEM = USHORT + USHORT + UINT;
|
||||
|
||||
/**
|
||||
* class_idx uint
|
||||
* access_flags uint
|
||||
* superclass_idx uint
|
||||
* interfaces_off uint
|
||||
* source_file_idx uint
|
||||
* annotations_off uint
|
||||
* class_data_off uint
|
||||
* static_values_off uint
|
||||
*/
|
||||
public static final int CLASS_DEF_ITEM = 8 * UINT;
|
||||
|
||||
/**
|
||||
* type ushort
|
||||
* unused ushort
|
||||
* size uint
|
||||
* offset uint
|
||||
*/
|
||||
public static final int MAP_ITEM = USHORT + USHORT + UINT + UINT;
|
||||
|
||||
/**
|
||||
* start_addr uint
|
||||
* insn_count ushort
|
||||
* handler_off ushort
|
||||
*/
|
||||
public static final int TRY_ITEM = UINT + USHORT + USHORT;
|
||||
|
||||
/**
|
||||
* call_site_off uint
|
||||
*/
|
||||
public static final int CALL_SITE_ID_ITEM = UINT;
|
||||
|
||||
/**
|
||||
* method_handle_type ushort
|
||||
* unused ushort
|
||||
* field_or_method_id ushort
|
||||
* unused ushort
|
||||
*/
|
||||
public static final int METHOD_HANDLE_ITEM = USHORT + USHORT + USHORT + USHORT;
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The file header and map.
|
||||
*/
|
||||
public final class TableOfContents {
|
||||
|
||||
/*
|
||||
* TODO: factor out ID constants.
|
||||
*/
|
||||
|
||||
public final Section header = new Section(0x0000);
|
||||
public final Section stringIds = new Section(0x0001);
|
||||
public final Section typeIds = new Section(0x0002);
|
||||
public final Section protoIds = new Section(0x0003);
|
||||
public final Section fieldIds = new Section(0x0004);
|
||||
public final Section methodIds = new Section(0x0005);
|
||||
public final Section classDefs = new Section(0x0006);
|
||||
public final Section callSiteIds = new Section(0x0007);
|
||||
public final Section methodHandles = new Section(0x0008);
|
||||
public final Section mapList = new Section(0x1000);
|
||||
public final Section typeLists = new Section(0x1001);
|
||||
public final Section annotationSetRefLists = new Section(0x1002);
|
||||
public final Section annotationSets = new Section(0x1003);
|
||||
public final Section classDatas = new Section(0x2000);
|
||||
public final Section codes = new Section(0x2001);
|
||||
public final Section stringDatas = new Section(0x2002);
|
||||
public final Section debugInfos = new Section(0x2003);
|
||||
public final Section annotations = new Section(0x2004);
|
||||
public final Section encodedArrays = new Section(0x2005);
|
||||
public final Section annotationsDirectories = new Section(0x2006);
|
||||
public final Section[] sections = {
|
||||
header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList, callSiteIds,
|
||||
methodHandles, typeLists, annotationSetRefLists, annotationSets, classDatas, codes,
|
||||
stringDatas, debugInfos, annotations, encodedArrays, annotationsDirectories
|
||||
};
|
||||
|
||||
public int apiLevel;
|
||||
public int checksum;
|
||||
public byte[] signature;
|
||||
public int fileSize;
|
||||
public int linkSize;
|
||||
public int linkOff;
|
||||
public int dataSize;
|
||||
public int dataOff;
|
||||
|
||||
public TableOfContents() {
|
||||
signature = new byte[20];
|
||||
}
|
||||
|
||||
public void readFrom(Dex dex) throws IOException {
|
||||
readHeader(dex.open(0));
|
||||
readMap(dex.open(mapList.off));
|
||||
computeSizesFromOffsets();
|
||||
}
|
||||
|
||||
private void readHeader(Dex.Section headerIn) throws UnsupportedEncodingException {
|
||||
byte[] magic = headerIn.readByteArray(8);
|
||||
|
||||
if (!DexFormat.isSupportedDexMagic(magic)) {
|
||||
String msg =
|
||||
String.format("Unexpected magic: [0x%02x, 0x%02x, 0x%02x, 0x%02x, "
|
||||
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x]",
|
||||
magic[0], magic[1], magic[2], magic[3],
|
||||
magic[4], magic[5], magic[6], magic[7]);
|
||||
throw new DexException(msg);
|
||||
}
|
||||
|
||||
apiLevel = DexFormat.magicToApi(magic);
|
||||
checksum = headerIn.readInt();
|
||||
signature = headerIn.readByteArray(20);
|
||||
fileSize = headerIn.readInt();
|
||||
int headerSize = headerIn.readInt();
|
||||
if (headerSize != SizeOf.HEADER_ITEM) {
|
||||
throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
|
||||
}
|
||||
int endianTag = headerIn.readInt();
|
||||
if (endianTag != DexFormat.ENDIAN_TAG) {
|
||||
throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
|
||||
}
|
||||
linkSize = headerIn.readInt();
|
||||
linkOff = headerIn.readInt();
|
||||
mapList.off = headerIn.readInt();
|
||||
if (mapList.off == 0) {
|
||||
throw new DexException("Cannot merge dex files that do not contain a map");
|
||||
}
|
||||
stringIds.size = headerIn.readInt();
|
||||
stringIds.off = headerIn.readInt();
|
||||
typeIds.size = headerIn.readInt();
|
||||
typeIds.off = headerIn.readInt();
|
||||
protoIds.size = headerIn.readInt();
|
||||
protoIds.off = headerIn.readInt();
|
||||
fieldIds.size = headerIn.readInt();
|
||||
fieldIds.off = headerIn.readInt();
|
||||
methodIds.size = headerIn.readInt();
|
||||
methodIds.off = headerIn.readInt();
|
||||
classDefs.size = headerIn.readInt();
|
||||
classDefs.off = headerIn.readInt();
|
||||
dataSize = headerIn.readInt();
|
||||
dataOff = headerIn.readInt();
|
||||
}
|
||||
|
||||
private void readMap(Dex.Section in) throws IOException {
|
||||
int mapSize = in.readInt();
|
||||
Section previous = null;
|
||||
for (int i = 0; i < mapSize; i++) {
|
||||
short type = in.readShort();
|
||||
in.readShort(); // unused
|
||||
Section section = getSection(type);
|
||||
int size = in.readInt();
|
||||
int offset = in.readInt();
|
||||
|
||||
if ((section.size != 0 && section.size != size)
|
||||
|| (section.off != -1 && section.off != offset)) {
|
||||
throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type));
|
||||
}
|
||||
|
||||
section.size = size;
|
||||
section.off = offset;
|
||||
|
||||
if (previous != null && previous.off > section.off) {
|
||||
throw new DexException("Map is unsorted at " + previous + ", " + section);
|
||||
}
|
||||
|
||||
previous = section;
|
||||
}
|
||||
Arrays.sort(sections);
|
||||
}
|
||||
|
||||
public void computeSizesFromOffsets() {
|
||||
int end = dataOff + dataSize;
|
||||
for (int i = sections.length - 1; i >= 0; i--) {
|
||||
Section section = sections[i];
|
||||
if (section.off == -1) {
|
||||
continue;
|
||||
}
|
||||
if (section.off > end) {
|
||||
throw new DexException("Map is unsorted at " + section);
|
||||
}
|
||||
section.byteCount = end - section.off;
|
||||
end = section.off;
|
||||
}
|
||||
}
|
||||
|
||||
private Section getSection(short type) {
|
||||
for (Section section : sections) {
|
||||
if (section.type == type) {
|
||||
return section;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No such map item: " + type);
|
||||
}
|
||||
|
||||
public void writeHeader(Dex.Section out, int api) throws IOException {
|
||||
out.write(DexFormat.apiToMagic(api).getBytes("UTF-8"));
|
||||
out.writeInt(checksum);
|
||||
out.write(signature);
|
||||
out.writeInt(fileSize);
|
||||
out.writeInt(SizeOf.HEADER_ITEM);
|
||||
out.writeInt(DexFormat.ENDIAN_TAG);
|
||||
out.writeInt(linkSize);
|
||||
out.writeInt(linkOff);
|
||||
out.writeInt(mapList.off);
|
||||
out.writeInt(stringIds.size);
|
||||
out.writeInt(stringIds.off);
|
||||
out.writeInt(typeIds.size);
|
||||
out.writeInt(typeIds.off);
|
||||
out.writeInt(protoIds.size);
|
||||
out.writeInt(protoIds.off);
|
||||
out.writeInt(fieldIds.size);
|
||||
out.writeInt(fieldIds.off);
|
||||
out.writeInt(methodIds.size);
|
||||
out.writeInt(methodIds.off);
|
||||
out.writeInt(classDefs.size);
|
||||
out.writeInt(classDefs.off);
|
||||
out.writeInt(dataSize);
|
||||
out.writeInt(dataOff);
|
||||
}
|
||||
|
||||
public void writeMap(Dex.Section out) throws IOException {
|
||||
int count = 0;
|
||||
for (Section section : sections) {
|
||||
if (section.exists()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
out.writeInt(count);
|
||||
for (Section section : sections) {
|
||||
if (section.exists()) {
|
||||
out.writeShort(section.type);
|
||||
out.writeShort((short) 0);
|
||||
out.writeInt(section.size);
|
||||
out.writeInt(section.off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Section implements Comparable<Section> {
|
||||
public final short type;
|
||||
public int size = 0;
|
||||
public int off = -1;
|
||||
public int byteCount = 0;
|
||||
|
||||
public Section(int type) {
|
||||
this.type = (short) type;
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Section section) {
|
||||
if (off != section.off) {
|
||||
return off < section.off ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex;
|
||||
|
||||
import com.pojavdx.dex.util.Unsigned;
|
||||
|
||||
public final class TypeList implements Comparable<TypeList> {
|
||||
|
||||
public static final TypeList EMPTY = new TypeList(null, Dex.EMPTY_SHORT_ARRAY);
|
||||
|
||||
private final Dex dex;
|
||||
private final short[] types;
|
||||
|
||||
public TypeList(Dex dex, short[] types) {
|
||||
this.dex = dex;
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
public short[] getTypes() {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TypeList other) {
|
||||
for (int i = 0; i < types.length && i < other.types.length; i++) {
|
||||
if (types[i] != other.types[i]) {
|
||||
return Unsigned.compare(types[i], other.types[i]);
|
||||
}
|
||||
}
|
||||
return Unsigned.compare(types.length, other.types.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("(");
|
||||
for (int i = 0, typesLength = types.length; i < typesLength; i++) {
|
||||
result.append(dex != null ? dex.typeNames().get(types[i]) : types[i]);
|
||||
}
|
||||
result.append(")");
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex.util;
|
||||
|
||||
public final class ByteArrayByteInput implements ByteInput {
|
||||
|
||||
private final byte[] bytes;
|
||||
private int position;
|
||||
|
||||
public ByteArrayByteInput(byte... bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() {
|
||||
return bytes[position++];
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex.util;
|
||||
|
||||
/**
|
||||
* A byte source.
|
||||
*/
|
||||
public interface ByteInput {
|
||||
|
||||
/**
|
||||
* Returns a byte.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if all bytes have been read.
|
||||
*/
|
||||
byte readByte();
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex.util;
|
||||
|
||||
/**
|
||||
* A byte sink.
|
||||
*/
|
||||
public interface ByteOutput {
|
||||
|
||||
/**
|
||||
* Writes a byte.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException if all bytes have been written.
|
||||
*/
|
||||
void writeByte(int i);
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex.util;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Exception which carries around structured context.
|
||||
*/
|
||||
public class ExceptionWithContext extends RuntimeException {
|
||||
/** {@code non-null;} human-oriented context of the exception */
|
||||
private StringBuffer context;
|
||||
|
||||
/**
|
||||
* Augments the given exception with the given context, and return the
|
||||
* result. The result is either the given exception if it was an
|
||||
* {@link ExceptionWithContext}, or a newly-constructed exception if it
|
||||
* was not.
|
||||
*
|
||||
* @param ex {@code non-null;} the exception to augment
|
||||
* @param str {@code non-null;} context to add
|
||||
* @return {@code non-null;} an appropriate instance
|
||||
*/
|
||||
public static ExceptionWithContext withContext(Throwable ex, String str) {
|
||||
ExceptionWithContext ewc;
|
||||
|
||||
if (ex instanceof ExceptionWithContext) {
|
||||
ewc = (ExceptionWithContext) ex;
|
||||
} else {
|
||||
ewc = new ExceptionWithContext(ex);
|
||||
}
|
||||
|
||||
ewc.addContext(str);
|
||||
return ewc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param message human-oriented message
|
||||
*/
|
||||
public ExceptionWithContext(String message) {
|
||||
this(message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param cause {@code null-ok;} exception that caused this one
|
||||
*/
|
||||
public ExceptionWithContext(Throwable cause) {
|
||||
this(null, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param message human-oriented message
|
||||
* @param cause {@code null-ok;} exception that caused this one
|
||||
*/
|
||||
public ExceptionWithContext(String message, Throwable cause) {
|
||||
super((message != null) ? message :
|
||||
(cause != null) ? cause.getMessage() : null,
|
||||
cause);
|
||||
|
||||
if (cause instanceof ExceptionWithContext) {
|
||||
String ctx = ((ExceptionWithContext) cause).context.toString();
|
||||
context = new StringBuffer(ctx.length() + 200);
|
||||
context.append(ctx);
|
||||
} else {
|
||||
context = new StringBuffer(200);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void printStackTrace(PrintStream out) {
|
||||
super.printStackTrace(out);
|
||||
out.println(context);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void printStackTrace(PrintWriter out) {
|
||||
super.printStackTrace(out);
|
||||
out.println(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a line of context to this instance.
|
||||
*
|
||||
* @param str {@code non-null;} new context
|
||||
*/
|
||||
public void addContext(String str) {
|
||||
if (str == null) {
|
||||
throw new NullPointerException("str == null");
|
||||
}
|
||||
|
||||
context.append(str);
|
||||
if (!str.endsWith("\n")) {
|
||||
context.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the context.
|
||||
*
|
||||
* @return {@code non-null;} the context
|
||||
*/
|
||||
public String getContext() {
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the message and context.
|
||||
*
|
||||
* @param out {@code non-null;} where to print to
|
||||
*/
|
||||
public void printContext(PrintStream out) {
|
||||
out.println(getMessage());
|
||||
out.print(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the message and context.
|
||||
*
|
||||
* @param out {@code non-null;} where to print to
|
||||
*/
|
||||
public void printContext(PrintWriter out) {
|
||||
out.println(getMessage());
|
||||
out.print(context);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* File I/O utilities.
|
||||
*/
|
||||
public final class FileUtils {
|
||||
private FileUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the named file, translating {@link IOException} to a
|
||||
* {@link RuntimeException} of some sort.
|
||||
*
|
||||
* @param fileName {@code non-null;} name of the file to read
|
||||
* @return {@code non-null;} contents of the file
|
||||
*/
|
||||
public static byte[] readFile(String fileName) {
|
||||
File file = new File(fileName);
|
||||
return readFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given file, translating {@link IOException} to a
|
||||
* {@link RuntimeException} of some sort.
|
||||
*
|
||||
* @param file {@code non-null;} the file to read
|
||||
* @return {@code non-null;} contents of the file
|
||||
*/
|
||||
public static byte[] readFile(File file) {
|
||||
if (!file.exists()) {
|
||||
throw new RuntimeException(file + ": file not found");
|
||||
}
|
||||
|
||||
if (!file.isFile()) {
|
||||
throw new RuntimeException(file + ": not a file");
|
||||
}
|
||||
|
||||
if (!file.canRead()) {
|
||||
throw new RuntimeException(file + ": file not readable");
|
||||
}
|
||||
|
||||
long longLength = file.length();
|
||||
int length = (int) longLength;
|
||||
if (length != longLength) {
|
||||
throw new RuntimeException(file + ": file too long");
|
||||
}
|
||||
|
||||
byte[] result = new byte[length];
|
||||
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
int at = 0;
|
||||
while (length > 0) {
|
||||
int amt = in.read(result, at, length);
|
||||
if (amt == -1) {
|
||||
throw new RuntimeException(file + ": unexpected EOF");
|
||||
}
|
||||
at += amt;
|
||||
length -= amt;
|
||||
}
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(file + ": trouble reading", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code fileName} names a .zip, .jar, or .apk.
|
||||
*/
|
||||
public static boolean hasArchiveSuffix(String fileName) {
|
||||
return fileName.endsWith(".zip")
|
||||
|| fileName.endsWith(".jar")
|
||||
|| fileName.endsWith(".apk");
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dex.util;
|
||||
|
||||
/**
|
||||
* Unsigned arithmetic over Java's signed types.
|
||||
*/
|
||||
public final class Unsigned {
|
||||
private Unsigned() {}
|
||||
|
||||
public static int compare(short ushortA, short ushortB) {
|
||||
if (ushortA == ushortB) {
|
||||
return 0;
|
||||
}
|
||||
int a = ushortA & 0xFFFF;
|
||||
int b = ushortB & 0xFFFF;
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
|
||||
public static int compare(int uintA, int uintB) {
|
||||
if (uintA == uintB) {
|
||||
return 0;
|
||||
}
|
||||
long a = uintA & 0xFFFFFFFFL;
|
||||
long b = uintB & 0xFFFFFFFFL;
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx;
|
||||
|
||||
/**
|
||||
* Version number for dx.
|
||||
*/
|
||||
public class Version {
|
||||
/** {@code non-null;} version string */
|
||||
public static final String VERSION = "1.16";
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
|
||||
/**
|
||||
* Attribute class for {@code AnnotationDefault} attributes.
|
||||
*/
|
||||
public final class AttAnnotationDefault extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "AnnotationDefault";
|
||||
|
||||
/** {@code non-null;} the annotation default value */
|
||||
private final Constant value;
|
||||
|
||||
/** {@code >= 0;} attribute data length in the original classfile (not
|
||||
* including the attribute header) */
|
||||
private final int byteLength;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param value {@code non-null;} the annotation default value
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public AttAnnotationDefault(Constant value, int byteLength) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (value == null) {
|
||||
throw new NullPointerException("value == null");
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.byteLength = byteLength;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
// Add six for the standard attribute header.
|
||||
return byteLength + 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the annotation default value.
|
||||
*
|
||||
* @return {@code non-null;} the value
|
||||
*/
|
||||
public Constant getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.code.BootstrapMethodsList;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code AttBootstrapMethods} attributes.
|
||||
*/
|
||||
public class AttBootstrapMethods extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "BootstrapMethods";
|
||||
|
||||
private static final int ATTRIBUTE_HEADER_BYTES = 8;
|
||||
private static final int BOOTSTRAP_METHOD_BYTES = 4;
|
||||
private static final int BOOTSTRAP_ARGUMENT_BYTES = 2;
|
||||
|
||||
private final BootstrapMethodsList bootstrapMethods;
|
||||
|
||||
private final int byteLength;
|
||||
|
||||
public AttBootstrapMethods(BootstrapMethodsList bootstrapMethods) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
this.bootstrapMethods = bootstrapMethods;
|
||||
|
||||
int bytes = ATTRIBUTE_HEADER_BYTES + bootstrapMethods.size() * BOOTSTRAP_METHOD_BYTES;
|
||||
for (int i = 0; i < bootstrapMethods.size(); ++i) {
|
||||
int numberOfArguments = bootstrapMethods.get(i).getBootstrapMethodArguments().size();
|
||||
bytes += numberOfArguments * BOOTSTRAP_ARGUMENT_BYTES;
|
||||
}
|
||||
this.byteLength = bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return byteLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bootstrap methods present in attribute.
|
||||
* @return bootstrap methods list
|
||||
*/
|
||||
public BootstrapMethodsList getBootstrapMethods() {
|
||||
return bootstrapMethods;
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.code.ByteCatchList;
|
||||
import com.pojavdx.dx.cf.code.BytecodeArray;
|
||||
import com.pojavdx.dx.cf.iface.AttributeList;
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code Code} attributes.
|
||||
*/
|
||||
public final class AttCode extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "Code";
|
||||
|
||||
/** {@code >= 0;} the stack size */
|
||||
private final int maxStack;
|
||||
|
||||
/** {@code >= 0;} the number of locals */
|
||||
private final int maxLocals;
|
||||
|
||||
/** {@code non-null;} array containing the bytecode per se */
|
||||
private final BytecodeArray code;
|
||||
|
||||
/** {@code non-null;} the exception table */
|
||||
private final ByteCatchList catches;
|
||||
|
||||
/** {@code non-null;} the associated list of attributes */
|
||||
private final AttributeList attributes;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param maxStack {@code >= 0;} the stack size
|
||||
* @param maxLocals {@code >= 0;} the number of locals
|
||||
* @param code {@code non-null;} array containing the bytecode per se
|
||||
* @param catches {@code non-null;} the exception table
|
||||
* @param attributes {@code non-null;} the associated list of attributes
|
||||
*/
|
||||
public AttCode(int maxStack, int maxLocals, BytecodeArray code,
|
||||
ByteCatchList catches, AttributeList attributes) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (maxStack < 0) {
|
||||
throw new IllegalArgumentException("maxStack < 0");
|
||||
}
|
||||
|
||||
if (maxLocals < 0) {
|
||||
throw new IllegalArgumentException("maxLocals < 0");
|
||||
}
|
||||
|
||||
if (code == null) {
|
||||
throw new NullPointerException("code == null");
|
||||
}
|
||||
|
||||
try {
|
||||
if (catches.isMutable()) {
|
||||
throw new MutabilityException("catches.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("catches == null");
|
||||
}
|
||||
|
||||
try {
|
||||
if (attributes.isMutable()) {
|
||||
throw new MutabilityException("attributes.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("attributes == null");
|
||||
}
|
||||
|
||||
this.maxStack = maxStack;
|
||||
this.maxLocals = maxLocals;
|
||||
this.code = code;
|
||||
this.catches = catches;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 10 + code.byteLength() + catches.byteLength() +
|
||||
attributes.byteLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum stack size.
|
||||
*
|
||||
* @return {@code >= 0;} the maximum stack size
|
||||
*/
|
||||
public int getMaxStack() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of locals.
|
||||
*
|
||||
* @return {@code >= 0;} the number of locals
|
||||
*/
|
||||
public int getMaxLocals() {
|
||||
return maxLocals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bytecode array.
|
||||
*
|
||||
* @return {@code non-null;} the bytecode array
|
||||
*/
|
||||
public BytecodeArray getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception table.
|
||||
*
|
||||
* @return {@code non-null;} the exception table
|
||||
*/
|
||||
public ByteCatchList getCatches() {
|
||||
return catches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the associated attribute list.
|
||||
*
|
||||
* @return {@code non-null;} the attribute list
|
||||
*/
|
||||
public AttributeList getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstDouble;
|
||||
import com.pojavdx.dx.rop.cst.CstFloat;
|
||||
import com.pojavdx.dx.rop.cst.CstInteger;
|
||||
import com.pojavdx.dx.rop.cst.CstLong;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.TypedConstant;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code ConstantValue} attributes.
|
||||
*/
|
||||
public final class AttConstantValue extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "ConstantValue";
|
||||
|
||||
/** {@code non-null;} the constant value */
|
||||
private final TypedConstant constantValue;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param constantValue {@code non-null;} the constant value, which must
|
||||
* be an instance of one of: {@code CstString},
|
||||
* {@code CstInteger}, {@code CstLong},
|
||||
* {@code CstFloat}, or {@code CstDouble}
|
||||
*/
|
||||
public AttConstantValue(TypedConstant constantValue) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (!((constantValue instanceof CstString) ||
|
||||
(constantValue instanceof CstInteger) ||
|
||||
(constantValue instanceof CstLong) ||
|
||||
(constantValue instanceof CstFloat) ||
|
||||
(constantValue instanceof CstDouble))) {
|
||||
if (constantValue == null) {
|
||||
throw new NullPointerException("constantValue == null");
|
||||
}
|
||||
throw new IllegalArgumentException("bad type for constantValue");
|
||||
}
|
||||
|
||||
this.constantValue = constantValue;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant value of this instance. The returned value
|
||||
* is an instance of one of: {@code CstString},
|
||||
* {@code CstInteger}, {@code CstLong},
|
||||
* {@code CstFloat}, or {@code CstDouble}.
|
||||
*
|
||||
* @return {@code non-null;} the constant value
|
||||
*/
|
||||
public TypedConstant getConstantValue() {
|
||||
return constantValue;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code Deprecated} attributes.
|
||||
*/
|
||||
public final class AttDeprecated extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "Deprecated";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public AttDeprecated() {
|
||||
super(ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstNat;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
|
||||
/**
|
||||
* Attribute class for standards-track {@code EnclosingMethod}
|
||||
* attributes.
|
||||
*/
|
||||
public final class AttEnclosingMethod extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "EnclosingMethod";
|
||||
|
||||
/** {@code non-null;} the innermost enclosing class */
|
||||
private final CstType type;
|
||||
|
||||
/** {@code null-ok;} the name-and-type of the innermost enclosing method, if any */
|
||||
private final CstNat method;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param type {@code non-null;} the innermost enclosing class
|
||||
* @param method {@code null-ok;} the name-and-type of the innermost enclosing
|
||||
* method, if any
|
||||
*/
|
||||
public AttEnclosingMethod(CstType type, CstNat method) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (type == null) {
|
||||
throw new NullPointerException("type == null");
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the innermost enclosing class.
|
||||
*
|
||||
* @return {@code non-null;} the innermost enclosing class
|
||||
*/
|
||||
public CstType getEnclosingClass() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name-and-type of the innermost enclosing method, if
|
||||
* any.
|
||||
*
|
||||
* @return {@code null-ok;} the name-and-type of the innermost enclosing
|
||||
* method, if any
|
||||
*/
|
||||
public CstNat getMethod() {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.type.TypeList;
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code Exceptions} attributes.
|
||||
*/
|
||||
public final class AttExceptions extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "Exceptions";
|
||||
|
||||
/** {@code non-null;} list of exception classes */
|
||||
private final TypeList exceptions;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param exceptions {@code non-null;} list of classes, presumed but not
|
||||
* verified to be subclasses of {@code Throwable}
|
||||
*/
|
||||
public AttExceptions(TypeList exceptions) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
try {
|
||||
if (exceptions.isMutable()) {
|
||||
throw new MutabilityException("exceptions.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("exceptions == null");
|
||||
}
|
||||
|
||||
this.exceptions = exceptions;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 8 + exceptions.size() * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of classes associated with this instance. In
|
||||
* general, these classes are not pre-verified to be subclasses of
|
||||
* {@code Throwable}.
|
||||
*
|
||||
* @return {@code non-null;} the list of classes
|
||||
*/
|
||||
public TypeList getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code InnerClasses} attributes.
|
||||
*/
|
||||
public final class AttInnerClasses extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "InnerClasses";
|
||||
|
||||
/** {@code non-null;} list of inner class entries */
|
||||
private final InnerClassList innerClasses;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param innerClasses {@code non-null;} list of inner class entries
|
||||
*/
|
||||
public AttInnerClasses(InnerClassList innerClasses) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
try {
|
||||
if (innerClasses.isMutable()) {
|
||||
throw new MutabilityException("innerClasses.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("innerClasses == null");
|
||||
}
|
||||
|
||||
this.innerClasses = innerClasses;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 8 + innerClasses.size() * 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of "inner class" entries associated with this instance.
|
||||
*
|
||||
* @return {@code non-null;} the list
|
||||
*/
|
||||
public InnerClassList getInnerClasses() {
|
||||
return innerClasses;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.code.LineNumberList;
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code LineNumberTable} attributes.
|
||||
*/
|
||||
public final class AttLineNumberTable extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "LineNumberTable";
|
||||
|
||||
/** {@code non-null;} list of line number entries */
|
||||
private final LineNumberList lineNumbers;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param lineNumbers {@code non-null;} list of line number entries
|
||||
*/
|
||||
public AttLineNumberTable(LineNumberList lineNumbers) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
try {
|
||||
if (lineNumbers.isMutable()) {
|
||||
throw new MutabilityException("lineNumbers.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("lineNumbers == null");
|
||||
}
|
||||
|
||||
this.lineNumbers = lineNumbers;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 8 + 4 * lineNumbers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of "line number" entries associated with this instance.
|
||||
*
|
||||
* @return {@code non-null;} the list
|
||||
*/
|
||||
public LineNumberList getLineNumbers() {
|
||||
return lineNumbers;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.code.LocalVariableList;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code LocalVariableTable} attributes.
|
||||
*/
|
||||
public final class AttLocalVariableTable extends BaseLocalVariables {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "LocalVariableTable";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param localVariables {@code non-null;} list of local variable entries
|
||||
*/
|
||||
public AttLocalVariableTable(LocalVariableList localVariables) {
|
||||
super(ATTRIBUTE_NAME, localVariables);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.code.LocalVariableList;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code LocalVariableTypeTable} attributes.
|
||||
*/
|
||||
public final class AttLocalVariableTypeTable extends BaseLocalVariables {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "LocalVariableTypeTable";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param localVariables {@code non-null;} list of local variable entries
|
||||
*/
|
||||
public AttLocalVariableTypeTable(LocalVariableList localVariables) {
|
||||
super(ATTRIBUTE_NAME, localVariables);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.annotation.Annotations;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code RuntimeInvisibleAnnotations}
|
||||
* attributes.
|
||||
*/
|
||||
public final class AttRuntimeInvisibleAnnotations extends BaseAnnotations {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "RuntimeInvisibleAnnotations";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param annotations {@code non-null;} the list of annotations
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public AttRuntimeInvisibleAnnotations(Annotations annotations,
|
||||
int byteLength) {
|
||||
super(ATTRIBUTE_NAME, annotations, byteLength);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.annotation.AnnotationsList;
|
||||
|
||||
/**
|
||||
* Attribute class for standard
|
||||
* {@code RuntimeInvisibleParameterAnnotations} attributes.
|
||||
*/
|
||||
public final class AttRuntimeInvisibleParameterAnnotations
|
||||
extends BaseParameterAnnotations {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME =
|
||||
"RuntimeInvisibleParameterAnnotations";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param parameterAnnotations {@code non-null;} the parameter annotations
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public AttRuntimeInvisibleParameterAnnotations(
|
||||
AnnotationsList parameterAnnotations, int byteLength) {
|
||||
super(ATTRIBUTE_NAME, parameterAnnotations, byteLength);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.annotation.Annotations;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code RuntimeVisibleAnnotations}
|
||||
* attributes.
|
||||
*/
|
||||
public final class AttRuntimeVisibleAnnotations extends BaseAnnotations {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "RuntimeVisibleAnnotations";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param annotations {@code non-null;} the list of annotations
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public AttRuntimeVisibleAnnotations(Annotations annotations,
|
||||
int byteLength) {
|
||||
super(ATTRIBUTE_NAME, annotations, byteLength);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.annotation.AnnotationsList;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code RuntimeVisibleParameterAnnotations}
|
||||
* attributes.
|
||||
*/
|
||||
public final class AttRuntimeVisibleParameterAnnotations
|
||||
extends BaseParameterAnnotations {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME =
|
||||
"RuntimeVisibleParameterAnnotations";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param annotations {@code non-null;} the parameter annotations
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public AttRuntimeVisibleParameterAnnotations(
|
||||
AnnotationsList annotations, int byteLength) {
|
||||
super(ATTRIBUTE_NAME, annotations, byteLength);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
|
||||
/**
|
||||
* Attribute class for standards-track {@code Signature} attributes.
|
||||
*/
|
||||
public final class AttSignature extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "Signature";
|
||||
|
||||
/** {@code non-null;} the signature string */
|
||||
private final CstString signature;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param signature {@code non-null;} the signature string
|
||||
*/
|
||||
public AttSignature(CstString signature) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (signature == null) {
|
||||
throw new NullPointerException("signature == null");
|
||||
}
|
||||
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the signature string.
|
||||
*
|
||||
* @return {@code non-null;} the signature string
|
||||
*/
|
||||
public CstString getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code SourceDebugExtension} attributes.
|
||||
*/
|
||||
public final class AttSourceDebugExtension extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "SourceDebugExtension";
|
||||
|
||||
/** {@code non-null;} Contents of SMAP */
|
||||
private final CstString smapString;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param smapString {@code non-null;} the SMAP data from the class file.
|
||||
*/
|
||||
public AttSourceDebugExtension(CstString smapString) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (smapString == null) {
|
||||
throw new NullPointerException("smapString == null");
|
||||
}
|
||||
|
||||
this.smapString = smapString;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
// Add 6 for the standard attribute header: the attribute name
|
||||
// index (2 bytes) and the attribute length (4 bytes).
|
||||
return 6 + smapString.getUtf8Size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SMAP data of this instance.
|
||||
*
|
||||
* @return {@code non-null;} the SMAP data.
|
||||
*/
|
||||
public CstString getSmapString() {
|
||||
return smapString;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code SourceFile} attributes.
|
||||
*/
|
||||
public final class AttSourceFile extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "SourceFile";
|
||||
|
||||
/** {@code non-null;} name of the source file */
|
||||
private final CstString sourceFile;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param sourceFile {@code non-null;} the name of the source file
|
||||
*/
|
||||
public AttSourceFile(CstString sourceFile) {
|
||||
super(ATTRIBUTE_NAME);
|
||||
|
||||
if (sourceFile == null) {
|
||||
throw new NullPointerException("sourceFile == null");
|
||||
}
|
||||
|
||||
this.sourceFile = sourceFile;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source file name of this instance.
|
||||
*
|
||||
* @return {@code non-null;} the source file
|
||||
*/
|
||||
public CstString getSourceFile() {
|
||||
return sourceFile;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
/**
|
||||
* Attribute class for standard {@code Synthetic} attributes.
|
||||
*/
|
||||
public final class AttSynthetic extends BaseAttribute {
|
||||
/** {@code non-null;} attribute name for attributes of this type */
|
||||
public static final String ATTRIBUTE_NAME = "Synthetic";
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public AttSynthetic() {
|
||||
super(ATTRIBUTE_NAME);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.annotation.Annotations;
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Base class for annotations attributes.
|
||||
*/
|
||||
public abstract class BaseAnnotations extends BaseAttribute {
|
||||
/** {@code non-null;} list of annotations */
|
||||
private final Annotations annotations;
|
||||
|
||||
/** {@code >= 0;} attribute data length in the original classfile (not
|
||||
* including the attribute header) */
|
||||
private final int byteLength;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param attributeName {@code non-null;} the name of the attribute
|
||||
* @param annotations {@code non-null;} the list of annotations
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public BaseAnnotations(String attributeName, Annotations annotations,
|
||||
int byteLength) {
|
||||
super(attributeName);
|
||||
|
||||
try {
|
||||
if (annotations.isMutable()) {
|
||||
throw new MutabilityException("annotations.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("annotations == null");
|
||||
}
|
||||
|
||||
this.annotations = annotations;
|
||||
this.byteLength = byteLength;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final int byteLength() {
|
||||
// Add six for the standard attribute header.
|
||||
return byteLength + 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of annotations associated with this instance.
|
||||
*
|
||||
* @return {@code non-null;} the list
|
||||
*/
|
||||
public final Annotations getAnnotations() {
|
||||
return annotations;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.iface.Attribute;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link Attribute}, which directly stores
|
||||
* the attribute name but leaves the rest up to subclasses.
|
||||
*/
|
||||
public abstract class BaseAttribute implements Attribute {
|
||||
/** {@code non-null;} attribute name */
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param name {@code non-null;} attribute name
|
||||
*/
|
||||
public BaseAttribute(String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name == null");
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.cf.code.LocalVariableList;
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Base attribute class for standard {@code LocalVariableTable}
|
||||
* and {@code LocalVariableTypeTable} attributes.
|
||||
*/
|
||||
public abstract class BaseLocalVariables extends BaseAttribute {
|
||||
/** {@code non-null;} list of local variable entries */
|
||||
private final LocalVariableList localVariables;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param name {@code non-null;} attribute name
|
||||
* @param localVariables {@code non-null;} list of local variable entries
|
||||
*/
|
||||
public BaseLocalVariables(String name,
|
||||
LocalVariableList localVariables) {
|
||||
super(name);
|
||||
|
||||
try {
|
||||
if (localVariables.isMutable()) {
|
||||
throw new MutabilityException("localVariables.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("localVariables == null");
|
||||
}
|
||||
|
||||
this.localVariables = localVariables;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final int byteLength() {
|
||||
return 8 + localVariables.size() * 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of "local variable" entries associated with this instance.
|
||||
*
|
||||
* @return {@code non-null;} the list
|
||||
*/
|
||||
public final LocalVariableList getLocalVariables() {
|
||||
return localVariables;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.annotation.AnnotationsList;
|
||||
import com.pojavdx.dx.util.MutabilityException;
|
||||
|
||||
/**
|
||||
* Base class for parameter annotation list attributes.
|
||||
*/
|
||||
public abstract class BaseParameterAnnotations extends BaseAttribute {
|
||||
/** {@code non-null;} list of annotations */
|
||||
private final AnnotationsList parameterAnnotations;
|
||||
|
||||
/** {@code >= 0;} attribute data length in the original classfile (not
|
||||
* including the attribute header) */
|
||||
private final int byteLength;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param attributeName {@code non-null;} the name of the attribute
|
||||
* @param parameterAnnotations {@code non-null;} the annotations
|
||||
* @param byteLength {@code >= 0;} attribute data length in the original
|
||||
* classfile (not including the attribute header)
|
||||
*/
|
||||
public BaseParameterAnnotations(String attributeName,
|
||||
AnnotationsList parameterAnnotations, int byteLength) {
|
||||
super(attributeName);
|
||||
|
||||
try {
|
||||
if (parameterAnnotations.isMutable()) {
|
||||
throw new MutabilityException(
|
||||
"parameterAnnotations.isMutable()");
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Translate the exception.
|
||||
throw new NullPointerException("parameterAnnotations == null");
|
||||
}
|
||||
|
||||
this.parameterAnnotations = parameterAnnotations;
|
||||
this.byteLength = byteLength;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final int byteLength() {
|
||||
// Add six for the standard attribute header.
|
||||
return byteLength + 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of annotation lists associated with this instance.
|
||||
*
|
||||
* @return {@code non-null;} the list
|
||||
*/
|
||||
public final AnnotationsList getParameterAnnotations() {
|
||||
return parameterAnnotations;
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.util.FixedSizeList;
|
||||
|
||||
/**
|
||||
* List of "inner class" entries, which are the contents of
|
||||
* {@code InnerClasses} attributes.
|
||||
*/
|
||||
public final class InnerClassList extends FixedSizeList {
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param count the number of elements to be in the list of inner classes
|
||||
*/
|
||||
public InnerClassList(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated item.
|
||||
*
|
||||
* @param n {@code >= 0;} which item
|
||||
* @return {@code null-ok;} the indicated item
|
||||
*/
|
||||
public Item get(int n) {
|
||||
return (Item) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which class
|
||||
* @param innerClass {@code non-null;} class this item refers to
|
||||
* @param outerClass {@code null-ok;} outer class that this class is a
|
||||
* member of, if any
|
||||
* @param innerName {@code null-ok;} original simple name of this class,
|
||||
* if not anonymous
|
||||
* @param accessFlags original declared access flags
|
||||
*/
|
||||
public void set(int n, CstType innerClass, CstType outerClass,
|
||||
CstString innerName, int accessFlags) {
|
||||
set0(n, new Item(innerClass, outerClass, innerName, accessFlags));
|
||||
}
|
||||
|
||||
/**
|
||||
* Item in an inner classes list.
|
||||
*/
|
||||
public static class Item {
|
||||
/** {@code non-null;} class this item refers to */
|
||||
private final CstType innerClass;
|
||||
|
||||
/** {@code null-ok;} outer class that this class is a member of, if any */
|
||||
private final CstType outerClass;
|
||||
|
||||
/** {@code null-ok;} original simple name of this class, if not anonymous */
|
||||
private final CstString innerName;
|
||||
|
||||
/** original declared access flags */
|
||||
private final int accessFlags;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param innerClass {@code non-null;} class this item refers to
|
||||
* @param outerClass {@code null-ok;} outer class that this class is a
|
||||
* member of, if any
|
||||
* @param innerName {@code null-ok;} original simple name of this
|
||||
* class, if not anonymous
|
||||
* @param accessFlags original declared access flags
|
||||
*/
|
||||
public Item(CstType innerClass, CstType outerClass,
|
||||
CstString innerName, int accessFlags) {
|
||||
if (innerClass == null) {
|
||||
throw new NullPointerException("innerClass == null");
|
||||
}
|
||||
|
||||
this.innerClass = innerClass;
|
||||
this.outerClass = outerClass;
|
||||
this.innerName = innerName;
|
||||
this.accessFlags = accessFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class this item refers to.
|
||||
*
|
||||
* @return {@code non-null;} the class
|
||||
*/
|
||||
public CstType getInnerClass() {
|
||||
return innerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the outer class that this item's class is a member of, if any.
|
||||
*
|
||||
* @return {@code null-ok;} the class
|
||||
*/
|
||||
public CstType getOuterClass() {
|
||||
return outerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original name of this item's class, if not anonymous.
|
||||
*
|
||||
* @return {@code null-ok;} the name
|
||||
*/
|
||||
public CstString getInnerName() {
|
||||
return innerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original declared access flags.
|
||||
*
|
||||
* @return the access flags
|
||||
*/
|
||||
public int getAccessFlags() {
|
||||
return accessFlags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.attrib;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.ConstantPool;
|
||||
import com.pojavdx.dx.util.ByteArray;
|
||||
|
||||
/**
|
||||
* Raw attribute, for holding onto attributes that are unrecognized.
|
||||
*/
|
||||
public final class RawAttribute extends BaseAttribute {
|
||||
/** {@code non-null;} attribute data */
|
||||
private final ByteArray data;
|
||||
|
||||
/**
|
||||
* {@code null-ok;} constant pool to use for resolution of cpis in {@link
|
||||
* #data}
|
||||
*/
|
||||
private final ConstantPool pool;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param name {@code non-null;} attribute name
|
||||
* @param data {@code non-null;} attribute data
|
||||
* @param pool {@code null-ok;} constant pool to use for cpi resolution
|
||||
*/
|
||||
public RawAttribute(String name, ByteArray data, ConstantPool pool) {
|
||||
super(name);
|
||||
|
||||
if (data == null) {
|
||||
throw new NullPointerException("data == null");
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance from a sub-array of a {@link ByteArray}.
|
||||
*
|
||||
* @param name {@code non-null;} attribute name
|
||||
* @param data {@code non-null;} array containing the attribute data
|
||||
* @param offset offset in {@code data} to the attribute data
|
||||
* @param length length of the attribute data, in bytes
|
||||
* @param pool {@code null-ok;} constant pool to use for cpi resolution
|
||||
*/
|
||||
public RawAttribute(String name, ByteArray data, int offset,
|
||||
int length, ConstantPool pool) {
|
||||
this(name, data.slice(offset, offset + length), pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw data of the attribute.
|
||||
*
|
||||
* @return {@code non-null;} the data
|
||||
*/
|
||||
public ByteArray getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int byteLength() {
|
||||
return data.size() + 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant pool to use for cpi resolution, if any. It
|
||||
* presumably came from the class file that this attribute came
|
||||
* from.
|
||||
*
|
||||
* @return {@code null-ok;} the constant pool
|
||||
*/
|
||||
public ConstantPool getPool() {
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<body>
|
||||
<p>Implementation of containers and utilities for all the standard Java
|
||||
attribute types.</p>
|
||||
|
||||
<p><b>PACKAGES USED:</b>
|
||||
<ul>
|
||||
<li><code>com.pojavdx.dx.cf.iface</code></li>
|
||||
<li><code>com.pojavdx.dx.rop.pool</code></li>
|
||||
<li><code>com.pojavdx.dx.util</code></li>
|
||||
</ul>
|
||||
</body>
|
||||
@@ -1,595 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.code.LocalItem;
|
||||
import com.pojavdx.dx.rop.code.RegisterSpec;
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.type.Prototype;
|
||||
import com.pojavdx.dx.rop.type.StdTypeList;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link Machine}.
|
||||
*
|
||||
* <p><b>Note:</b> For the most part, the documentation for this class
|
||||
* ignores the distinction between {@link Type} and {@link
|
||||
* TypeBearer}.</p>
|
||||
*/
|
||||
public abstract class BaseMachine implements Machine {
|
||||
/* {@code non-null;} the prototype for the associated method */
|
||||
private final Prototype prototype;
|
||||
|
||||
/** {@code non-null;} primary arguments */
|
||||
private TypeBearer[] args;
|
||||
|
||||
/** {@code >= 0;} number of primary arguments */
|
||||
private int argCount;
|
||||
|
||||
/** {@code null-ok;} type of the operation, if salient */
|
||||
private Type auxType;
|
||||
|
||||
/** auxiliary {@code int} argument */
|
||||
private int auxInt;
|
||||
|
||||
/** {@code null-ok;} auxiliary constant argument */
|
||||
private Constant auxCst;
|
||||
|
||||
/** auxiliary branch target argument */
|
||||
private int auxTarget;
|
||||
|
||||
/** {@code null-ok;} auxiliary switch cases argument */
|
||||
private SwitchList auxCases;
|
||||
|
||||
/** {@code null-ok;} auxiliary initial value list for newarray */
|
||||
private ArrayList<Constant> auxInitValues;
|
||||
|
||||
/** {@code >= -1;} last local accessed */
|
||||
private int localIndex;
|
||||
|
||||
/** specifies if local has info in the local variable table */
|
||||
private boolean localInfo;
|
||||
|
||||
/** {@code null-ok;} local target spec, if salient and calculated */
|
||||
private RegisterSpec localTarget;
|
||||
|
||||
/** {@code non-null;} results */
|
||||
private TypeBearer[] results;
|
||||
|
||||
/**
|
||||
* {@code >= -1;} count of the results, or {@code -1} if no results
|
||||
* have been set
|
||||
*/
|
||||
private int resultCount;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param prototype {@code non-null;} the prototype for the
|
||||
* associated method
|
||||
*/
|
||||
public BaseMachine(Prototype prototype) {
|
||||
if (prototype == null) {
|
||||
throw new NullPointerException("prototype == null");
|
||||
}
|
||||
|
||||
this.prototype = prototype;
|
||||
args = new TypeBearer[10];
|
||||
results = new TypeBearer[6];
|
||||
clearArgs();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Prototype getPrototype() {
|
||||
return prototype;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void clearArgs() {
|
||||
argCount = 0;
|
||||
auxType = null;
|
||||
auxInt = 0;
|
||||
auxCst = null;
|
||||
auxTarget = 0;
|
||||
auxCases = null;
|
||||
auxInitValues = null;
|
||||
localIndex = -1;
|
||||
localInfo = false;
|
||||
localTarget = null;
|
||||
resultCount = -1;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void popArgs(Frame frame, int count) {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
|
||||
clearArgs();
|
||||
|
||||
if (count > args.length) {
|
||||
// Grow args, and add a little extra room to grow even more.
|
||||
args = new TypeBearer[count + 10];
|
||||
}
|
||||
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
args[i] = stack.pop();
|
||||
}
|
||||
|
||||
argCount = count;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void popArgs(Frame frame, Prototype prototype) {
|
||||
StdTypeList types = prototype.getParameterTypes();
|
||||
int size = types.size();
|
||||
|
||||
// Use the above method to do the actual popping...
|
||||
popArgs(frame, size);
|
||||
|
||||
// ...and then verify the popped types.
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (! Merger.isPossiblyAssignableFrom(types.getType(i), args[i])) {
|
||||
throw new SimException("at stack depth " + (size - 1 - i) +
|
||||
", expected type " + types.getType(i).toHuman() +
|
||||
" but found " + args[i].getType().toHuman());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void popArgs(Frame frame, Type type) {
|
||||
// Use the above method to do the actual popping...
|
||||
popArgs(frame, 1);
|
||||
|
||||
// ...and then verify the popped type.
|
||||
if (! Merger.isPossiblyAssignableFrom(type, args[0])) {
|
||||
throw new SimException("expected type " + type.toHuman() +
|
||||
" but found " + args[0].getType().toHuman());
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void popArgs(Frame frame, Type type1, Type type2) {
|
||||
// Use the above method to do the actual popping...
|
||||
popArgs(frame, 2);
|
||||
|
||||
// ...and then verify the popped types.
|
||||
|
||||
if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
|
||||
throw new SimException("expected type " + type1.toHuman() +
|
||||
" but found " + args[0].getType().toHuman());
|
||||
}
|
||||
|
||||
if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
|
||||
throw new SimException("expected type " + type2.toHuman() +
|
||||
" but found " + args[1].getType().toHuman());
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void popArgs(Frame frame, Type type1, Type type2,
|
||||
Type type3) {
|
||||
// Use the above method to do the actual popping...
|
||||
popArgs(frame, 3);
|
||||
|
||||
// ...and then verify the popped types.
|
||||
|
||||
if (! Merger.isPossiblyAssignableFrom(type1, args[0])) {
|
||||
throw new SimException("expected type " + type1.toHuman() +
|
||||
" but found " + args[0].getType().toHuman());
|
||||
}
|
||||
|
||||
if (! Merger.isPossiblyAssignableFrom(type2, args[1])) {
|
||||
throw new SimException("expected type " + type2.toHuman() +
|
||||
" but found " + args[1].getType().toHuman());
|
||||
}
|
||||
|
||||
if (! Merger.isPossiblyAssignableFrom(type3, args[2])) {
|
||||
throw new SimException("expected type " + type3.toHuman() +
|
||||
" but found " + args[2].getType().toHuman());
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void localArg(Frame frame, int idx) {
|
||||
clearArgs();
|
||||
args[0] = frame.getLocals().get(idx);
|
||||
argCount = 1;
|
||||
localIndex = idx;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void localInfo(boolean local) {
|
||||
localInfo = local;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void auxType(Type type) {
|
||||
auxType = type;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void auxIntArg(int value) {
|
||||
auxInt = value;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void auxCstArg(Constant cst) {
|
||||
if (cst == null) {
|
||||
throw new NullPointerException("cst == null");
|
||||
}
|
||||
|
||||
auxCst = cst;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void auxTargetArg(int target) {
|
||||
auxTarget = target;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void auxSwitchArg(SwitchList cases) {
|
||||
if (cases == null) {
|
||||
throw new NullPointerException("cases == null");
|
||||
}
|
||||
|
||||
auxCases = cases;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void auxInitValues(ArrayList<Constant> initValues) {
|
||||
auxInitValues = initValues;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public final void localTarget(int idx, Type type, LocalItem local) {
|
||||
localTarget = RegisterSpec.makeLocalOptional(idx, type, local);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of primary arguments.
|
||||
*
|
||||
* @return {@code >= 0;} the number of primary arguments
|
||||
*/
|
||||
protected final int argCount() {
|
||||
return argCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of the arguments (where a category-2 value counts as
|
||||
* two).
|
||||
*
|
||||
* @return {@code >= 0;} the argument width
|
||||
*/
|
||||
protected final int argWidth() {
|
||||
int result = 0;
|
||||
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
result += args[i].getType().getCategory();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@code n}th primary argument.
|
||||
*
|
||||
* @param n {@code >= 0, < argCount();} which argument
|
||||
* @return {@code non-null;} the indicated argument
|
||||
*/
|
||||
protected final TypeBearer arg(int n) {
|
||||
if (n >= argCount) {
|
||||
throw new IllegalArgumentException("n >= argCount");
|
||||
}
|
||||
|
||||
try {
|
||||
return args[n];
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
// Translate the exception.
|
||||
throw new IllegalArgumentException("n < 0");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type auxiliary argument.
|
||||
*
|
||||
* @return {@code null-ok;} the salient type
|
||||
*/
|
||||
protected final Type getAuxType() {
|
||||
return auxType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@code int} auxiliary argument.
|
||||
*
|
||||
* @return the argument value
|
||||
*/
|
||||
protected final int getAuxInt() {
|
||||
return auxInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant auxiliary argument.
|
||||
*
|
||||
* @return {@code null-ok;} the argument value
|
||||
*/
|
||||
protected final Constant getAuxCst() {
|
||||
return auxCst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the branch target auxiliary argument.
|
||||
*
|
||||
* @return the argument value
|
||||
*/
|
||||
protected final int getAuxTarget() {
|
||||
return auxTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the switch cases auxiliary argument.
|
||||
*
|
||||
* @return {@code null-ok;} the argument value
|
||||
*/
|
||||
protected final SwitchList getAuxCases() {
|
||||
return auxCases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the init values auxiliary argument.
|
||||
*
|
||||
* @return {@code null-ok;} the argument value
|
||||
*/
|
||||
protected final ArrayList<Constant> getInitValues() {
|
||||
return auxInitValues;
|
||||
}
|
||||
/**
|
||||
* Gets the last local index accessed.
|
||||
*
|
||||
* @return {@code >= -1;} the salient local index or {@code -1} if none
|
||||
* was set since the last time {@link #clearArgs} was called
|
||||
*/
|
||||
protected final int getLocalIndex() {
|
||||
return localIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the loaded local has info in the local variable table.
|
||||
*
|
||||
* @return {@code true} if local arg has info in the local variable table
|
||||
*/
|
||||
protected final boolean getLocalInfo() {
|
||||
return localInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target local register spec of the current operation, if any.
|
||||
* The local target spec is the combination of the values indicated
|
||||
* by a previous call to {@link #localTarget} with the type of what
|
||||
* should be the sole result set by a call to {@link #setResult} (or
|
||||
* the combination {@link #clearResult} then {@link #addResult}.
|
||||
*
|
||||
* @param isMove {@code true} if the operation being performed on the
|
||||
* local is a move. This will cause constant values to be propagated
|
||||
* to the returned local
|
||||
* @return {@code null-ok;} the salient register spec or {@code null} if no
|
||||
* local target was set since the last time {@link #clearArgs} was
|
||||
* called
|
||||
*/
|
||||
protected final RegisterSpec getLocalTarget(boolean isMove) {
|
||||
if (localTarget == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resultCount != 1) {
|
||||
throw new SimException("local target with " +
|
||||
((resultCount == 0) ? "no" : "multiple") + " results");
|
||||
}
|
||||
|
||||
TypeBearer result = results[0];
|
||||
Type resultType = result.getType();
|
||||
Type localType = localTarget.getType();
|
||||
|
||||
if (resultType == localType) {
|
||||
/*
|
||||
* If this is to be a move operation and the result is a
|
||||
* known value, make the returned localTarget embody that
|
||||
* value.
|
||||
*/
|
||||
if (isMove) {
|
||||
return localTarget.withType(result);
|
||||
} else {
|
||||
return localTarget;
|
||||
}
|
||||
}
|
||||
|
||||
if (! Merger.isPossiblyAssignableFrom(localType, resultType)) {
|
||||
// The result and local types are inconsistent. Complain!
|
||||
throwLocalMismatch(resultType, localType);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (localType == Type.OBJECT) {
|
||||
/*
|
||||
* The result type is more specific than the local type,
|
||||
* so use that instead.
|
||||
*/
|
||||
localTarget = localTarget.withType(result);
|
||||
}
|
||||
|
||||
return localTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the results.
|
||||
*/
|
||||
protected final void clearResult() {
|
||||
resultCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the results list to be the given single value.
|
||||
*
|
||||
* <p><b>Note:</b> If there is more than one result value, the
|
||||
* others may be added by using {@link #addResult}.</p>
|
||||
*
|
||||
* @param result {@code non-null;} result value
|
||||
*/
|
||||
protected final void setResult(TypeBearer result) {
|
||||
if (result == null) {
|
||||
throw new NullPointerException("result == null");
|
||||
}
|
||||
|
||||
results[0] = result;
|
||||
resultCount = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an additional element to the list of results.
|
||||
*
|
||||
* @see #setResult
|
||||
*
|
||||
* @param result {@code non-null;} result value
|
||||
*/
|
||||
protected final void addResult(TypeBearer result) {
|
||||
if (result == null) {
|
||||
throw new NullPointerException("result == null");
|
||||
}
|
||||
|
||||
results[resultCount] = result;
|
||||
resultCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of results. This throws an exception if results were
|
||||
* never set. (Explicitly clearing the results counts as setting them.)
|
||||
*
|
||||
* @return {@code >= 0;} the count
|
||||
*/
|
||||
protected final int resultCount() {
|
||||
if (resultCount < 0) {
|
||||
throw new SimException("results never set");
|
||||
}
|
||||
|
||||
return resultCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of the results (where a category-2 value counts as
|
||||
* two).
|
||||
*
|
||||
* @return {@code >= 0;} the result width
|
||||
*/
|
||||
protected final int resultWidth() {
|
||||
int width = 0;
|
||||
|
||||
for (int i = 0; i < resultCount; i++) {
|
||||
width += results[i].getType().getCategory();
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@code n}th result value.
|
||||
*
|
||||
* @param n {@code >= 0, < resultCount();} which result
|
||||
* @return {@code non-null;} the indicated result value
|
||||
*/
|
||||
protected final TypeBearer result(int n) {
|
||||
if (n >= resultCount) {
|
||||
throw new IllegalArgumentException("n >= resultCount");
|
||||
}
|
||||
|
||||
try {
|
||||
return results[n];
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
// Translate the exception.
|
||||
throw new IllegalArgumentException("n < 0");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the results of the latest operation into the given frame. If
|
||||
* there is a local target (see {@link #localTarget}), then the sole
|
||||
* result is stored to that target; otherwise any results are pushed
|
||||
* onto the stack.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
*/
|
||||
protected final void storeResults(Frame frame) {
|
||||
if (resultCount < 0) {
|
||||
throw new SimException("results never set");
|
||||
}
|
||||
|
||||
if (resultCount == 0) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (localTarget != null) {
|
||||
/*
|
||||
* Note: getLocalTarget() doesn't necessarily return
|
||||
* localTarget directly.
|
||||
*/
|
||||
frame.getLocals().set(getLocalTarget(false));
|
||||
} else {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
for (int i = 0; i < resultCount; i++) {
|
||||
if (localInfo) {
|
||||
stack.setLocal();
|
||||
}
|
||||
stack.push(results[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception that indicates a mismatch in local variable
|
||||
* types.
|
||||
*
|
||||
* @param found {@code non-null;} the encountered type
|
||||
* @param local {@code non-null;} the local variable's claimed type
|
||||
*/
|
||||
public static void throwLocalMismatch(TypeBearer found,
|
||||
TypeBearer local) {
|
||||
throw new SimException("local variable type mismatch: " +
|
||||
"attempt to set or access a value of type " +
|
||||
found.toHuman() +
|
||||
" using a local variable of type " +
|
||||
local.toHuman() +
|
||||
". This is symptomatic of .class transformation tools " +
|
||||
"that ignore local variable information.");
|
||||
}
|
||||
}
|
||||
@@ -1,465 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.cst.CstInvokeDynamic;
|
||||
import com.pojavdx.dx.rop.cst.CstMemberRef;
|
||||
import com.pojavdx.dx.rop.cst.CstMethodHandle;
|
||||
import com.pojavdx.dx.rop.cst.CstProtoRef;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.util.Bits;
|
||||
import com.pojavdx.dx.util.IntList;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Utility that identifies basic blocks in bytecode.
|
||||
*/
|
||||
public final class BasicBlocker implements BytecodeArray.Visitor {
|
||||
/** {@code non-null;} method being converted */
|
||||
private final ConcreteMethod method;
|
||||
|
||||
/**
|
||||
* {@code non-null;} work set; bits indicate offsets in need of
|
||||
* examination
|
||||
*/
|
||||
private final int[] workSet;
|
||||
|
||||
/**
|
||||
* {@code non-null;} live set; bits indicate potentially-live
|
||||
* opcodes; contrawise, a bit that isn't on is either in the
|
||||
* middle of an instruction or is a definitely-dead opcode
|
||||
*/
|
||||
private final int[] liveSet;
|
||||
|
||||
/**
|
||||
* {@code non-null;} block start set; bits indicate the starts of
|
||||
* basic blocks, including the opcodes that start blocks of
|
||||
* definitely-dead code
|
||||
*/
|
||||
private final int[] blockSet;
|
||||
|
||||
/**
|
||||
* {@code non-null, sparse;} for each instruction offset to a branch of
|
||||
* some sort, the list of targets for that instruction
|
||||
*/
|
||||
private final IntList[] targetLists;
|
||||
|
||||
/**
|
||||
* {@code non-null, sparse;} for each instruction offset to a throwing
|
||||
* instruction, the list of exception handlers for that instruction
|
||||
*/
|
||||
private final ByteCatchList[] catchLists;
|
||||
|
||||
/** offset of the previously parsed bytecode */
|
||||
private int previousOffset;
|
||||
|
||||
/**
|
||||
* Identifies and enumerates the basic blocks in the given method,
|
||||
* returning a list of them. The returned list notably omits any
|
||||
* definitely-dead code that is identified in the process.
|
||||
*
|
||||
* @param method {@code non-null;} method to convert
|
||||
* @return {@code non-null;} list of basic blocks
|
||||
*/
|
||||
public static ByteBlockList identifyBlocks(ConcreteMethod method) {
|
||||
BasicBlocker bb = new BasicBlocker(method);
|
||||
|
||||
bb.doit();
|
||||
return bb.getBlockList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance. This class is not publicly instantiable; use
|
||||
* {@link #identifyBlocks}.
|
||||
*
|
||||
* @param method {@code non-null;} method to convert
|
||||
*/
|
||||
private BasicBlocker(ConcreteMethod method) {
|
||||
if (method == null) {
|
||||
throw new NullPointerException("method == null");
|
||||
}
|
||||
|
||||
this.method = method;
|
||||
|
||||
/*
|
||||
* The "+1" below is so the idx-past-end is also valid,
|
||||
* avoiding a special case, but without preventing
|
||||
* flow-of-control falling past the end of the method from
|
||||
* getting properly reported.
|
||||
*/
|
||||
int sz = method.getCode().size() + 1;
|
||||
|
||||
workSet = Bits.makeBitSet(sz);
|
||||
liveSet = Bits.makeBitSet(sz);
|
||||
blockSet = Bits.makeBitSet(sz);
|
||||
targetLists = new IntList[sz];
|
||||
catchLists = new ByteCatchList[sz];
|
||||
previousOffset = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: These methods are defined implementation of the interface
|
||||
* BytecodeArray.Visitor; since the class isn't publicly
|
||||
* instantiable, no external code ever gets a chance to actually
|
||||
* call these methods.
|
||||
*/
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitInvalid(int opcode, int offset, int length) {
|
||||
visitCommon(offset, length, true);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitNoArgs(int opcode, int offset, int length, Type type) {
|
||||
switch (opcode) {
|
||||
case ByteOps.IRETURN:
|
||||
case ByteOps.RETURN: {
|
||||
visitCommon(offset, length, false);
|
||||
targetLists[offset] = IntList.EMPTY;
|
||||
break;
|
||||
}
|
||||
case ByteOps.ATHROW: {
|
||||
visitCommon(offset, length, false);
|
||||
visitThrowing(offset, length, false);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IALOAD:
|
||||
case ByteOps.LALOAD:
|
||||
case ByteOps.FALOAD:
|
||||
case ByteOps.DALOAD:
|
||||
case ByteOps.AALOAD:
|
||||
case ByteOps.BALOAD:
|
||||
case ByteOps.CALOAD:
|
||||
case ByteOps.SALOAD:
|
||||
case ByteOps.IASTORE:
|
||||
case ByteOps.LASTORE:
|
||||
case ByteOps.FASTORE:
|
||||
case ByteOps.DASTORE:
|
||||
case ByteOps.AASTORE:
|
||||
case ByteOps.BASTORE:
|
||||
case ByteOps.CASTORE:
|
||||
case ByteOps.SASTORE:
|
||||
case ByteOps.ARRAYLENGTH:
|
||||
case ByteOps.MONITORENTER:
|
||||
case ByteOps.MONITOREXIT: {
|
||||
/*
|
||||
* These instructions can all throw, so they have to end
|
||||
* the block they appear in (since throws are branches).
|
||||
*/
|
||||
visitCommon(offset, length, true);
|
||||
visitThrowing(offset, length, true);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IDIV:
|
||||
case ByteOps.IREM: {
|
||||
/*
|
||||
* The int and long versions of division and remainder may
|
||||
* throw, but not the other types.
|
||||
*/
|
||||
visitCommon(offset, length, true);
|
||||
if ((type == Type.INT) || (type == Type.LONG)) {
|
||||
visitThrowing(offset, length, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
visitCommon(offset, length, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitLocal(int opcode, int offset, int length,
|
||||
int idx, Type type, int value) {
|
||||
if (opcode == ByteOps.RET) {
|
||||
visitCommon(offset, length, false);
|
||||
targetLists[offset] = IntList.EMPTY;
|
||||
} else {
|
||||
visitCommon(offset, length, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitConstant(int opcode, int offset, int length,
|
||||
Constant cst, int value) {
|
||||
visitCommon(offset, length, true);
|
||||
|
||||
if (cst instanceof CstMemberRef || cst instanceof CstType ||
|
||||
cst instanceof CstString || cst instanceof CstInvokeDynamic ||
|
||||
cst instanceof CstMethodHandle || cst instanceof CstProtoRef) {
|
||||
/*
|
||||
* Instructions with these sorts of constants have the
|
||||
* possibility of throwing, so this instruction needs to
|
||||
* end its block (since it can throw, and possible-throws
|
||||
* are branch points).
|
||||
*/
|
||||
visitThrowing(offset, length, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitBranch(int opcode, int offset, int length,
|
||||
int target) {
|
||||
switch (opcode) {
|
||||
case ByteOps.GOTO: {
|
||||
visitCommon(offset, length, false);
|
||||
targetLists[offset] = IntList.makeImmutable(target);
|
||||
break;
|
||||
}
|
||||
case ByteOps.JSR: {
|
||||
/*
|
||||
* Each jsr is quarantined into a separate block (containing
|
||||
* only the jsr instruction) but is otherwise treated
|
||||
* as a conditional branch. (That is to say, both its
|
||||
* target and next instruction begin new blocks.)
|
||||
*/
|
||||
addWorkIfNecessary(offset, true);
|
||||
// Fall through to next case...
|
||||
}
|
||||
default: {
|
||||
int next = offset + length;
|
||||
visitCommon(offset, length, true);
|
||||
addWorkIfNecessary(next, true);
|
||||
targetLists[offset] = IntList.makeImmutable(next, target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
addWorkIfNecessary(target, true);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitSwitch(int opcode, int offset, int length,
|
||||
SwitchList cases, int padding) {
|
||||
visitCommon(offset, length, false);
|
||||
addWorkIfNecessary(cases.getDefaultTarget(), true);
|
||||
|
||||
int sz = cases.size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
addWorkIfNecessary(cases.getTarget(i), true);
|
||||
}
|
||||
|
||||
targetLists[offset] = cases.getTargets();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitNewarray(int offset, int length, CstType type,
|
||||
ArrayList<Constant> intVals) {
|
||||
visitCommon(offset, length, true);
|
||||
visitThrowing(offset, length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the list of basic blocks from the bit sets.
|
||||
*
|
||||
* @return {@code non-null;} the list of basic blocks
|
||||
*/
|
||||
private ByteBlockList getBlockList() {
|
||||
BytecodeArray bytes = method.getCode();
|
||||
ByteBlock[] bbs = new ByteBlock[bytes.size()];
|
||||
int count = 0;
|
||||
|
||||
for (int at = 0, next; /*at*/; at = next) {
|
||||
next = Bits.findFirst(blockSet, at + 1);
|
||||
if (next < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (Bits.get(liveSet, at)) {
|
||||
/*
|
||||
* Search backward for the branch or throwing
|
||||
* instruction at the end of this block, if any. If
|
||||
* there isn't any, then "next" is the sole target.
|
||||
*/
|
||||
IntList targets = null;
|
||||
int targetsAt = -1;
|
||||
ByteCatchList blockCatches;
|
||||
|
||||
for (int i = next - 1; i >= at; i--) {
|
||||
targets = targetLists[i];
|
||||
if (targets != null) {
|
||||
targetsAt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targets == null) {
|
||||
targets = IntList.makeImmutable(next);
|
||||
blockCatches = ByteCatchList.EMPTY;
|
||||
} else {
|
||||
blockCatches = catchLists[targetsAt];
|
||||
if (blockCatches == null) {
|
||||
blockCatches = ByteCatchList.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
bbs[count] =
|
||||
new ByteBlock(at, at, next, targets, blockCatches);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
ByteBlockList result = new ByteBlockList(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
result.set(i, bbs[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does basic block identification.
|
||||
*/
|
||||
private void doit() {
|
||||
BytecodeArray bytes = method.getCode();
|
||||
ByteCatchList catches = method.getCatches();
|
||||
int catchSz = catches.size();
|
||||
|
||||
/*
|
||||
* Start by setting offset 0 as the start of a block and in need
|
||||
* of work...
|
||||
*/
|
||||
Bits.set(workSet, 0);
|
||||
Bits.set(blockSet, 0);
|
||||
|
||||
/*
|
||||
* And then process the work set, add new work based on
|
||||
* exception ranges that are active, and iterate until there's
|
||||
* nothing left to work on.
|
||||
*/
|
||||
while (!Bits.isEmpty(workSet)) {
|
||||
try {
|
||||
bytes.processWorkSet(workSet, this);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Translate the exception.
|
||||
throw new SimException("flow of control falls off " +
|
||||
"end of method",
|
||||
ex);
|
||||
}
|
||||
|
||||
for (int i = 0; i < catchSz; i++) {
|
||||
ByteCatchList.Item item = catches.get(i);
|
||||
int start = item.getStartPc();
|
||||
int end = item.getEndPc();
|
||||
if (Bits.anyInRange(liveSet, start, end)) {
|
||||
Bits.set(blockSet, start);
|
||||
Bits.set(blockSet, end);
|
||||
addWorkIfNecessary(item.getHandlerPc(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a bit in the work set, but only if the instruction in question
|
||||
* isn't yet known to be possibly-live.
|
||||
*
|
||||
* @param offset offset to the instruction in question
|
||||
* @param blockStart {@code true} iff this instruction starts a
|
||||
* basic block
|
||||
*/
|
||||
private void addWorkIfNecessary(int offset, boolean blockStart) {
|
||||
if (!Bits.get(liveSet, offset)) {
|
||||
Bits.set(workSet, offset);
|
||||
}
|
||||
|
||||
if (blockStart) {
|
||||
Bits.set(blockSet, offset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by all the visitor methods.
|
||||
*
|
||||
* @param offset offset to the instruction
|
||||
* @param length length of the instruction, in bytes
|
||||
* @param nextIsLive {@code true} iff the instruction after
|
||||
* the indicated one is possibly-live (because this one isn't an
|
||||
* unconditional branch, a return, or a switch)
|
||||
*/
|
||||
private void visitCommon(int offset, int length, boolean nextIsLive) {
|
||||
Bits.set(liveSet, offset);
|
||||
|
||||
if (nextIsLive) {
|
||||
/*
|
||||
* If the next instruction is flowed to by this one, just
|
||||
* add it to the work set, and then a subsequent visit*()
|
||||
* will deal with it as appropriate.
|
||||
*/
|
||||
addWorkIfNecessary(offset + length, false);
|
||||
} else {
|
||||
/*
|
||||
* If the next instruction isn't flowed to by this one,
|
||||
* then mark it as a start of a block but *don't* add it
|
||||
* to the work set, so that in the final phase we can know
|
||||
* dead code blocks as those marked as blocks but not also marked
|
||||
* live.
|
||||
*/
|
||||
Bits.set(blockSet, offset + length);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by all the visitor methods that deal with
|
||||
* opcodes that possibly throw. This method should be called after calling
|
||||
* {@link #visitCommon}.
|
||||
*
|
||||
* @param offset offset to the instruction
|
||||
* @param length length of the instruction, in bytes
|
||||
* @param nextIsLive {@code true} iff the instruction after
|
||||
* the indicated one is possibly-live (because this one isn't an
|
||||
* unconditional throw)
|
||||
*/
|
||||
private void visitThrowing(int offset, int length, boolean nextIsLive) {
|
||||
int next = offset + length;
|
||||
|
||||
if (nextIsLive) {
|
||||
addWorkIfNecessary(next, true);
|
||||
}
|
||||
|
||||
ByteCatchList catches = method.getCatches().listFor(offset);
|
||||
catchLists[offset] = catches;
|
||||
targetLists[offset] = catches.toTargetList(nextIsLive ? next : -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setPreviousOffset(int offset) {
|
||||
previousOffset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getPreviousOffset() {
|
||||
return previousOffset;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.cst.CstDouble;
|
||||
import com.pojavdx.dx.rop.cst.CstFloat;
|
||||
import com.pojavdx.dx.rop.cst.CstInteger;
|
||||
import com.pojavdx.dx.rop.cst.CstLong;
|
||||
import com.pojavdx.dx.rop.cst.CstMethodHandle;
|
||||
import com.pojavdx.dx.rop.cst.CstProtoRef;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.util.FixedSizeList;
|
||||
|
||||
/**
|
||||
* List of bootstrap method arguments, which are part of the contents of
|
||||
* {@code BootstrapMethods} attributes.
|
||||
*/
|
||||
public class BootstrapMethodArgumentsList extends FixedSizeList {
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param count the number of elements to be in the list
|
||||
*/
|
||||
public BootstrapMethodArgumentsList(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bootstrap argument from the indicated position.
|
||||
*
|
||||
* @param n position of argument to get
|
||||
* @return {@code Constant} instance
|
||||
*/
|
||||
public Constant get(int n) {
|
||||
return (Constant) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bootstrap argument at the indicated position.
|
||||
*
|
||||
* @param n position of argument to set
|
||||
* @param cst {@code Constant} instance
|
||||
*/
|
||||
public void set(int n, Constant cst) {
|
||||
// The set of permitted types is defined by the JVMS 8, section 4.7.23.
|
||||
if (cst instanceof CstString ||
|
||||
cst instanceof CstType ||
|
||||
cst instanceof CstInteger ||
|
||||
cst instanceof CstLong ||
|
||||
cst instanceof CstFloat ||
|
||||
cst instanceof CstDouble ||
|
||||
cst instanceof CstMethodHandle ||
|
||||
cst instanceof CstProtoRef) {
|
||||
set0(n, cst);
|
||||
} else {
|
||||
Class<?> klass = cst.getClass();
|
||||
throw new IllegalArgumentException("bad type for bootstrap argument: " + klass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstMethodHandle;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.util.FixedSizeList;
|
||||
|
||||
/**
|
||||
* List of bootstrap method entries, which are the contents of
|
||||
* {@code BootstrapMethods} attributes.
|
||||
*/
|
||||
public class BootstrapMethodsList extends FixedSizeList {
|
||||
/** {@code non-null;} zero-size instance */
|
||||
public static final BootstrapMethodsList EMPTY = new BootstrapMethodsList(0);
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param count the number of elements to be in the list
|
||||
*/
|
||||
public BootstrapMethodsList(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated item.
|
||||
*
|
||||
* @param n {@code >= 0;} which item
|
||||
* @return {@code null-ok;} the indicated item
|
||||
*/
|
||||
public Item get(int n) {
|
||||
return (Item) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param item {@code non-null;} the item
|
||||
*/
|
||||
public void set(int n, Item item) {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("item == null");
|
||||
}
|
||||
|
||||
set0(n, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param declaringClass {@code non-null;} the class declaring bootstrap method.
|
||||
* @param bootstrapMethodHandle {@code non-null;} the bootstrap method handle
|
||||
* @param arguments {@code non-null;} the arguments of the bootstrap method
|
||||
*/
|
||||
public void set(int n, CstType declaringClass, CstMethodHandle bootstrapMethodHandle,
|
||||
BootstrapMethodArgumentsList arguments) {
|
||||
set(n, new Item(declaringClass, bootstrapMethodHandle, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance which is the concatenation of the two given
|
||||
* instances.
|
||||
*
|
||||
* @param list1 {@code non-null;} first instance
|
||||
* @param list2 {@code non-null;} second instance
|
||||
* @return {@code non-null;} combined instance
|
||||
*/
|
||||
public static BootstrapMethodsList concat(BootstrapMethodsList list1,
|
||||
BootstrapMethodsList list2) {
|
||||
if (list1 == EMPTY) {
|
||||
return list2;
|
||||
} else if (list2 == EMPTY) {
|
||||
return list1;
|
||||
}
|
||||
|
||||
int sz1 = list1.size();
|
||||
int sz2 = list2.size();
|
||||
BootstrapMethodsList result = new BootstrapMethodsList(sz1 + sz2);
|
||||
|
||||
for (int i = 0; i < sz1; i++) {
|
||||
result.set(i, list1.get(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz2; i++) {
|
||||
result.set(sz1 + i, list2.get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
private final BootstrapMethodArgumentsList bootstrapMethodArgumentsList;
|
||||
private final CstMethodHandle bootstrapMethodHandle;
|
||||
private final CstType declaringClass;
|
||||
|
||||
public Item(CstType declaringClass, CstMethodHandle bootstrapMethodHandle,
|
||||
BootstrapMethodArgumentsList bootstrapMethodArguments) {
|
||||
if (declaringClass == null) {
|
||||
throw new NullPointerException("declaringClass == null");
|
||||
}
|
||||
if (bootstrapMethodHandle == null) {
|
||||
throw new NullPointerException("bootstrapMethodHandle == null");
|
||||
}
|
||||
if (bootstrapMethodArguments == null) {
|
||||
throw new NullPointerException("bootstrapMethodArguments == null");
|
||||
}
|
||||
this.bootstrapMethodHandle = bootstrapMethodHandle;
|
||||
this.bootstrapMethodArgumentsList = bootstrapMethodArguments;
|
||||
this.declaringClass = declaringClass;
|
||||
}
|
||||
|
||||
public CstMethodHandle getBootstrapMethodHandle() {
|
||||
return bootstrapMethodHandle;
|
||||
}
|
||||
|
||||
public BootstrapMethodArgumentsList getBootstrapMethodArguments() {
|
||||
return bootstrapMethodArgumentsList;
|
||||
}
|
||||
|
||||
public CstType getDeclaringClass() {
|
||||
return declaringClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import com.pojavdx.dx.util.IntList;
|
||||
import com.pojavdx.dx.util.LabeledItem;
|
||||
|
||||
/**
|
||||
* Representation of a basic block in a bytecode array.
|
||||
*/
|
||||
public final class ByteBlock implements LabeledItem {
|
||||
/** {@code >= 0;} label for this block */
|
||||
private final int label;
|
||||
|
||||
/** {@code >= 0;} bytecode offset (inclusive) of the start of the block */
|
||||
private final int start;
|
||||
|
||||
/** {@code > start;} bytecode offset (exclusive) of the end of the block */
|
||||
private final int end;
|
||||
|
||||
/** {@code non-null;} list of successors that this block may branch to */
|
||||
private final IntList successors;
|
||||
|
||||
/** {@code non-null;} list of exceptions caught and their handler targets */
|
||||
private final ByteCatchList catches;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param label {@code >= 0;} target label for this block
|
||||
* @param start {@code >= 0;} bytecode offset (inclusive) of the start
|
||||
* of the block
|
||||
* @param end {@code > start;} bytecode offset (exclusive) of the end
|
||||
* of the block
|
||||
* @param successors {@code non-null;} list of successors that this block may
|
||||
* branch to
|
||||
* @param catches {@code non-null;} list of exceptions caught and their
|
||||
* handler targets
|
||||
*/
|
||||
public ByteBlock(int label, int start, int end, IntList successors,
|
||||
ByteCatchList catches) {
|
||||
if (label < 0) {
|
||||
throw new IllegalArgumentException("label < 0");
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("start < 0");
|
||||
}
|
||||
|
||||
if (end <= start) {
|
||||
throw new IllegalArgumentException("end <= start");
|
||||
}
|
||||
|
||||
if (successors == null) {
|
||||
throw new NullPointerException("targets == null");
|
||||
}
|
||||
|
||||
int sz = successors.size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
if (successors.get(i) < 0) {
|
||||
throw new IllegalArgumentException("successors[" + i +
|
||||
"] == " +
|
||||
successors.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (catches == null) {
|
||||
throw new NullPointerException("catches == null");
|
||||
}
|
||||
|
||||
this.label = label;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.successors = successors;
|
||||
this.catches = catches;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String toString() {
|
||||
return '{' + Hex.u2(label) + ": " + Hex.u2(start) + ".." +
|
||||
Hex.u2(end) + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label of this block.
|
||||
*
|
||||
* @return {@code >= 0;} the label
|
||||
*/
|
||||
@Override
|
||||
public int getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bytecode offset (inclusive) of the start of this block.
|
||||
*
|
||||
* @return {@code >= 0;} the start offset
|
||||
*/
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bytecode offset (exclusive) of the end of this block.
|
||||
*
|
||||
* @return {@code > getStart();} the end offset
|
||||
*/
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of successors that this block may branch to
|
||||
* non-exceptionally.
|
||||
*
|
||||
* @return {@code non-null;} the successor list
|
||||
*/
|
||||
public IntList getSuccessors() {
|
||||
return successors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of exceptions caught and their handler targets.
|
||||
*
|
||||
* @return {@code non-null;} the catch list
|
||||
*/
|
||||
public ByteCatchList getCatches() {
|
||||
return catches;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import com.pojavdx.dx.util.LabeledList;
|
||||
|
||||
/**
|
||||
* List of {@link ByteBlock} instances.
|
||||
*/
|
||||
public final class ByteBlockList extends LabeledList {
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param size {@code >= 0;} the number of elements to be in the list
|
||||
*/
|
||||
public ByteBlockList(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated element. It is an error to call this with the
|
||||
* index for an element which was never set; if you do that, this
|
||||
* will throw {@code NullPointerException}.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @return {@code non-null;} the indicated element
|
||||
*/
|
||||
public ByteBlock get(int n) {
|
||||
return (ByteBlock) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block with the given label.
|
||||
*
|
||||
* @param label the label to look for
|
||||
* @return {@code non-null;} the block with the given label
|
||||
*/
|
||||
public ByteBlock labelToBlock(int label) {
|
||||
int idx = indexOfLabel(label);
|
||||
|
||||
if (idx < 0) {
|
||||
throw new IllegalArgumentException("no such label: "
|
||||
+ Hex.u2(label));
|
||||
}
|
||||
|
||||
return get(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the element at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param bb {@code null-ok;} the value to store
|
||||
*/
|
||||
public void set(int n, ByteBlock bb) {
|
||||
super.set(n, bb);
|
||||
}
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.StdTypeList;
|
||||
import com.pojavdx.dx.rop.type.TypeList;
|
||||
import com.pojavdx.dx.util.FixedSizeList;
|
||||
import com.pojavdx.dx.util.IntList;
|
||||
|
||||
/**
|
||||
* List of catch entries, that is, the elements of an "exception table,"
|
||||
* which is part of a standard {@code Code} attribute.
|
||||
*/
|
||||
public final class ByteCatchList extends FixedSizeList {
|
||||
/** {@code non-null;} convenient zero-entry instance */
|
||||
public static final ByteCatchList EMPTY = new ByteCatchList(0);
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param count the number of elements to be in the table
|
||||
*/
|
||||
public ByteCatchList(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total length of this structure in bytes, when included in
|
||||
* a {@code Code} attribute. The returned value includes the
|
||||
* two bytes for {@code exception_table_length}.
|
||||
*
|
||||
* @return {@code >= 2;} the total length, in bytes
|
||||
*/
|
||||
public int byteLength() {
|
||||
return 2 + size() * 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated item.
|
||||
*
|
||||
* @param n {@code >= 0;} which item
|
||||
* @return {@code null-ok;} the indicated item
|
||||
*/
|
||||
public Item get(int n) {
|
||||
return (Item) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which entry to set
|
||||
* @param item {@code non-null;} the item
|
||||
*/
|
||||
public void set(int n, Item item) {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("item == null");
|
||||
}
|
||||
|
||||
set0(n, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which entry to set
|
||||
* @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range
|
||||
* @param endPc {@code >= startPc;} the end pc (exclusive) of the
|
||||
* handler's range
|
||||
* @param handlerPc {@code >= 0;} the pc of the exception handler
|
||||
* @param exceptionClass {@code null-ok;} the exception class or
|
||||
* {@code null} to catch all exceptions with this handler
|
||||
*/
|
||||
public void set(int n, int startPc, int endPc, int handlerPc,
|
||||
CstType exceptionClass) {
|
||||
set0(n, new Item(startPc, endPc, handlerPc, exceptionClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of items active at the given address. The result is
|
||||
* automatically made immutable.
|
||||
*
|
||||
* @param pc which address
|
||||
* @return {@code non-null;} list of exception handlers active at
|
||||
* {@code pc}
|
||||
*/
|
||||
public ByteCatchList listFor(int pc) {
|
||||
int sz = size();
|
||||
Item[] resultArr = new Item[sz];
|
||||
int resultSz = 0;
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
Item one = get(i);
|
||||
if (one.covers(pc) && typeNotFound(one, resultArr, resultSz)) {
|
||||
resultArr[resultSz] = one;
|
||||
resultSz++;
|
||||
}
|
||||
}
|
||||
|
||||
if (resultSz == 0) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
ByteCatchList result = new ByteCatchList(resultSz);
|
||||
for (int i = 0; i < resultSz; i++) {
|
||||
result.set(i, resultArr[i]);
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for {@link #listFor}, which tells whether a match
|
||||
* is <i>not</i> found for the exception type of the given item in
|
||||
* the given array. A match is considered to be either an exact type
|
||||
* match or the class {@code Object} which represents a catch-all.
|
||||
*
|
||||
* @param item {@code non-null;} item with the exception type to look for
|
||||
* @param arr {@code non-null;} array to search in
|
||||
* @param count {@code non-null;} maximum number of elements in the array to check
|
||||
* @return {@code true} iff the exception type is <i>not</i> found
|
||||
*/
|
||||
private static boolean typeNotFound(Item item, Item[] arr, int count) {
|
||||
CstType type = item.getExceptionClass();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
CstType one = arr[i].getExceptionClass();
|
||||
if ((one == type) || (one == CstType.OBJECT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a target list corresponding to this instance. The result
|
||||
* is a list of all the exception handler addresses, with the given
|
||||
* {@code noException} address appended if appropriate. The
|
||||
* result is automatically made immutable.
|
||||
*
|
||||
* @param noException {@code >= -1;} the no-exception address to append, or
|
||||
* {@code -1} not to append anything
|
||||
* @return {@code non-null;} list of exception targets, with
|
||||
* {@code noException} appended if necessary
|
||||
*/
|
||||
public IntList toTargetList(int noException) {
|
||||
if (noException < -1) {
|
||||
throw new IllegalArgumentException("noException < -1");
|
||||
}
|
||||
|
||||
boolean hasDefault = (noException >= 0);
|
||||
int sz = size();
|
||||
|
||||
if (sz == 0) {
|
||||
if (hasDefault) {
|
||||
/*
|
||||
* The list is empty, but there is a no-exception
|
||||
* address; so, the result is just that address.
|
||||
*/
|
||||
return IntList.makeImmutable(noException);
|
||||
}
|
||||
/*
|
||||
* The list is empty and there isn't even a no-exception
|
||||
* address.
|
||||
*/
|
||||
return IntList.EMPTY;
|
||||
}
|
||||
|
||||
IntList result = new IntList(sz + (hasDefault ? 1 : 0));
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
result.add(get(i).getHandlerPc());
|
||||
}
|
||||
|
||||
if (hasDefault) {
|
||||
result.add(noException);
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a rop-style catches list equivalent to this one.
|
||||
*
|
||||
* @return {@code non-null;} the converted instance
|
||||
*/
|
||||
public TypeList toRopCatchList() {
|
||||
int sz = size();
|
||||
if (sz == 0) {
|
||||
return StdTypeList.EMPTY;
|
||||
}
|
||||
|
||||
StdTypeList result = new StdTypeList(sz);
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
result.set(i, get(i).getExceptionClass().getClassType());
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item in an exception handler list.
|
||||
*/
|
||||
public static class Item {
|
||||
/** {@code >= 0;} the start pc (inclusive) of the handler's range */
|
||||
private final int startPc;
|
||||
|
||||
/** {@code >= startPc;} the end pc (exclusive) of the handler's range */
|
||||
private final int endPc;
|
||||
|
||||
/** {@code >= 0;} the pc of the exception handler */
|
||||
private final int handlerPc;
|
||||
|
||||
/** {@code null-ok;} the exception class or {@code null} to catch all
|
||||
* exceptions with this handler */
|
||||
private final CstType exceptionClass;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param startPc {@code >= 0;} the start pc (inclusive) of the
|
||||
* handler's range
|
||||
* @param endPc {@code >= startPc;} the end pc (exclusive) of the
|
||||
* handler's range
|
||||
* @param handlerPc {@code >= 0;} the pc of the exception handler
|
||||
* @param exceptionClass {@code null-ok;} the exception class or
|
||||
* {@code null} to catch all exceptions with this handler
|
||||
*/
|
||||
public Item(int startPc, int endPc, int handlerPc,
|
||||
CstType exceptionClass) {
|
||||
if (startPc < 0) {
|
||||
throw new IllegalArgumentException("startPc < 0");
|
||||
}
|
||||
|
||||
if (endPc < startPc) {
|
||||
throw new IllegalArgumentException("endPc < startPc");
|
||||
}
|
||||
|
||||
if (handlerPc < 0) {
|
||||
throw new IllegalArgumentException("handlerPc < 0");
|
||||
}
|
||||
|
||||
this.startPc = startPc;
|
||||
this.endPc = endPc;
|
||||
this.handlerPc = handlerPc;
|
||||
this.exceptionClass = exceptionClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start pc (inclusive) of the handler's range.
|
||||
*
|
||||
* @return {@code >= 0;} the start pc (inclusive) of the handler's range.
|
||||
*/
|
||||
public int getStartPc() {
|
||||
return startPc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end pc (exclusive) of the handler's range.
|
||||
*
|
||||
* @return {@code >= startPc;} the end pc (exclusive) of the
|
||||
* handler's range.
|
||||
*/
|
||||
public int getEndPc() {
|
||||
return endPc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pc of the exception handler.
|
||||
*
|
||||
* @return {@code >= 0;} the pc of the exception handler
|
||||
*/
|
||||
public int getHandlerPc() {
|
||||
return handlerPc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of exception handled.
|
||||
*
|
||||
* @return {@code non-null;} the exception class; {@link CstType#OBJECT}
|
||||
* if this entry handles all possible exceptions
|
||||
*/
|
||||
public CstType getExceptionClass() {
|
||||
return (exceptionClass != null) ?
|
||||
exceptionClass : CstType.OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given address is in the range of this item.
|
||||
*
|
||||
* @param pc the address
|
||||
* @return {@code true} iff this item covers {@code pc}
|
||||
*/
|
||||
public boolean covers(int pc) {
|
||||
return (pc >= startPc) && (pc < endPc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,650 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* Constants and utility methods for dealing with bytecode arrays at an
|
||||
* opcode level.
|
||||
*/
|
||||
public class ByteOps {
|
||||
// one constant per opcode
|
||||
public static final int NOP = 0x00;
|
||||
public static final int ACONST_NULL = 0x01;
|
||||
public static final int ICONST_M1 = 0x02;
|
||||
public static final int ICONST_0 = 0x03;
|
||||
public static final int ICONST_1 = 0x04;
|
||||
public static final int ICONST_2 = 0x05;
|
||||
public static final int ICONST_3 = 0x06;
|
||||
public static final int ICONST_4 = 0x07;
|
||||
public static final int ICONST_5 = 0x08;
|
||||
public static final int LCONST_0 = 0x09;
|
||||
public static final int LCONST_1 = 0x0a;
|
||||
public static final int FCONST_0 = 0x0b;
|
||||
public static final int FCONST_1 = 0x0c;
|
||||
public static final int FCONST_2 = 0x0d;
|
||||
public static final int DCONST_0 = 0x0e;
|
||||
public static final int DCONST_1 = 0x0f;
|
||||
public static final int BIPUSH = 0x10;
|
||||
public static final int SIPUSH = 0x11;
|
||||
public static final int LDC = 0x12;
|
||||
public static final int LDC_W = 0x13;
|
||||
public static final int LDC2_W = 0x14;
|
||||
public static final int ILOAD = 0x15;
|
||||
public static final int LLOAD = 0x16;
|
||||
public static final int FLOAD = 0x17;
|
||||
public static final int DLOAD = 0x18;
|
||||
public static final int ALOAD = 0x19;
|
||||
public static final int ILOAD_0 = 0x1a;
|
||||
public static final int ILOAD_1 = 0x1b;
|
||||
public static final int ILOAD_2 = 0x1c;
|
||||
public static final int ILOAD_3 = 0x1d;
|
||||
public static final int LLOAD_0 = 0x1e;
|
||||
public static final int LLOAD_1 = 0x1f;
|
||||
public static final int LLOAD_2 = 0x20;
|
||||
public static final int LLOAD_3 = 0x21;
|
||||
public static final int FLOAD_0 = 0x22;
|
||||
public static final int FLOAD_1 = 0x23;
|
||||
public static final int FLOAD_2 = 0x24;
|
||||
public static final int FLOAD_3 = 0x25;
|
||||
public static final int DLOAD_0 = 0x26;
|
||||
public static final int DLOAD_1 = 0x27;
|
||||
public static final int DLOAD_2 = 0x28;
|
||||
public static final int DLOAD_3 = 0x29;
|
||||
public static final int ALOAD_0 = 0x2a;
|
||||
public static final int ALOAD_1 = 0x2b;
|
||||
public static final int ALOAD_2 = 0x2c;
|
||||
public static final int ALOAD_3 = 0x2d;
|
||||
public static final int IALOAD = 0x2e;
|
||||
public static final int LALOAD = 0x2f;
|
||||
public static final int FALOAD = 0x30;
|
||||
public static final int DALOAD = 0x31;
|
||||
public static final int AALOAD = 0x32;
|
||||
public static final int BALOAD = 0x33;
|
||||
public static final int CALOAD = 0x34;
|
||||
public static final int SALOAD = 0x35;
|
||||
public static final int ISTORE = 0x36;
|
||||
public static final int LSTORE = 0x37;
|
||||
public static final int FSTORE = 0x38;
|
||||
public static final int DSTORE = 0x39;
|
||||
public static final int ASTORE = 0x3a;
|
||||
public static final int ISTORE_0 = 0x3b;
|
||||
public static final int ISTORE_1 = 0x3c;
|
||||
public static final int ISTORE_2 = 0x3d;
|
||||
public static final int ISTORE_3 = 0x3e;
|
||||
public static final int LSTORE_0 = 0x3f;
|
||||
public static final int LSTORE_1 = 0x40;
|
||||
public static final int LSTORE_2 = 0x41;
|
||||
public static final int LSTORE_3 = 0x42;
|
||||
public static final int FSTORE_0 = 0x43;
|
||||
public static final int FSTORE_1 = 0x44;
|
||||
public static final int FSTORE_2 = 0x45;
|
||||
public static final int FSTORE_3 = 0x46;
|
||||
public static final int DSTORE_0 = 0x47;
|
||||
public static final int DSTORE_1 = 0x48;
|
||||
public static final int DSTORE_2 = 0x49;
|
||||
public static final int DSTORE_3 = 0x4a;
|
||||
public static final int ASTORE_0 = 0x4b;
|
||||
public static final int ASTORE_1 = 0x4c;
|
||||
public static final int ASTORE_2 = 0x4d;
|
||||
public static final int ASTORE_3 = 0x4e;
|
||||
public static final int IASTORE = 0x4f;
|
||||
public static final int LASTORE = 0x50;
|
||||
public static final int FASTORE = 0x51;
|
||||
public static final int DASTORE = 0x52;
|
||||
public static final int AASTORE = 0x53;
|
||||
public static final int BASTORE = 0x54;
|
||||
public static final int CASTORE = 0x55;
|
||||
public static final int SASTORE = 0x56;
|
||||
public static final int POP = 0x57;
|
||||
public static final int POP2 = 0x58;
|
||||
public static final int DUP = 0x59;
|
||||
public static final int DUP_X1 = 0x5a;
|
||||
public static final int DUP_X2 = 0x5b;
|
||||
public static final int DUP2 = 0x5c;
|
||||
public static final int DUP2_X1 = 0x5d;
|
||||
public static final int DUP2_X2 = 0x5e;
|
||||
public static final int SWAP = 0x5f;
|
||||
public static final int IADD = 0x60;
|
||||
public static final int LADD = 0x61;
|
||||
public static final int FADD = 0x62;
|
||||
public static final int DADD = 0x63;
|
||||
public static final int ISUB = 0x64;
|
||||
public static final int LSUB = 0x65;
|
||||
public static final int FSUB = 0x66;
|
||||
public static final int DSUB = 0x67;
|
||||
public static final int IMUL = 0x68;
|
||||
public static final int LMUL = 0x69;
|
||||
public static final int FMUL = 0x6a;
|
||||
public static final int DMUL = 0x6b;
|
||||
public static final int IDIV = 0x6c;
|
||||
public static final int LDIV = 0x6d;
|
||||
public static final int FDIV = 0x6e;
|
||||
public static final int DDIV = 0x6f;
|
||||
public static final int IREM = 0x70;
|
||||
public static final int LREM = 0x71;
|
||||
public static final int FREM = 0x72;
|
||||
public static final int DREM = 0x73;
|
||||
public static final int INEG = 0x74;
|
||||
public static final int LNEG = 0x75;
|
||||
public static final int FNEG = 0x76;
|
||||
public static final int DNEG = 0x77;
|
||||
public static final int ISHL = 0x78;
|
||||
public static final int LSHL = 0x79;
|
||||
public static final int ISHR = 0x7a;
|
||||
public static final int LSHR = 0x7b;
|
||||
public static final int IUSHR = 0x7c;
|
||||
public static final int LUSHR = 0x7d;
|
||||
public static final int IAND = 0x7e;
|
||||
public static final int LAND = 0x7f;
|
||||
public static final int IOR = 0x80;
|
||||
public static final int LOR = 0x81;
|
||||
public static final int IXOR = 0x82;
|
||||
public static final int LXOR = 0x83;
|
||||
public static final int IINC = 0x84;
|
||||
public static final int I2L = 0x85;
|
||||
public static final int I2F = 0x86;
|
||||
public static final int I2D = 0x87;
|
||||
public static final int L2I = 0x88;
|
||||
public static final int L2F = 0x89;
|
||||
public static final int L2D = 0x8a;
|
||||
public static final int F2I = 0x8b;
|
||||
public static final int F2L = 0x8c;
|
||||
public static final int F2D = 0x8d;
|
||||
public static final int D2I = 0x8e;
|
||||
public static final int D2L = 0x8f;
|
||||
public static final int D2F = 0x90;
|
||||
public static final int I2B = 0x91;
|
||||
public static final int I2C = 0x92;
|
||||
public static final int I2S = 0x93;
|
||||
public static final int LCMP = 0x94;
|
||||
public static final int FCMPL = 0x95;
|
||||
public static final int FCMPG = 0x96;
|
||||
public static final int DCMPL = 0x97;
|
||||
public static final int DCMPG = 0x98;
|
||||
public static final int IFEQ = 0x99;
|
||||
public static final int IFNE = 0x9a;
|
||||
public static final int IFLT = 0x9b;
|
||||
public static final int IFGE = 0x9c;
|
||||
public static final int IFGT = 0x9d;
|
||||
public static final int IFLE = 0x9e;
|
||||
public static final int IF_ICMPEQ = 0x9f;
|
||||
public static final int IF_ICMPNE = 0xa0;
|
||||
public static final int IF_ICMPLT = 0xa1;
|
||||
public static final int IF_ICMPGE = 0xa2;
|
||||
public static final int IF_ICMPGT = 0xa3;
|
||||
public static final int IF_ICMPLE = 0xa4;
|
||||
public static final int IF_ACMPEQ = 0xa5;
|
||||
public static final int IF_ACMPNE = 0xa6;
|
||||
public static final int GOTO = 0xa7;
|
||||
public static final int JSR = 0xa8;
|
||||
public static final int RET = 0xa9;
|
||||
public static final int TABLESWITCH = 0xaa;
|
||||
public static final int LOOKUPSWITCH = 0xab;
|
||||
public static final int IRETURN = 0xac;
|
||||
public static final int LRETURN = 0xad;
|
||||
public static final int FRETURN = 0xae;
|
||||
public static final int DRETURN = 0xaf;
|
||||
public static final int ARETURN = 0xb0;
|
||||
public static final int RETURN = 0xb1;
|
||||
public static final int GETSTATIC = 0xb2;
|
||||
public static final int PUTSTATIC = 0xb3;
|
||||
public static final int GETFIELD = 0xb4;
|
||||
public static final int PUTFIELD = 0xb5;
|
||||
public static final int INVOKEVIRTUAL = 0xb6;
|
||||
public static final int INVOKESPECIAL = 0xb7;
|
||||
public static final int INVOKESTATIC = 0xb8;
|
||||
public static final int INVOKEINTERFACE = 0xb9;
|
||||
public static final int INVOKEDYNAMIC = 0xba;
|
||||
public static final int NEW = 0xbb;
|
||||
public static final int NEWARRAY = 0xbc;
|
||||
public static final int ANEWARRAY = 0xbd;
|
||||
public static final int ARRAYLENGTH = 0xbe;
|
||||
public static final int ATHROW = 0xbf;
|
||||
public static final int CHECKCAST = 0xc0;
|
||||
public static final int INSTANCEOF = 0xc1;
|
||||
public static final int MONITORENTER = 0xc2;
|
||||
public static final int MONITOREXIT = 0xc3;
|
||||
public static final int WIDE = 0xc4;
|
||||
public static final int MULTIANEWARRAY = 0xc5;
|
||||
public static final int IFNULL = 0xc6;
|
||||
public static final int IFNONNULL = 0xc7;
|
||||
public static final int GOTO_W = 0xc8;
|
||||
public static final int JSR_W = 0xc9;
|
||||
|
||||
// a constant for each valid argument to "newarray"
|
||||
|
||||
public static final int NEWARRAY_BOOLEAN = 4;
|
||||
public static final int NEWARRAY_CHAR = 5;
|
||||
public static final int NEWARRAY_FLOAT = 6;
|
||||
public static final int NEWARRAY_DOUBLE = 7;
|
||||
public static final int NEWARRAY_BYTE = 8;
|
||||
public static final int NEWARRAY_SHORT = 9;
|
||||
public static final int NEWARRAY_INT = 10;
|
||||
public static final int NEWARRAY_LONG = 11;
|
||||
|
||||
// a constant for each possible instruction format
|
||||
|
||||
/** invalid */
|
||||
public static final int FMT_INVALID = 0;
|
||||
|
||||
/** "-": {@code op} */
|
||||
public static final int FMT_NO_ARGS = 1;
|
||||
|
||||
/** "0": {@code op}; implies {@code max_locals >= 1} */
|
||||
public static final int FMT_NO_ARGS_LOCALS_1 = 2;
|
||||
|
||||
/** "1": {@code op}; implies {@code max_locals >= 2} */
|
||||
public static final int FMT_NO_ARGS_LOCALS_2 = 3;
|
||||
|
||||
/** "2": {@code op}; implies {@code max_locals >= 3} */
|
||||
public static final int FMT_NO_ARGS_LOCALS_3 = 4;
|
||||
|
||||
/** "3": {@code op}; implies {@code max_locals >= 4} */
|
||||
public static final int FMT_NO_ARGS_LOCALS_4 = 5;
|
||||
|
||||
/** "4": {@code op}; implies {@code max_locals >= 5} */
|
||||
public static final int FMT_NO_ARGS_LOCALS_5 = 6;
|
||||
|
||||
/** "b": {@code op target target} */
|
||||
public static final int FMT_BRANCH = 7;
|
||||
|
||||
/** "c": {@code op target target target target} */
|
||||
public static final int FMT_WIDE_BRANCH = 8;
|
||||
|
||||
/** "p": {@code op #cpi #cpi}; constant restricted as specified */
|
||||
public static final int FMT_CPI = 9;
|
||||
|
||||
/**
|
||||
* "l": {@code op local}; category-1 local; implies
|
||||
* {@code max_locals} is at least two more than the given
|
||||
* local number
|
||||
*/
|
||||
public static final int FMT_LOCAL_1 = 10;
|
||||
|
||||
/**
|
||||
* "m": {@code op local}; category-2 local; implies
|
||||
* {@code max_locals} is at least two more than the given
|
||||
* local number
|
||||
*/
|
||||
public static final int FMT_LOCAL_2 = 11;
|
||||
|
||||
/**
|
||||
* "y": {@code op #byte} ({@code bipush} and
|
||||
* {@code newarray})
|
||||
*/
|
||||
public static final int FMT_LITERAL_BYTE = 12;
|
||||
|
||||
/** "I": {@code invokeinterface cpi cpi count 0} */
|
||||
public static final int FMT_INVOKEINTERFACE = 13;
|
||||
|
||||
/** "L": {@code ldc #cpi}; constant restricted as specified */
|
||||
public static final int FMT_LDC = 14;
|
||||
|
||||
/** "S": {@code sipush #byte #byte} */
|
||||
public static final int FMT_SIPUSH = 15;
|
||||
|
||||
/** "T": {@code tableswitch ...} */
|
||||
public static final int FMT_TABLESWITCH = 16;
|
||||
|
||||
/** "U": {@code lookupswitch ...} */
|
||||
public static final int FMT_LOOKUPSWITCH = 17;
|
||||
|
||||
/** "M": {@code multianewarray cpi cpi dims} */
|
||||
public static final int FMT_MULTIANEWARRAY = 18;
|
||||
|
||||
/** "W": {@code wide ...} */
|
||||
public static final int FMT_WIDE = 19;
|
||||
|
||||
/** mask for the bits representing the opcode format */
|
||||
public static final int FMT_MASK = 0x1f;
|
||||
|
||||
/** "I": flag bit for valid cp type for {@code Integer} */
|
||||
public static final int CPOK_Integer = 0x20;
|
||||
|
||||
/** "F": flag bit for valid cp type for {@code Float} */
|
||||
public static final int CPOK_Float = 0x40;
|
||||
|
||||
/** "J": flag bit for valid cp type for {@code Long} */
|
||||
public static final int CPOK_Long = 0x80;
|
||||
|
||||
/** "D": flag bit for valid cp type for {@code Double} */
|
||||
public static final int CPOK_Double = 0x100;
|
||||
|
||||
/** "c": flag bit for valid cp type for {@code Class} */
|
||||
public static final int CPOK_Class = 0x200;
|
||||
|
||||
/** "s": flag bit for valid cp type for {@code String} */
|
||||
public static final int CPOK_String = 0x400;
|
||||
|
||||
/** "f": flag bit for valid cp type for {@code Fieldref} */
|
||||
public static final int CPOK_Fieldref = 0x800;
|
||||
|
||||
/** "m": flag bit for valid cp type for {@code Methodref} */
|
||||
public static final int CPOK_Methodref = 0x1000;
|
||||
|
||||
/** "i": flag bit for valid cp type for {@code InterfaceMethodref} */
|
||||
public static final int CPOK_InterfaceMethodref = 0x2000;
|
||||
|
||||
/**
|
||||
* {@code non-null;} map from opcodes to format or'ed with allowed constant
|
||||
* pool types
|
||||
*/
|
||||
private static final int[] OPCODE_INFO = new int[256];
|
||||
|
||||
/** {@code non-null;} map from opcodes to their names */
|
||||
private static final String[] OPCODE_NAMES = new String[256];
|
||||
|
||||
/** {@code non-null;} bigass string describing all the opcodes */
|
||||
private static final String OPCODE_DETAILS =
|
||||
"00 - nop;" +
|
||||
"01 - aconst_null;" +
|
||||
"02 - iconst_m1;" +
|
||||
"03 - iconst_0;" +
|
||||
"04 - iconst_1;" +
|
||||
"05 - iconst_2;" +
|
||||
"06 - iconst_3;" +
|
||||
"07 - iconst_4;" +
|
||||
"08 - iconst_5;" +
|
||||
"09 - lconst_0;" +
|
||||
"0a - lconst_1;" +
|
||||
"0b - fconst_0;" +
|
||||
"0c - fconst_1;" +
|
||||
"0d - fconst_2;" +
|
||||
"0e - dconst_0;" +
|
||||
"0f - dconst_1;" +
|
||||
"10 y bipush;" +
|
||||
"11 S sipush;" +
|
||||
"12 L:IFcs ldc;" +
|
||||
"13 p:IFcs ldc_w;" +
|
||||
"14 p:DJ ldc2_w;" +
|
||||
"15 l iload;" +
|
||||
"16 m lload;" +
|
||||
"17 l fload;" +
|
||||
"18 m dload;" +
|
||||
"19 l aload;" +
|
||||
"1a 0 iload_0;" +
|
||||
"1b 1 iload_1;" +
|
||||
"1c 2 iload_2;" +
|
||||
"1d 3 iload_3;" +
|
||||
"1e 1 lload_0;" +
|
||||
"1f 2 lload_1;" +
|
||||
"20 3 lload_2;" +
|
||||
"21 4 lload_3;" +
|
||||
"22 0 fload_0;" +
|
||||
"23 1 fload_1;" +
|
||||
"24 2 fload_2;" +
|
||||
"25 3 fload_3;" +
|
||||
"26 1 dload_0;" +
|
||||
"27 2 dload_1;" +
|
||||
"28 3 dload_2;" +
|
||||
"29 4 dload_3;" +
|
||||
"2a 0 aload_0;" +
|
||||
"2b 1 aload_1;" +
|
||||
"2c 2 aload_2;" +
|
||||
"2d 3 aload_3;" +
|
||||
"2e - iaload;" +
|
||||
"2f - laload;" +
|
||||
"30 - faload;" +
|
||||
"31 - daload;" +
|
||||
"32 - aaload;" +
|
||||
"33 - baload;" +
|
||||
"34 - caload;" +
|
||||
"35 - saload;" +
|
||||
"36 - istore;" +
|
||||
"37 - lstore;" +
|
||||
"38 - fstore;" +
|
||||
"39 - dstore;" +
|
||||
"3a - astore;" +
|
||||
"3b 0 istore_0;" +
|
||||
"3c 1 istore_1;" +
|
||||
"3d 2 istore_2;" +
|
||||
"3e 3 istore_3;" +
|
||||
"3f 1 lstore_0;" +
|
||||
"40 2 lstore_1;" +
|
||||
"41 3 lstore_2;" +
|
||||
"42 4 lstore_3;" +
|
||||
"43 0 fstore_0;" +
|
||||
"44 1 fstore_1;" +
|
||||
"45 2 fstore_2;" +
|
||||
"46 3 fstore_3;" +
|
||||
"47 1 dstore_0;" +
|
||||
"48 2 dstore_1;" +
|
||||
"49 3 dstore_2;" +
|
||||
"4a 4 dstore_3;" +
|
||||
"4b 0 astore_0;" +
|
||||
"4c 1 astore_1;" +
|
||||
"4d 2 astore_2;" +
|
||||
"4e 3 astore_3;" +
|
||||
"4f - iastore;" +
|
||||
"50 - lastore;" +
|
||||
"51 - fastore;" +
|
||||
"52 - dastore;" +
|
||||
"53 - aastore;" +
|
||||
"54 - bastore;" +
|
||||
"55 - castore;" +
|
||||
"56 - sastore;" +
|
||||
"57 - pop;" +
|
||||
"58 - pop2;" +
|
||||
"59 - dup;" +
|
||||
"5a - dup_x1;" +
|
||||
"5b - dup_x2;" +
|
||||
"5c - dup2;" +
|
||||
"5d - dup2_x1;" +
|
||||
"5e - dup2_x2;" +
|
||||
"5f - swap;" +
|
||||
"60 - iadd;" +
|
||||
"61 - ladd;" +
|
||||
"62 - fadd;" +
|
||||
"63 - dadd;" +
|
||||
"64 - isub;" +
|
||||
"65 - lsub;" +
|
||||
"66 - fsub;" +
|
||||
"67 - dsub;" +
|
||||
"68 - imul;" +
|
||||
"69 - lmul;" +
|
||||
"6a - fmul;" +
|
||||
"6b - dmul;" +
|
||||
"6c - idiv;" +
|
||||
"6d - ldiv;" +
|
||||
"6e - fdiv;" +
|
||||
"6f - ddiv;" +
|
||||
"70 - irem;" +
|
||||
"71 - lrem;" +
|
||||
"72 - frem;" +
|
||||
"73 - drem;" +
|
||||
"74 - ineg;" +
|
||||
"75 - lneg;" +
|
||||
"76 - fneg;" +
|
||||
"77 - dneg;" +
|
||||
"78 - ishl;" +
|
||||
"79 - lshl;" +
|
||||
"7a - ishr;" +
|
||||
"7b - lshr;" +
|
||||
"7c - iushr;" +
|
||||
"7d - lushr;" +
|
||||
"7e - iand;" +
|
||||
"7f - land;" +
|
||||
"80 - ior;" +
|
||||
"81 - lor;" +
|
||||
"82 - ixor;" +
|
||||
"83 - lxor;" +
|
||||
"84 l iinc;" +
|
||||
"85 - i2l;" +
|
||||
"86 - i2f;" +
|
||||
"87 - i2d;" +
|
||||
"88 - l2i;" +
|
||||
"89 - l2f;" +
|
||||
"8a - l2d;" +
|
||||
"8b - f2i;" +
|
||||
"8c - f2l;" +
|
||||
"8d - f2d;" +
|
||||
"8e - d2i;" +
|
||||
"8f - d2l;" +
|
||||
"90 - d2f;" +
|
||||
"91 - i2b;" +
|
||||
"92 - i2c;" +
|
||||
"93 - i2s;" +
|
||||
"94 - lcmp;" +
|
||||
"95 - fcmpl;" +
|
||||
"96 - fcmpg;" +
|
||||
"97 - dcmpl;" +
|
||||
"98 - dcmpg;" +
|
||||
"99 b ifeq;" +
|
||||
"9a b ifne;" +
|
||||
"9b b iflt;" +
|
||||
"9c b ifge;" +
|
||||
"9d b ifgt;" +
|
||||
"9e b ifle;" +
|
||||
"9f b if_icmpeq;" +
|
||||
"a0 b if_icmpne;" +
|
||||
"a1 b if_icmplt;" +
|
||||
"a2 b if_icmpge;" +
|
||||
"a3 b if_icmpgt;" +
|
||||
"a4 b if_icmple;" +
|
||||
"a5 b if_acmpeq;" +
|
||||
"a6 b if_acmpne;" +
|
||||
"a7 b goto;" +
|
||||
"a8 b jsr;" +
|
||||
"a9 l ret;" +
|
||||
"aa T tableswitch;" +
|
||||
"ab U lookupswitch;" +
|
||||
"ac - ireturn;" +
|
||||
"ad - lreturn;" +
|
||||
"ae - freturn;" +
|
||||
"af - dreturn;" +
|
||||
"b0 - areturn;" +
|
||||
"b1 - return;" +
|
||||
"b2 p:f getstatic;" +
|
||||
"b3 p:f putstatic;" +
|
||||
"b4 p:f getfield;" +
|
||||
"b5 p:f putfield;" +
|
||||
"b6 p:m invokevirtual;" +
|
||||
"b7 p:m invokespecial;" +
|
||||
"b8 p:m invokestatic;" +
|
||||
"b9 I:i invokeinterface;" +
|
||||
"bb p:c new;" +
|
||||
"bc y newarray;" +
|
||||
"bd p:c anewarray;" +
|
||||
"be - arraylength;" +
|
||||
"bf - athrow;" +
|
||||
"c0 p:c checkcast;" +
|
||||
"c1 p:c instanceof;" +
|
||||
"c2 - monitorenter;" +
|
||||
"c3 - monitorexit;" +
|
||||
"c4 W wide;" +
|
||||
"c5 M:c multianewarray;" +
|
||||
"c6 b ifnull;" +
|
||||
"c7 b ifnonnull;" +
|
||||
"c8 c goto_w;" +
|
||||
"c9 c jsr_w;";
|
||||
|
||||
static {
|
||||
// Set up OPCODE_INFO and OPCODE_NAMES.
|
||||
String s = OPCODE_DETAILS;
|
||||
int len = s.length();
|
||||
|
||||
for (int i = 0; i < len; /*i*/) {
|
||||
int idx = (Character.digit(s.charAt(i), 16) << 4) |
|
||||
Character.digit(s.charAt(i + 1), 16);
|
||||
int info;
|
||||
switch (s.charAt(i + 3)) {
|
||||
case '-': info = FMT_NO_ARGS; break;
|
||||
case '0': info = FMT_NO_ARGS_LOCALS_1; break;
|
||||
case '1': info = FMT_NO_ARGS_LOCALS_2; break;
|
||||
case '2': info = FMT_NO_ARGS_LOCALS_3; break;
|
||||
case '3': info = FMT_NO_ARGS_LOCALS_4; break;
|
||||
case '4': info = FMT_NO_ARGS_LOCALS_5; break;
|
||||
case 'b': info = FMT_BRANCH; break;
|
||||
case 'c': info = FMT_WIDE_BRANCH; break;
|
||||
case 'p': info = FMT_CPI; break;
|
||||
case 'l': info = FMT_LOCAL_1; break;
|
||||
case 'm': info = FMT_LOCAL_2; break;
|
||||
case 'y': info = FMT_LITERAL_BYTE; break;
|
||||
case 'I': info = FMT_INVOKEINTERFACE; break;
|
||||
case 'L': info = FMT_LDC; break;
|
||||
case 'S': info = FMT_SIPUSH; break;
|
||||
case 'T': info = FMT_TABLESWITCH; break;
|
||||
case 'U': info = FMT_LOOKUPSWITCH; break;
|
||||
case 'M': info = FMT_MULTIANEWARRAY; break;
|
||||
case 'W': info = FMT_WIDE; break;
|
||||
default: info = FMT_INVALID; break;
|
||||
}
|
||||
|
||||
i += 5;
|
||||
if (s.charAt(i - 1) == ':') {
|
||||
inner:
|
||||
for (;;) {
|
||||
switch (s.charAt(i)) {
|
||||
case 'I': info |= CPOK_Integer; break;
|
||||
case 'F': info |= CPOK_Float; break;
|
||||
case 'J': info |= CPOK_Long; break;
|
||||
case 'D': info |= CPOK_Double; break;
|
||||
case 'c': info |= CPOK_Class; break;
|
||||
case 's': info |= CPOK_String; break;
|
||||
case 'f': info |= CPOK_Fieldref; break;
|
||||
case 'm': info |= CPOK_Methodref; break;
|
||||
case 'i': info |= CPOK_InterfaceMethodref; break;
|
||||
default: break inner;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
int endAt = s.indexOf(';', i);
|
||||
OPCODE_INFO[idx] = info;
|
||||
OPCODE_NAMES[idx] = s.substring(i, endAt);
|
||||
i = endAt + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is uninstantiable.
|
||||
*/
|
||||
private ByteOps() {
|
||||
// This space intentionally left blank.
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the given opcode.
|
||||
*
|
||||
* @param opcode {@code >= 0, <= 255;} the opcode
|
||||
* @return {@code non-null;} its name
|
||||
*/
|
||||
public static String opName(int opcode) {
|
||||
String result = OPCODE_NAMES[opcode];
|
||||
|
||||
if (result == null) {
|
||||
result = "unused_" + Hex.u1(opcode);
|
||||
OPCODE_NAMES[opcode] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the format and allowed cp types of the given opcode.
|
||||
*
|
||||
* @param opcode {@code >= 0, <= 255;} the opcode
|
||||
* @return its format and allowed cp types
|
||||
*/
|
||||
public static int opInfo(int opcode) {
|
||||
return OPCODE_INFO[opcode];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.cf.attrib.AttCode;
|
||||
import com.pojavdx.dx.cf.attrib.AttLineNumberTable;
|
||||
import com.pojavdx.dx.cf.attrib.AttLocalVariableTable;
|
||||
import com.pojavdx.dx.cf.attrib.AttLocalVariableTypeTable;
|
||||
import com.pojavdx.dx.cf.iface.AttributeList;
|
||||
import com.pojavdx.dx.cf.iface.ClassFile;
|
||||
import com.pojavdx.dx.cf.iface.Method;
|
||||
import com.pojavdx.dx.rop.code.AccessFlags;
|
||||
import com.pojavdx.dx.rop.code.SourcePosition;
|
||||
import com.pojavdx.dx.rop.cst.CstNat;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.Prototype;
|
||||
|
||||
/**
|
||||
* Container for all the giblets that make up a concrete Java bytecode method.
|
||||
* It implements {@link Method}, so it provides all the original access
|
||||
* (by delegation), but it also constructs and keeps useful versions of
|
||||
* stuff extracted from the method's {@code Code} attribute.
|
||||
*/
|
||||
public final class ConcreteMethod implements Method {
|
||||
/** {@code non-null;} method being wrapped */
|
||||
private final Method method;
|
||||
|
||||
/** {@code non-null;} the {@code ClassFile} the method belongs to. */
|
||||
private final ClassFile classFile;
|
||||
|
||||
/** {@code non-null;} the code attribute */
|
||||
private final AttCode attCode;
|
||||
|
||||
/** {@code non-null;} line number list */
|
||||
private final LineNumberList lineNumbers;
|
||||
|
||||
/** {@code non-null;} local variable list */
|
||||
private final LocalVariableList localVariables;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param method {@code non-null;} the method to be based on
|
||||
* @param classFile {@code non-null;} the class file that contains this method
|
||||
* @param keepLines whether to keep the line number information
|
||||
* (if any)
|
||||
* @param keepLocals whether to keep the local variable
|
||||
* information (if any)
|
||||
*/
|
||||
public ConcreteMethod(Method method, ClassFile classFile,
|
||||
boolean keepLines, boolean keepLocals) {
|
||||
this.method = method;
|
||||
this.classFile = classFile;
|
||||
|
||||
AttributeList attribs = method.getAttributes();
|
||||
this.attCode = (AttCode) attribs.findFirst(AttCode.ATTRIBUTE_NAME);
|
||||
|
||||
AttributeList codeAttribs = attCode.getAttributes();
|
||||
|
||||
/*
|
||||
* Combine all LineNumberTable attributes into one, with the
|
||||
* combined result saved into the instance. The following code
|
||||
* isn't particularly efficient for doing merges, but as far
|
||||
* as I know, this situation rarely occurs "in the
|
||||
* wild," so there's not much point in optimizing for it.
|
||||
*/
|
||||
LineNumberList lnl = LineNumberList.EMPTY;
|
||||
if (keepLines) {
|
||||
for (AttLineNumberTable lnt = (AttLineNumberTable)
|
||||
codeAttribs.findFirst(AttLineNumberTable.ATTRIBUTE_NAME);
|
||||
lnt != null;
|
||||
lnt = (AttLineNumberTable) codeAttribs.findNext(lnt)) {
|
||||
lnl = LineNumberList.concat(lnl, lnt.getLineNumbers());
|
||||
}
|
||||
}
|
||||
this.lineNumbers = lnl;
|
||||
|
||||
LocalVariableList lvl = LocalVariableList.EMPTY;
|
||||
if (keepLocals) {
|
||||
/*
|
||||
* Do likewise (and with the same caveat) for
|
||||
* LocalVariableTable and LocalVariableTypeTable attributes.
|
||||
* This combines both of these kinds of attribute into a
|
||||
* single LocalVariableList.
|
||||
*/
|
||||
for (AttLocalVariableTable lvt = (AttLocalVariableTable)
|
||||
codeAttribs.findFirst(
|
||||
AttLocalVariableTable.ATTRIBUTE_NAME);
|
||||
lvt != null;
|
||||
lvt = (AttLocalVariableTable) codeAttribs.findNext(lvt)) {
|
||||
|
||||
lvl = LocalVariableList.concat(lvl, lvt.getLocalVariables());
|
||||
}
|
||||
|
||||
LocalVariableList typeList = LocalVariableList.EMPTY;
|
||||
for (AttLocalVariableTypeTable lvtt = (AttLocalVariableTypeTable)
|
||||
codeAttribs.findFirst(
|
||||
AttLocalVariableTypeTable.ATTRIBUTE_NAME);
|
||||
lvtt != null;
|
||||
lvtt = (AttLocalVariableTypeTable) codeAttribs.findNext(lvtt)) {
|
||||
typeList = LocalVariableList.concat(typeList, lvtt.getLocalVariables());
|
||||
}
|
||||
|
||||
if (typeList.size() != 0) {
|
||||
|
||||
lvl = LocalVariableList.mergeDescriptorsAndSignatures(lvl, typeList);
|
||||
}
|
||||
}
|
||||
this.localVariables = lvl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the source file associated with the method if known.
|
||||
* @return {null-ok;} the source file defining the method if known, null otherwise.
|
||||
*/
|
||||
public CstString getSourceFile() {
|
||||
return classFile.getSourceFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the method is being defined on an interface.
|
||||
* @return true if the method is being defined on an interface.
|
||||
*/
|
||||
public final boolean isDefaultOrStaticInterfaceMethod() {
|
||||
return (classFile.getAccessFlags() & AccessFlags.ACC_INTERFACE) != 0
|
||||
&& !getNat().isClassInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the method is being defined is declared as static.
|
||||
* @return true if the method is being defined is declared as static.
|
||||
*/
|
||||
public final boolean isStaticMethod() {
|
||||
return (getAccessFlags() & AccessFlags.ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public CstNat getNat() {
|
||||
return method.getNat();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public CstString getName() {
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public CstString getDescriptor() {
|
||||
return method.getDescriptor();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getAccessFlags() {
|
||||
return method.getAccessFlags();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public AttributeList getAttributes() {
|
||||
return method.getAttributes();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public CstType getDefiningClass() {
|
||||
return method.getDefiningClass();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Prototype getEffectiveDescriptor() {
|
||||
return method.getEffectiveDescriptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum stack size.
|
||||
*
|
||||
* @return {@code >= 0;} the maximum stack size
|
||||
*/
|
||||
public int getMaxStack() {
|
||||
return attCode.getMaxStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of locals.
|
||||
*
|
||||
* @return {@code >= 0;} the number of locals
|
||||
*/
|
||||
public int getMaxLocals() {
|
||||
return attCode.getMaxLocals();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bytecode array.
|
||||
*
|
||||
* @return {@code non-null;} the bytecode array
|
||||
*/
|
||||
public BytecodeArray getCode() {
|
||||
return attCode.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception table.
|
||||
*
|
||||
* @return {@code non-null;} the exception table
|
||||
*/
|
||||
public ByteCatchList getCatches() {
|
||||
return attCode.getCatches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number list.
|
||||
*
|
||||
* @return {@code non-null;} the line number list
|
||||
*/
|
||||
public LineNumberList getLineNumbers() {
|
||||
return lineNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local variable list.
|
||||
*
|
||||
* @return {@code non-null;} the local variable list
|
||||
*/
|
||||
public LocalVariableList getLocalVariables() {
|
||||
return localVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link SourcePosition} instance corresponding to the
|
||||
* given bytecode offset.
|
||||
*
|
||||
* @param offset {@code >= 0;} the bytecode offset
|
||||
* @return {@code non-null;} an appropriate instance
|
||||
*/
|
||||
public SourcePosition makeSourcePosistion(int offset) {
|
||||
return new SourcePosition(getSourceFile(), offset,
|
||||
lineNumbers.pcToLine(offset));
|
||||
}
|
||||
}
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import com.pojavdx.dx.util.MutabilityControl;
|
||||
|
||||
/**
|
||||
* Representation of a Java method execution stack.
|
||||
*
|
||||
* <p><b>Note:</b> For the most part, the documentation for this class
|
||||
* ignores the distinction between {@link Type} and {@link
|
||||
* TypeBearer}.</p>
|
||||
*/
|
||||
public final class ExecutionStack extends MutabilityControl {
|
||||
/** {@code non-null;} array of stack contents */
|
||||
private final TypeBearer[] stack;
|
||||
|
||||
/**
|
||||
* {@code non-null;} array specifying whether stack contents have entries
|
||||
* in the local variable table
|
||||
*/
|
||||
private final boolean[] local;
|
||||
/**
|
||||
* {@code >= 0;} stack pointer (points one past the end) / current stack
|
||||
* size
|
||||
*/
|
||||
private int stackPtr;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param maxStack {@code >= 0;} the maximum size of the stack for this
|
||||
* instance
|
||||
*/
|
||||
public ExecutionStack(int maxStack) {
|
||||
super(maxStack != 0);
|
||||
stack = new TypeBearer[maxStack];
|
||||
local = new boolean[maxStack];
|
||||
stackPtr = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes and returns a mutable copy of this instance.
|
||||
*
|
||||
* @return {@code non-null;} the copy
|
||||
*/
|
||||
public ExecutionStack copy() {
|
||||
ExecutionStack result = new ExecutionStack(stack.length);
|
||||
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(stack, 0, result.stack, 0, stack.length);
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(local, 0, result.local, 0, local.length);
|
||||
result.stackPtr = stackPtr;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates (adds context to) the given exception with information
|
||||
* about this instance.
|
||||
*
|
||||
* @param ex {@code non-null;} the exception to annotate
|
||||
*/
|
||||
public void annotate(ExceptionWithContext ex) {
|
||||
int limit = stackPtr - 1;
|
||||
|
||||
for (int i = 0; i <= limit; i++) {
|
||||
String idx = (i == limit) ? "top0" : Hex.u2(limit - i);
|
||||
|
||||
ex.addContext("stack[" + idx + "]: " +
|
||||
stackElementString(stack[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all the occurrences of the given uninitialized type in
|
||||
* this stack with its initialized equivalent.
|
||||
*
|
||||
* @param type {@code non-null;} type to replace
|
||||
*/
|
||||
public void makeInitialized(Type type) {
|
||||
if (stackPtr == 0) {
|
||||
// We have to check for this before checking for immutability.
|
||||
return;
|
||||
}
|
||||
|
||||
throwIfImmutable();
|
||||
|
||||
Type initializedType = type.getInitializedType();
|
||||
|
||||
for (int i = 0; i < stackPtr; i++) {
|
||||
if (stack[i] == type) {
|
||||
stack[i] = initializedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum stack size for this instance.
|
||||
*
|
||||
* @return {@code >= 0;} the max stack size
|
||||
*/
|
||||
public int getMaxStack() {
|
||||
return stack.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current stack size.
|
||||
*
|
||||
* @return {@code >= 0, < getMaxStack();} the current stack size
|
||||
*/
|
||||
public int size() {
|
||||
return stackPtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the stack. (That is, this method pops everything off.)
|
||||
*/
|
||||
public void clear() {
|
||||
throwIfImmutable();
|
||||
|
||||
for (int i = 0; i < stackPtr; i++) {
|
||||
stack[i] = null;
|
||||
local[i] = false;
|
||||
}
|
||||
|
||||
stackPtr = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes a value of the given type onto the stack.
|
||||
*
|
||||
* @param type {@code non-null;} type of the value
|
||||
* @throws SimException thrown if there is insufficient room on the
|
||||
* stack for the value
|
||||
*/
|
||||
public void push(TypeBearer type) {
|
||||
throwIfImmutable();
|
||||
|
||||
int category;
|
||||
|
||||
try {
|
||||
type = type.getFrameType();
|
||||
category = type.getType().getCategory();
|
||||
} catch (NullPointerException ex) {
|
||||
// Elucidate the exception.
|
||||
throw new NullPointerException("type == null");
|
||||
}
|
||||
|
||||
if ((stackPtr + category) > stack.length) {
|
||||
throwSimException("overflow");
|
||||
return;
|
||||
}
|
||||
|
||||
if (category == 2) {
|
||||
stack[stackPtr] = null;
|
||||
stackPtr++;
|
||||
}
|
||||
|
||||
stack[stackPtr] = type;
|
||||
stackPtr++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags the next value pushed onto the stack as having local info.
|
||||
*/
|
||||
public void setLocal() {
|
||||
throwIfImmutable();
|
||||
|
||||
local[stackPtr] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the {@code n}th element down from the top of the stack.
|
||||
* {@code n == 0} means to peek at the top of the stack. Note that
|
||||
* this will return {@code null} if the indicated element is the
|
||||
* deeper half of a category-2 value.
|
||||
*
|
||||
* @param n {@code >= 0;} which element to peek at
|
||||
* @return {@code null-ok;} the type of value stored at that element
|
||||
* @throws SimException thrown if {@code n >= size()}
|
||||
*/
|
||||
public TypeBearer peek(int n) {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("n < 0");
|
||||
}
|
||||
|
||||
if (n >= stackPtr) {
|
||||
return throwSimException("underflow");
|
||||
}
|
||||
|
||||
return stack[stackPtr - n - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the {@code n}th element down from the top of the
|
||||
* stack, returning whether or not it has local info.
|
||||
*
|
||||
* @param n {@code >= 0;} which element to peek at
|
||||
* @return {@code true} if the value has local info, {@code false} otherwise
|
||||
* @throws SimException thrown if {@code n >= size()}
|
||||
*/
|
||||
public boolean peekLocal(int n) {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("n < 0");
|
||||
}
|
||||
|
||||
if (n >= stackPtr) {
|
||||
throw new SimException("stack: underflow");
|
||||
}
|
||||
|
||||
return local[stackPtr - n - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the {@code n}th element down from the top of the
|
||||
* stack, returning the type per se, as opposed to the
|
||||
* <i>type-bearer</i>. This method is just a convenient shorthand
|
||||
* for {@code peek(n).getType()}.
|
||||
*
|
||||
* @see #peek
|
||||
*/
|
||||
public Type peekType(int n) {
|
||||
return peek(n).getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the top element off of the stack.
|
||||
*
|
||||
* @return {@code non-null;} the type formerly on the top of the stack
|
||||
* @throws SimException thrown if the stack is empty
|
||||
*/
|
||||
public TypeBearer pop() {
|
||||
throwIfImmutable();
|
||||
|
||||
TypeBearer result = peek(0);
|
||||
|
||||
stack[stackPtr - 1] = null;
|
||||
local[stackPtr - 1] = false;
|
||||
stackPtr -= result.getType().getCategory();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes an element already on a stack. This method is useful in limited
|
||||
* contexts, particularly when merging two instances. As such, it places
|
||||
* the following restriction on its behavior: You may only replace
|
||||
* values with other values of the same category.
|
||||
*
|
||||
* @param n {@code >= 0;} which element to change, where {@code 0} is
|
||||
* the top element of the stack
|
||||
* @param type {@code non-null;} type of the new value
|
||||
* @throws SimException thrown if {@code n >= size()} or
|
||||
* the action is otherwise prohibited
|
||||
*/
|
||||
public void change(int n, TypeBearer type) {
|
||||
throwIfImmutable();
|
||||
|
||||
try {
|
||||
type = type.getFrameType();
|
||||
} catch (NullPointerException ex) {
|
||||
// Elucidate the exception.
|
||||
throw new NullPointerException("type == null");
|
||||
}
|
||||
|
||||
int idx = stackPtr - n - 1;
|
||||
TypeBearer orig = stack[idx];
|
||||
|
||||
if ((orig == null) ||
|
||||
(orig.getType().getCategory() != type.getType().getCategory())) {
|
||||
throwSimException("incompatible substitution: " +
|
||||
stackElementString(orig) + " -> " +
|
||||
stackElementString(type));
|
||||
}
|
||||
|
||||
stack[idx] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this stack with another stack. A new instance is returned if
|
||||
* this merge results in a change. If no change results, this instance is
|
||||
* returned. See {@link Merger#mergeStack(ExecutionStack,ExecutionStack)
|
||||
* Merger.mergeStack()}
|
||||
*
|
||||
* @param other {@code non-null;} a stack to merge with
|
||||
* @return {@code non-null;} the result of the merge
|
||||
*/
|
||||
public ExecutionStack merge(ExecutionStack other) {
|
||||
try {
|
||||
return Merger.mergeStack(this, other);
|
||||
} catch (SimException ex) {
|
||||
ex.addContext("underlay stack:");
|
||||
this.annotate(ex);
|
||||
ex.addContext("overlay stack:");
|
||||
other.annotate(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string form for a stack element. This is the same as
|
||||
* {@code toString()} except that {@code null} is converted
|
||||
* to {@code "<invalid>"}.
|
||||
*
|
||||
* @param type {@code null-ok;} the stack element
|
||||
* @return {@code non-null;} the string form
|
||||
*/
|
||||
private static String stackElementString(TypeBearer type) {
|
||||
if (type == null) {
|
||||
return "<invalid>";
|
||||
}
|
||||
|
||||
return type.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a properly-formatted exception.
|
||||
*
|
||||
* @param msg {@code non-null;} useful message
|
||||
* @return never (keeps compiler happy)
|
||||
*/
|
||||
private static TypeBearer throwSimException(String msg) {
|
||||
throw new SimException("stack: " + msg);
|
||||
}
|
||||
}
|
||||
@@ -1,415 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.StdTypeList;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.util.IntList;
|
||||
|
||||
/**
|
||||
* Representation of a Java method execution frame. A frame consists
|
||||
* of a set of locals and a value stack, and it can be told to act on
|
||||
* them to load and store values between them and an "arguments /
|
||||
* results" area.
|
||||
*/
|
||||
public final class Frame {
|
||||
/** {@code non-null;} the locals */
|
||||
private final LocalsArray locals;
|
||||
|
||||
/** {@code non-null;} the stack */
|
||||
private final ExecutionStack stack;
|
||||
|
||||
/** {@code null-ok;} stack of labels of subroutines that this block is nested in */
|
||||
private final IntList subroutines;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param locals {@code non-null;} the locals array to use
|
||||
* @param stack {@code non-null;} the execution stack to use
|
||||
*/
|
||||
private Frame(LocalsArray locals, ExecutionStack stack) {
|
||||
this(locals, stack, IntList.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param locals {@code non-null;} the locals array to use
|
||||
* @param stack {@code non-null;} the execution stack to use
|
||||
* @param subroutines {@code non-null;} list of subroutine start labels for
|
||||
* subroutines this frame is nested in
|
||||
*/
|
||||
private Frame(LocalsArray locals,
|
||||
ExecutionStack stack, IntList subroutines) {
|
||||
if (locals == null) {
|
||||
throw new NullPointerException("locals == null");
|
||||
}
|
||||
|
||||
if (stack == null) {
|
||||
throw new NullPointerException("stack == null");
|
||||
}
|
||||
|
||||
subroutines.throwIfMutable();
|
||||
|
||||
this.locals = locals;
|
||||
this.stack = stack;
|
||||
this.subroutines = subroutines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance. The locals array initially consists of
|
||||
* all-uninitialized values (represented as {@code null}s) and
|
||||
* the stack starts out empty.
|
||||
*
|
||||
* @param maxLocals {@code >= 0;} the maximum number of locals this instance
|
||||
* can refer to
|
||||
* @param maxStack {@code >= 0;} the maximum size of the stack for this
|
||||
* instance
|
||||
*/
|
||||
public Frame(int maxLocals, int maxStack) {
|
||||
this(new OneLocalsArray(maxLocals), new ExecutionStack(maxStack));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes and returns a mutable copy of this instance. The copy
|
||||
* contains copies of the locals and stack (that is, it doesn't
|
||||
* share them with the original).
|
||||
*
|
||||
* @return {@code non-null;} the copy
|
||||
*/
|
||||
public Frame copy() {
|
||||
return new Frame(locals.copy(), stack.copy(), subroutines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes this instance immutable.
|
||||
*/
|
||||
public void setImmutable() {
|
||||
locals.setImmutable();
|
||||
stack.setImmutable();
|
||||
// "subroutines" is always immutable
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all the occurrences of the given uninitialized type in
|
||||
* this frame with its initialized equivalent.
|
||||
*
|
||||
* @param type {@code non-null;} type to replace
|
||||
*/
|
||||
public void makeInitialized(Type type) {
|
||||
locals.makeInitialized(type);
|
||||
stack.makeInitialized(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locals array for this instance.
|
||||
*
|
||||
* @return {@code non-null;} the locals array
|
||||
*/
|
||||
public LocalsArray getLocals() {
|
||||
return locals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the execution stack for this instance.
|
||||
*
|
||||
* @return {@code non-null;} the execution stack
|
||||
*/
|
||||
public ExecutionStack getStack() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the largest subroutine nesting this block may be in. An
|
||||
* empty list is returned if this block is not in any subroutine.
|
||||
* Subroutines are identified by the label of their start block. The
|
||||
* list is ordered such that the deepest nesting (the actual subroutine
|
||||
* this block is in) is the last label in the list.
|
||||
*
|
||||
* @return {@code non-null;} list as noted above
|
||||
*/
|
||||
public IntList getSubroutines() {
|
||||
return subroutines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this frame with the method's parameters. Used for the first
|
||||
* frame.
|
||||
*
|
||||
* @param params Type list of method parameters.
|
||||
*/
|
||||
public void initializeWithParameters(StdTypeList params) {
|
||||
int at = 0;
|
||||
int sz = params.size();
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
Type one = params.get(i);
|
||||
locals.set(at, one);
|
||||
at += one.getCategory();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Frame instance representing the frame state that should
|
||||
* be used when returning from a subroutine. The stack state of all
|
||||
* subroutine invocations is identical, but the locals state may differ.
|
||||
*
|
||||
* @param startLabel {@code >=0;} The label of the returning subroutine's
|
||||
* start block
|
||||
* @param subLabel {@code >=0;} A calling label of a subroutine
|
||||
* @return {@code null-ok;} an appropriatly-constructed instance, or null
|
||||
* if label is not in the set
|
||||
*/
|
||||
public Frame subFrameForLabel(int startLabel, int subLabel) {
|
||||
LocalsArray subLocals = null;
|
||||
|
||||
if (locals instanceof LocalsArraySet) {
|
||||
subLocals = ((LocalsArraySet)locals).subArrayForLabel(subLabel);
|
||||
}
|
||||
|
||||
IntList newSubroutines;
|
||||
try {
|
||||
newSubroutines = subroutines.mutableCopy();
|
||||
|
||||
if (newSubroutines.pop() != startLabel) {
|
||||
throw new RuntimeException("returning from invalid subroutine");
|
||||
}
|
||||
newSubroutines.setImmutable();
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new RuntimeException("returning from invalid subroutine");
|
||||
} catch (NullPointerException ex) {
|
||||
throw new NullPointerException("can't return from non-subroutine");
|
||||
}
|
||||
|
||||
return (subLocals == null) ? null
|
||||
: new Frame(subLocals, stack, newSubroutines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two frames. If the merged result is the same as this frame,
|
||||
* then this instance is returned.
|
||||
*
|
||||
* @param other {@code non-null;} another frame
|
||||
* @return {@code non-null;} the result of merging the two frames
|
||||
*/
|
||||
public Frame mergeWith(Frame other) {
|
||||
LocalsArray resultLocals;
|
||||
ExecutionStack resultStack;
|
||||
IntList resultSubroutines;
|
||||
|
||||
resultLocals = getLocals().merge(other.getLocals());
|
||||
resultStack = getStack().merge(other.getStack());
|
||||
resultSubroutines = mergeSubroutineLists(other.subroutines);
|
||||
|
||||
resultLocals = adjustLocalsForSubroutines(
|
||||
resultLocals, resultSubroutines);
|
||||
|
||||
if ((resultLocals == getLocals())
|
||||
&& (resultStack == getStack())
|
||||
&& subroutines == resultSubroutines) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new Frame(resultLocals, resultStack, resultSubroutines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this frame's subroutine lists with another. The result
|
||||
* is the deepest common nesting (effectively, the common prefix of the
|
||||
* two lists).
|
||||
*
|
||||
* @param otherSubroutines label list of subroutine start blocks, from
|
||||
* least-nested to most-nested.
|
||||
* @return {@code non-null;} merged subroutine nest list as described above
|
||||
*/
|
||||
private IntList mergeSubroutineLists(IntList otherSubroutines) {
|
||||
if (subroutines.equals(otherSubroutines)) {
|
||||
return subroutines;
|
||||
}
|
||||
|
||||
IntList resultSubroutines = new IntList();
|
||||
|
||||
int szSubroutines = subroutines.size();
|
||||
int szOthers = otherSubroutines.size();
|
||||
for (int i = 0; i < szSubroutines && i < szOthers
|
||||
&& (subroutines.get(i) == otherSubroutines.get(i)); i++) {
|
||||
resultSubroutines.add(i);
|
||||
}
|
||||
|
||||
resultSubroutines.setImmutable();
|
||||
|
||||
return resultSubroutines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts a locals array to account for a merged subroutines list.
|
||||
* If a frame merge results in, effectively, a subroutine return through
|
||||
* a throw then the current locals will be a LocalsArraySet that will
|
||||
* need to be trimmed of all OneLocalsArray elements that relevent to
|
||||
* the subroutine that is returning.
|
||||
*
|
||||
* @param locals {@code non-null;} LocalsArray from before a merge
|
||||
* @param subroutines {@code non-null;} a label list of subroutine start blocks
|
||||
* representing the subroutine nesting of the block being merged into.
|
||||
* @return {@code non-null;} locals set appropriate for merge
|
||||
*/
|
||||
private static LocalsArray adjustLocalsForSubroutines(
|
||||
LocalsArray locals, IntList subroutines) {
|
||||
if (! (locals instanceof LocalsArraySet)) {
|
||||
// nothing to see here
|
||||
return locals;
|
||||
}
|
||||
|
||||
LocalsArraySet laSet = (LocalsArraySet)locals;
|
||||
|
||||
if (subroutines.size() == 0) {
|
||||
/*
|
||||
* We've merged from a subroutine context to a non-subroutine
|
||||
* context, likely via a throw. Our successor will only need
|
||||
* to consider the primary locals state, not the state of
|
||||
* all possible subroutine paths.
|
||||
*/
|
||||
|
||||
return laSet.getPrimary();
|
||||
}
|
||||
|
||||
/*
|
||||
* It's unclear to me if the locals set needs to be trimmed here.
|
||||
* If it does, then I believe it is all of the calling blocks
|
||||
* in the subroutine at the end of "subroutines" passed into
|
||||
* this method that should be removed.
|
||||
*/
|
||||
return laSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this frame with the frame of a subroutine caller at
|
||||
* {@code predLabel}. Only called on the frame at the first
|
||||
* block of a subroutine.
|
||||
*
|
||||
* @param other {@code non-null;} another frame
|
||||
* @param subLabel label of subroutine start block
|
||||
* @param predLabel label of calling block
|
||||
* @return {@code non-null;} the result of merging the two frames
|
||||
*/
|
||||
public Frame mergeWithSubroutineCaller(Frame other, int subLabel,
|
||||
int predLabel) {
|
||||
LocalsArray resultLocals;
|
||||
ExecutionStack resultStack;
|
||||
|
||||
resultLocals = getLocals().mergeWithSubroutineCaller(
|
||||
other.getLocals(), predLabel);
|
||||
resultStack = getStack().merge(other.getStack());
|
||||
|
||||
IntList newOtherSubroutines = other.subroutines.mutableCopy();
|
||||
newOtherSubroutines.add(subLabel);
|
||||
newOtherSubroutines.setImmutable();
|
||||
|
||||
if ((resultLocals == getLocals())
|
||||
&& (resultStack == getStack())
|
||||
&& subroutines.equals(newOtherSubroutines)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
IntList resultSubroutines;
|
||||
|
||||
if (subroutines.equals(newOtherSubroutines)) {
|
||||
resultSubroutines = subroutines;
|
||||
} else {
|
||||
/*
|
||||
* The new subroutines list should be the deepest of the two
|
||||
* lists being merged, but the postfix of the resultant list
|
||||
* must be equal to the shorter list.
|
||||
*/
|
||||
IntList nonResultSubroutines;
|
||||
|
||||
if (subroutines.size() > newOtherSubroutines.size()) {
|
||||
resultSubroutines = subroutines;
|
||||
nonResultSubroutines = newOtherSubroutines;
|
||||
} else {
|
||||
resultSubroutines = newOtherSubroutines;
|
||||
nonResultSubroutines = subroutines;
|
||||
}
|
||||
|
||||
int szResult = resultSubroutines.size();
|
||||
int szNonResult = nonResultSubroutines.size();
|
||||
|
||||
for (int i = szNonResult - 1; i >=0; i-- ) {
|
||||
if (nonResultSubroutines.get(i)
|
||||
!= resultSubroutines.get(
|
||||
i + (szResult - szNonResult))) {
|
||||
throw new
|
||||
RuntimeException("Incompatible merged subroutines");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Frame(resultLocals, resultStack, resultSubroutines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a frame for a subroutine start block, given that this is the
|
||||
* ending frame of one of the subroutine's calling blocks. Subroutine
|
||||
* calls may be nested and thus may have nested locals state, so we
|
||||
* start with an initial state as seen by the subroutine, but keep track
|
||||
* of the individual locals states that will be expected when the individual
|
||||
* subroutine calls return.
|
||||
*
|
||||
* @param subLabel label of subroutine start block
|
||||
* @param callerLabel {@code >=0;} label of the caller block where this frame
|
||||
* came from.
|
||||
* @return a new instance to begin a called subroutine.
|
||||
*/
|
||||
public Frame makeNewSubroutineStartFrame(int subLabel, int callerLabel) {
|
||||
IntList newSubroutines = subroutines.mutableCopy();
|
||||
newSubroutines.add(subLabel);
|
||||
Frame newFrame = new Frame(locals.getPrimary(), stack,
|
||||
IntList.makeImmutable(subLabel));
|
||||
return newFrame.mergeWithSubroutineCaller(this, subLabel, callerLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new frame for an exception handler block invoked from this
|
||||
* frame.
|
||||
*
|
||||
* @param exceptionClass exception that the handler block will handle
|
||||
* @return new frame
|
||||
*/
|
||||
public Frame makeExceptionHandlerStartFrame(CstType exceptionClass) {
|
||||
ExecutionStack newStack = getStack().copy();
|
||||
|
||||
newStack.clear();
|
||||
newStack.push(exceptionClass);
|
||||
|
||||
return new Frame(getLocals(), newStack, subroutines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotates (adds context to) the given exception with information
|
||||
* about this frame.
|
||||
*
|
||||
* @param ex {@code non-null;} the exception to annotate
|
||||
*/
|
||||
public void annotate(ExceptionWithContext ex) {
|
||||
locals.annotate(ex);
|
||||
stack.annotate(ex);
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.util.FixedSizeList;
|
||||
|
||||
/**
|
||||
* List of "line number" entries, which are the contents of
|
||||
* {@code LineNumberTable} attributes.
|
||||
*/
|
||||
public final class LineNumberList extends FixedSizeList {
|
||||
/** {@code non-null;} zero-size instance */
|
||||
public static final LineNumberList EMPTY = new LineNumberList(0);
|
||||
|
||||
/**
|
||||
* Returns an instance which is the concatenation of the two given
|
||||
* instances.
|
||||
*
|
||||
* @param list1 {@code non-null;} first instance
|
||||
* @param list2 {@code non-null;} second instance
|
||||
* @return {@code non-null;} combined instance
|
||||
*/
|
||||
public static LineNumberList concat(LineNumberList list1,
|
||||
LineNumberList list2) {
|
||||
if (list1 == EMPTY) {
|
||||
// easy case
|
||||
return list2;
|
||||
}
|
||||
|
||||
int sz1 = list1.size();
|
||||
int sz2 = list2.size();
|
||||
LineNumberList result = new LineNumberList(sz1 + sz2);
|
||||
|
||||
for (int i = 0; i < sz1; i++) {
|
||||
result.set(i, list1.get(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz2; i++) {
|
||||
result.set(sz1 + i, list2.get(i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param count the number of elements to be in the list
|
||||
*/
|
||||
public LineNumberList(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated item.
|
||||
*
|
||||
* @param n {@code >= 0;} which item
|
||||
* @return {@code null-ok;} the indicated item
|
||||
*/
|
||||
public Item get(int n) {
|
||||
return (Item) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param item {@code non-null;} the item
|
||||
*/
|
||||
public void set(int n, Item item) {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("item == null");
|
||||
}
|
||||
|
||||
set0(n, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param startPc {@code >= 0;} start pc of this item
|
||||
* @param lineNumber {@code >= 0;} corresponding line number
|
||||
*/
|
||||
public void set(int n, int startPc, int lineNumber) {
|
||||
set0(n, new Item(startPc, lineNumber));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number associated with the given address.
|
||||
*
|
||||
* @param pc {@code >= 0;} the address to look up
|
||||
* @return {@code >= -1;} the associated line number, or {@code -1} if
|
||||
* none is known
|
||||
*/
|
||||
public int pcToLine(int pc) {
|
||||
/*
|
||||
* Line number entries don't have to appear in any particular
|
||||
* order, so we have to do a linear search. TODO: If
|
||||
* this turns out to be a bottleneck, consider sorting the
|
||||
* list prior to use.
|
||||
*/
|
||||
int sz = size();
|
||||
int bestPc = -1;
|
||||
int bestLine = -1;
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
Item one = get(i);
|
||||
int onePc = one.getStartPc();
|
||||
if ((onePc <= pc) && (onePc > bestPc)) {
|
||||
bestPc = onePc;
|
||||
bestLine = one.getLineNumber();
|
||||
if (bestPc == pc) {
|
||||
// We can't do better than this
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item in a line number table.
|
||||
*/
|
||||
public static class Item {
|
||||
/** {@code >= 0;} start pc of this item */
|
||||
private final int startPc;
|
||||
|
||||
/** {@code >= 0;} corresponding line number */
|
||||
private final int lineNumber;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param startPc {@code >= 0;} start pc of this item
|
||||
* @param lineNumber {@code >= 0;} corresponding line number
|
||||
*/
|
||||
public Item(int startPc, int lineNumber) {
|
||||
if (startPc < 0) {
|
||||
throw new IllegalArgumentException("startPc < 0");
|
||||
}
|
||||
|
||||
if (lineNumber < 0) {
|
||||
throw new IllegalArgumentException("lineNumber < 0");
|
||||
}
|
||||
|
||||
this.startPc = startPc;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start pc of this item.
|
||||
*
|
||||
* @return the start pc
|
||||
*/
|
||||
public int getStartPc() {
|
||||
return startPc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number of this item.
|
||||
*
|
||||
* @return the line number
|
||||
*/
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.code.LocalItem;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.util.FixedSizeList;
|
||||
|
||||
/**
|
||||
* List of "local variable" entries, which are the contents of
|
||||
* {@code LocalVariableTable} and {@code LocalVariableTypeTable}
|
||||
* attributes, as well as combinations of the two.
|
||||
*/
|
||||
public final class LocalVariableList extends FixedSizeList {
|
||||
/** {@code non-null;} zero-size instance */
|
||||
public static final LocalVariableList EMPTY = new LocalVariableList(0);
|
||||
|
||||
/**
|
||||
* Returns an instance which is the concatenation of the two given
|
||||
* instances. The result is immutable.
|
||||
*
|
||||
* @param list1 {@code non-null;} first instance
|
||||
* @param list2 {@code non-null;} second instance
|
||||
* @return {@code non-null;} combined instance
|
||||
*/
|
||||
public static LocalVariableList concat(LocalVariableList list1,
|
||||
LocalVariableList list2) {
|
||||
if (list1 == EMPTY) {
|
||||
// easy case
|
||||
return list2;
|
||||
}
|
||||
|
||||
int sz1 = list1.size();
|
||||
int sz2 = list2.size();
|
||||
LocalVariableList result = new LocalVariableList(sz1 + sz2);
|
||||
|
||||
for (int i = 0; i < sz1; i++) {
|
||||
result.set(i, list1.get(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz2; i++) {
|
||||
result.set(sz1 + i, list2.get(i));
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance which is the result of merging the two
|
||||
* given instances, where one instance should have only type
|
||||
* descriptors and the other only type signatures. The merged
|
||||
* result is identical to the one with descriptors, except that
|
||||
* any element whose {name, index, start, length} matches an
|
||||
* element in the signature list gets augmented with the
|
||||
* corresponding signature. The result is immutable.
|
||||
*
|
||||
* @param descriptorList {@code non-null;} list with descriptors
|
||||
* @param signatureList {@code non-null;} list with signatures
|
||||
* @return {@code non-null;} the merged result
|
||||
*/
|
||||
public static LocalVariableList mergeDescriptorsAndSignatures(
|
||||
LocalVariableList descriptorList,
|
||||
LocalVariableList signatureList) {
|
||||
int descriptorSize = descriptorList.size();
|
||||
LocalVariableList result = new LocalVariableList(descriptorSize);
|
||||
|
||||
for (int i = 0; i < descriptorSize; i++) {
|
||||
Item item = descriptorList.get(i);
|
||||
Item signatureItem = signatureList.itemToLocal(item);
|
||||
if (signatureItem != null) {
|
||||
CstString signature = signatureItem.getSignature();
|
||||
item = item.withSignature(signature);
|
||||
}
|
||||
result.set(i, item);
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param count the number of elements to be in the list
|
||||
*/
|
||||
public LocalVariableList(int count) {
|
||||
super(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated item.
|
||||
*
|
||||
* @param n {@code >= 0;} which item
|
||||
* @return {@code null-ok;} the indicated item
|
||||
*/
|
||||
public Item get(int n) {
|
||||
return (Item) get0(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param item {@code non-null;} the item
|
||||
*/
|
||||
public void set(int n, Item item) {
|
||||
if (item == null) {
|
||||
throw new NullPointerException("item == null");
|
||||
}
|
||||
|
||||
set0(n, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the item at the given index.
|
||||
*
|
||||
* <p><b>Note:</b> At least one of {@code descriptor} or
|
||||
* {@code signature} must be passed as non-null.</p>
|
||||
*
|
||||
* @param n {@code >= 0, < size();} which element
|
||||
* @param startPc {@code >= 0;} the start pc of this variable's scope
|
||||
* @param length {@code >= 0;} the length (in bytecodes) of this variable's
|
||||
* scope
|
||||
* @param name {@code non-null;} the variable's name
|
||||
* @param descriptor {@code null-ok;} the variable's type descriptor
|
||||
* @param signature {@code null-ok;} the variable's type signature
|
||||
* @param index {@code >= 0;} the variable's local index
|
||||
*/
|
||||
public void set(int n, int startPc, int length, CstString name,
|
||||
CstString descriptor, CstString signature, int index) {
|
||||
set0(n, new Item(startPc, length, name, descriptor, signature, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local variable information in this instance which matches
|
||||
* the given {@link com.pojavdx.dx.cf.code.LocalVariableList.Item}
|
||||
* in all respects but the type descriptor and signature, if any.
|
||||
*
|
||||
* @param item {@code non-null;} local variable information to match
|
||||
* @return {@code null-ok;} the corresponding local variable information stored
|
||||
* in this instance, or {@code null} if there is no matching
|
||||
* information
|
||||
*/
|
||||
public Item itemToLocal(Item item) {
|
||||
int sz = size();
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
Item one = (Item) get0(i);
|
||||
|
||||
if ((one != null) && one.matchesAllButType(item)) {
|
||||
return one;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local variable information associated with a given address
|
||||
* and local index, if any. <b>Note:</b> In standard classfiles, a
|
||||
* variable's start point is listed as the address of the instruction
|
||||
* <i>just past</i> the one that sets the variable.
|
||||
*
|
||||
* @param pc {@code >= 0;} the address to look up
|
||||
* @param index {@code >= 0;} the local variable index
|
||||
* @return {@code null-ok;} the associated local variable information, or
|
||||
* {@code null} if none is known
|
||||
*/
|
||||
public Item pcAndIndexToLocal(int pc, int index) {
|
||||
int sz = size();
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
Item one = (Item) get0(i);
|
||||
|
||||
if ((one != null) && one.matchesPcAndIndex(pc, index)) {
|
||||
return one;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Item in a local variable table.
|
||||
*/
|
||||
public static class Item {
|
||||
/** {@code >= 0;} the start pc of this variable's scope */
|
||||
private final int startPc;
|
||||
|
||||
/** {@code >= 0;} the length (in bytecodes) of this variable's scope */
|
||||
private final int length;
|
||||
|
||||
/** {@code non-null;} the variable's name */
|
||||
private final CstString name;
|
||||
|
||||
/** {@code null-ok;} the variable's type descriptor */
|
||||
private final CstString descriptor;
|
||||
|
||||
/** {@code null-ok;} the variable's type signature */
|
||||
private final CstString signature;
|
||||
|
||||
/** {@code >= 0;} the variable's local index */
|
||||
private final int index;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* <p><b>Note:</b> At least one of {@code descriptor} or
|
||||
* {@code signature} must be passed as non-null.</p>
|
||||
*
|
||||
* @param startPc {@code >= 0;} the start pc of this variable's scope
|
||||
* @param length {@code >= 0;} the length (in bytecodes) of this variable's
|
||||
* scope
|
||||
* @param name {@code non-null;} the variable's name
|
||||
* @param descriptor {@code null-ok;} the variable's type descriptor
|
||||
* @param signature {@code null-ok;} the variable's type signature
|
||||
* @param index {@code >= 0;} the variable's local index
|
||||
*/
|
||||
public Item(int startPc, int length, CstString name,
|
||||
CstString descriptor, CstString signature, int index) {
|
||||
if (startPc < 0) {
|
||||
throw new IllegalArgumentException("startPc < 0");
|
||||
}
|
||||
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("length < 0");
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
throw new NullPointerException("name == null");
|
||||
}
|
||||
|
||||
if ((descriptor == null) && (signature == null)) {
|
||||
throw new NullPointerException(
|
||||
"(descriptor == null) && (signature == null)");
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("index < 0");
|
||||
}
|
||||
|
||||
this.startPc = startPc;
|
||||
this.length = length;
|
||||
this.name = name;
|
||||
this.descriptor = descriptor;
|
||||
this.signature = signature;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start pc of this variable's scope.
|
||||
*
|
||||
* @return {@code >= 0;} the start pc of this variable's scope
|
||||
*/
|
||||
public int getStartPc() {
|
||||
return startPc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length (in bytecodes) of this variable's scope.
|
||||
*
|
||||
* @return {@code >= 0;} the length (in bytecodes) of this variable's scope
|
||||
*/
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable's type descriptor.
|
||||
*
|
||||
* @return {@code null-ok;} the variable's type descriptor
|
||||
*/
|
||||
public CstString getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable's LocalItem, a (name, signature) tuple
|
||||
*
|
||||
* @return {@code null-ok;} the variable's type descriptor
|
||||
*/
|
||||
public LocalItem getLocalItem() {
|
||||
return LocalItem.make(name, signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable's type signature. Private because if you need this,
|
||||
* you want getLocalItem() instead.
|
||||
*
|
||||
* @return {@code null-ok;} the variable's type signature
|
||||
*/
|
||||
private CstString getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable's local index.
|
||||
*
|
||||
* @return {@code >= 0;} the variable's local index
|
||||
*/
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable's type descriptor. This is a convenient shorthand
|
||||
* for {@code Type.intern(getDescriptor().getString())}.
|
||||
*
|
||||
* @return {@code non-null;} the variable's type
|
||||
*/
|
||||
public Type getType() {
|
||||
return Type.intern(descriptor.getString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs and returns an instance which is identical to this
|
||||
* one, except that the signature is changed to the given value.
|
||||
*
|
||||
* @param newSignature {@code non-null;} the new signature
|
||||
* @return {@code non-null;} an appropriately-constructed instance
|
||||
*/
|
||||
public Item withSignature(CstString newSignature) {
|
||||
return new Item(startPc, length, name, descriptor, newSignature,
|
||||
index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this instance matches (describes) the given
|
||||
* address and index.
|
||||
*
|
||||
* @param pc {@code >= 0;} the address in question
|
||||
* @param index {@code >= 0;} the local variable index in question
|
||||
* @return {@code true} iff this instance matches {@code pc}
|
||||
* and {@code index}
|
||||
*/
|
||||
public boolean matchesPcAndIndex(int pc, int index) {
|
||||
return (index == this.index) &&
|
||||
(pc >= startPc) &&
|
||||
(pc < (startPc + length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether this instance matches (describes) the given
|
||||
* other instance exactly in all fields except type descriptor and
|
||||
* type signature.
|
||||
*
|
||||
* @param other {@code non-null;} the instance to compare to
|
||||
* @return {@code true} iff this instance matches
|
||||
*/
|
||||
public boolean matchesAllButType(Item other) {
|
||||
return (startPc == other.startPc)
|
||||
&& (length == other.length)
|
||||
&& (index == other.index)
|
||||
&& name.equals(other.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
import com.pojavdx.dx.rop.code.RegisterSpec;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.MutabilityControl;
|
||||
import com.pojavdx.dx.util.ToHuman;
|
||||
|
||||
/**
|
||||
* Representation of an array of local variables, with Java semantics.
|
||||
*
|
||||
* <p><b>Note:</b> For the most part, the documentation for this class
|
||||
* ignores the distinction between {@link Type} and {@link
|
||||
* TypeBearer}.</p>
|
||||
*/
|
||||
public abstract class LocalsArray extends MutabilityControl implements ToHuman {
|
||||
|
||||
/**
|
||||
* Constructs an instance, explicitly indicating the mutability.
|
||||
*
|
||||
* @param mutable {@code true} if this instance is mutable
|
||||
*/
|
||||
protected LocalsArray(boolean mutable) {
|
||||
super(mutable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes and returns a mutable copy of this instance.
|
||||
*
|
||||
* @return {@code non-null;} the copy
|
||||
*/
|
||||
public abstract LocalsArray copy();
|
||||
|
||||
/**
|
||||
* Annotates (adds context to) the given exception with information
|
||||
* about this instance.
|
||||
*
|
||||
* @param ex {@code non-null;} the exception to annotate
|
||||
*/
|
||||
public abstract void annotate(ExceptionWithContext ex);
|
||||
|
||||
/**
|
||||
* Replaces all the occurrences of the given uninitialized type in
|
||||
* this array with its initialized equivalent.
|
||||
*
|
||||
* @param type {@code non-null;} type to replace
|
||||
*/
|
||||
public abstract void makeInitialized(Type type);
|
||||
|
||||
/**
|
||||
* Gets the maximum number of locals this instance can refer to.
|
||||
*
|
||||
* @return the max locals
|
||||
*/
|
||||
public abstract int getMaxLocals();
|
||||
|
||||
/**
|
||||
* Sets the type stored at the given local index. If the given type
|
||||
* is category-2, then (a) the index must be at least two less than
|
||||
* {@link #getMaxLocals} and (b) the next index gets invalidated
|
||||
* by the operation. In case of either category, if the <i>previous</i>
|
||||
* local contains a category-2 value, then it too is invalidated by
|
||||
* this operation.
|
||||
*
|
||||
* @param idx {@code >= 0, < getMaxLocals();} which local
|
||||
* @param type {@code non-null;} new type for the local at {@code idx}
|
||||
*/
|
||||
public abstract void set(int idx, TypeBearer type);
|
||||
|
||||
/**
|
||||
* Sets the type for the local indicated by the given register spec
|
||||
* to that register spec (which includes type and optional name
|
||||
* information). This is identical to calling
|
||||
* {@code set(spec.getReg(), spec)}.
|
||||
*
|
||||
* @param spec {@code non-null;} register spec to use as the basis for the update
|
||||
*/
|
||||
public abstract void set(RegisterSpec spec);
|
||||
|
||||
/**
|
||||
* Invalidates the local at the given index.
|
||||
*
|
||||
* @param idx {@code >= 0, < getMaxLocals();} which local
|
||||
*/
|
||||
public abstract void invalidate(int idx);
|
||||
|
||||
/**
|
||||
* Gets the type stored at the given local index, or {@code null}
|
||||
* if the given local is uninitialized / invalid.
|
||||
*
|
||||
* @param idx {@code >= 0, < getMaxLocals();} which local
|
||||
* @return {@code null-ok;} the type of value stored in that local
|
||||
*/
|
||||
public abstract TypeBearer getOrNull(int idx);
|
||||
|
||||
/**
|
||||
* Gets the type stored at the given local index, only succeeding if
|
||||
* the given local contains a valid type (though it is allowed to
|
||||
* be an uninitialized instance).
|
||||
*
|
||||
* @param idx {@code >= 0, < getMaxLocals();} which local
|
||||
* @return {@code non-null;} the type of value stored in that local
|
||||
* @throws SimException thrown if {@code idx} is valid, but
|
||||
* the contents are invalid
|
||||
*/
|
||||
public abstract TypeBearer get(int idx);
|
||||
|
||||
/**
|
||||
* Gets the type stored at the given local index, which is expected
|
||||
* to be an initialized category-1 value.
|
||||
*
|
||||
* @param idx {@code >= 0, < getMaxLocals();} which local
|
||||
* @return {@code non-null;} the type of value stored in that local
|
||||
* @throws SimException thrown if {@code idx} is valid, but
|
||||
* one of the following holds: (a) the local is invalid; (b) the local
|
||||
* contains an uninitialized instance; (c) the local contains a
|
||||
* category-2 value
|
||||
*/
|
||||
public abstract TypeBearer getCategory1(int idx);
|
||||
|
||||
/**
|
||||
* Gets the type stored at the given local index, which is expected
|
||||
* to be a category-2 value.
|
||||
*
|
||||
* @param idx {@code >= 0, < getMaxLocals();} which local
|
||||
* @return {@code non-null;} the type of value stored in that local
|
||||
* @throws SimException thrown if {@code idx} is valid, but
|
||||
* one of the following holds: (a) the local is invalid; (b) the local
|
||||
* contains a category-1 value
|
||||
*/
|
||||
public abstract TypeBearer getCategory2(int idx);
|
||||
|
||||
/**
|
||||
* Merges this instance with {@code other}. If the merged result is
|
||||
* the same as this instance, then this is returned (not a copy).
|
||||
*
|
||||
* @param other {@code non-null;} another LocalsArray
|
||||
* @return {@code non-null;} the merge result, a new instance or this
|
||||
*/
|
||||
public abstract LocalsArray merge(LocalsArray other);
|
||||
|
||||
/**
|
||||
* Merges this instance with a {@code LocalsSet} from a subroutine
|
||||
* caller. To be used when merging in the first block of a subroutine.
|
||||
*
|
||||
* @param other {@code other non-null;} another LocalsArray. The final locals
|
||||
* state of a subroutine caller.
|
||||
* @param predLabel the label of the subroutine caller block.
|
||||
* @return {@code non-null;} the merge result, a new instance or this
|
||||
*/
|
||||
public abstract LocalsArraySet mergeWithSubroutineCaller
|
||||
(LocalsArray other, int predLabel);
|
||||
|
||||
/**
|
||||
* Gets the locals set appropriate for the current execution context.
|
||||
* That is, if this is a {@code OneLocalsArray} instance, then return
|
||||
* {@code this}, otherwise return {@code LocalsArraySet}'s
|
||||
* primary.
|
||||
*
|
||||
* @return locals for this execution context.
|
||||
*/
|
||||
protected abstract OneLocalsArray getPrimary();
|
||||
|
||||
}
|
||||
@@ -1,461 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
import com.pojavdx.dx.rop.code.RegisterSpec;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Representation of a set of local variable arrays, with Java semantics.
|
||||
* This peculiar case is to support in-method subroutines, which can
|
||||
* have different locals sets for each caller.
|
||||
*
|
||||
* <p><b>Note:</b> For the most part, the documentation for this class
|
||||
* ignores the distinction between {@link com.pojavdx.dx.rop.type.Type} and {@link
|
||||
* com.pojavdx.dx.rop.type.TypeBearer}.</p>
|
||||
*/
|
||||
public class LocalsArraySet extends LocalsArray {
|
||||
|
||||
/**
|
||||
* The primary LocalsArray represents the locals as seen from
|
||||
* the subroutine itself, which is the merged representation of all the
|
||||
* individual locals states.
|
||||
*/
|
||||
private final OneLocalsArray primary;
|
||||
|
||||
/**
|
||||
* Indexed by label of caller block: the locals specific to each caller's
|
||||
* invocation of the subroutine.
|
||||
*/
|
||||
private final ArrayList<LocalsArray> secondaries;
|
||||
|
||||
/**
|
||||
* Constructs an instance. The locals array initially consists of
|
||||
* all-uninitialized values (represented as {@code null}s).
|
||||
*
|
||||
* @param maxLocals {@code >= 0;} the maximum number of locals this instance
|
||||
* can refer to
|
||||
*/
|
||||
public LocalsArraySet(int maxLocals) {
|
||||
super(maxLocals != 0);
|
||||
primary = new OneLocalsArray(maxLocals);
|
||||
secondaries = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance with the specified primary and secondaries set.
|
||||
*
|
||||
* @param primary {@code non-null;} primary locals to use
|
||||
* @param secondaries {@code non-null;} secondaries set, indexed by subroutine
|
||||
* caller label.
|
||||
*/
|
||||
public LocalsArraySet(OneLocalsArray primary,
|
||||
ArrayList<LocalsArray> secondaries) {
|
||||
super(primary.getMaxLocals() > 0);
|
||||
|
||||
this.primary = primary;
|
||||
this.secondaries = secondaries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance which is a copy of another.
|
||||
*
|
||||
* @param toCopy {@code non-null;} instance to copy.
|
||||
*/
|
||||
private LocalsArraySet(LocalsArraySet toCopy) {
|
||||
super(toCopy.getMaxLocals() > 0);
|
||||
|
||||
primary = toCopy.primary.copy();
|
||||
secondaries = new ArrayList(toCopy.secondaries.size());
|
||||
|
||||
int sz = toCopy.secondaries.size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
LocalsArray la = toCopy.secondaries.get(i);
|
||||
|
||||
if (la == null) {
|
||||
secondaries.add(null);
|
||||
} else {
|
||||
secondaries.add(la.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setImmutable() {
|
||||
primary.setImmutable();
|
||||
|
||||
for (LocalsArray la : secondaries) {
|
||||
if (la != null) {
|
||||
la.setImmutable();
|
||||
}
|
||||
}
|
||||
super.setImmutable();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public LocalsArray copy() {
|
||||
return new LocalsArraySet(this);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void annotate(ExceptionWithContext ex) {
|
||||
ex.addContext("(locals array set; primary)");
|
||||
primary.annotate(ex);
|
||||
|
||||
int sz = secondaries.size();
|
||||
for (int label = 0; label < sz; label++) {
|
||||
LocalsArray la = secondaries.get(label);
|
||||
|
||||
if (la != null) {
|
||||
ex.addContext("(locals array set: primary for caller "
|
||||
+ Hex.u2(label) + ')');
|
||||
|
||||
la.getPrimary().annotate(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String toHuman() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("(locals array set; primary)\n");
|
||||
|
||||
sb.append(getPrimary().toHuman());
|
||||
sb.append('\n');
|
||||
|
||||
int sz = secondaries.size();
|
||||
for (int label = 0; label < sz; label++) {
|
||||
LocalsArray la = secondaries.get(label);
|
||||
|
||||
if (la != null) {
|
||||
sb.append("(locals array set: primary for caller "
|
||||
+ Hex.u2(label) + ")\n");
|
||||
|
||||
sb.append(la.getPrimary().toHuman());
|
||||
sb.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void makeInitialized(Type type) {
|
||||
int len = primary.getMaxLocals();
|
||||
|
||||
if (len == 0) {
|
||||
// We have to check for this before checking for immutability.
|
||||
return;
|
||||
}
|
||||
|
||||
throwIfImmutable();
|
||||
|
||||
primary.makeInitialized(type);
|
||||
|
||||
for (LocalsArray la : secondaries) {
|
||||
if (la != null) {
|
||||
la.makeInitialized(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getMaxLocals() {
|
||||
return primary.getMaxLocals();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void set(int idx, TypeBearer type) {
|
||||
throwIfImmutable();
|
||||
|
||||
primary.set(idx, type);
|
||||
|
||||
for (LocalsArray la : secondaries) {
|
||||
if (la != null) {
|
||||
la.set(idx, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void set(RegisterSpec spec) {
|
||||
set(spec.getReg(), spec);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void invalidate(int idx) {
|
||||
throwIfImmutable();
|
||||
|
||||
primary.invalidate(idx);
|
||||
|
||||
for (LocalsArray la : secondaries) {
|
||||
if (la != null) {
|
||||
la.invalidate(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getOrNull(int idx) {
|
||||
return primary.getOrNull(idx);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer get(int idx) {
|
||||
return primary.get(idx);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getCategory1(int idx) {
|
||||
return primary.getCategory1(idx);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getCategory2(int idx) {
|
||||
return primary.getCategory2(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this set with another {@code LocalsArraySet} instance.
|
||||
*
|
||||
* @param other {@code non-null;} to merge
|
||||
* @return {@code non-null;} this instance if merge was a no-op, or
|
||||
* new merged instance.
|
||||
*/
|
||||
private LocalsArraySet mergeWithSet(LocalsArraySet other) {
|
||||
OneLocalsArray newPrimary;
|
||||
ArrayList<LocalsArray> newSecondaries;
|
||||
boolean secondariesChanged = false;
|
||||
|
||||
newPrimary = primary.merge(other.getPrimary());
|
||||
|
||||
int sz1 = secondaries.size();
|
||||
int sz2 = other.secondaries.size();
|
||||
int sz = Math.max(sz1, sz2);
|
||||
newSecondaries = new ArrayList(sz);
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
LocalsArray la1 = (i < sz1 ? secondaries.get(i) : null);
|
||||
LocalsArray la2 = (i < sz2 ? other.secondaries.get(i) : null);
|
||||
LocalsArray resultla = null;
|
||||
|
||||
if (la1 == la2) {
|
||||
resultla = la1;
|
||||
} else if (la1 == null) {
|
||||
resultla = la2;
|
||||
} else if (la2 == null) {
|
||||
resultla = la1;
|
||||
} else {
|
||||
try {
|
||||
resultla = la1.merge(la2);
|
||||
} catch (SimException ex) {
|
||||
ex.addContext(
|
||||
"Merging locals set for caller block " + Hex.u2(i));
|
||||
}
|
||||
}
|
||||
|
||||
secondariesChanged = secondariesChanged || (la1 != resultla);
|
||||
|
||||
newSecondaries.add(resultla);
|
||||
}
|
||||
|
||||
if ((primary == newPrimary) && ! secondariesChanged ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new LocalsArraySet(newPrimary, newSecondaries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this set with a {@code OneLocalsArray} instance.
|
||||
*
|
||||
* @param other {@code non-null;} to merge
|
||||
* @return {@code non-null;} this instance if merge was a no-op, or
|
||||
* new merged instance.
|
||||
*/
|
||||
private LocalsArraySet mergeWithOne(OneLocalsArray other) {
|
||||
OneLocalsArray newPrimary;
|
||||
ArrayList<LocalsArray> newSecondaries;
|
||||
boolean secondariesChanged = false;
|
||||
|
||||
newPrimary = primary.merge(other.getPrimary());
|
||||
newSecondaries = new ArrayList(secondaries.size());
|
||||
|
||||
int sz = secondaries.size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
LocalsArray la = secondaries.get(i);
|
||||
LocalsArray resultla = null;
|
||||
|
||||
if (la != null) {
|
||||
try {
|
||||
resultla = la.merge(other);
|
||||
} catch (SimException ex) {
|
||||
ex.addContext("Merging one locals against caller block "
|
||||
+ Hex.u2(i));
|
||||
}
|
||||
}
|
||||
|
||||
secondariesChanged = secondariesChanged || (la != resultla);
|
||||
|
||||
newSecondaries.add(resultla);
|
||||
}
|
||||
|
||||
if ((primary == newPrimary) && ! secondariesChanged ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new LocalsArraySet(newPrimary, newSecondaries);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public LocalsArraySet merge(LocalsArray other) {
|
||||
LocalsArraySet result;
|
||||
|
||||
try {
|
||||
if (other instanceof LocalsArraySet) {
|
||||
result = mergeWithSet((LocalsArraySet) other);
|
||||
} else {
|
||||
result = mergeWithOne((OneLocalsArray) other);
|
||||
}
|
||||
} catch (SimException ex) {
|
||||
ex.addContext("underlay locals:");
|
||||
annotate(ex);
|
||||
ex.addContext("overlay locals:");
|
||||
other.annotate(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@code LocalsArray} instance for a specified subroutine
|
||||
* caller label, or null if label has no locals associated with it.
|
||||
*
|
||||
* @param label {@code >= 0;} subroutine caller label
|
||||
* @return {@code null-ok;} locals if available.
|
||||
*/
|
||||
private LocalsArray getSecondaryForLabel(int label) {
|
||||
if (label >= secondaries.size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return secondaries.get(label);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public LocalsArraySet mergeWithSubroutineCaller
|
||||
(LocalsArray other, int predLabel) {
|
||||
|
||||
LocalsArray mine = getSecondaryForLabel(predLabel);
|
||||
LocalsArray newSecondary;
|
||||
OneLocalsArray newPrimary;
|
||||
|
||||
newPrimary = primary.merge(other.getPrimary());
|
||||
|
||||
if (mine == other) {
|
||||
newSecondary = mine;
|
||||
} else if (mine == null) {
|
||||
newSecondary = other;
|
||||
} else {
|
||||
newSecondary = mine.merge(other);
|
||||
}
|
||||
|
||||
if ((newSecondary == mine) && (newPrimary == primary)) {
|
||||
return this;
|
||||
} else {
|
||||
/*
|
||||
* We're going to re-build a primary as a merge of all the
|
||||
* secondaries.
|
||||
*/
|
||||
newPrimary = null;
|
||||
|
||||
int szSecondaries = secondaries.size();
|
||||
int sz = Math.max(predLabel + 1, szSecondaries);
|
||||
ArrayList<LocalsArray> newSecondaries = new ArrayList(sz);
|
||||
for (int i = 0; i < sz; i++) {
|
||||
LocalsArray la = null;
|
||||
|
||||
if (i == predLabel) {
|
||||
/*
|
||||
* This LocalsArray always replaces any existing one,
|
||||
* since this is the result of a refined iteration.
|
||||
*/
|
||||
la = newSecondary;
|
||||
} else if (i < szSecondaries) {
|
||||
la = secondaries.get(i);
|
||||
}
|
||||
|
||||
if (la != null) {
|
||||
if (newPrimary == null) {
|
||||
newPrimary = la.getPrimary();
|
||||
} else {
|
||||
newPrimary = newPrimary.merge(la.getPrimary());
|
||||
}
|
||||
}
|
||||
|
||||
newSecondaries.add(la);
|
||||
}
|
||||
|
||||
LocalsArraySet result
|
||||
= new LocalsArraySet(newPrimary, newSecondaries);
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a LocalsArray instance representing the locals state that should
|
||||
* be used when returning to a subroutine caller.
|
||||
*
|
||||
* @param subLabel {@code >= 0;} A calling label of a subroutine
|
||||
* @return {@code null-ok;} an instance for this subroutine, or null if subroutine
|
||||
* is not in this set.
|
||||
*/
|
||||
public LocalsArray subArrayForLabel(int subLabel) {
|
||||
LocalsArray result = getSecondaryForLabel(subLabel);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**{@inheritDoc}*/
|
||||
@Override
|
||||
protected OneLocalsArray getPrimary() {
|
||||
return primary;
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.code.LocalItem;
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.type.Prototype;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Interface for machines capable of executing bytecode by acting
|
||||
* upon a {@link Frame}. A machine conceptually contains four arbitrary-value
|
||||
* argument slots, slots for several literal-value arguments, and slots for
|
||||
* branch target information.
|
||||
*/
|
||||
public interface Machine {
|
||||
/**
|
||||
* Gets the effective prototype of the method that this instance is
|
||||
* being used for. The <i>effective</i> prototype includes an initial
|
||||
* {@code this} argument for instance methods.
|
||||
*
|
||||
* @return {@code non-null;} the method prototype
|
||||
*/
|
||||
public Prototype getPrototype();
|
||||
|
||||
/**
|
||||
* Clears the regular and auxiliary arguments area.
|
||||
*/
|
||||
public void clearArgs();
|
||||
|
||||
/**
|
||||
* Pops the given number of values from the stack (of either category),
|
||||
* and store them in the arguments area, indicating that there are now
|
||||
* that many arguments. Also, clear the auxiliary arguments.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param count {@code >= 0;} number of values to pop
|
||||
*/
|
||||
public void popArgs(Frame frame, int count);
|
||||
|
||||
/**
|
||||
* Pops values from the stack of the types indicated by the given
|
||||
* {@code Prototype} (popped in reverse of the argument
|
||||
* order, so the first prototype argument type is for the deepest
|
||||
* element of the stack), and store them in the arguments area,
|
||||
* indicating that there are now that many arguments. Also, clear
|
||||
* the auxiliary arguments.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param prototype {@code non-null;} prototype indicating arguments to pop
|
||||
*/
|
||||
public void popArgs(Frame frame, Prototype prototype);
|
||||
|
||||
/**
|
||||
* Pops a value from the stack of the indicated type, and store it
|
||||
* in the arguments area, indicating that there are now that many
|
||||
* arguments. Also, clear the auxiliary arguments.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param type {@code non-null;} type of the argument
|
||||
*/
|
||||
public void popArgs(Frame frame, Type type);
|
||||
|
||||
/**
|
||||
* Pops values from the stack of the indicated types (popped in
|
||||
* reverse argument order, so the first indicated type is for the
|
||||
* deepest element of the stack), and store them in the arguments
|
||||
* area, indicating that there are now that many arguments. Also,
|
||||
* clear the auxiliary arguments.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param type1 {@code non-null;} type of the first argument
|
||||
* @param type2 {@code non-null;} type of the second argument
|
||||
*/
|
||||
public void popArgs(Frame frame, Type type1, Type type2);
|
||||
|
||||
/**
|
||||
* Pops values from the stack of the indicated types (popped in
|
||||
* reverse argument order, so the first indicated type is for the
|
||||
* deepest element of the stack), and store them in the arguments
|
||||
* area, indicating that there are now that many arguments. Also,
|
||||
* clear the auxiliary arguments.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param type1 {@code non-null;} type of the first argument
|
||||
* @param type2 {@code non-null;} type of the second argument
|
||||
* @param type3 {@code non-null;} type of the third argument
|
||||
*/
|
||||
public void popArgs(Frame frame, Type type1, Type type2, Type type3);
|
||||
|
||||
/**
|
||||
* Loads the local variable with the given index as the sole argument in
|
||||
* the arguments area. Also, clear the auxiliary arguments.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param idx {@code >= 0;} the local variable index
|
||||
*/
|
||||
public void localArg(Frame frame, int idx);
|
||||
|
||||
/**
|
||||
* Used to specify if a loaded local variable has info in the local
|
||||
* variable table.
|
||||
*
|
||||
* @param local {@code true} if local arg has info in local variable table
|
||||
*/
|
||||
public void localInfo(boolean local);
|
||||
|
||||
/**
|
||||
* Indicates that the salient type of this operation is as
|
||||
* given. This differentiates between, for example, the various
|
||||
* arithmetic opcodes, which, by the time they hit a
|
||||
* {@code Machine} are collapsed to the {@code int}
|
||||
* variant. (See {@link BytecodeArray#parseInstruction} for
|
||||
* details.)
|
||||
*
|
||||
* @param type {@code non-null;} the salient type of the upcoming operation
|
||||
*/
|
||||
public void auxType(Type type);
|
||||
|
||||
/**
|
||||
* Indicates that there is an auxiliary (inline, not stack)
|
||||
* argument of type {@code int}, with the given value.
|
||||
*
|
||||
* <p><b>Note:</b> Perhaps unintuitively, the stack manipulation
|
||||
* ops (e.g., {@code dup} and {@code swap}) use this to
|
||||
* indicate the result stack pattern with a straightforward hex
|
||||
* encoding of the push order starting with least-significant
|
||||
* nibbles getting pushed first). For example, an all-category-1
|
||||
* {@code dup2_x1} sets this to {@code 0x12312}, and the
|
||||
* other form of that op sets this to
|
||||
* {@code 0x121}.</p>
|
||||
*
|
||||
* <p><b>Also Note:</b> For {@code switch*} instructions, this is
|
||||
* used to indicate the padding value (which is only useful for
|
||||
* verification).</p>
|
||||
*
|
||||
* @param value the argument value
|
||||
*/
|
||||
public void auxIntArg(int value);
|
||||
|
||||
/**
|
||||
* Indicates that there is an auxiliary (inline, not stack) object
|
||||
* argument, with the value based on the given constant.
|
||||
*
|
||||
* <p><b>Note:</b> Some opcodes use both {@code int} and
|
||||
* constant auxiliary arguments.</p>
|
||||
*
|
||||
* @param cst {@code non-null;} the constant containing / referencing
|
||||
* the value
|
||||
*/
|
||||
public void auxCstArg(Constant cst);
|
||||
|
||||
/**
|
||||
* Indicates that there is an auxiliary (inline, not stack) argument
|
||||
* indicating a branch target.
|
||||
*
|
||||
* @param target the argument value
|
||||
*/
|
||||
public void auxTargetArg(int target);
|
||||
|
||||
/**
|
||||
* Indicates that there is an auxiliary (inline, not stack) argument
|
||||
* consisting of a {@code switch*} table.
|
||||
*
|
||||
* <p><b>Note:</b> This is generally used in conjunction with
|
||||
* {@link #auxIntArg} (which holds the padding).</p>
|
||||
*
|
||||
* @param cases {@code non-null;} the list of key-target pairs, plus the default
|
||||
* target
|
||||
*/
|
||||
public void auxSwitchArg(SwitchList cases);
|
||||
|
||||
/**
|
||||
* Indicates that there is an auxiliary (inline, not stack) argument
|
||||
* consisting of a list of initial values for a newly created array.
|
||||
*
|
||||
* @param initValues {@code non-null;} the list of constant values to initialize
|
||||
* the array
|
||||
*/
|
||||
public void auxInitValues(ArrayList<Constant> initValues);
|
||||
|
||||
/**
|
||||
* Indicates that the target of this operation is the given local.
|
||||
*
|
||||
* @param idx {@code >= 0;} the local variable index
|
||||
* @param type {@code non-null;} the type of the local
|
||||
* @param local {@code null-ok;} the name and signature of the local, if known
|
||||
*/
|
||||
public void localTarget(int idx, Type type, LocalItem local);
|
||||
|
||||
/**
|
||||
* "Runs" the indicated opcode in an appropriate way, using the arguments
|
||||
* area as appropriate, and modifying the given frame in response.
|
||||
*
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @param offset {@code >= 0;} byte offset in the method to the opcode being
|
||||
* run
|
||||
* @param opcode {@code >= 0;} the opcode to run
|
||||
*/
|
||||
public void run(Frame frame, int offset, int opcode);
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* Utility methods to merge various frame information.
|
||||
*/
|
||||
public final class Merger {
|
||||
/**
|
||||
* This class is uninstantiable.
|
||||
*/
|
||||
private Merger() {
|
||||
// This space intentionally left blank.
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two locals arrays. If the merged result is the same as the first
|
||||
* argument, then return the first argument (not a copy).
|
||||
*
|
||||
* @param locals1 {@code non-null;} a locals array
|
||||
* @param locals2 {@code non-null;} another locals array
|
||||
* @return {@code non-null;} the result of merging the two locals arrays
|
||||
*/
|
||||
public static OneLocalsArray mergeLocals(OneLocalsArray locals1,
|
||||
OneLocalsArray locals2) {
|
||||
if (locals1 == locals2) {
|
||||
// Easy out.
|
||||
return locals1;
|
||||
}
|
||||
|
||||
int sz = locals1.getMaxLocals();
|
||||
OneLocalsArray result = null;
|
||||
|
||||
if (locals2.getMaxLocals() != sz) {
|
||||
throw new SimException("mismatched maxLocals values");
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
TypeBearer tb1 = locals1.getOrNull(i);
|
||||
TypeBearer tb2 = locals2.getOrNull(i);
|
||||
TypeBearer resultType = mergeType(tb1, tb2);
|
||||
if (resultType != tb1) {
|
||||
/*
|
||||
* We only need to do anything when the result differs
|
||||
* from what is in the first array, since that's what the
|
||||
* result gets initialized to.
|
||||
*/
|
||||
if (result == null) {
|
||||
result = locals1.copy();
|
||||
}
|
||||
|
||||
if (resultType == null) {
|
||||
result.invalidate(i);
|
||||
} else {
|
||||
result.set(i, resultType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return locals1;
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two stacks. If the merged result is the same as the first
|
||||
* argument, then return the first argument (not a copy).
|
||||
*
|
||||
* @param stack1 {@code non-null;} a stack
|
||||
* @param stack2 {@code non-null;} another stack
|
||||
* @return {@code non-null;} the result of merging the two stacks
|
||||
*/
|
||||
public static ExecutionStack mergeStack(ExecutionStack stack1,
|
||||
ExecutionStack stack2) {
|
||||
if (stack1 == stack2) {
|
||||
// Easy out.
|
||||
return stack1;
|
||||
}
|
||||
|
||||
int sz = stack1.size();
|
||||
ExecutionStack result = null;
|
||||
|
||||
if (stack2.size() != sz) {
|
||||
throw new SimException("mismatched stack depths");
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
TypeBearer tb1 = stack1.peek(i);
|
||||
TypeBearer tb2 = stack2.peek(i);
|
||||
TypeBearer resultType = mergeType(tb1, tb2);
|
||||
if (resultType != tb1) {
|
||||
/*
|
||||
* We only need to do anything when the result differs
|
||||
* from what is in the first stack, since that's what the
|
||||
* result gets initialized to.
|
||||
*/
|
||||
if (result == null) {
|
||||
result = stack1.copy();
|
||||
}
|
||||
|
||||
try {
|
||||
if (resultType == null) {
|
||||
throw new SimException("incompatible: " + tb1 + ", " +
|
||||
tb2);
|
||||
} else {
|
||||
result.change(i, resultType);
|
||||
}
|
||||
} catch (SimException ex) {
|
||||
ex.addContext("...while merging stack[" + Hex.u2(i) + "]");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
return stack1;
|
||||
}
|
||||
|
||||
result.setImmutable();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges two frame types.
|
||||
*
|
||||
* @param ft1 {@code non-null;} a frame type
|
||||
* @param ft2 {@code non-null;} another frame type
|
||||
* @return {@code non-null;} the result of merging the two types
|
||||
*/
|
||||
public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) {
|
||||
if ((ft1 == null) || ft1.equals(ft2)) {
|
||||
return ft1;
|
||||
} else if (ft2 == null) {
|
||||
return null;
|
||||
} else {
|
||||
Type type1 = ft1.getType();
|
||||
Type type2 = ft2.getType();
|
||||
|
||||
if (type1 == type2) {
|
||||
return type1;
|
||||
} else if (type1.isReference() && type2.isReference()) {
|
||||
if (type1 == Type.KNOWN_NULL) {
|
||||
/*
|
||||
* A known-null merges with any other reference type to
|
||||
* be that reference type.
|
||||
*/
|
||||
return type2;
|
||||
} else if (type2 == Type.KNOWN_NULL) {
|
||||
/*
|
||||
* The same as above, but this time it's type2 that's
|
||||
* the known-null.
|
||||
*/
|
||||
return type1;
|
||||
} else if (type1.isArray() && type2.isArray()) {
|
||||
TypeBearer componentUnion =
|
||||
mergeType(type1.getComponentType(),
|
||||
type2.getComponentType());
|
||||
if (componentUnion == null) {
|
||||
/*
|
||||
* At least one of the types is a primitive type,
|
||||
* so the merged result is just Object.
|
||||
*/
|
||||
return Type.OBJECT;
|
||||
}
|
||||
return ((Type) componentUnion).getArrayType();
|
||||
} else {
|
||||
/*
|
||||
* All other unequal reference types get merged to be
|
||||
* Object in this phase. This is fine here, but it
|
||||
* won't be the right thing to do in the verifier.
|
||||
*/
|
||||
return Type.OBJECT;
|
||||
}
|
||||
} else if (type1.isIntlike() && type2.isIntlike()) {
|
||||
/*
|
||||
* Merging two non-identical int-like types results in
|
||||
* the type int.
|
||||
*/
|
||||
return Type.INT;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given supertype is possibly assignable from
|
||||
* the given subtype. This takes into account primitiveness,
|
||||
* int-likeness, known-nullness, and array dimensions, but does
|
||||
* not assume anything about class hierarchy other than that the
|
||||
* type {@code Object} is the supertype of all reference
|
||||
* types and all arrays are assignable to
|
||||
* {@code Serializable} and {@code Cloneable}.
|
||||
*
|
||||
* @param supertypeBearer {@code non-null;} the supertype
|
||||
* @param subtypeBearer {@code non-null;} the subtype
|
||||
*/
|
||||
public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer,
|
||||
TypeBearer subtypeBearer) {
|
||||
Type supertype = supertypeBearer.getType();
|
||||
Type subtype = subtypeBearer.getType();
|
||||
|
||||
if (supertype.equals(subtype)) {
|
||||
// Easy out.
|
||||
return true;
|
||||
}
|
||||
|
||||
int superBt = supertype.getBasicType();
|
||||
int subBt = subtype.getBasicType();
|
||||
|
||||
// Treat return types as Object for the purposes of this method.
|
||||
|
||||
if (superBt == Type.BT_ADDR) {
|
||||
supertype = Type.OBJECT;
|
||||
superBt = Type.BT_OBJECT;
|
||||
}
|
||||
|
||||
if (subBt == Type.BT_ADDR) {
|
||||
subtype = Type.OBJECT;
|
||||
subBt = Type.BT_OBJECT;
|
||||
}
|
||||
|
||||
if ((superBt != Type.BT_OBJECT) || (subBt != Type.BT_OBJECT)) {
|
||||
/*
|
||||
* No two distinct primitive types are assignable in this sense,
|
||||
* unless they are both int-like.
|
||||
*/
|
||||
return supertype.isIntlike() && subtype.isIntlike();
|
||||
}
|
||||
|
||||
// At this point, we know both types are reference types.
|
||||
|
||||
if (supertype == Type.KNOWN_NULL) {
|
||||
/*
|
||||
* A known-null supertype is only assignable from another
|
||||
* known-null (handled in the easy out at the top of the
|
||||
* method).
|
||||
*/
|
||||
return false;
|
||||
} else if (subtype == Type.KNOWN_NULL) {
|
||||
/*
|
||||
* A known-null subtype is in fact assignable to any
|
||||
* reference type.
|
||||
*/
|
||||
return true;
|
||||
} else if (supertype == Type.OBJECT) {
|
||||
/*
|
||||
* Object is assignable from any reference type.
|
||||
*/
|
||||
return true;
|
||||
} else if (supertype.isArray()) {
|
||||
// The supertype is an array type.
|
||||
if (! subtype.isArray()) {
|
||||
// The subtype isn't an array, and so can't be assignable.
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Strip off as many matched component types from both
|
||||
* types as possible, and check the assignability of the
|
||||
* results.
|
||||
*/
|
||||
do {
|
||||
supertype = supertype.getComponentType();
|
||||
subtype = subtype.getComponentType();
|
||||
} while (supertype.isArray() && subtype.isArray());
|
||||
|
||||
return isPossiblyAssignableFrom(supertype, subtype);
|
||||
} else if (subtype.isArray()) {
|
||||
/*
|
||||
* Other than Object (handled above), array types are
|
||||
* assignable only to Serializable and Cloneable.
|
||||
*/
|
||||
return (supertype == Type.SERIALIZABLE) ||
|
||||
(supertype == Type.CLONEABLE);
|
||||
} else {
|
||||
/*
|
||||
* All other unequal reference types are considered at
|
||||
* least possibly assignable.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
import com.pojavdx.dx.rop.code.RegisterSpec;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* Representation of an array of local variables, with Java semantics.
|
||||
*
|
||||
* <p><b>Note:</b> For the most part, the documentation for this class
|
||||
* ignores the distinction between {@link com.pojavdx.dx.rop.type.Type} and {@link
|
||||
* com.pojavdx.dx.rop.type.TypeBearer}.</p>
|
||||
*/
|
||||
public class OneLocalsArray extends LocalsArray {
|
||||
/** {@code non-null;} actual array */
|
||||
private final TypeBearer[] locals;
|
||||
|
||||
/**
|
||||
* Constructs an instance. The locals array initially consists of
|
||||
* all-uninitialized values (represented as {@code null}s).
|
||||
*
|
||||
* @param maxLocals {@code >= 0;} the maximum number of locals this instance
|
||||
* can refer to
|
||||
*/
|
||||
public OneLocalsArray(int maxLocals) {
|
||||
super(maxLocals != 0);
|
||||
locals = new TypeBearer[maxLocals];
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public OneLocalsArray copy() {
|
||||
OneLocalsArray result = new OneLocalsArray(locals.length);
|
||||
|
||||
net.kdt.pojavlaunch.SystemCrackResolver.arraycopy(locals, 0, result.locals, 0, locals.length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void annotate(ExceptionWithContext ex) {
|
||||
for (int i = 0; i < locals.length; i++) {
|
||||
TypeBearer type = locals[i];
|
||||
String s = (type == null) ? "<invalid>" : type.toString();
|
||||
ex.addContext("locals[" + Hex.u2(i) + "]: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String toHuman() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < locals.length; i++) {
|
||||
TypeBearer type = locals[i];
|
||||
String s = (type == null) ? "<invalid>" : type.toString();
|
||||
sb.append("locals[" + Hex.u2(i) + "]: " + s + "\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void makeInitialized(Type type) {
|
||||
int len = locals.length;
|
||||
|
||||
if (len == 0) {
|
||||
// We have to check for this before checking for immutability.
|
||||
return;
|
||||
}
|
||||
|
||||
throwIfImmutable();
|
||||
|
||||
Type initializedType = type.getInitializedType();
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (locals[i] == type) {
|
||||
locals[i] = initializedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getMaxLocals() {
|
||||
return locals.length;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void set(int idx, TypeBearer type) {
|
||||
throwIfImmutable();
|
||||
|
||||
try {
|
||||
type = type.getFrameType();
|
||||
} catch (NullPointerException ex) {
|
||||
// Elucidate the exception
|
||||
throw new NullPointerException("type == null");
|
||||
}
|
||||
|
||||
if (idx < 0) {
|
||||
throw new IndexOutOfBoundsException("idx < 0");
|
||||
}
|
||||
|
||||
// Make highest possible out-of-bounds check happen first.
|
||||
if (type.getType().isCategory2()) {
|
||||
locals[idx + 1] = null;
|
||||
}
|
||||
|
||||
locals[idx] = type;
|
||||
|
||||
if (idx != 0) {
|
||||
TypeBearer prev = locals[idx - 1];
|
||||
if ((prev != null) && prev.getType().isCategory2()) {
|
||||
locals[idx - 1] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void set(RegisterSpec spec) {
|
||||
set(spec.getReg(), spec);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void invalidate(int idx) {
|
||||
throwIfImmutable();
|
||||
locals[idx] = null;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getOrNull(int idx) {
|
||||
return locals[idx];
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer get(int idx) {
|
||||
TypeBearer result = locals[idx];
|
||||
|
||||
if (result == null) {
|
||||
return throwSimException(idx, "invalid");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getCategory1(int idx) {
|
||||
TypeBearer result = get(idx);
|
||||
Type type = result.getType();
|
||||
|
||||
if (type.isUninitialized()) {
|
||||
return throwSimException(idx, "uninitialized instance");
|
||||
}
|
||||
|
||||
if (type.isCategory2()) {
|
||||
return throwSimException(idx, "category-2");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getCategory2(int idx) {
|
||||
TypeBearer result = get(idx);
|
||||
|
||||
if (result.getType().isCategory1()) {
|
||||
return throwSimException(idx, "category-1");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public LocalsArray merge(LocalsArray other) {
|
||||
if (other instanceof OneLocalsArray) {
|
||||
return merge((OneLocalsArray)other);
|
||||
} else { //LocalsArraySet
|
||||
// LocalsArraySet knows how to merge me.
|
||||
return other.merge(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges this OneLocalsArray instance with another OneLocalsArray
|
||||
* instance. A more-refined version of {@link #merge(LocalsArray) merge}
|
||||
* which is called by that method when appropriate.
|
||||
*
|
||||
* @param other locals array with which to merge
|
||||
* @return this instance if merge was a no-op, or a new instance if
|
||||
* the merge resulted in a change.
|
||||
*/
|
||||
public OneLocalsArray merge(OneLocalsArray other) {
|
||||
try {
|
||||
return Merger.mergeLocals(this, other);
|
||||
} catch (SimException ex) {
|
||||
ex.addContext("underlay locals:");
|
||||
annotate(ex);
|
||||
ex.addContext("overlay locals:");
|
||||
other.annotate(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public LocalsArraySet mergeWithSubroutineCaller
|
||||
(LocalsArray other, int predLabel) {
|
||||
|
||||
LocalsArraySet result = new LocalsArraySet(getMaxLocals());
|
||||
return result.mergeWithSubroutineCaller(other, predLabel);
|
||||
}
|
||||
|
||||
/**{@inheritDoc}*/
|
||||
@Override
|
||||
protected OneLocalsArray getPrimary() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a properly-formatted exception.
|
||||
*
|
||||
* @param idx the salient local index
|
||||
* @param msg {@code non-null;} useful message
|
||||
* @return never (keeps compiler happy)
|
||||
*/
|
||||
private static TypeBearer throwSimException(int idx, String msg) {
|
||||
throw new SimException("local " + Hex.u2(idx) + ": " + msg);
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* Representation of a subroutine return address. In Java verification,
|
||||
* somewhat counterintuitively, the salient bit of information you need to
|
||||
* know about a return address is the <i>start address</i> of the subroutine
|
||||
* being returned from, not the address being returned <i>to</i>, so that's
|
||||
* what instances of this class hang onto.
|
||||
*/
|
||||
public final class ReturnAddress implements TypeBearer {
|
||||
/** {@code >= 0;} the start address of the subroutine being returned from */
|
||||
private final int subroutineAddress;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param subroutineAddress {@code >= 0;} the start address of the
|
||||
* subroutine being returned from
|
||||
*/
|
||||
public ReturnAddress(int subroutineAddress) {
|
||||
if (subroutineAddress < 0) {
|
||||
throw new IllegalArgumentException("subroutineAddress < 0");
|
||||
}
|
||||
|
||||
this.subroutineAddress = subroutineAddress;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String toString() {
|
||||
return ("<addr:" + Hex.u2(subroutineAddress) + ">");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String toHuman() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.RETURN_ADDRESS;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public TypeBearer getFrameType() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getBasicType() {
|
||||
return Type.RETURN_ADDRESS.getBasicType();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getBasicFrameType() {
|
||||
return Type.RETURN_ADDRESS.getBasicFrameType();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isConstant() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ReturnAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return subroutineAddress == ((ReturnAddress) other).subroutineAddress;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return subroutineAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subroutine address.
|
||||
*
|
||||
* @return {@code >= 0;} the subroutine address
|
||||
*/
|
||||
public int getSubroutineAddress() {
|
||||
return subroutineAddress;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.util.ExceptionWithContext;
|
||||
|
||||
/**
|
||||
* Exception from simulation.
|
||||
*/
|
||||
public class SimException
|
||||
extends ExceptionWithContext {
|
||||
public SimException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SimException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SimException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -1,955 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dex.DexFormat;
|
||||
import com.pojavdx.dx.dex.DexOptions;
|
||||
import com.pojavdx.dx.rop.code.LocalItem;
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.cst.CstFieldRef;
|
||||
import com.pojavdx.dx.rop.cst.CstInteger;
|
||||
import com.pojavdx.dx.rop.cst.CstInterfaceMethodRef;
|
||||
import com.pojavdx.dx.rop.cst.CstInvokeDynamic;
|
||||
import com.pojavdx.dx.rop.cst.CstMethodHandle;
|
||||
import com.pojavdx.dx.rop.cst.CstMethodRef;
|
||||
import com.pojavdx.dx.rop.cst.CstProtoRef;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.Prototype;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Class which knows how to simulate the effects of executing bytecode.
|
||||
*
|
||||
* <p><b>Note:</b> This class is not thread-safe. If multiple threads
|
||||
* need to use a single instance, they must synchronize access explicitly
|
||||
* between themselves.</p>
|
||||
*/
|
||||
public class Simulator {
|
||||
/**
|
||||
* {@code non-null;} canned error message for local variable
|
||||
* table mismatches
|
||||
*/
|
||||
private static final String LOCAL_MISMATCH_ERROR =
|
||||
"This is symptomatic of .class transformation tools that ignore " +
|
||||
"local variable information.";
|
||||
|
||||
/** {@code non-null;} machine to use when simulating */
|
||||
private final Machine machine;
|
||||
|
||||
/** {@code non-null;} array of bytecode */
|
||||
private final BytecodeArray code;
|
||||
|
||||
/** {@code non-null;} the method being simulated */
|
||||
private ConcreteMethod method;
|
||||
|
||||
/** {@code non-null;} local variable information */
|
||||
private final LocalVariableList localVariables;
|
||||
|
||||
/** {@code non-null;} visitor instance to use */
|
||||
private final SimVisitor visitor;
|
||||
|
||||
/** {@code non-null;} options for dex output */
|
||||
private final DexOptions dexOptions;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param machine {@code non-null;} machine to use when simulating
|
||||
* @param method {@code non-null;} method data to use
|
||||
* @param dexOptions {@code non-null;} options for dex output
|
||||
*/
|
||||
public Simulator(Machine machine, ConcreteMethod method, DexOptions dexOptions) {
|
||||
if (machine == null) {
|
||||
throw new NullPointerException("machine == null");
|
||||
}
|
||||
|
||||
if (method == null) {
|
||||
throw new NullPointerException("method == null");
|
||||
}
|
||||
|
||||
if (dexOptions == null) {
|
||||
throw new NullPointerException("dexOptions == null");
|
||||
}
|
||||
|
||||
this.machine = machine;
|
||||
this.code = method.getCode();
|
||||
this.method = method;
|
||||
this.localVariables = method.getLocalVariables();
|
||||
this.visitor = new SimVisitor();
|
||||
this.dexOptions = dexOptions;
|
||||
|
||||
// This check assumes class is initialized (accesses dexOptions).
|
||||
if (method.isDefaultOrStaticInterfaceMethod()) {
|
||||
checkInterfaceMethodDeclaration(method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates the effect of executing the given basic block. This modifies
|
||||
* the passed-in frame to represent the end result.
|
||||
*
|
||||
* @param bb {@code non-null;} the basic block
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
*/
|
||||
public void simulate(ByteBlock bb, Frame frame) {
|
||||
int end = bb.getEnd();
|
||||
|
||||
visitor.setFrame(frame);
|
||||
|
||||
try {
|
||||
for (int off = bb.getStart(); off < end; /*off*/) {
|
||||
int length = code.parseInstruction(off, visitor);
|
||||
visitor.setPreviousOffset(off);
|
||||
off += length;
|
||||
}
|
||||
} catch (SimException ex) {
|
||||
frame.annotate(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates the effect of the instruction at the given offset, by
|
||||
* making appropriate calls on the given frame.
|
||||
*
|
||||
* @param offset {@code offset >= 0;} offset of the instruction to simulate
|
||||
* @param frame {@code non-null;} frame to operate on
|
||||
* @return the length of the instruction, in bytes
|
||||
*/
|
||||
public int simulate(int offset, Frame frame) {
|
||||
visitor.setFrame(frame);
|
||||
return code.parseInstruction(offset, visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an "illegal top-of-stack" exception, for the stack
|
||||
* manipulation opcodes.
|
||||
*/
|
||||
private static SimException illegalTos() {
|
||||
return new SimException("stack mismatch: illegal " +
|
||||
"top-of-stack for opcode");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the required array type for an array load or store
|
||||
* instruction, based on a given implied type and an observed
|
||||
* actual array type.
|
||||
*
|
||||
* <p>The interesting cases here have to do with object arrays,
|
||||
* <code>byte[]</code>s, <code>boolean[]</code>s, and
|
||||
* known-nulls.</p>
|
||||
*
|
||||
* <p>In the case of arrays of objects, we want to narrow the type
|
||||
* to the actual array present on the stack, as long as what is
|
||||
* present is an object type. Similarly, due to a quirk of the
|
||||
* original bytecode representation, the instructions for dealing
|
||||
* with <code>byte[]</code> and <code>boolean[]</code> are
|
||||
* undifferentiated, and we aim here to return whichever one was
|
||||
* actually present on the stack.</p>
|
||||
*
|
||||
* <p>In the case where there is a known-null on the stack where
|
||||
* an array is expected, our behavior depends on the implied type
|
||||
* of the instruction. When the implied type is a reference, we
|
||||
* don't attempt to infer anything, as we don't know the dimension
|
||||
* of the null constant and thus any explicit inferred type could
|
||||
* be wrong. When the implied type is a primitive, we fall back to
|
||||
* the implied type of the instruction. Due to the quirk described
|
||||
* above, this means that source code that uses
|
||||
* <code>boolean[]</code> might get translated surprisingly -- but
|
||||
* correctly -- into an instruction that specifies a
|
||||
* <code>byte[]</code>. It will be correct, because should the
|
||||
* code actually execute, it will necessarily throw a
|
||||
* <code>NullPointerException</code>, and it won't matter what
|
||||
* opcode variant is used to achieve that result.</p>
|
||||
*
|
||||
* @param impliedType {@code non-null;} type implied by the
|
||||
* instruction; is <i>not</i> an array type
|
||||
* @param foundArrayType {@code non-null;} type found on the
|
||||
* stack; is either an array type or a known-null
|
||||
* @return {@code non-null;} the array type that should be
|
||||
* required in this context
|
||||
*/
|
||||
private static Type requiredArrayTypeFor(Type impliedType,
|
||||
Type foundArrayType) {
|
||||
if (foundArrayType == Type.KNOWN_NULL) {
|
||||
return impliedType.isReference()
|
||||
? Type.KNOWN_NULL
|
||||
: impliedType.getArrayType();
|
||||
}
|
||||
|
||||
if ((impliedType == Type.OBJECT)
|
||||
&& foundArrayType.isArray()
|
||||
&& foundArrayType.getComponentType().isReference()) {
|
||||
return foundArrayType;
|
||||
}
|
||||
|
||||
if ((impliedType == Type.BYTE)
|
||||
&& (foundArrayType == Type.BOOLEAN_ARRAY)) {
|
||||
/*
|
||||
* Per above, an instruction with implied byte[] is also
|
||||
* allowed to be used on boolean[].
|
||||
*/
|
||||
return Type.BOOLEAN_ARRAY;
|
||||
}
|
||||
|
||||
return impliedType.getArrayType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bytecode visitor used during simulation.
|
||||
*/
|
||||
private class SimVisitor implements BytecodeArray.Visitor {
|
||||
/**
|
||||
* {@code non-null;} machine instance to use (just to avoid excessive
|
||||
* cross-object field access)
|
||||
*/
|
||||
private final Machine machine;
|
||||
|
||||
/**
|
||||
* {@code null-ok;} frame to use; set with each call to
|
||||
* {@link Simulator#simulate}
|
||||
*/
|
||||
private Frame frame;
|
||||
|
||||
/** offset of the previous bytecode */
|
||||
private int previousOffset;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public SimVisitor() {
|
||||
this.machine = Simulator.this.machine;
|
||||
this.frame = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the frame to act on.
|
||||
*
|
||||
* @param frame {@code non-null;} the frame
|
||||
*/
|
||||
public void setFrame(Frame frame) {
|
||||
if (frame == null) {
|
||||
throw new NullPointerException("frame == null");
|
||||
}
|
||||
|
||||
this.frame = frame;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitInvalid(int opcode, int offset, int length) {
|
||||
throw new SimException("invalid opcode " + Hex.u1(opcode));
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitNoArgs(int opcode, int offset, int length,
|
||||
Type type) {
|
||||
switch (opcode) {
|
||||
case ByteOps.NOP: {
|
||||
machine.clearArgs();
|
||||
break;
|
||||
}
|
||||
case ByteOps.INEG: {
|
||||
machine.popArgs(frame, type);
|
||||
break;
|
||||
}
|
||||
case ByteOps.I2L:
|
||||
case ByteOps.I2F:
|
||||
case ByteOps.I2D:
|
||||
case ByteOps.I2B:
|
||||
case ByteOps.I2C:
|
||||
case ByteOps.I2S: {
|
||||
machine.popArgs(frame, Type.INT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.L2I:
|
||||
case ByteOps.L2F:
|
||||
case ByteOps.L2D: {
|
||||
machine.popArgs(frame, Type.LONG);
|
||||
break;
|
||||
}
|
||||
case ByteOps.F2I:
|
||||
case ByteOps.F2L:
|
||||
case ByteOps.F2D: {
|
||||
machine.popArgs(frame, Type.FLOAT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.D2I:
|
||||
case ByteOps.D2L:
|
||||
case ByteOps.D2F: {
|
||||
machine.popArgs(frame, Type.DOUBLE);
|
||||
break;
|
||||
}
|
||||
case ByteOps.RETURN: {
|
||||
machine.clearArgs();
|
||||
checkReturnType(Type.VOID);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IRETURN: {
|
||||
Type checkType = type;
|
||||
if (type == Type.OBJECT) {
|
||||
/*
|
||||
* For an object return, use the best-known
|
||||
* type of the popped value.
|
||||
*/
|
||||
checkType = frame.getStack().peekType(0);
|
||||
}
|
||||
machine.popArgs(frame, type);
|
||||
checkReturnType(checkType);
|
||||
break;
|
||||
}
|
||||
case ByteOps.POP: {
|
||||
Type peekType = frame.getStack().peekType(0);
|
||||
if (peekType.isCategory2()) {
|
||||
throw illegalTos();
|
||||
}
|
||||
machine.popArgs(frame, 1);
|
||||
break;
|
||||
}
|
||||
case ByteOps.ARRAYLENGTH: {
|
||||
Type arrayType = frame.getStack().peekType(0);
|
||||
if (!arrayType.isArrayOrKnownNull()) {
|
||||
fail("type mismatch: expected array type but encountered " +
|
||||
arrayType.toHuman());
|
||||
}
|
||||
machine.popArgs(frame, Type.OBJECT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.ATHROW:
|
||||
case ByteOps.MONITORENTER:
|
||||
case ByteOps.MONITOREXIT: {
|
||||
machine.popArgs(frame, Type.OBJECT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IALOAD: {
|
||||
/*
|
||||
* See comment on requiredArrayTypeFor() for explanation
|
||||
* about what's going on here.
|
||||
*/
|
||||
Type foundArrayType = frame.getStack().peekType(1);
|
||||
Type requiredArrayType =
|
||||
requiredArrayTypeFor(type, foundArrayType);
|
||||
|
||||
// Make type agree with the discovered requiredArrayType.
|
||||
type = (requiredArrayType == Type.KNOWN_NULL)
|
||||
? Type.KNOWN_NULL
|
||||
: requiredArrayType.getComponentType();
|
||||
|
||||
machine.popArgs(frame, requiredArrayType, Type.INT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IADD:
|
||||
case ByteOps.ISUB:
|
||||
case ByteOps.IMUL:
|
||||
case ByteOps.IDIV:
|
||||
case ByteOps.IREM:
|
||||
case ByteOps.IAND:
|
||||
case ByteOps.IOR:
|
||||
case ByteOps.IXOR: {
|
||||
machine.popArgs(frame, type, type);
|
||||
break;
|
||||
}
|
||||
case ByteOps.ISHL:
|
||||
case ByteOps.ISHR:
|
||||
case ByteOps.IUSHR: {
|
||||
machine.popArgs(frame, type, Type.INT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.LCMP: {
|
||||
machine.popArgs(frame, Type.LONG, Type.LONG);
|
||||
break;
|
||||
}
|
||||
case ByteOps.FCMPL:
|
||||
case ByteOps.FCMPG: {
|
||||
machine.popArgs(frame, Type.FLOAT, Type.FLOAT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.DCMPL:
|
||||
case ByteOps.DCMPG: {
|
||||
machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IASTORE: {
|
||||
/*
|
||||
* See comment on requiredArrayTypeFor() for
|
||||
* explanation about what's going on here. In
|
||||
* addition to that, the category 1 vs. 2 thing
|
||||
* below is to deal with the fact that, if the
|
||||
* element type is category 2, we have to skip
|
||||
* over one extra stack slot to find the array.
|
||||
*/
|
||||
ExecutionStack stack = frame.getStack();
|
||||
int peekDepth = type.isCategory1() ? 2 : 3;
|
||||
Type foundArrayType = stack.peekType(peekDepth);
|
||||
boolean foundArrayLocal = stack.peekLocal(peekDepth);
|
||||
|
||||
Type requiredArrayType =
|
||||
requiredArrayTypeFor(type, foundArrayType);
|
||||
|
||||
/*
|
||||
* Make type agree with the discovered requiredArrayType
|
||||
* if it has local info.
|
||||
*/
|
||||
if (foundArrayLocal) {
|
||||
type = (requiredArrayType == Type.KNOWN_NULL)
|
||||
? Type.KNOWN_NULL
|
||||
: requiredArrayType.getComponentType();
|
||||
}
|
||||
|
||||
machine.popArgs(frame, requiredArrayType, Type.INT, type);
|
||||
break;
|
||||
}
|
||||
case ByteOps.POP2:
|
||||
case ByteOps.DUP2: {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
int pattern;
|
||||
|
||||
if (stack.peekType(0).isCategory2()) {
|
||||
// "form 2" in vmspec-2
|
||||
machine.popArgs(frame, 1);
|
||||
pattern = 0x11;
|
||||
} else if (stack.peekType(1).isCategory1()) {
|
||||
// "form 1"
|
||||
machine.popArgs(frame, 2);
|
||||
pattern = 0x2121;
|
||||
} else {
|
||||
throw illegalTos();
|
||||
}
|
||||
|
||||
if (opcode == ByteOps.DUP2) {
|
||||
machine.auxIntArg(pattern);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.DUP: {
|
||||
Type peekType = frame.getStack().peekType(0);
|
||||
|
||||
if (peekType.isCategory2()) {
|
||||
throw illegalTos();
|
||||
}
|
||||
|
||||
machine.popArgs(frame, 1);
|
||||
machine.auxIntArg(0x11);
|
||||
break;
|
||||
}
|
||||
case ByteOps.DUP_X1: {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
|
||||
if (!(stack.peekType(0).isCategory1() &&
|
||||
stack.peekType(1).isCategory1())) {
|
||||
throw illegalTos();
|
||||
}
|
||||
|
||||
machine.popArgs(frame, 2);
|
||||
machine.auxIntArg(0x212);
|
||||
break;
|
||||
}
|
||||
case ByteOps.DUP_X2: {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
|
||||
if (stack.peekType(0).isCategory2()) {
|
||||
throw illegalTos();
|
||||
}
|
||||
|
||||
if (stack.peekType(1).isCategory2()) {
|
||||
// "form 2" in vmspec-2
|
||||
machine.popArgs(frame, 2);
|
||||
machine.auxIntArg(0x212);
|
||||
} else if (stack.peekType(2).isCategory1()) {
|
||||
// "form 1"
|
||||
machine.popArgs(frame, 3);
|
||||
machine.auxIntArg(0x3213);
|
||||
} else {
|
||||
throw illegalTos();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.DUP2_X1: {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
|
||||
if (stack.peekType(0).isCategory2()) {
|
||||
// "form 2" in vmspec-2
|
||||
if (stack.peekType(2).isCategory2()) {
|
||||
throw illegalTos();
|
||||
}
|
||||
machine.popArgs(frame, 2);
|
||||
machine.auxIntArg(0x212);
|
||||
} else {
|
||||
// "form 1"
|
||||
if (stack.peekType(1).isCategory2() ||
|
||||
stack.peekType(2).isCategory2()) {
|
||||
throw illegalTos();
|
||||
}
|
||||
machine.popArgs(frame, 3);
|
||||
machine.auxIntArg(0x32132);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.DUP2_X2: {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
|
||||
if (stack.peekType(0).isCategory2()) {
|
||||
if (stack.peekType(2).isCategory2()) {
|
||||
// "form 4" in vmspec-2
|
||||
machine.popArgs(frame, 2);
|
||||
machine.auxIntArg(0x212);
|
||||
} else if (stack.peekType(3).isCategory1()) {
|
||||
// "form 2"
|
||||
machine.popArgs(frame, 3);
|
||||
machine.auxIntArg(0x3213);
|
||||
} else {
|
||||
throw illegalTos();
|
||||
}
|
||||
} else if (stack.peekType(1).isCategory1()) {
|
||||
if (stack.peekType(2).isCategory2()) {
|
||||
// "form 3"
|
||||
machine.popArgs(frame, 3);
|
||||
machine.auxIntArg(0x32132);
|
||||
} else if (stack.peekType(3).isCategory1()) {
|
||||
// "form 1"
|
||||
machine.popArgs(frame, 4);
|
||||
machine.auxIntArg(0x432143);
|
||||
} else {
|
||||
throw illegalTos();
|
||||
}
|
||||
} else {
|
||||
throw illegalTos();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.SWAP: {
|
||||
ExecutionStack stack = frame.getStack();
|
||||
|
||||
if (!(stack.peekType(0).isCategory1() &&
|
||||
stack.peekType(1).isCategory1())) {
|
||||
throw illegalTos();
|
||||
}
|
||||
|
||||
machine.popArgs(frame, 2);
|
||||
machine.auxIntArg(0x12);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
visitInvalid(opcode, offset, length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
machine.auxType(type);
|
||||
machine.run(frame, offset, opcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the prototype is compatible with returning the
|
||||
* given type, and throws if not.
|
||||
*
|
||||
* @param encountered {@code non-null;} the encountered return type
|
||||
*/
|
||||
private void checkReturnType(Type encountered) {
|
||||
Type returnType = machine.getPrototype().getReturnType();
|
||||
|
||||
/*
|
||||
* Check to see if the prototype's return type is
|
||||
* possibly assignable from the type we encountered. This
|
||||
* takes care of all the salient cases (types are the same,
|
||||
* they're compatible primitive types, etc.).
|
||||
*/
|
||||
if (!Merger.isPossiblyAssignableFrom(returnType, encountered)) {
|
||||
fail("return type mismatch: prototype " +
|
||||
"indicates " + returnType.toHuman() +
|
||||
", but encountered type " + encountered.toHuman());
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitLocal(int opcode, int offset, int length,
|
||||
int idx, Type type, int value) {
|
||||
/*
|
||||
* Note that the "type" parameter is always the simplest
|
||||
* type based on the original opcode, e.g., "int" for
|
||||
* "iload" (per se) and "Object" for "aload". So, when
|
||||
* possible, we replace the type with the one indicated in
|
||||
* the local variable table, though we still need to check
|
||||
* to make sure it's valid for the opcode.
|
||||
*
|
||||
* The reason we use (offset + length) for the localOffset
|
||||
* for a store is because it is only after the store that
|
||||
* the local type becomes valid. On the other hand, the
|
||||
* type associated with a load is valid at the start of
|
||||
* the instruction.
|
||||
*/
|
||||
int localOffset =
|
||||
(opcode == ByteOps.ISTORE) ? (offset + length) : offset;
|
||||
LocalVariableList.Item local =
|
||||
localVariables.pcAndIndexToLocal(localOffset, idx);
|
||||
Type localType;
|
||||
|
||||
if (local != null) {
|
||||
localType = local.getType();
|
||||
if (localType.getBasicFrameType() !=
|
||||
type.getBasicFrameType()) {
|
||||
// wrong type, ignore local variable info
|
||||
local = null;
|
||||
localType = type;
|
||||
}
|
||||
} else {
|
||||
localType = type;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case ByteOps.ILOAD:
|
||||
case ByteOps.RET: {
|
||||
machine.localArg(frame, idx);
|
||||
machine.localInfo(local != null);
|
||||
machine.auxType(type);
|
||||
break;
|
||||
}
|
||||
case ByteOps.ISTORE: {
|
||||
LocalItem item
|
||||
= (local == null) ? null : local.getLocalItem();
|
||||
machine.popArgs(frame, type);
|
||||
machine.auxType(type);
|
||||
machine.localTarget(idx, localType, item);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IINC: {
|
||||
LocalItem item
|
||||
= (local == null) ? null : local.getLocalItem();
|
||||
machine.localArg(frame, idx);
|
||||
machine.localTarget(idx, localType, item);
|
||||
machine.auxType(type);
|
||||
machine.auxIntArg(value);
|
||||
machine.auxCstArg(CstInteger.make(value));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
visitInvalid(opcode, offset, length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
machine.run(frame, offset, opcode);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitConstant(int opcode, int offset, int length,
|
||||
Constant cst, int value) {
|
||||
switch (opcode) {
|
||||
case ByteOps.ANEWARRAY: {
|
||||
machine.popArgs(frame, Type.INT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.PUTSTATIC: {
|
||||
Type fieldType = ((CstFieldRef) cst).getType();
|
||||
machine.popArgs(frame, fieldType);
|
||||
break;
|
||||
}
|
||||
case ByteOps.GETFIELD:
|
||||
case ByteOps.CHECKCAST:
|
||||
case ByteOps.INSTANCEOF: {
|
||||
machine.popArgs(frame, Type.OBJECT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.PUTFIELD: {
|
||||
Type fieldType = ((CstFieldRef) cst).getType();
|
||||
machine.popArgs(frame, Type.OBJECT, fieldType);
|
||||
break;
|
||||
}
|
||||
case ByteOps.INVOKEINTERFACE:
|
||||
case ByteOps.INVOKEVIRTUAL:
|
||||
case ByteOps.INVOKESPECIAL:
|
||||
case ByteOps.INVOKESTATIC: {
|
||||
/*
|
||||
* Convert the interface method ref into a normal
|
||||
* method ref if necessary.
|
||||
*/
|
||||
if (cst instanceof CstInterfaceMethodRef) {
|
||||
cst = ((CstInterfaceMethodRef) cst).toMethodRef();
|
||||
checkInvokeInterfaceSupported(opcode, (CstMethodRef) cst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether invoke-polymorphic is required and supported.
|
||||
*/
|
||||
if (cst instanceof CstMethodRef) {
|
||||
CstMethodRef methodRef = (CstMethodRef) cst;
|
||||
if (methodRef.isSignaturePolymorphic()) {
|
||||
checkInvokeSignaturePolymorphic(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the instance or static prototype, and use it to
|
||||
* direct the machine.
|
||||
*/
|
||||
boolean staticMethod = (opcode == ByteOps.INVOKESTATIC);
|
||||
Prototype prototype
|
||||
= ((CstMethodRef) cst).getPrototype(staticMethod);
|
||||
machine.popArgs(frame, prototype);
|
||||
break;
|
||||
}
|
||||
case ByteOps.INVOKEDYNAMIC: {
|
||||
checkInvokeDynamicSupported(opcode);
|
||||
CstInvokeDynamic invokeDynamicRef = (CstInvokeDynamic) cst;
|
||||
Prototype prototype = invokeDynamicRef.getPrototype();
|
||||
machine.popArgs(frame, prototype);
|
||||
// Change the constant to be associated with instruction to
|
||||
// a call site reference.
|
||||
cst = invokeDynamicRef.addReference();
|
||||
break;
|
||||
}
|
||||
case ByteOps.MULTIANEWARRAY: {
|
||||
/*
|
||||
* The "value" here is the count of dimensions to
|
||||
* create. Make a prototype of that many "int"
|
||||
* types, and tell the machine to pop them. This
|
||||
* isn't the most efficient way in the world to do
|
||||
* this, but then again, multianewarray is pretty
|
||||
* darn rare and so not worth much effort
|
||||
* optimizing for.
|
||||
*/
|
||||
Prototype prototype =
|
||||
Prototype.internInts(Type.VOID, value);
|
||||
machine.popArgs(frame, prototype);
|
||||
break;
|
||||
}
|
||||
case ByteOps.LDC:
|
||||
case ByteOps.LDC_W: {
|
||||
if ((cst instanceof CstMethodHandle || cst instanceof CstProtoRef)) {
|
||||
checkConstMethodHandleSupported(cst);
|
||||
}
|
||||
machine.clearArgs();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
machine.clearArgs();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
machine.auxIntArg(value);
|
||||
machine.auxCstArg(cst);
|
||||
machine.run(frame, offset, opcode);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitBranch(int opcode, int offset, int length,
|
||||
int target) {
|
||||
switch (opcode) {
|
||||
case ByteOps.IFEQ:
|
||||
case ByteOps.IFNE:
|
||||
case ByteOps.IFLT:
|
||||
case ByteOps.IFGE:
|
||||
case ByteOps.IFGT:
|
||||
case ByteOps.IFLE: {
|
||||
machine.popArgs(frame, Type.INT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IFNULL:
|
||||
case ByteOps.IFNONNULL: {
|
||||
machine.popArgs(frame, Type.OBJECT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IF_ICMPEQ:
|
||||
case ByteOps.IF_ICMPNE:
|
||||
case ByteOps.IF_ICMPLT:
|
||||
case ByteOps.IF_ICMPGE:
|
||||
case ByteOps.IF_ICMPGT:
|
||||
case ByteOps.IF_ICMPLE: {
|
||||
machine.popArgs(frame, Type.INT, Type.INT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.IF_ACMPEQ:
|
||||
case ByteOps.IF_ACMPNE: {
|
||||
machine.popArgs(frame, Type.OBJECT, Type.OBJECT);
|
||||
break;
|
||||
}
|
||||
case ByteOps.GOTO:
|
||||
case ByteOps.JSR:
|
||||
case ByteOps.GOTO_W:
|
||||
case ByteOps.JSR_W: {
|
||||
machine.clearArgs();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
visitInvalid(opcode, offset, length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
machine.auxTargetArg(target);
|
||||
machine.run(frame, offset, opcode);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitSwitch(int opcode, int offset, int length,
|
||||
SwitchList cases, int padding) {
|
||||
machine.popArgs(frame, Type.INT);
|
||||
machine.auxIntArg(padding);
|
||||
machine.auxSwitchArg(cases);
|
||||
machine.run(frame, offset, opcode);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void visitNewarray(int offset, int length, CstType type,
|
||||
ArrayList<Constant> initValues) {
|
||||
machine.popArgs(frame, Type.INT);
|
||||
machine.auxInitValues(initValues);
|
||||
machine.auxCstArg(type);
|
||||
machine.run(frame, offset, ByteOps.NEWARRAY);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setPreviousOffset(int offset) {
|
||||
previousOffset = offset;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getPreviousOffset() {
|
||||
return previousOffset;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkConstMethodHandleSupported(Constant cst) throws SimException {
|
||||
if (!dexOptions.apiIsSupported(DexFormat.API_CONST_METHOD_HANDLE)) {
|
||||
fail(String.format("invalid constant type %s requires --min-sdk-version >= %d " +
|
||||
"(currently %d)",
|
||||
cst.typeName(), DexFormat.API_CONST_METHOD_HANDLE,
|
||||
dexOptions.minSdkVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkInvokeDynamicSupported(int opcode) throws SimException {
|
||||
if (!dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
|
||||
fail(String.format("invalid opcode %02x - invokedynamic requires " +
|
||||
"--min-sdk-version >= %d (currently %d)",
|
||||
opcode, DexFormat.API_METHOD_HANDLES, dexOptions.minSdkVersion));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkInvokeInterfaceSupported(final int opcode, CstMethodRef callee) {
|
||||
if (opcode == ByteOps.INVOKEINTERFACE) {
|
||||
// Invoked in the tranditional way, this is fine.
|
||||
return;
|
||||
}
|
||||
|
||||
if (dexOptions.apiIsSupported(DexFormat.API_INVOKE_INTERFACE_METHODS)) {
|
||||
// Running at the officially support API level for default
|
||||
// and static interface methods.
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// One might expect a hard API level for invoking interface
|
||||
// methods. It's either okay to have code invoking static (and
|
||||
// default) interface methods or not. Reality asks to be
|
||||
// prepared for a little compromise here because the
|
||||
// traditional guidance to Android developers when producing a
|
||||
// multi-API level DEX file is to guard the use of the newer
|
||||
// feature with an API level check, e.g.
|
||||
//
|
||||
// int x = (android.os.Build.VERSION.SDK_INT >= 038) ?
|
||||
// DoJava8Thing() : Do JavaOtherThing();
|
||||
//
|
||||
// This is fine advice if the bytecodes and VM semantics never
|
||||
// change. Unfortunately, changes like Java 8 support
|
||||
// introduce new bytecodes and also additional semantics to
|
||||
// existing bytecodes. Invoking static and default interface
|
||||
// methods is one of these awkward VM transitions.
|
||||
//
|
||||
// Experimentally invoke-static of static interface methods
|
||||
// breaks on VMs running below API level 21. Invocations of
|
||||
// default interface methods may soft-fail verification but so
|
||||
// long as they are not called that's okay.
|
||||
//
|
||||
boolean softFail = dexOptions.allowAllInterfaceMethodInvokes;
|
||||
if (opcode == ByteOps.INVOKESTATIC) {
|
||||
softFail &= dexOptions.apiIsSupported(DexFormat.API_INVOKE_STATIC_INTERFACE_METHODS);
|
||||
} else {
|
||||
assert (opcode == ByteOps.INVOKESPECIAL) || (opcode == ByteOps.INVOKEVIRTUAL);
|
||||
}
|
||||
|
||||
// Running below the officially supported API level. Fail hard
|
||||
// unless the user has explicitly allowed this with
|
||||
// "--allow-all-interface-method-invokes".
|
||||
String invokeKind = (opcode == ByteOps.INVOKESTATIC) ? "static" : "default";
|
||||
if (softFail) {
|
||||
// The code we are warning about here should have an API check
|
||||
// that protects it being used on API version < API_INVOKE_INTERFACE_METHODS.
|
||||
String reason =
|
||||
String.format(
|
||||
"invoking a %s interface method %s.%s strictly requires " +
|
||||
"--min-sdk-version >= %d (experimental at current API level %d)",
|
||||
invokeKind, callee.getDefiningClass().toHuman(), callee.getNat().toHuman(),
|
||||
DexFormat.API_INVOKE_INTERFACE_METHODS, dexOptions.minSdkVersion);
|
||||
warn(reason);
|
||||
} else {
|
||||
String reason =
|
||||
String.format(
|
||||
"invoking a %s interface method %s.%s strictly requires " +
|
||||
"--min-sdk-version >= %d (blocked at current API level %d)",
|
||||
invokeKind, callee.getDefiningClass().toHuman(), callee.getNat().toHuman(),
|
||||
DexFormat.API_INVOKE_INTERFACE_METHODS, dexOptions.minSdkVersion);
|
||||
fail(reason);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkInterfaceMethodDeclaration(ConcreteMethod declaredMethod) {
|
||||
if (!dexOptions.apiIsSupported(DexFormat.API_DEFINE_INTERFACE_METHODS)) {
|
||||
String reason
|
||||
= String.format(
|
||||
"defining a %s interface method requires --min-sdk-version >= %d (currently %d)"
|
||||
+ " for interface methods: %s.%s",
|
||||
declaredMethod.isStaticMethod() ? "static" : "default",
|
||||
DexFormat.API_DEFINE_INTERFACE_METHODS, dexOptions.minSdkVersion,
|
||||
declaredMethod.getDefiningClass().toHuman(), declaredMethod.getNat().toHuman());
|
||||
warn(reason);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkInvokeSignaturePolymorphic(final int opcode) {
|
||||
if (!dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
|
||||
fail(String.format(
|
||||
"invoking a signature-polymorphic requires --min-sdk-version >= %d (currently %d)",
|
||||
DexFormat.API_METHOD_HANDLES, dexOptions.minSdkVersion));
|
||||
} else if (opcode != ByteOps.INVOKEVIRTUAL) {
|
||||
fail("Unsupported signature polymorphic invocation (" + ByteOps.opName(opcode) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(String reason) {
|
||||
String message = String.format("ERROR in %s.%s: %s", method.getDefiningClass().toHuman(),
|
||||
method.getNat().toHuman(), reason);
|
||||
throw new SimException(message);
|
||||
}
|
||||
|
||||
private void warn(String reason) {
|
||||
String warning = String.format("WARNING in %s.%s: %s", method.getDefiningClass().toHuman(),
|
||||
method.getNat().toHuman(), reason);
|
||||
dexOptions.err.println(warning);
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.util.IntList;
|
||||
import com.pojavdx.dx.util.MutabilityControl;
|
||||
|
||||
/**
|
||||
* List of (value, target) mappings representing the choices of a
|
||||
* {@code tableswitch} or {@code lookupswitch} instruction. It
|
||||
* also holds the default target for the switch.
|
||||
*/
|
||||
public final class SwitchList extends MutabilityControl {
|
||||
/** {@code non-null;} list of test values */
|
||||
private final IntList values;
|
||||
|
||||
/**
|
||||
* {@code non-null;} list of targets corresponding to the test values; there
|
||||
* is always one extra element in the target list, to hold the
|
||||
* default target
|
||||
*/
|
||||
private final IntList targets;
|
||||
|
||||
/** ultimate size of the list */
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param size {@code >= 0;} the number of elements to be in the table
|
||||
*/
|
||||
public SwitchList(int size) {
|
||||
super(true);
|
||||
this.values = new IntList(size);
|
||||
this.targets = new IntList(size + 1);
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setImmutable() {
|
||||
values.setImmutable();
|
||||
targets.setImmutable();
|
||||
super.setImmutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the list.
|
||||
*
|
||||
* @return {@code >= 0;} the list size
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated test value.
|
||||
*
|
||||
* @param n {@code >= 0;}, < size(); which index
|
||||
* @return the test value
|
||||
*/
|
||||
public int getValue(int n) {
|
||||
return values.get(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the indicated target. Asking for the target at {@code size()}
|
||||
* returns the default target.
|
||||
*
|
||||
* @param n {@code >= 0, <= size();} which index
|
||||
* @return {@code >= 0;} the target
|
||||
*/
|
||||
public int getTarget(int n) {
|
||||
return targets.get(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default target. This is just a shorthand for
|
||||
* {@code getTarget(size())}.
|
||||
*
|
||||
* @return {@code >= 0;} the default target
|
||||
*/
|
||||
public int getDefaultTarget() {
|
||||
return targets.get(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of all targets. This includes one extra element at the
|
||||
* end of the list, which holds the default target.
|
||||
*
|
||||
* @return {@code non-null;} the target list
|
||||
*/
|
||||
public IntList getTargets() {
|
||||
return targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of all case values.
|
||||
*
|
||||
* @return {@code non-null;} the case value list
|
||||
*/
|
||||
public IntList getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default target. It is only valid to call this method
|
||||
* when all the non-default elements have been set.
|
||||
*
|
||||
* @param target {@code >= 0;} the absolute (not relative) default target
|
||||
* address
|
||||
*/
|
||||
public void setDefaultTarget(int target) {
|
||||
throwIfImmutable();
|
||||
|
||||
if (target < 0) {
|
||||
throw new IllegalArgumentException("target < 0");
|
||||
}
|
||||
|
||||
if (targets.size() != size) {
|
||||
throw new RuntimeException("non-default elements not all set");
|
||||
}
|
||||
|
||||
targets.add(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given item.
|
||||
*
|
||||
* @param value the test value
|
||||
* @param target {@code >= 0;} the absolute (not relative) target address
|
||||
*/
|
||||
public void add(int value, int target) {
|
||||
throwIfImmutable();
|
||||
|
||||
if (target < 0) {
|
||||
throw new IllegalArgumentException("target < 0");
|
||||
}
|
||||
|
||||
values.add(value);
|
||||
targets.add(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrinks this instance if possible, removing test elements that
|
||||
* refer to the default target. This is only valid after the instance
|
||||
* is fully populated, including the default target (naturally).
|
||||
*/
|
||||
public void removeSuperfluousDefaults() {
|
||||
throwIfImmutable();
|
||||
|
||||
int sz = size;
|
||||
|
||||
if (sz != (targets.size() - 1)) {
|
||||
throw new IllegalArgumentException("incomplete instance");
|
||||
}
|
||||
|
||||
int defaultTarget = targets.get(sz);
|
||||
int at = 0;
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
int target = targets.get(i);
|
||||
if (target != defaultTarget) {
|
||||
if (i != at) {
|
||||
targets.set(at, target);
|
||||
values.set(at, values.get(i));
|
||||
}
|
||||
at++;
|
||||
}
|
||||
}
|
||||
|
||||
if (at != sz) {
|
||||
values.shrink(at);
|
||||
targets.set(at, defaultTarget);
|
||||
targets.shrink(at + 1);
|
||||
size = at;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.code;
|
||||
|
||||
import com.pojavdx.dx.rop.cst.CstCallSiteRef;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.Prototype;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.rop.type.TypeBearer;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* {@link Machine} which keeps track of known values but does not do
|
||||
* smart/realistic reference type calculations.
|
||||
*/
|
||||
public class ValueAwareMachine extends BaseMachine {
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param prototype {@code non-null;} the prototype for the associated
|
||||
* method
|
||||
*/
|
||||
public ValueAwareMachine(Prototype prototype) {
|
||||
super(prototype);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void run(Frame frame, int offset, int opcode) {
|
||||
switch (opcode) {
|
||||
case ByteOps.NOP:
|
||||
case ByteOps.IASTORE:
|
||||
case ByteOps.POP:
|
||||
case ByteOps.POP2:
|
||||
case ByteOps.IFEQ:
|
||||
case ByteOps.IFNE:
|
||||
case ByteOps.IFLT:
|
||||
case ByteOps.IFGE:
|
||||
case ByteOps.IFGT:
|
||||
case ByteOps.IFLE:
|
||||
case ByteOps.IF_ICMPEQ:
|
||||
case ByteOps.IF_ICMPNE:
|
||||
case ByteOps.IF_ICMPLT:
|
||||
case ByteOps.IF_ICMPGE:
|
||||
case ByteOps.IF_ICMPGT:
|
||||
case ByteOps.IF_ICMPLE:
|
||||
case ByteOps.IF_ACMPEQ:
|
||||
case ByteOps.IF_ACMPNE:
|
||||
case ByteOps.GOTO:
|
||||
case ByteOps.RET:
|
||||
case ByteOps.LOOKUPSWITCH:
|
||||
case ByteOps.IRETURN:
|
||||
case ByteOps.RETURN:
|
||||
case ByteOps.PUTSTATIC:
|
||||
case ByteOps.PUTFIELD:
|
||||
case ByteOps.ATHROW:
|
||||
case ByteOps.MONITORENTER:
|
||||
case ByteOps.MONITOREXIT:
|
||||
case ByteOps.IFNULL:
|
||||
case ByteOps.IFNONNULL: {
|
||||
// Nothing to do for these ops in this class.
|
||||
clearResult();
|
||||
break;
|
||||
}
|
||||
case ByteOps.LDC:
|
||||
case ByteOps.LDC2_W: {
|
||||
setResult((TypeBearer) getAuxCst());
|
||||
break;
|
||||
}
|
||||
case ByteOps.ILOAD:
|
||||
case ByteOps.ISTORE: {
|
||||
setResult(arg(0));
|
||||
break;
|
||||
}
|
||||
case ByteOps.IALOAD:
|
||||
case ByteOps.IADD:
|
||||
case ByteOps.ISUB:
|
||||
case ByteOps.IMUL:
|
||||
case ByteOps.IDIV:
|
||||
case ByteOps.IREM:
|
||||
case ByteOps.INEG:
|
||||
case ByteOps.ISHL:
|
||||
case ByteOps.ISHR:
|
||||
case ByteOps.IUSHR:
|
||||
case ByteOps.IAND:
|
||||
case ByteOps.IOR:
|
||||
case ByteOps.IXOR:
|
||||
case ByteOps.IINC:
|
||||
case ByteOps.I2L:
|
||||
case ByteOps.I2F:
|
||||
case ByteOps.I2D:
|
||||
case ByteOps.L2I:
|
||||
case ByteOps.L2F:
|
||||
case ByteOps.L2D:
|
||||
case ByteOps.F2I:
|
||||
case ByteOps.F2L:
|
||||
case ByteOps.F2D:
|
||||
case ByteOps.D2I:
|
||||
case ByteOps.D2L:
|
||||
case ByteOps.D2F:
|
||||
case ByteOps.I2B:
|
||||
case ByteOps.I2C:
|
||||
case ByteOps.I2S:
|
||||
case ByteOps.LCMP:
|
||||
case ByteOps.FCMPL:
|
||||
case ByteOps.FCMPG:
|
||||
case ByteOps.DCMPL:
|
||||
case ByteOps.DCMPG:
|
||||
case ByteOps.ARRAYLENGTH: {
|
||||
setResult(getAuxType());
|
||||
break;
|
||||
}
|
||||
case ByteOps.DUP:
|
||||
case ByteOps.DUP_X1:
|
||||
case ByteOps.DUP_X2:
|
||||
case ByteOps.DUP2:
|
||||
case ByteOps.DUP2_X1:
|
||||
case ByteOps.DUP2_X2:
|
||||
case ByteOps.SWAP: {
|
||||
clearResult();
|
||||
for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) {
|
||||
int which = (pattern & 0x0f) - 1;
|
||||
addResult(arg(which));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteOps.JSR: {
|
||||
setResult(new ReturnAddress(getAuxTarget()));
|
||||
break;
|
||||
}
|
||||
case ByteOps.GETSTATIC:
|
||||
case ByteOps.GETFIELD:
|
||||
case ByteOps.INVOKEVIRTUAL:
|
||||
case ByteOps.INVOKESTATIC:
|
||||
case ByteOps.INVOKEINTERFACE: {
|
||||
Type type = ((TypeBearer) getAuxCst()).getType();
|
||||
if (type == Type.VOID) {
|
||||
clearResult();
|
||||
} else {
|
||||
setResult(type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.INVOKESPECIAL: {
|
||||
Type thisType = arg(0).getType();
|
||||
if (thisType.isUninitialized()) {
|
||||
frame.makeInitialized(thisType);
|
||||
}
|
||||
Type type = ((TypeBearer) getAuxCst()).getType();
|
||||
if (type == Type.VOID) {
|
||||
clearResult();
|
||||
} else {
|
||||
setResult(type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.INVOKEDYNAMIC: {
|
||||
Type type = ((CstCallSiteRef) getAuxCst()).getReturnType();
|
||||
if (type == Type.VOID) {
|
||||
clearResult();
|
||||
} else {
|
||||
setResult(type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByteOps.NEW: {
|
||||
Type type = ((CstType) getAuxCst()).getClassType();
|
||||
setResult(type.asUninitialized(offset));
|
||||
break;
|
||||
}
|
||||
case ByteOps.NEWARRAY:
|
||||
case ByteOps.CHECKCAST:
|
||||
case ByteOps.MULTIANEWARRAY: {
|
||||
Type type = ((CstType) getAuxCst()).getClassType();
|
||||
setResult(type);
|
||||
break;
|
||||
}
|
||||
case ByteOps.ANEWARRAY: {
|
||||
Type type = ((CstType) getAuxCst()).getClassType();
|
||||
setResult(type.getArrayType());
|
||||
break;
|
||||
}
|
||||
case ByteOps.INSTANCEOF: {
|
||||
setResult(Type.INT);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new RuntimeException("shouldn't happen: " +
|
||||
Hex.u1(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
storeResults(frame);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<body>
|
||||
<p>Implementation of classes having to do with Java simulation, such as
|
||||
is needed for verification or stack-to-register conversion.</p>
|
||||
|
||||
<p><b>PACKAGES USED:</b>
|
||||
<ul>
|
||||
<li><code>com.pojavdx.dx.rop.pool</code></li>
|
||||
<li><code>com.pojavdx.dx.util</code></li>
|
||||
</ul>
|
||||
</body>
|
||||
@@ -1,450 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.cst;
|
||||
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Class;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Double;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Fieldref;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Float;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Integer;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_InterfaceMethodref;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_InvokeDynamic;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Long;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_MethodHandle;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_MethodType;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Methodref;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_NameAndType;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_String;
|
||||
import static com.pojavdx.dx.cf.cst.ConstantTags.CONSTANT_Utf8;
|
||||
import com.pojavdx.dx.cf.iface.ParseException;
|
||||
import com.pojavdx.dx.cf.iface.ParseObserver;
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.cst.CstDouble;
|
||||
import com.pojavdx.dx.rop.cst.CstFieldRef;
|
||||
import com.pojavdx.dx.rop.cst.CstFloat;
|
||||
import com.pojavdx.dx.rop.cst.CstInteger;
|
||||
import com.pojavdx.dx.rop.cst.CstInterfaceMethodRef;
|
||||
import com.pojavdx.dx.rop.cst.CstInvokeDynamic;
|
||||
import com.pojavdx.dx.rop.cst.CstLong;
|
||||
import com.pojavdx.dx.rop.cst.CstMethodHandle;
|
||||
import com.pojavdx.dx.rop.cst.CstMethodRef;
|
||||
import com.pojavdx.dx.rop.cst.CstNat;
|
||||
import com.pojavdx.dx.rop.cst.CstProtoRef;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.cst.StdConstantPool;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.util.ByteArray;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
* Parser for a constant pool embedded in a class file.
|
||||
*/
|
||||
public final class ConstantPoolParser {
|
||||
/** {@code non-null;} the bytes of the constant pool */
|
||||
private final ByteArray bytes;
|
||||
|
||||
/** {@code non-null;} actual parsed constant pool contents */
|
||||
private final StdConstantPool pool;
|
||||
|
||||
/** {@code non-null;} byte offsets to each cst */
|
||||
private final int[] offsets;
|
||||
|
||||
/**
|
||||
* -1 || >= 10; the end offset of this constant pool in the
|
||||
* {@code byte[]} which it came from or {@code -1} if not
|
||||
* yet parsed
|
||||
*/
|
||||
private int endOffset;
|
||||
|
||||
/** {@code null-ok;} parse observer, if any */
|
||||
private ParseObserver observer;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param bytes {@code non-null;} the bytes of the file
|
||||
*/
|
||||
public ConstantPoolParser(ByteArray bytes) {
|
||||
int size = bytes.getUnsignedShort(8); // constant_pool_count
|
||||
|
||||
this.bytes = bytes;
|
||||
this.pool = new StdConstantPool(size);
|
||||
this.offsets = new int[size];
|
||||
this.endOffset = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parse observer for this instance.
|
||||
*
|
||||
* @param observer {@code null-ok;} the observer
|
||||
*/
|
||||
public void setObserver(ParseObserver observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end offset of this constant pool in the {@code byte[]}
|
||||
* which it came from.
|
||||
*
|
||||
* @return {@code >= 10;} the end offset
|
||||
*/
|
||||
public int getEndOffset() {
|
||||
parseIfNecessary();
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the actual constant pool.
|
||||
*
|
||||
* @return {@code non-null;} the constant pool
|
||||
*/
|
||||
public StdConstantPool getPool() {
|
||||
parseIfNecessary();
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs {@link #parse} if it has not yet been run successfully.
|
||||
*/
|
||||
private void parseIfNecessary() {
|
||||
if (endOffset < 0) {
|
||||
parse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual parsing.
|
||||
*/
|
||||
private void parse() {
|
||||
determineOffsets();
|
||||
|
||||
if (observer != null) {
|
||||
observer.parsed(bytes, 8, 2,
|
||||
"constant_pool_count: " + Hex.u2(offsets.length));
|
||||
observer.parsed(bytes, 10, 0, "\nconstant_pool:");
|
||||
observer.changeIndent(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Track the constant value's original string type. True if constants[i] was
|
||||
* a CONSTANT_Utf8, false for any other type including CONSTANT_string.
|
||||
*/
|
||||
BitSet wasUtf8 = new BitSet(offsets.length);
|
||||
|
||||
for (int i = 1; i < offsets.length; i++) {
|
||||
int offset = offsets[i];
|
||||
if ((offset != 0) && (pool.getOrNull(i) == null)) {
|
||||
parse0(i, wasUtf8);
|
||||
}
|
||||
}
|
||||
|
||||
if (observer != null) {
|
||||
for (int i = 1; i < offsets.length; i++) {
|
||||
Constant cst = pool.getOrNull(i);
|
||||
if (cst == null) {
|
||||
continue;
|
||||
}
|
||||
int offset = offsets[i];
|
||||
int nextOffset = endOffset;
|
||||
for (int j = i + 1; j < offsets.length; j++) {
|
||||
int off = offsets[j];
|
||||
if (off != 0) {
|
||||
nextOffset = off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
String human = wasUtf8.get(i)
|
||||
? Hex.u2(i) + ": utf8{\"" + cst.toHuman() + "\"}"
|
||||
: Hex.u2(i) + ": " + cst.toString();
|
||||
observer.parsed(bytes, offset, nextOffset - offset, human);
|
||||
}
|
||||
|
||||
observer.changeIndent(-1);
|
||||
observer.parsed(bytes, endOffset, 0, "end constant_pool");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates {@link #offsets} and also completely parse utf8 constants.
|
||||
*/
|
||||
private void determineOffsets() {
|
||||
int at = 10; // offset from the start of the file to the first cst
|
||||
int lastCategory;
|
||||
|
||||
for (int i = 1; i < offsets.length; i += lastCategory) {
|
||||
offsets[i] = at;
|
||||
int tag = bytes.getUnsignedByte(at);
|
||||
try {
|
||||
switch (tag) {
|
||||
case CONSTANT_Integer:
|
||||
case CONSTANT_Float:
|
||||
case CONSTANT_Fieldref:
|
||||
case CONSTANT_Methodref:
|
||||
case CONSTANT_InterfaceMethodref:
|
||||
case CONSTANT_NameAndType: {
|
||||
lastCategory = 1;
|
||||
at += 5;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Long:
|
||||
case CONSTANT_Double: {
|
||||
lastCategory = 2;
|
||||
at += 9;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Class:
|
||||
case CONSTANT_String: {
|
||||
lastCategory = 1;
|
||||
at += 3;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Utf8: {
|
||||
lastCategory = 1;
|
||||
at += bytes.getUnsignedShort(at + 1) + 3;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_MethodHandle: {
|
||||
lastCategory = 1;
|
||||
at += 4;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_MethodType: {
|
||||
lastCategory = 1;
|
||||
at += 3;
|
||||
break;
|
||||
}
|
||||
case CONSTANT_InvokeDynamic: {
|
||||
lastCategory = 1;
|
||||
at += 5;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new ParseException("unknown tag byte: " + Hex.u1(tag));
|
||||
}
|
||||
}
|
||||
} catch (ParseException ex) {
|
||||
ex.addContext("...while preparsing cst " + Hex.u2(i) + " at offset " + Hex.u4(at));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
endOffset = at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the constant for the given index if it hasn't already been
|
||||
* parsed, also storing it in the constant pool. This will also
|
||||
* have the side effect of parsing any entries the indicated one
|
||||
* depends on.
|
||||
*
|
||||
* @param idx which constant
|
||||
* @return {@code non-null;} the parsed constant
|
||||
*/
|
||||
private Constant parse0(int idx, BitSet wasUtf8) {
|
||||
Constant cst = pool.getOrNull(idx);
|
||||
if (cst != null) {
|
||||
return cst;
|
||||
}
|
||||
|
||||
int at = offsets[idx];
|
||||
|
||||
try {
|
||||
int tag = bytes.getUnsignedByte(at);
|
||||
switch (tag) {
|
||||
case CONSTANT_Utf8: {
|
||||
cst = parseUtf8(at);
|
||||
wasUtf8.set(idx);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Integer: {
|
||||
int value = bytes.getInt(at + 1);
|
||||
cst = CstInteger.make(value);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Float: {
|
||||
int bits = bytes.getInt(at + 1);
|
||||
cst = CstFloat.make(bits);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Long: {
|
||||
long value = bytes.getLong(at + 1);
|
||||
cst = CstLong.make(value);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Double: {
|
||||
long bits = bytes.getLong(at + 1);
|
||||
cst = CstDouble.make(bits);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Class: {
|
||||
int nameIndex = bytes.getUnsignedShort(at + 1);
|
||||
CstString name = (CstString) parse0(nameIndex, wasUtf8);
|
||||
cst = new CstType(Type.internClassName(name.getString()));
|
||||
break;
|
||||
}
|
||||
case CONSTANT_String: {
|
||||
int stringIndex = bytes.getUnsignedShort(at + 1);
|
||||
cst = parse0(stringIndex, wasUtf8);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Fieldref: {
|
||||
int classIndex = bytes.getUnsignedShort(at + 1);
|
||||
CstType type = (CstType) parse0(classIndex, wasUtf8);
|
||||
int natIndex = bytes.getUnsignedShort(at + 3);
|
||||
CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
|
||||
cst = new CstFieldRef(type, nat);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_Methodref: {
|
||||
int classIndex = bytes.getUnsignedShort(at + 1);
|
||||
CstType type = (CstType) parse0(classIndex, wasUtf8);
|
||||
int natIndex = bytes.getUnsignedShort(at + 3);
|
||||
CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
|
||||
cst = new CstMethodRef(type, nat);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_InterfaceMethodref: {
|
||||
int classIndex = bytes.getUnsignedShort(at + 1);
|
||||
CstType type = (CstType) parse0(classIndex, wasUtf8);
|
||||
int natIndex = bytes.getUnsignedShort(at + 3);
|
||||
CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
|
||||
cst = new CstInterfaceMethodRef(type, nat);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_NameAndType: {
|
||||
int nameIndex = bytes.getUnsignedShort(at + 1);
|
||||
CstString name = (CstString) parse0(nameIndex, wasUtf8);
|
||||
int descriptorIndex = bytes.getUnsignedShort(at + 3);
|
||||
CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
|
||||
cst = new CstNat(name, descriptor);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_MethodHandle: {
|
||||
final int kind = bytes.getUnsignedByte(at + 1);
|
||||
final int constantIndex = bytes.getUnsignedShort(at + 2);
|
||||
final Constant ref;
|
||||
switch (kind) {
|
||||
case MethodHandleKind.REF_getField:
|
||||
case MethodHandleKind.REF_getStatic:
|
||||
case MethodHandleKind.REF_putField:
|
||||
case MethodHandleKind.REF_putStatic:
|
||||
ref = (CstFieldRef) parse0(constantIndex, wasUtf8);
|
||||
break;
|
||||
case MethodHandleKind.REF_invokeVirtual:
|
||||
case MethodHandleKind.REF_newInvokeSpecial:
|
||||
ref = (CstMethodRef) parse0(constantIndex, wasUtf8);
|
||||
break;
|
||||
case MethodHandleKind.REF_invokeStatic:
|
||||
case MethodHandleKind.REF_invokeSpecial:
|
||||
ref = parse0(constantIndex, wasUtf8);
|
||||
if (!(ref instanceof CstMethodRef
|
||||
|| ref instanceof CstInterfaceMethodRef)) {
|
||||
throw new ParseException(
|
||||
"Unsupported ref constant type for MethodHandle "
|
||||
+ ref.getClass());
|
||||
}
|
||||
break;
|
||||
case MethodHandleKind.REF_invokeInterface:
|
||||
ref = (CstInterfaceMethodRef) parse0(constantIndex, wasUtf8);
|
||||
break;
|
||||
default:
|
||||
throw new ParseException("Unsupported MethodHandle kind: " + kind);
|
||||
}
|
||||
|
||||
final int methodHandleType = getMethodHandleTypeForKind(kind);
|
||||
cst = CstMethodHandle.make(methodHandleType, ref);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_MethodType: {
|
||||
int descriptorIndex = bytes.getUnsignedShort(at + 1);
|
||||
CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
|
||||
cst = CstProtoRef.make(descriptor);
|
||||
break;
|
||||
}
|
||||
case CONSTANT_InvokeDynamic: {
|
||||
int bootstrapMethodIndex = bytes.getUnsignedShort(at + 1);
|
||||
int natIndex = bytes.getUnsignedShort(at + 3);
|
||||
CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
|
||||
cst = CstInvokeDynamic.make(bootstrapMethodIndex, nat);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new ParseException("unknown tag byte: " + Hex.u1(tag));
|
||||
}
|
||||
}
|
||||
} catch (ParseException ex) {
|
||||
ex.addContext("...while parsing cst " + Hex.u2(idx) +
|
||||
" at offset " + Hex.u4(at));
|
||||
throw ex;
|
||||
} catch (RuntimeException ex) {
|
||||
ParseException pe = new ParseException(ex);
|
||||
pe.addContext("...while parsing cst " + Hex.u2(idx) +
|
||||
" at offset " + Hex.u4(at));
|
||||
throw pe;
|
||||
}
|
||||
|
||||
pool.set(idx, cst);
|
||||
return cst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a utf8 constant.
|
||||
*
|
||||
* @param at offset to the start of the constant (where the tag byte is)
|
||||
* @return {@code non-null;} the parsed value
|
||||
*/
|
||||
private CstString parseUtf8(int at) {
|
||||
int length = bytes.getUnsignedShort(at + 1);
|
||||
|
||||
at += 3; // Skip to the data.
|
||||
|
||||
ByteArray ubytes = bytes.slice(at, at + length);
|
||||
|
||||
try {
|
||||
return new CstString(ubytes);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Translate the exception
|
||||
throw new ParseException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static int getMethodHandleTypeForKind(int kind) {
|
||||
switch (kind) {
|
||||
case MethodHandleKind.REF_getField:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INSTANCE_GET;
|
||||
case MethodHandleKind.REF_getStatic:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_STATIC_GET;
|
||||
case MethodHandleKind.REF_putField:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INSTANCE_PUT;
|
||||
case MethodHandleKind.REF_putStatic:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_STATIC_PUT;
|
||||
case MethodHandleKind.REF_invokeVirtual:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_INSTANCE;
|
||||
case MethodHandleKind.REF_invokeStatic:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_STATIC;
|
||||
case MethodHandleKind.REF_invokeSpecial:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_DIRECT;
|
||||
case MethodHandleKind.REF_newInvokeSpecial:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR;
|
||||
case MethodHandleKind.REF_invokeInterface:
|
||||
return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_INTERFACE;
|
||||
}
|
||||
throw new IllegalArgumentException("invalid kind: " + kind);
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.cst;
|
||||
|
||||
/**
|
||||
* Tags for constant pool constants.
|
||||
*/
|
||||
public interface ConstantTags {
|
||||
/** tag for a {@code CONSTANT_Utf8_info} */
|
||||
int CONSTANT_Utf8 = 1;
|
||||
|
||||
/** tag for a {@code CONSTANT_Integer_info} */
|
||||
int CONSTANT_Integer = 3;
|
||||
|
||||
/** tag for a {@code CONSTANT_Float_info} */
|
||||
int CONSTANT_Float = 4;
|
||||
|
||||
/** tag for a {@code CONSTANT_Long_info} */
|
||||
int CONSTANT_Long = 5;
|
||||
|
||||
/** tag for a {@code CONSTANT_Double_info} */
|
||||
int CONSTANT_Double = 6;
|
||||
|
||||
/** tag for a {@code CONSTANT_Class_info} */
|
||||
int CONSTANT_Class = 7;
|
||||
|
||||
/** tag for a {@code CONSTANT_String_info} */
|
||||
int CONSTANT_String = 8;
|
||||
|
||||
/** tag for a {@code CONSTANT_Fieldref_info} */
|
||||
int CONSTANT_Fieldref = 9;
|
||||
|
||||
/** tag for a {@code CONSTANT_Methodref_info} */
|
||||
int CONSTANT_Methodref = 10;
|
||||
|
||||
/** tag for a {@code CONSTANT_InterfaceMethodref_info} */
|
||||
int CONSTANT_InterfaceMethodref = 11;
|
||||
|
||||
/** tag for a {@code CONSTANT_NameAndType_info} */
|
||||
int CONSTANT_NameAndType = 12;
|
||||
|
||||
/** tag for a {@code CONSTANT_MethodHandle} */
|
||||
int CONSTANT_MethodHandle = 15;
|
||||
|
||||
/** tag for a {@code CONSTANT_MethodType} */
|
||||
int CONSTANT_MethodType = 16;
|
||||
|
||||
/** tag for a {@code CONSTANT_InvokeDynamic} */
|
||||
int CONSTANT_InvokeDynamic = 18;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.cst;
|
||||
|
||||
/**
|
||||
* Method Handle kinds for {@code CONSTANT_MethodHandle_info} constants.
|
||||
*/
|
||||
public interface MethodHandleKind {
|
||||
/** A method handle that gets an instance field. */
|
||||
int REF_getField = 1;
|
||||
|
||||
/** A method handle that gets a static field. */
|
||||
int REF_getStatic = 2;
|
||||
|
||||
/** A method handle that sets an instance field. */
|
||||
int REF_putField = 3;
|
||||
|
||||
/** A method handle that sets a static field. */
|
||||
int REF_putStatic = 4;
|
||||
|
||||
/** A method handle for {@code invokevirtual}. */
|
||||
int REF_invokeVirtual = 5;
|
||||
|
||||
/** A method handle for {@code invokestatic}. */
|
||||
int REF_invokeStatic = 6;
|
||||
|
||||
/** A method handle for {@code invokespecial}. */
|
||||
int REF_invokeSpecial = 7;
|
||||
|
||||
/** A method handle for invoking a constructor. */
|
||||
int REF_newInvokeSpecial = 8;
|
||||
|
||||
/** A method handle for {@code invokeinterface}. */
|
||||
int REF_invokeInterface = 9;
|
||||
}
|
||||
@@ -1,470 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.direct;
|
||||
|
||||
import com.pojavdx.dx.cf.iface.ParseException;
|
||||
import com.pojavdx.dx.cf.iface.ParseObserver;
|
||||
import com.pojavdx.dx.rop.annotation.Annotation;
|
||||
import com.pojavdx.dx.rop.annotation.AnnotationVisibility;
|
||||
import com.pojavdx.dx.rop.annotation.Annotations;
|
||||
import com.pojavdx.dx.rop.annotation.AnnotationsList;
|
||||
import com.pojavdx.dx.rop.annotation.NameValuePair;
|
||||
import com.pojavdx.dx.rop.cst.Constant;
|
||||
import com.pojavdx.dx.rop.cst.ConstantPool;
|
||||
import com.pojavdx.dx.rop.cst.CstAnnotation;
|
||||
import com.pojavdx.dx.rop.cst.CstArray;
|
||||
import com.pojavdx.dx.rop.cst.CstBoolean;
|
||||
import com.pojavdx.dx.rop.cst.CstByte;
|
||||
import com.pojavdx.dx.rop.cst.CstChar;
|
||||
import com.pojavdx.dx.rop.cst.CstDouble;
|
||||
import com.pojavdx.dx.rop.cst.CstEnumRef;
|
||||
import com.pojavdx.dx.rop.cst.CstFloat;
|
||||
import com.pojavdx.dx.rop.cst.CstInteger;
|
||||
import com.pojavdx.dx.rop.cst.CstLong;
|
||||
import com.pojavdx.dx.rop.cst.CstNat;
|
||||
import com.pojavdx.dx.rop.cst.CstShort;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.rop.cst.CstType;
|
||||
import com.pojavdx.dx.rop.type.Type;
|
||||
import com.pojavdx.dx.util.ByteArray;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Parser for annotations.
|
||||
*/
|
||||
public final class AnnotationParser {
|
||||
/** {@code non-null;} class file being parsed */
|
||||
private final DirectClassFile cf;
|
||||
|
||||
/** {@code non-null;} constant pool to use */
|
||||
private final ConstantPool pool;
|
||||
|
||||
/** {@code non-null;} bytes of the attribute data */
|
||||
private final ByteArray bytes;
|
||||
|
||||
/** {@code null-ok;} parse observer, if any */
|
||||
private final ParseObserver observer;
|
||||
|
||||
/** {@code non-null;} input stream to parse from */
|
||||
private final ByteArray.MyDataInputStream input;
|
||||
|
||||
/**
|
||||
* {@code non-null;} cursor for use when informing the observer of what
|
||||
* was parsed
|
||||
*/
|
||||
private int parseCursor;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param cf {@code non-null;} class file to parse from
|
||||
* @param offset {@code >= 0;} offset into the class file data to parse at
|
||||
* @param length {@code >= 0;} number of bytes left in the attribute data
|
||||
* @param observer {@code null-ok;} parse observer to notify, if any
|
||||
*/
|
||||
public AnnotationParser(DirectClassFile cf, int offset, int length,
|
||||
ParseObserver observer) {
|
||||
if (cf == null) {
|
||||
throw new NullPointerException("cf == null");
|
||||
}
|
||||
|
||||
this.cf = cf;
|
||||
this.pool = cf.getConstantPool();
|
||||
this.observer = observer;
|
||||
this.bytes = cf.getBytes().slice(offset, offset + length);
|
||||
this.input = bytes.makeDataInputStream();
|
||||
this.parseCursor = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an annotation value ({@code element_value}) attribute.
|
||||
*
|
||||
* @return {@code non-null;} the parsed constant value
|
||||
*/
|
||||
public Constant parseValueAttribute() {
|
||||
Constant result;
|
||||
|
||||
try {
|
||||
result = parseValue();
|
||||
|
||||
if (input.available() != 0) {
|
||||
throw new ParseException("extra data in attribute");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// ByteArray.MyDataInputStream should never throw.
|
||||
throw new RuntimeException("shouldn't happen", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a parameter annotation attribute.
|
||||
*
|
||||
* @param visibility {@code non-null;} visibility of the parsed annotations
|
||||
* @return {@code non-null;} the parsed list of lists of annotations
|
||||
*/
|
||||
public AnnotationsList parseParameterAttribute(
|
||||
AnnotationVisibility visibility) {
|
||||
AnnotationsList result;
|
||||
|
||||
try {
|
||||
result = parseAnnotationsList(visibility);
|
||||
|
||||
if (input.available() != 0) {
|
||||
throw new ParseException("extra data in attribute");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// ByteArray.MyDataInputStream should never throw.
|
||||
throw new RuntimeException("shouldn't happen", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an annotation attribute, per se.
|
||||
*
|
||||
* @param visibility {@code non-null;} visibility of the parsed annotations
|
||||
* @return {@code non-null;} the list of annotations read from the attribute
|
||||
* data
|
||||
*/
|
||||
public Annotations parseAnnotationAttribute(
|
||||
AnnotationVisibility visibility) {
|
||||
Annotations result;
|
||||
|
||||
try {
|
||||
result = parseAnnotations(visibility);
|
||||
|
||||
if (input.available() != 0) {
|
||||
throw new ParseException("extra data in attribute");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// ByteArray.MyDataInputStream should never throw.
|
||||
throw new RuntimeException("shouldn't happen", ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a list of annotation lists.
|
||||
*
|
||||
* @param visibility {@code non-null;} visibility of the parsed annotations
|
||||
* @return {@code non-null;} the list of annotation lists read from the attribute
|
||||
* data
|
||||
*/
|
||||
private AnnotationsList parseAnnotationsList(
|
||||
AnnotationVisibility visibility) throws IOException {
|
||||
int count = input.readUnsignedByte();
|
||||
|
||||
if (observer != null) {
|
||||
parsed(1, "num_parameters: " + Hex.u1(count));
|
||||
}
|
||||
|
||||
AnnotationsList outerList = new AnnotationsList(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (observer != null) {
|
||||
parsed(0, "parameter_annotations[" + i + "]:");
|
||||
changeIndent(1);
|
||||
}
|
||||
|
||||
Annotations annotations = parseAnnotations(visibility);
|
||||
outerList.set(i, annotations);
|
||||
|
||||
if (observer != null) {
|
||||
observer.changeIndent(-1);
|
||||
}
|
||||
}
|
||||
|
||||
outerList.setImmutable();
|
||||
return outerList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an annotation list.
|
||||
*
|
||||
* @param visibility {@code non-null;} visibility of the parsed annotations
|
||||
* @return {@code non-null;} the list of annotations read from the attribute
|
||||
* data
|
||||
*/
|
||||
private Annotations parseAnnotations(AnnotationVisibility visibility)
|
||||
throws IOException {
|
||||
int count = input.readUnsignedShort();
|
||||
|
||||
if (observer != null) {
|
||||
parsed(2, "num_annotations: " + Hex.u2(count));
|
||||
}
|
||||
|
||||
Annotations annotations = new Annotations();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (observer != null) {
|
||||
parsed(0, "annotations[" + i + "]:");
|
||||
changeIndent(1);
|
||||
}
|
||||
|
||||
Annotation annotation = parseAnnotation(visibility);
|
||||
annotations.add(annotation);
|
||||
|
||||
if (observer != null) {
|
||||
observer.changeIndent(-1);
|
||||
}
|
||||
}
|
||||
|
||||
annotations.setImmutable();
|
||||
return annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single annotation.
|
||||
*
|
||||
* @param visibility {@code non-null;} visibility of the parsed annotation
|
||||
* @return {@code non-null;} the parsed annotation
|
||||
*/
|
||||
private Annotation parseAnnotation(AnnotationVisibility visibility)
|
||||
throws IOException {
|
||||
requireLength(4);
|
||||
|
||||
int typeIndex = input.readUnsignedShort();
|
||||
int numElements = input.readUnsignedShort();
|
||||
CstString typeString = (CstString) pool.get(typeIndex);
|
||||
CstType type = new CstType(Type.intern(typeString.getString()));
|
||||
|
||||
if (observer != null) {
|
||||
parsed(2, "type: " + type.toHuman());
|
||||
parsed(2, "num_elements: " + numElements);
|
||||
}
|
||||
|
||||
Annotation annotation = new Annotation(type, visibility);
|
||||
|
||||
for (int i = 0; i < numElements; i++) {
|
||||
if (observer != null) {
|
||||
parsed(0, "elements[" + i + "]:");
|
||||
changeIndent(1);
|
||||
}
|
||||
|
||||
NameValuePair element = parseElement();
|
||||
annotation.add(element);
|
||||
|
||||
if (observer != null) {
|
||||
changeIndent(-1);
|
||||
}
|
||||
}
|
||||
|
||||
annotation.setImmutable();
|
||||
return annotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a {@link NameValuePair}.
|
||||
*
|
||||
* @return {@code non-null;} the parsed element
|
||||
*/
|
||||
private NameValuePair parseElement() throws IOException {
|
||||
requireLength(5);
|
||||
|
||||
int elementNameIndex = input.readUnsignedShort();
|
||||
CstString elementName = (CstString) pool.get(elementNameIndex);
|
||||
|
||||
if (observer != null) {
|
||||
parsed(2, "element_name: " + elementName.toHuman());
|
||||
parsed(0, "value: ");
|
||||
changeIndent(1);
|
||||
}
|
||||
|
||||
Constant value = parseValue();
|
||||
|
||||
if (observer != null) {
|
||||
changeIndent(-1);
|
||||
}
|
||||
|
||||
return new NameValuePair(elementName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an annotation value.
|
||||
*
|
||||
* @return {@code non-null;} the parsed value
|
||||
*/
|
||||
private Constant parseValue() throws IOException {
|
||||
int tag = input.readUnsignedByte();
|
||||
|
||||
if (observer != null) {
|
||||
CstString humanTag = new CstString(Character.toString((char) tag));
|
||||
parsed(1, "tag: " + humanTag.toQuoted());
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
case 'B': {
|
||||
CstInteger value = (CstInteger) parseConstant();
|
||||
return CstByte.make(value.getValue());
|
||||
}
|
||||
case 'C': {
|
||||
CstInteger value = (CstInteger) parseConstant();
|
||||
int intValue = value.getValue();
|
||||
return CstChar.make(value.getValue());
|
||||
}
|
||||
case 'D': {
|
||||
CstDouble value = (CstDouble) parseConstant();
|
||||
return value;
|
||||
}
|
||||
case 'F': {
|
||||
CstFloat value = (CstFloat) parseConstant();
|
||||
return value;
|
||||
}
|
||||
case 'I': {
|
||||
CstInteger value = (CstInteger) parseConstant();
|
||||
return value;
|
||||
}
|
||||
case 'J': {
|
||||
CstLong value = (CstLong) parseConstant();
|
||||
return value;
|
||||
}
|
||||
case 'S': {
|
||||
CstInteger value = (CstInteger) parseConstant();
|
||||
return CstShort.make(value.getValue());
|
||||
}
|
||||
case 'Z': {
|
||||
CstInteger value = (CstInteger) parseConstant();
|
||||
return CstBoolean.make(value.getValue());
|
||||
}
|
||||
case 'c': {
|
||||
int classInfoIndex = input.readUnsignedShort();
|
||||
CstString value = (CstString) pool.get(classInfoIndex);
|
||||
Type type = Type.internReturnType(value.getString());
|
||||
|
||||
if (observer != null) {
|
||||
parsed(2, "class_info: " + type.toHuman());
|
||||
}
|
||||
|
||||
return new CstType(type);
|
||||
}
|
||||
case 's': {
|
||||
return parseConstant();
|
||||
}
|
||||
case 'e': {
|
||||
requireLength(4);
|
||||
|
||||
int typeNameIndex = input.readUnsignedShort();
|
||||
int constNameIndex = input.readUnsignedShort();
|
||||
CstString typeName = (CstString) pool.get(typeNameIndex);
|
||||
CstString constName = (CstString) pool.get(constNameIndex);
|
||||
|
||||
if (observer != null) {
|
||||
parsed(2, "type_name: " + typeName.toHuman());
|
||||
parsed(2, "const_name: " + constName.toHuman());
|
||||
}
|
||||
|
||||
return new CstEnumRef(new CstNat(constName, typeName));
|
||||
}
|
||||
case '@': {
|
||||
Annotation annotation =
|
||||
parseAnnotation(AnnotationVisibility.EMBEDDED);
|
||||
return new CstAnnotation(annotation);
|
||||
}
|
||||
case '[': {
|
||||
requireLength(2);
|
||||
|
||||
int numValues = input.readUnsignedShort();
|
||||
CstArray.List list = new CstArray.List(numValues);
|
||||
|
||||
if (observer != null) {
|
||||
parsed(2, "num_values: " + numValues);
|
||||
changeIndent(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numValues; i++) {
|
||||
if (observer != null) {
|
||||
changeIndent(-1);
|
||||
parsed(0, "element_value[" + i + "]:");
|
||||
changeIndent(1);
|
||||
}
|
||||
list.set(i, parseValue());
|
||||
}
|
||||
|
||||
if (observer != null) {
|
||||
changeIndent(-1);
|
||||
}
|
||||
|
||||
list.setImmutable();
|
||||
return new CstArray(list);
|
||||
}
|
||||
default: {
|
||||
throw new ParseException("unknown annotation tag: " +
|
||||
Hex.u1(tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for {@link #parseValue}, which parses a constant reference
|
||||
* and returns the referred-to constant value.
|
||||
*
|
||||
* @return {@code non-null;} the parsed value
|
||||
*/
|
||||
private Constant parseConstant() throws IOException {
|
||||
int constValueIndex = input.readUnsignedShort();
|
||||
Constant value = (Constant) pool.get(constValueIndex);
|
||||
|
||||
if (observer != null) {
|
||||
String human = (value instanceof CstString)
|
||||
? ((CstString) value).toQuoted()
|
||||
: value.toHuman();
|
||||
parsed(2, "constant_value: " + human);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper which will throw an exception if the given number of bytes
|
||||
* is not available to be read.
|
||||
*
|
||||
* @param requiredLength the number of required bytes
|
||||
*/
|
||||
private void requireLength(int requiredLength) throws IOException {
|
||||
if (input.available() < requiredLength) {
|
||||
throw new ParseException("truncated annotation attribute");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper which indicates that some bytes were just parsed. This should
|
||||
* only be used (for efficiency sake) if the parse is known to be
|
||||
* observed.
|
||||
*
|
||||
* @param length {@code >= 0;} number of bytes parsed
|
||||
* @param message {@code non-null;} associated message
|
||||
*/
|
||||
private void parsed(int length, String message) {
|
||||
observer.parsed(bytes, parseCursor, length, message);
|
||||
parseCursor += length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper that simply calls through to
|
||||
* {@code observer.changeIndent()}.
|
||||
*
|
||||
* @param indent the amount to change the indent by
|
||||
*/
|
||||
private void changeIndent(int indent) {
|
||||
observer.changeIndent(indent);
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.direct;
|
||||
|
||||
import com.pojavdx.dx.cf.attrib.RawAttribute;
|
||||
import com.pojavdx.dx.cf.iface.Attribute;
|
||||
import com.pojavdx.dx.cf.iface.ParseException;
|
||||
import com.pojavdx.dx.cf.iface.ParseObserver;
|
||||
import com.pojavdx.dx.rop.cst.ConstantPool;
|
||||
import com.pojavdx.dx.rop.cst.CstString;
|
||||
import com.pojavdx.dx.util.ByteArray;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* Factory capable of instantiating various {@link Attribute} subclasses
|
||||
* depending on the context and name.
|
||||
*/
|
||||
public class AttributeFactory {
|
||||
/** context for attributes on class files */
|
||||
public static final int CTX_CLASS = 0;
|
||||
|
||||
/** context for attributes on fields */
|
||||
public static final int CTX_FIELD = 1;
|
||||
|
||||
/** context for attributes on methods */
|
||||
public static final int CTX_METHOD = 2;
|
||||
|
||||
/** context for attributes on code attributes */
|
||||
public static final int CTX_CODE = 3;
|
||||
|
||||
/** number of contexts */
|
||||
public static final int CTX_COUNT = 4;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*/
|
||||
public AttributeFactory() {
|
||||
// This space intentionally left blank.
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses and makes an attribute based on the bytes at the
|
||||
* indicated position in the given array. This method figures out
|
||||
* the name, and then does all the setup to call on to {@link #parse0},
|
||||
* which does the actual construction.
|
||||
*
|
||||
* @param cf {@code non-null;} class file to parse from
|
||||
* @param context context to parse in; one of the {@code CTX_*}
|
||||
* constants
|
||||
* @param offset offset into {@code dcf}'s {@code bytes}
|
||||
* to start parsing at
|
||||
* @param observer {@code null-ok;} parse observer to report to, if any
|
||||
* @return {@code non-null;} an appropriately-constructed {@link Attribute}
|
||||
*/
|
||||
public final Attribute parse(DirectClassFile cf, int context, int offset,
|
||||
ParseObserver observer) {
|
||||
if (cf == null) {
|
||||
throw new NullPointerException("cf == null");
|
||||
}
|
||||
|
||||
if ((context < 0) || (context >= CTX_COUNT)) {
|
||||
throw new IllegalArgumentException("bad context");
|
||||
}
|
||||
|
||||
CstString name = null;
|
||||
|
||||
try {
|
||||
ByteArray bytes = cf.getBytes();
|
||||
ConstantPool pool = cf.getConstantPool();
|
||||
int nameIdx = bytes.getUnsignedShort(offset);
|
||||
int length = bytes.getInt(offset + 2);
|
||||
|
||||
name = (CstString) pool.get(nameIdx);
|
||||
|
||||
if (observer != null) {
|
||||
observer.parsed(bytes, offset, 2,
|
||||
"name: " + name.toHuman());
|
||||
observer.parsed(bytes, offset + 2, 4,
|
||||
"length: " + Hex.u4(length));
|
||||
}
|
||||
|
||||
return parse0(cf, context, name.getString(), offset + 6, length,
|
||||
observer);
|
||||
} catch (ParseException ex) {
|
||||
ex.addContext("...while parsing " +
|
||||
((name != null) ? (name.toHuman() + " ") : "") +
|
||||
"attribute at offset " + Hex.u4(offset));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses attribute content. The base class implements this by constructing
|
||||
* an instance of {@link RawAttribute}. Subclasses are expected to
|
||||
* override this to do something better in most cases.
|
||||
*
|
||||
* @param cf {@code non-null;} class file to parse from
|
||||
* @param context context to parse in; one of the {@code CTX_*}
|
||||
* constants
|
||||
* @param name {@code non-null;} the attribute name
|
||||
* @param offset offset into {@code bytes} to start parsing at; this
|
||||
* is the offset to the start of attribute data, not to the header
|
||||
* @param length the length of the attribute data
|
||||
* @param observer {@code null-ok;} parse observer to report to, if any
|
||||
* @return {@code non-null;} an appropriately-constructed {@link Attribute}
|
||||
*/
|
||||
protected Attribute parse0(DirectClassFile cf, int context, String name,
|
||||
int offset, int length,
|
||||
ParseObserver observer) {
|
||||
ByteArray bytes = cf.getBytes();
|
||||
ConstantPool pool = cf.getConstantPool();
|
||||
Attribute result = new RawAttribute(name, bytes, offset, length, pool);
|
||||
|
||||
if (observer != null) {
|
||||
observer.parsed(bytes, offset, length, "attribute data");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pojavdx.dx.cf.direct;
|
||||
|
||||
import com.pojavdx.dx.cf.iface.Attribute;
|
||||
import com.pojavdx.dx.cf.iface.ParseException;
|
||||
import com.pojavdx.dx.cf.iface.ParseObserver;
|
||||
import com.pojavdx.dx.cf.iface.StdAttributeList;
|
||||
import com.pojavdx.dx.util.ByteArray;
|
||||
import com.pojavdx.dx.util.Hex;
|
||||
|
||||
/**
|
||||
* Parser for lists of attributes.
|
||||
*/
|
||||
final /*package*/ class AttributeListParser {
|
||||
/** {@code non-null;} the class file to parse from */
|
||||
private final DirectClassFile cf;
|
||||
|
||||
/** attribute parsing context */
|
||||
private final int context;
|
||||
|
||||
/** offset in the byte array of the classfile to the start of the list */
|
||||
private final int offset;
|
||||
|
||||
/** {@code non-null;} attribute factory to use */
|
||||
private final AttributeFactory attributeFactory;
|
||||
|
||||
/** {@code non-null;} list of parsed attributes */
|
||||
private final StdAttributeList list;
|
||||
|
||||
/** {@code >= -1;} the end offset of this list in the byte array of the
|
||||
* classfile, or {@code -1} if not yet parsed */
|
||||
private int endOffset;
|
||||
|
||||
/** {@code null-ok;} parse observer, if any */
|
||||
private ParseObserver observer;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
*
|
||||
* @param cf {@code non-null;} class file to parse from
|
||||
* @param context attribute parsing context (see {@link AttributeFactory})
|
||||
* @param offset offset in {@code bytes} to the start of the list
|
||||
* @param attributeFactory {@code non-null;} attribute factory to use
|
||||
*/
|
||||
public AttributeListParser(DirectClassFile cf, int context, int offset,
|
||||
AttributeFactory attributeFactory) {
|
||||
if (cf == null) {
|
||||
throw new NullPointerException("cf == null");
|
||||
}
|
||||
|
||||
if (attributeFactory == null) {
|
||||
throw new NullPointerException("attributeFactory == null");
|
||||
}
|
||||
|
||||
int size = cf.getBytes().getUnsignedShort(offset);
|
||||
|
||||
this.cf = cf;
|
||||
this.context = context;
|
||||
this.offset = offset;
|
||||
this.attributeFactory = attributeFactory;
|
||||
this.list = new StdAttributeList(size);
|
||||
this.endOffset = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parse observer for this instance.
|
||||
*
|
||||
* @param observer {@code null-ok;} the observer
|
||||
*/
|
||||
public void setObserver(ParseObserver observer) {
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end offset of this constant pool in the {@code byte[]}
|
||||
* which it came from.
|
||||
*
|
||||
* @return {@code >= 0;} the end offset
|
||||
*/
|
||||
public int getEndOffset() {
|
||||
parseIfNecessary();
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parsed list.
|
||||
*
|
||||
* @return {@code non-null;} the list
|
||||
*/
|
||||
public StdAttributeList getList() {
|
||||
parseIfNecessary();
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs {@link #parse} if it has not yet been run successfully.
|
||||
*/
|
||||
private void parseIfNecessary() {
|
||||
if (endOffset < 0) {
|
||||
parse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the actual parsing.
|
||||
*/
|
||||
private void parse() {
|
||||
int sz = list.size();
|
||||
int at = offset + 2; // Skip the count.
|
||||
|
||||
ByteArray bytes = cf.getBytes();
|
||||
|
||||
if (observer != null) {
|
||||
observer.parsed(bytes, offset, 2,
|
||||
"attributes_count: " + Hex.u2(sz));
|
||||
}
|
||||
|
||||
for (int i = 0; i < sz; i++) {
|
||||
try {
|
||||
if (observer != null) {
|
||||
observer.parsed(bytes, at, 0,
|
||||
"\nattributes[" + i + "]:\n");
|
||||
observer.changeIndent(1);
|
||||
}
|
||||
|
||||
Attribute attrib =
|
||||
attributeFactory.parse(cf, context, at, observer);
|
||||
|
||||
at += attrib.byteLength();
|
||||
list.set(i, attrib);
|
||||
|
||||
if (observer != null) {
|
||||
observer.changeIndent(-1);
|
||||
observer.parsed(bytes, at, 0,
|
||||
"end attributes[" + i + "]\n");
|
||||
}
|
||||
} catch (ParseException ex) {
|
||||
ex.addContext("...while parsing attributes[" + i + "]");
|
||||
throw ex;
|
||||
} catch (RuntimeException ex) {
|
||||
ParseException pe = new ParseException(ex);
|
||||
pe.addContext("...while parsing attributes[" + i + "]");
|
||||
throw pe;
|
||||
}
|
||||
}
|
||||
|
||||
endOffset = at;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user