Skeleton part of v3: remove AWT, PojavDX

This commit is contained in:
khanhduytran0
2020-04-26 08:38:11 +07:00
parent 9807cf500c
commit b869b7dfd8
1031 changed files with 126 additions and 235353 deletions

View File

@@ -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'

View File

@@ -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);
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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);

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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 + ")";
}
}

View File

@@ -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;
}
}

View File

@@ -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()));
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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++];
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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>

View File

@@ -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.");
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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];
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;}, &lt; 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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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>

View File

@@ -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 || &gt;= 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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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