How to create or retrieve a StackMapTable in Javassist - java

In the javassist API it seems there is no direct way to add anything by hand in the CodeAttribute property.
Given this example:
CtMethod m;
m.getMethodInfo().getCodeAttribute().setAttribute(???);
this seems to be the only way to alter attributes. But, neither is possible to retrieve the original attributes and modify them (it returns a list od AttributeInfo), nor a public constructor exists for StackMapTable, which seems to be the only accepted input.
Anyone knows how to do something like this?

The CodeAttribute object is responsible for holding the bytecode that represents the method flow. You can use it to iterate the bytecode that represents the method logic.
This basically means that it represents the compiled method itself. I know you can use this object to manually modify the bytecode itself for example deleting all the instruction between two lines:
// erase from line 4 to 6
int startingLineNumber= 6;
// Access the code attribute
CodeAttribute codeAttribute = mymethod.getMethodInfo().getCodeAttribute();
LineNumberAttribute lineNumberAttribute = (LineNumberAttribute) codeAttribute.getAttribute(LineNumberAttribute.tag);
// Index in bytecode array where we start to delete
int firstIndex = lineNumberAttribute.toStartPc(startingLineNumber);
// Index in the bytecode where the first instruction that we keep starts
int secondIndex = lineNumberAttribute.toStartPc(startingLineNumber+2);
// go through the bytecode array
byte[] code = codeAttribute.getCode();
for (int i = firstIndex ; i < secondIndex ; i++) {
// set the bytecode of this line to No OPeration
code[i] = CodeAttribute.NOP;
}
However this could be really tricky and can get out of hands quite easily.
What I suggest to do is to just create a new CodeAttribute rom scratch and substitute it into you method in order to avoid unplesant surprises:
ClassFile cf = ...;
MethodInfo minfo = cf.getMethod("yourMethod");
Bytecode code = new Bytecode(cf.getConstPool());
// insert your logic here
code.addAload(0);
code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
code.addReturn(null);
code.setMaxLocals(1);
MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V");
minfo.setCodeAttribute(code.toCodeAttribute());
However thi method also may be quite risky since you are handling directly bytecode. Probably the best would be just to delete your method and add a new one with your logic instead.

Related

Check size of sun.misc.Queue

My project is using a Queue by extending sun.misc.Queue and I need to know size of this queue every second.
As sun.misc.Queue does not provide size method API. I tried to add toString method to my class extending sun.misc.queue, and calling the toString in a seprate thread dedicated for printing the queue every second.
public String toString()
{
int count = 0;
Enumeration er = null;
synchronized (this)
{
er = myQ.elements();
while (er.hasMoreElements())
{
count++;
}
}
return "" + count ;
}
myQ is field in this class.
But this ain't working too. After I add this code, complete system goes for toss.
Can anybody help in identifying if I am doing something wrong.
Use the standard java Queues (LinkedList for example is designed to be able to use it as a dequeue). They provide a size method.
Alternatively you could over-ride the various add/remove methods and keep a counter internally so you always know the size. You would be better off just using the right tool for the job though.
Disclaimer: do not use reflection unless you have to.
Queue q = new Queue();
Field f = Queue.class.getDeclaredField("length");
f.setAccessible(true);
int size = f.getInt(q);
System.out.println(size);
I read your question again... now I'm mad at you for just writing "goes for toss". Stacktrace or GTFO.

Get the array from an AtomicLongArray

