summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode/MethodParametersAttribute.java
blob: baf1ac1ecea8da50d3dce6254008a520790e4ce2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*******************************************************************************
 * Copyright (c) 2014 Jeff Martin.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Jeff Martin - initial API and implementation
 ******************************************************************************/
package cuchaz.enigma.bytecode;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javassist.bytecode.AttributeInfo;
import javassist.bytecode.ConstPool;
import javassist.bytecode.MethodInfo;

public class MethodParametersAttribute extends AttributeInfo
{
	private MethodParametersAttribute( ConstPool pool, List<Integer> parameterNameIndices )
	{
		super( pool, "MethodParameters", writeStruct( parameterNameIndices ) );
	}
	
	public static void updateClass( MethodInfo info, List<String> names )
	{
		// add the names to the class const pool
		ConstPool constPool = info.getConstPool();
		List<Integer> parameterNameIndices = new ArrayList<Integer>();
		for( String name : names )
		{
			if( name != null )
			{
				parameterNameIndices.add( constPool.addUtf8Info( name ) );
			}
			else
			{
				parameterNameIndices.add( 0 );
			}
		}
		
		// add the attribute to the method
		info.addAttribute( new MethodParametersAttribute( constPool, parameterNameIndices ) );
	}
	
	private static byte[] writeStruct( List<Integer> parameterNameIndices )
	{
		// JVM 8 Spec says the struct looks like this:
		// http://cr.openjdk.java.net/~mr/se/8/java-se-8-fr-spec-01/java-se-8-jvms-fr-diffs.pdf
		// uint8 num_params
		// for each param:
		//    uint16 name_index -> points to UTF8 entry in constant pool, or 0 for no entry
		//    uint16 access_flags -> don't care, just set to 0
		
		ByteArrayOutputStream buf = new ByteArrayOutputStream();
		DataOutputStream out = new DataOutputStream( buf );
		
		// NOTE: java hates unsigned integers, so we have to be careful here
		// the writeShort(), writeByte() methods will read 16,8 low-order bits from the int argument
		// as long as the int argument is in range of the unsigned short/byte type, it will be written as an unsigned short/byte
		// if the int is out of range, the byte stream won't look the way we want and weird things will happen
		final int SIZEOF_UINT8 = 1;
		final int SIZEOF_UINT16 = 2;
		final int MAX_UINT8 = ( 1 << 8 ) - 1;
		final int MAX_UINT16 = ( 1 << 16 ) - 1;
		
		try
		{
			assert( parameterNameIndices.size() >= 0 && parameterNameIndices.size() <= MAX_UINT8 );
			out.writeByte( parameterNameIndices.size() );
			
			for( Integer index : parameterNameIndices )
			{
				assert( index >= 0 && index <= MAX_UINT16 );
				out.writeShort( index );
				
				// just write 0 for the access flags
				out.writeShort( 0 );
			}
			
			out.close();
			byte[] data = buf.toByteArray();
			assert( data.length == SIZEOF_UINT8 + parameterNameIndices.size()*( SIZEOF_UINT16 + SIZEOF_UINT16 ) );
			return data;
		}
		catch( IOException ex )
		{
			throw new Error( ex );
		}
	}
}