Using Java 1.6 and the AtomicLongArray, I'd like to "copy" the original AtomicLongArray into a new one. There is a constructor that takes an array (AtomicLongArray(long[])), so I thought I could just get the array from the original one and give it to the constructor.
Sadly, the actual long[] in the AtomicLongArray is private and there seem to be no getters for it. Is there any way to do this, meaning copy the values from one AtomicLongArray to another? I can't create my own class based on this class, as the sun.misc.Unsafe class is not available to me.
This is needed because I'm going to iterate over the values, and I don't want them modified by another thread during iteration. So I thought I could make a copy and use that for the iteration...
Thanks!
Phillip
I suspect you have to create your own long[] and populate it first, or just iterate over the original:
AtomicLongArray copy = new AtomicLongArray(original.length());
for (int i = 0; i < copy.length(); i++)
{
copy.set(i, original.get(i));
}
Note that although each individual operation in AtomicLongArray is atomic, there are no bulk operations - so there's no way of getting a "snapshot" of the whole array at time T. If you want that sort of behaviour, I believe you'll need to use synchronization.
This data structure allows concurrent updates to individual entries in the collection. There is not overall lock, so you can't prevent another thread changing the contents while you are iterating over it.
If you need this, you need a workaround, e.g. copy the array and loop again to check it hasn't changed. If changed, repeat. Or you need a collection which supports a global lock.
long[] copy = new long[original.length()];
boolean changed = true;
// repeat until we get an unchanged copy.
while(true) {
for (int i = 0; i < copy.length(); i++) {
long l = original.get(i);
changed |= copy[i] != l;
copy[i] = l;
}
if (!changed) break;
changed = false;
}
This is not completely safe, but may be enough for what you need.

Custom Class - have to use new rather than overwriting. Why?

I have a custom Vector2 class and a variable named tempVect.
I reuse this throughout a game thread but originally I was just overwriting the new variables
tempVect.x = blahhere;
tempVect.y = blahthere;
tempVect = conversion(tempVect); //this just changes the float to * 0.8
if(ball.velocity != tempVector)
ball.velocity = tempVector;
tempVect.x = thisHere;
tempVect.y = thisThere;
tempVect = conversion(tempVect);
if(ball.position != tempVector)
ball.position = tempVector;
for(int i = 0; i < somevairablenum; i++)
{
tempVect.x = anotherHere;
tempVect.y = anotherThere;
tempVect = conversion(tempVect);
player.position = tempVect;
}
The do somethings would conflict with each other whilst using the tempVect. (so the do something 2 would use the original blahHere and blahThere)
However I solved this by just doing
tempVect = new Vector2(blahHere, blahThere);
//do something
tempVect = new Vector2(thisHere, thisThere);
//do something different
Is anyone able to explain why this was the case? Unfortunately this is my university final project and so any problems I have I need to write down why they happened and how I resolved them, but I don't understand the theory behind this.
Can anyone else?
TIA
----- edit ------
The actual problem was the for loop, the player position was getting mixed up with the ball position.
In the first case, each time you use 'tempVect', it references the same vector, hence the clash.
When you allocate a new vector you create a new object - the .x and .y members are distinct - they refer to a different object (i.e. a different set of variables).
So, even though you are using the 'tempVect' variable each time, 'tempVect' it a reference - and by using 'new' you make it reference different objects, so the assignments do not conflict - since they go to separate objects.
Unfortunately, you've left out the critical part - the content of the "do something"s.
However, most likely one of those "do somethings" was stashing a reference to your vector somewhere, and referring to its contents later. This would then result in it seeing the updated values when it later looks at the contents of the vector.

Tutorials for Java errors and syntax

I am asking for help on self-help, which is kind of an oxymoron. How do I bug you nice folks less by solving more of my own problems?
I am in my last week of Java programming and I am having a huge hurdle with learning Java. I have read all the books but I keep getting hung up on tiny little issues. It is like trying to build a house of cards. I only know about the parts of the syntax and the uses that the book shows. When I am combining things, I run into horrible hurdles. I try for hours of tinkering to figure them out. The sun docs only show basic uses that don't seem to help
Here is what I would like:
When I am trying something and it doesn't work like the following manipulations of an array list, I want to find a place or program that can show examples code of things like adding an additional class instance to an arrayList. Where can I learn concisely about this without having to ask a question or 2 for every syntax error? Where is the Google for Java? Is there a program that will take your errors and show you how to fix them (or offer suggestions)?
/tmp/jc_4083/Inventory.java:101: incompatible types
found : RatedDVD[]
required: java.util.ArrayList
dvdlist = temp;
^
/tmp/jc_4083/Inventory.java:110: array required, but java.util.ArrayList found
if (p != dvdlist[i]) {
^
/tmp/jc_4083/Inventory.java:111: array required, but java.util.ArrayList found
temp[i-adj] = dvdlist[i];
^
/tmp/jc_4083/Inventory.java:115: incompatible types
found : RatedDVD[]
required: java.util.ArrayList
dvdlist = temp;
Here is my code for this class if anyone is interested in looking at it for me:
//Contruct inv and allow for methods add, get, size, sort, and value
import java.util.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
public class Inventory
{// class Inventory
private ArrayList<RatedDVD> dvdlist;// declare dvdlist as ArrayList of RatedDVD
private int numDVDs;
public Inventory()
{// method Inventory
dvdlist = new ArrayList<RatedDVD>();
}// end method
// add & get
public RatedDVD get(int i){return dvdlist.get(i);}// method get
public void add(DVD d){
dvdlist = dvdlist d;
sort();
}// method add
public double value()
{// method value
double total = 0.0;
for (int i = 0; i < dvdlist.size(); i++)
{// for every pass thru dvdlist add total
// [DEBUG] consider enhanced for
total += get(i).feeValue();
}
return total;
}// end method value
public void sort()
{// method sort
// [DEBUG] consider optimization
int n = dvdlist.size();
for (int search = 1; search < n; search++)
{// for do the following and increment till dvdlist has been searched
for (int i = 0; i < n-search; i++)
{// for step through comparison for entire dvdlist
if (dvdlist.get(i).getName().compareToIgnoreCase(dvdlist.get(i+1).getName()) > 0)
{// if swap necessary then swap
RatedDVD temp = dvdlist.get(i);
dvdlist.set(i,dvdlist.get(i+1));
dvdlist.set(i+1,temp);
}// end if swap
}// end for compareto
}// end outer for
}// end method sort
public int size(){return dvdlist.size();}// method size
public void save() {
save(true);
}
// save it to C:\data\inventory.dat
public void save(boolean saveagain) {
try {
BufferedWriter w = new BufferedWriter(new FileWriter("c:\\data\\inventory.dat"));
for (int i = 0; i < size(); i++) {
RatedDVD dvd = get(i);
w.write( dvd.getItem() + "\n");
w.write( dvd.getName() + "\n");
w.write( dvd.getRating() + "\n");
w.write( dvd.getUnits() + "\n");
w.write( dvd.getPrice() + "\n");
w.write( dvd.value() + "\n");
w.write( dvd.fee() + "\n");
w.write( dvd.feeValue() + "\n");
w.newLine();
}
// total value of it
//w.write( value() + "\n");
w.close();
} catch (Exception ex) {
if (saveagain) {
new File("c:\\data\\").mkdir(); // make file if doesn't exist
save(false);
}
}
}
public int search(String name) {
for (int i = 0; i < size(); i++) { // check if name string is equal
if (get(i).getName().equalsIgnoreCase(name)) return i;
}
return -1; // we didn't find anything
}
// add a new dvd to the end, increasing the array size
public void add(RatedDVD p) {
RatedDVD[] temp = new RatedDVD[dvdlist.size()+1];
for (int i = 0; i < dvdlist.size(); i++) {
temp[i] = dvdlist[i];
}
temp[temp.length-1] = p; // add it at the end
dvdlist = temp;
}
// remove a DVD from the array, and shrink the array size
public void delete(RatedDVD p) {
RatedDVD[] temp = new RatedDVD[dvdlist.size()-1];
int adj = 0;
for (int i = 0; i < dvdlist.size(); i++) {
if (p != dvdlist[i]) {
temp[i-adj] = dvdlist[i];
}
else adj = 1;
}
dvdlist = temp;
}
public int highestNumber() {
int numb = 0;
for (int i = 0; i < dvdlist.size(); i++) {
if (get(i).getItem() > numb) {
numb = get(i).getItem();
}
}
return numb;
}
}// end class inventory
The dvdlist is an ArrayList, which implements the Collection interface, not an Array (BTW, and this is known as the "program to an interface, not an implementation" principle, you should decalare dvdlist as a java.util.List):
private ArrayList<RatedDVD> dvdlist;// declare dvdlist as ArrayList of RatedDVD
Have a look at the methods on the Collection interface, you'll find everything you need for adding and removing elements.
So, to add a RatedDVD, you don't need to use a temporary array of RatedDVD that won't fit anyway into an ArrayList like you're doing here:
// add a new dvd to the end, increasing the array size
public void add(RatedDVD p) {
RatedDVD[] temp = new RatedDVD[dvdlist.size()+1];
for (int i = 0; i < dvdlist.size(); i++) {
temp[i] = dvdlist[i];
}
temp[temp.length-1] = p; // add it at the end
dvdlist = temp;
}
Instead, just call the add(Object o) method on dvdlist.
To delete a RatedDVD instance, use the remove(Object o) method on dvdlist.
For the search() method, consider using contains(Object o) on dvdlist.
If you need to iterate over a collection, use an Iterator:
for (Iterator iter = dvdlist.iterator(); iter.hasNext();) {
RatedDVD ratedDVD = (RatedDVD) iter.next();
//rest of the code block removed
}
Or even faster now with Java 5+ and Generics:
for (RatedDVD ratedDVD : dvdlist) {
// rest of the code here
}
Really, you need to dig the the Collection Framework.
The compiler errors seem to be quite descriptive of what you're doing wrong, but I can see why you might be confused about how to do it right. You seem to be misunderstanding how an ArrayList is meant to be used. If you look at the docs, you will see it has methods add() and remove() that do the operations you've created add() and delete() methods for. You're attempting to treat the ArrayList as if it is a raw array. Don't do that; use the methods provided by the API. Not only will this solve your errors, but it will make your code cleaner and clearer to future programmers.
Actually, the compiler error is very clear:
/tmp/jc_4083/Inventory.java:101: incompatible types
found : RatedDVD[]
required: java.util.ArrayList
dvdlist = temp;
It says "incompatible types" and that it expected a java.util.ArrayList but found instead a RatedDVD[].
Your problem is simply that, unlike in languages like Python, Java does not treat lists and arrays interchangeably. They are completely different things - arrays are special language-level constructs, while ArrayList is a class like any other.
So you cannot assign an array to a variably of type list. You either have to decide on using only one of these two types throughout your program, or you have to convert between them manually, using methods such as java.util.Arrays.asList() and List.toArray().
It seems that you're trying to do too advanced things too fast - you should probably look at Sun's Java tutorials first - though they are quite comprehensive and can also be used as a reference for looking up language details. There is also a section about conversion between collections and arrays.
I suggest you use an IDE (like Eclipse, entirely free). It will help you through the API syntax by making suggestions as you type, and show you errors when you type them, so that you can pinpoint exact syntax errors and ask about them. In terms of asking, that is what StackOverflow is for.
Others beat me to your specific syntax question, so I'm just limiting my answer to the general question of how you get help.
To resolve compiler errors, usually it's best to start with the first one and fix it first. After fixing that, the rest of the compiler errors might also be solved, or they might be different kinds of errors.
To understand what some compiler error means, there is an article called Compile and Runtime Errors in Java (PDF) that goes through different kinds of error messages and gives examples of what kind of code may cause them. And as for runtime error messages, Java Glossary has quite a big list of them. They also have a list of compile-time error messages.
So, your problem here is that you're trying to access an ArrayList like an array, which is incorrect because Java doesn't do stuff like that. You need to use list.get(i) to get the ith element in an Array. Similarly, when you tried to set an ArrayList variable to an array, the compiler got mad at you. You need to create a new ArrayList with the contents of temp and then set dvdlist to that, eg. dvdlist = new ArrayList<RatedDVD>(temp);.
As for your continued problems: There is an API Specification for Java which tells you basically how to use all the classes that are included in the Java API. For example, ArrayList is a generic collection which has certain methods and constructors that you need to use. Java does not have operator overloading, so you can't just access elements in a List using array syntax. Also, arrays are their own data type so you can't just treat an ArrayList as an array and vice versa.
It looks like you are confused about the difference between an array and an ArrayList. An array is a static list of elements, and is constructed using the [] symbols. An ArrayList is an object in the Collections system in Java that acts like a size-modifiable array - i.e. it can be indexed into, and can be added onto, inserted into, etc. They are not interchangable.
As to where you can look, etc. If this is your first programming experience, you are not alone. Many compiler errors are less than helpful. One suggestion I can give you that I learned through many years of trial and error - start small and build up. Get a main method that compiles. Then add the first little piece (creating a class for instance). Then add the next piece, and so on. Test as you go, etc. You can google for particular compiler errors - I have been surprised what I have found. Beyond that, a lot of it is trial and error - these are things you learn from experience, and a lot of the speed of "old hands" comes from the long experience of seeing what you can do wrong over and over again, not any sort of innate intelligence. I totally understand your frustration - I felt that way when I was starting out about 15 years ago now (but I was on Borland Pascal - yuck).
One of the biggest issues beginning programmers seem to have is not being able to read and interpret error messages very well.
You would be well served by carefully examining the errors that javac (or any compiler/interpreter) provides. Maybe even start by making some mistakes that you understand in your code (ie, assign an incorrect typed value to a variable, extend a loop beyond the bounds) and see how your compiler handles these.
Try to think in object oriented terms...
It looks to me that something (classwork, I guess) has pushed you into writing an object-oriented program but it's possible that you haven't yet accepted that you will need to think in those terms.
In Java most things are objects, but Java supports primitive types, and arrays of both. It's possible to program in Java in a flat, procedural, mutable way, but also possible to write in an object-oriented functional way. It's possible to do both and get confused, which is where you may be right now.
You are trying to mix the two styles. This isn't always a bad thing, but for coursework we can safely bet the farm that your instructor will want to see more objects and fewer arrays, unless those arrays are the private internal implementation of an object.
So think of the data structures as black boxes with methods, and then see how what you are doing is implementing one yourself.
You have probably been here, but these are the things that you can do with an ArrayList. And you have an ArrayList<RatedDVD> which further restricts what you can do with it. Try to understand this first, and then fix the program to work with the available operations on an ArrayList object.

Java SWT interop with COM - putting a float[] into a Variant?

In my Java SWT application I'm hosting an 3rd party ActiveX control. I'm using OleClientSite to do this.
// Ah, this works. :-)
OleAutomation comObject = new OleAutomation(...);
There are 2 easy little functions I want to call from Java. Here are the COM function definitions:
[id(5)]
void easyFoo([in] int blah);
[id(20)]
void problemFoo([in] VARIANT floatArray);
Easy, right? Here's my pretend code:
// Ah, this works. :-)
OleAutomation comObject = new OleAutomation("Some3rdPartyControlHere");
// Call easyFoo(42). This works. :-)
int easyFooId = 5;
comObject.invoke(easyFooId, new Variant[] { new Variant(42) });
// Call problemFoo(new float[] { 4.2, 7.0 }). This doesn't work. :-(
int problemFooId = 20;
comObject.invoke(problemFooId, [ACK! What goes here?]);
The problem is on the last line: how do I pass a float array to the 3rd party COM object? HELP!
You need to pass a float array. In COM terms, that mean s a VARIANT with vt set to VT_R4|VT_ARRAY. An array of variants may not work as the document does not say it can accept an array of variants (VT_VARIANT |VT_ARRAY). In java you should be able to use float[] as the parameter type. If not you can always call the Windows API to construct a safe array of desired type.
I suspect there is no constructor that takes a float[] because VARIANTs don't have a float array member.
I think what you need to do to make this work is pack up your floats into a SAFEARRAY (ick; and I have no idea how to create one in Java).
Alternatively, you may try serializing your array to raw bits and use the BYTE* member of the VARIANT struct, and pass an int that has the count of bytes so you can accurately de-serialize on the other side (and I assume this is all in the same process and thread, otherwise it gets harder).
[id(20)]
void problemFoo([in] VARIANT bytes /* VT_BYREF|VT_UI1 */, [in] VARIANT byteCount /* VT_UI4 */);
What's wrong with creating an array of Variant and filling it with your float array values?
Variant[] problemFooArgs = new Variant[myFloats.length];
for( int i=0; i<myFloats.length; i++)
{
problemFooArgs[i] = new Variant(myFloats[i]);
}
If it really needs only one argument (an array of float), you could try one level of indirection:
Variant[] problemFooArgs = new Variant[1];
Variant[] myFooArgs = new Variant[1];
for( int i=0; i<myFloats.length; i++)
{
myFooArgs [i] = new Variant(myFloats[i]);
}
problemFooArgs[0] = myFooArgs;
If the simple approach does not work and you do need a SAFEARRAY, you could try and create one after the example "Reading and writing to a SAFEARRAY", using the constants of org.eclipse.swt.internal.win32.OS. But it seems for char[] only.
Other source of inspiration for creating the relevant SAFEARRAY:
class SafeArray of project com4j (and its associated class, like Variant)

Categories