aboutsummaryrefslogtreecommitdiffstats
path: root/documentation/components/components-extensions.asciidoc
blob: a84ad92f6ee7a78514004a298056c9e8ea8e6c53 (plain)
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
---
title: Component Extensions
order: 6
layout: page
---

[[components.extensions]]
= Component Extensions

Components and UIs can have extensions which are attached to the component
dynamically. Especially, many add-ons are extensions.

How a component is extended depends on the extension. Typically, they have an
[methodname]#extend()# method that takes the component to be extended as the
parameter.


[source, java]
----
TextField tf = new TextField("Hello");
layout.addComponent(tf);

// Add a simple extension
new CapsLockWarning().extend(tf);

// Add an extension that requires some parameters
CSValidator validator = new CSValidator();
validator.setRegExp("[0-9]*");
validator.setErrorMessage("Must be a number");
validator.extend(tf);
----

Development of custom extensions is described in
<<dummy/../../../framework/gwt/gwt-extension#gwt.extension,"Component and UI
Extensions">>.
f='#n302'>302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * All rights reserved. 
 * This program and the accompanying materials are made available 
 * under the terms of the Common Public License v1.0 
 * which accompanies this distribution and is available at 
 * http://www.eclipse.org/legal/cpl-v10.html 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/


package org.aspectj.weaver.bcel;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Hashtable;
import java.util.List;

import org.aspectj.apache.bcel.Constants;
import org.aspectj.apache.bcel.classfile.ClassParser;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.aspectj.apache.bcel.classfile.Method;
import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue;
import org.aspectj.apache.bcel.classfile.annotation.ElementNameValuePair;
import org.aspectj.apache.bcel.classfile.annotation.ElementValue;
import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue;
import org.aspectj.apache.bcel.generic.ArrayType;
import org.aspectj.apache.bcel.generic.BIPUSH;
import org.aspectj.apache.bcel.generic.BasicType;
import org.aspectj.apache.bcel.generic.BranchInstruction;
import org.aspectj.apache.bcel.generic.ConstantPushInstruction;
import org.aspectj.apache.bcel.generic.INSTANCEOF;
import org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionConstants;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionList;
import org.aspectj.apache.bcel.generic.InstructionTargeter;
import org.aspectj.apache.bcel.generic.LDC;
import org.aspectj.apache.bcel.generic.ObjectType;
import org.aspectj.apache.bcel.generic.ReferenceType;
import org.aspectj.apache.bcel.generic.SIPUSH;
import org.aspectj.apache.bcel.generic.SWITCH;
import org.aspectj.apache.bcel.generic.Select;
import org.aspectj.apache.bcel.generic.TargetLostException;
import org.aspectj.apache.bcel.generic.Type;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.weaver.AnnotationX;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;

public class Utility {

    private Utility() {
        super();
    }
	
	/*
	 * Ensure we report a nice source location - particular in the case
	 * where the source info is missing (binary weave).
	 */
	public static String beautifyLocation(ISourceLocation isl) {
		StringBuffer nice = new StringBuffer();
		if (isl==null || isl.getSourceFile()==null || isl.getSourceFile().getName().indexOf("no debug info available")!=-1) {
			nice.append("no debug info available");
	    } else {
	    	// can't use File.getName() as this fails when a Linux box encounters a path created on Windows and vice-versa
	    	int takeFrom = isl.getSourceFile().getPath().lastIndexOf('/');
	    	if (takeFrom == -1) {
	    		takeFrom = isl.getSourceFile().getPath().lastIndexOf('\\');
	    	}    			
	    	nice.append(isl.getSourceFile().getPath().substring(takeFrom +1));
	    	if (isl.getLine()!=0) nice.append(":").append(isl.getLine());
		}
		return nice.toString();
	}
    
    
    public static Instruction createSuperInvoke(
    		InstructionFactory fact,
    		BcelWorld world,
    		Member signature) {
        short kind;
        if (signature.isInterface()) {
            throw new RuntimeException("bad");
        } else if (signature.isPrivate() || signature.getName().equals("<init>")) {
           	throw new RuntimeException("unimplemented, possibly bad");
        } else if (signature.isStatic()) {
            throw new RuntimeException("bad");
        } else {
            kind = Constants.INVOKESPECIAL;
        }
        
        return fact.createInvoke(
            signature.getDeclaringType().getName(),
            signature.getName(),
            BcelWorld.makeBcelType(signature.getReturnType()),
            BcelWorld.makeBcelTypes(signature.getParameterTypes()),
            kind);
	}
    
    // XXX don't need the world now
    public static Instruction createInvoke(
    		InstructionFactory fact,
    		BcelWorld world,
    		Member signature) {
        short kind;
        if (signature.isInterface()) {
            kind = Constants.INVOKEINTERFACE;
        } else if (signature.isStatic()) {
            kind = Constants.INVOKESTATIC;
        } else if (signature.isPrivate() || signature.getName().equals("<init>")) {
            kind = Constants.INVOKESPECIAL;
        } else {
            kind = Constants.INVOKEVIRTUAL;
        }

        return fact.createInvoke(
            signature.getDeclaringType().getName(),
            signature.getName(),
            BcelWorld.makeBcelType(signature.getReturnType()),
            BcelWorld.makeBcelTypes(signature.getParameterTypes()),
            kind);
	}

	public static Instruction createGet(InstructionFactory fact, Member signature) {
        short kind;
        if (signature.isStatic()) {
            kind = Constants.GETSTATIC;
        } else {
            kind = Constants.GETFIELD;
        }

        return fact.createFieldAccess(
            signature.getDeclaringType().getName(),
            signature.getName(),
            BcelWorld.makeBcelType(signature.getReturnType()),
            kind);
	}

    /**
     * Creae a field GET instruction
     *
     * @param fact
     * @param signature
     * @param declaringType
     * @return
     */
    public static Instruction createGetOn(InstructionFactory fact, Member signature, UnresolvedType declaringType) {
        short kind;
        if (signature.isStatic()) {
            kind = Constants.GETSTATIC;
        } else {
            kind = Constants.GETFIELD;
        }

        return fact.createFieldAccess(
            declaringType.getName(),
            signature.getName(),
            BcelWorld.makeBcelType(signature.getReturnType()),
            kind);
    }

	public static Instruction createSet(InstructionFactory fact, Member signature) {
        short kind;
        if (signature.isStatic()) {
            kind = Constants.PUTSTATIC;
        } else {
            kind = Constants.PUTFIELD;
        }

        return fact.createFieldAccess(
            signature.getDeclaringType().getName(),
            signature.getName(),
            BcelWorld.makeBcelType(signature.getReturnType()),
            kind);
	}

	public static Instruction createInvoke(
    		InstructionFactory fact,
    		JavaClass declaringClass,
    		Method newMethod) {
        short kind;
        if (newMethod.isInterface()) {
            kind = Constants.INVOKEINTERFACE;
        } else if (newMethod.isStatic()) {
            kind = Constants.INVOKESTATIC;
        } else if (newMethod.isPrivate() || newMethod.getName().equals("<init>")) {
            kind = Constants.INVOKESPECIAL;
        } else {
            kind = Constants.INVOKEVIRTUAL;
        }

        return fact.createInvoke(
            declaringClass.getClassName(),
            newMethod.getName(),
            Type.getReturnType(newMethod.getSignature()),
            Type.getArgumentTypes(newMethod.getSignature()),
            kind);
	}
	
	public static byte[] stringToUTF(String s) {
		try {
			ByteArrayOutputStream out0 = new ByteArrayOutputStream();
			DataOutputStream out1 = new DataOutputStream(out0);
			out1.writeUTF(s);
			return out0.toByteArray();
		} catch (IOException e) {
			throw new RuntimeException("sanity check");
		}
	}

    public static Instruction createInstanceof(InstructionFactory fact, ReferenceType t) {
		int cpoolEntry =
			(t instanceof ArrayType)
			? fact.getConstantPool().addArrayClass((ArrayType)t)
			: fact.getConstantPool().addClass((ObjectType)t);
		return new INSTANCEOF(cpoolEntry);
    }

    public static Instruction createInvoke(
            InstructionFactory fact,
            LazyMethodGen m) {
        short kind;
        if (m.getEnclosingClass().isInterface()) {
            kind = Constants.INVOKEINTERFACE;
        } else if (m.isStatic()) {
            kind = Constants.INVOKESTATIC;
        } else if (m.isPrivate() || m.getName().equals("<init>")) {
            kind = Constants.INVOKESPECIAL;
        } else {
            kind = Constants.INVOKEVIRTUAL;
        }

        return fact.createInvoke(
            m.getClassName(),
            m.getName(),
            m.getReturnType(),
            m.getArgumentTypes(),
            kind);
    }

    /**
     * Create an invoke instruction
     *
     * @param fact
     * @param kind INVOKEINTERFACE, INVOKEVIRTUAL..
     * @param member
     * @return
     */
    public static Instruction createInvoke(
            InstructionFactory fact,
            short kind,
            Member member) {
        return fact.createInvoke(
            member.getDeclaringType().getName(),
            member.getName(),
            BcelWorld.makeBcelType(member.getReturnType()),
            BcelWorld.makeBcelTypes(member.getParameterTypes()),
            kind);
    }

    // ??? these should perhaps be cached.  Remember to profile this to see if it's a problem.
    public static String[] makeArgNames(int n) {
        String[] ret = new String[n];
        for (int i=0; i<n; i++) {
            ret[i] = "arg" + i;
        }
        return ret;
    }
    
    // Lookup table, for converting between pairs of types, it gives
    // us the method name in the Conversions class
    private static Hashtable validBoxing = new Hashtable();
    
    static {
      validBoxing.put("Ljava/lang/Byte;B","byteObject");
      validBoxing.put("Ljava/lang/Character;C","charObject");
      validBoxing.put("Ljava/lang/Double;D","doubleObject");
      validBoxing.put("Ljava/lang/Float;F","floatObject");
      validBoxing.put("Ljava/lang/Integer;I","intObject");
      validBoxing.put("Ljava/lang/Long;J","longObject");
      validBoxing.put("Ljava/lang/Short;S","shortObject");
      validBoxing.put("Ljava/lang/Boolean;Z","booleanObject");
      validBoxing.put("BLjava/lang/Byte;","byteValue");
      validBoxing.put("CLjava/lang/Character;","charValue");
      validBoxing.put("DLjava/lang/Double;","doubleValue");
      validBoxing.put("FLjava/lang/Float;","floatValue");
      validBoxing.put("ILjava/lang/Integer;","intValue");
      validBoxing.put("JLjava/lang/Long;","longValue");
      validBoxing.put("SLjava/lang/Short;","shortValue");
      validBoxing.put("ZLjava/lang/Boolean;","booleanValue");
    }
    
    public static void appendConversion(
        InstructionList il,
        InstructionFactory fact,
        ResolvedType fromType,
        ResolvedType toType)
    {
        if (! toType.isConvertableFrom(fromType) &&
        	 ! fromType.isConvertableFrom(toType)) {
            throw new BCException("can't convert from " + fromType + " to " + toType);
        }
	    // XXX I'm sure this test can be simpler but my brain hurts and this works
        if (!toType.getWorld().isInJava5Mode()) {
        	if (toType.needsNoConversionFrom(fromType)) return;
        } else {
        	if (toType.needsNoConversionFrom(fromType) && !(toType.isPrimitiveType()^fromType.isPrimitiveType())) return;
        }
        if (toType.equals(ResolvedType.VOID)) {
            // assert fromType.equals(UnresolvedType.OBJECT)
            il.append(InstructionFactory.createPop(fromType.getSize()));
        } else if (fromType.equals(ResolvedType.VOID)) {
            // assert toType.equals(UnresolvedType.OBJECT)
            il.append(InstructionFactory.createNull(Type.OBJECT));
            return;
        } else if (fromType.equals(UnresolvedType.OBJECT)) {
            Type to = BcelWorld.makeBcelType(toType);
            if (toType.isPrimitiveType()) {
                String name = toType.toString() + "Value";
                il.append(
                    fact.createInvoke(
                        "org.aspectj.runtime.internal.Conversions",
                        name,
                        to,
                        new Type[] { Type.OBJECT },
                        Constants.INVOKESTATIC));
            } else {
                il.append(fact.createCheckCast((ReferenceType)to));
            }
        } else if (toType.equals(UnresolvedType.OBJECT)) {
            // assert fromType.isPrimitive()
            Type from = BcelWorld.makeBcelType(fromType);
            String name = fromType.toString() + "Object";
            il.append(
                fact.createInvoke(
                    "org.aspectj.runtime.internal.Conversions",
                    name,
                    Type.OBJECT,
                    new Type[] { from },
                    Constants.INVOKESTATIC));
        } else if (toType.getWorld().isInJava5Mode() && validBoxing.get(toType.getSignature()+fromType.getSignature())!=null) {
        	// XXX could optimize by using any java boxing code that may be just before the call...
        	Type from   = BcelWorld.makeBcelType(fromType);
        	Type to     = BcelWorld.makeBcelType(toType);
            String name = (String)validBoxing.get(toType.getSignature()+fromType.getSignature());
            if (toType.isPrimitiveType()) {
            	il.append(
                        fact.createInvoke(
                          "org.aspectj.runtime.internal.Conversions",
                          name,
						  to,
                          new Type[]{Type.OBJECT},
                          Constants.INVOKESTATIC));
            } else {
                il.append(
                  fact.createInvoke(
                    "org.aspectj.runtime.internal.Conversions",
                    name,
                    Type.OBJECT,
                    new Type[] { from },
                    Constants.INVOKESTATIC));
                il.append(fact.createCheckCast((ReferenceType) to));
            }
        } else if (fromType.isPrimitiveType()) {
            // assert toType.isPrimitive()
            Type from = BcelWorld.makeBcelType(fromType);
            Type to = BcelWorld.makeBcelType(toType);
            try {
                il.append(fact.createCast(from, to));
            } catch (RuntimeException e) {
                il.append(fact.createCast(from, Type.INT));
                il.append(fact.createCast(Type.INT, to));
            }
        } else {
            Type to = BcelWorld.makeBcelType(toType);
            // assert ! fromType.isPrimitive() && ! toType.isPrimitive()
            il.append(fact.createCheckCast((ReferenceType) to));
        }
    }

    
    public static InstructionList createConversion(
            InstructionFactory fact,
            Type fromType,
            Type toType) {
        //System.out.println("cast to: " + toType);

        InstructionList il = new InstructionList();
        
        //PR71273
        if ((fromType.equals(Type.BYTE) || fromType.equals(Type.CHAR) || fromType.equals(Type.SHORT)) &&
            (toType.equals(Type.INT))) {
        	return il;
        }
        
        if (fromType.equals(toType))
            return il;
        if (toType.equals(Type.VOID)) {
            il.append(InstructionFactory.createPop(fromType.getSize()));
            return il;
        }

        if (fromType.equals(Type.VOID)) {
            if (toType instanceof BasicType) 
                throw new BCException("attempting to cast from void to basic type");
            il.append(InstructionFactory.createNull(Type.OBJECT));
            return il;
        }

        if (fromType.equals(Type.OBJECT)) {
            if (toType instanceof BasicType) {
                String name = toType.toString() + "Value";
                il.append(
                    fact.createInvoke(
                        "org.aspectj.runtime.internal.Conversions",
                        name,
                        toType,
                        new Type[] { Type.OBJECT },
                        Constants.INVOKESTATIC));
                return il;
            }
        }

        if (toType.equals(Type.OBJECT)) {
            if (fromType instanceof BasicType) {
                String name = fromType.toString() + "Object";
                il.append(
                    fact.createInvoke(
                        "org.aspectj.runtime.internal.Conversions",
                        name,
                        Type.OBJECT,
                        new Type[] { fromType },
                        Constants.INVOKESTATIC));
                return il;
            } else if (fromType instanceof ReferenceType) {
                return il;
            } else {
                throw new RuntimeException();
            }
        }
        
        if (fromType instanceof ReferenceType 
                && ((ReferenceType)fromType).isAssignmentCompatibleWith(toType)) {
            return il;
        }

        il.append(fact.createCast(fromType, toType));
        return il;
    }

	public static Instruction createConstant(
    		InstructionFactory fact,
    		int i) {
		Instruction inst;
		switch(i) {
			case -1: inst =  InstructionConstants.ICONST_M1; break;
			case 0: inst =  InstructionConstants.ICONST_0;	break;			
			case 1: inst =  InstructionConstants.ICONST_1; break;
			case 2: inst =  InstructionConstants.ICONST_2; break;				
			case 3: inst =  InstructionConstants.ICONST_3; break;
			case 4: inst =  InstructionConstants.ICONST_4;	break;			
			case 5: inst =  InstructionConstants.ICONST_5;	break;	
		}
		if (i <= Byte.MAX_VALUE && i >= Byte.MIN_VALUE) {
	     	inst =  new BIPUSH((byte)i);
		} else if (i <= Short.MAX_VALUE && i >= Short.MIN_VALUE) {
			inst =  new SIPUSH((short)i);
		} else {
			inst =  new LDC(fact.getClassGen().getConstantPool().addInteger(i));
		}
		return inst;
	}

	public static JavaClass makeJavaClass(String filename, byte[] bytes) {
		try {
		    ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), filename);
            return parser.parse();
		} catch (IOException e) {
			throw new BCException("malformed class file");
		}		
	}
	
    public static String arrayToString(int[] a) {
        int len = a.length;
        if (len == 0) return "[]";
        StringBuffer buf = new StringBuffer("[");
        buf.append(a[0]);
        for (int i = 1; i < len; i++) {
            buf.append(", ");
            buf.append(a[i]);
        }
        buf.append("]");
        return buf.toString();
    }

	/**
	 * replace an instruction handle with another instruction, in this case, a branch instruction.
	 * 
	 * @param ih the instruction handle to replace.
	 * @param branchInstruction the branch instruction to replace ih with
	 * @param enclosingMethod where to find ih's instruction list.
	 */
    public static void replaceInstruction(
        InstructionHandle ih,
        BranchInstruction branchInstruction,
        LazyMethodGen enclosingMethod) 
    {
        
        InstructionList il = enclosingMethod.getBody();
        InstructionHandle fresh = il.append(ih, branchInstruction);
		deleteInstruction(ih, fresh, enclosingMethod);
    }
    
	/** delete an instruction handle and retarget all targeters of the deleted instruction
	 * to the next instruction.  Obviously, this should not be used to delete
	 * a control transfer instruction unless you know what you're doing.
	 * 
	 * @param ih the instruction handle to delete.
	 * @param enclosingMethod where to find ih's instruction list.
	 */
	public static void deleteInstruction(
    	InstructionHandle ih,
    	LazyMethodGen enclosingMethod) 
    {
    	deleteInstruction(ih, ih.getNext(), enclosingMethod);
    }
    		
		
	/** delete an instruction handle and retarget all targeters of the deleted instruction
	 * to the provided target.
	 * 
	 * @param ih the instruction handle to delete
	 * @param retargetTo the instruction handle to retarget targeters of ih to.
	 * @param enclosingMethod where to find ih's instruction list.
	 */
    public static void deleteInstruction(
    	InstructionHandle ih,
    	InstructionHandle retargetTo,
    	LazyMethodGen enclosingMethod) 
    {
        InstructionList il = enclosingMethod.getBody();
        InstructionTargeter[] targeters = ih.getTargeters();
        if (targeters != null) {
            for (int i = targeters.length - 1; i >= 0; i--) {
                InstructionTargeter targeter = targeters[i];
                targeter.updateTarget(ih, retargetTo);
            }
            ih.removeAllTargeters();
        }
        try {
            il.delete(ih);
        } catch (TargetLostException e) {
            throw new BCException("this really can't happen");
        }
   	}
   	
   	/**
   	 * Fix for Bugzilla #39479, #40109 patch contributed by Andy Clement
   	 * 
   	 * Need to manually copy Select instructions - if we rely on the the 'fresh' object
   	 * created by copy(), the InstructionHandle array 'targets' inside the Select
   	 * object will not have been deep copied, so modifying targets in fresh will modify
   	 * the original Select - not what we want !  (It is a bug in BCEL to do with cloning
   	 * Select objects).
   	 * 
   	 * <pre>
   	 * declare error:
   	 *     call(* Instruction.copy()) && within(org.aspectj.weaver)
   	 *       && !withincode(* Utility.copyInstruction(Instruction)):
   	 *     "use Utility.copyInstruction to work-around bug in Select.copy()";
   	 * </pre>
   	 */
	public static Instruction copyInstruction(Instruction i) {
		if (i instanceof Select) {
			Select freshSelect = (Select)i;
				  
			// Create a new targets array that looks just like the existing one
			InstructionHandle[] targets = new InstructionHandle[freshSelect.getTargets().length];
			for (int ii = 0; ii < targets.length; ii++) {
			  targets[ii] = freshSelect.getTargets()[ii];
			}
				  
			// Create a new select statement with the new targets array
			SWITCH switchStatement =
				new SWITCH(freshSelect.getMatchs(), targets, freshSelect.getTarget());
			return (Select)switchStatement.getInstruction();	
		} else {
			return i.copy(); // Use clone for shallow copy...
		}
	}
   	

	/** returns -1 if no source line attribute */
	// this naive version overruns the JVM stack size, if only Java understood tail recursion...
//	public static int getSourceLine(InstructionHandle ih) {
//		if (ih == null) return -1;
//		
//		InstructionTargeter[] ts = ih.getTargeters();
//		if (ts != null) { 
//			for (int j = ts.length - 1; j >= 0; j--) {
//				InstructionTargeter t = ts[j];
//				if (t instanceof LineNumberTag) {
//					return ((LineNumberTag)t).getLineNumber();
//				}
//			}
//		}
//		return getSourceLine(ih.getNext());
//	}
	

	public static int getSourceLine(InstructionHandle ih) {
		int lookahead=0;
		// arbitrary rule that we will never lookahead more than 100 instructions for a line #
		while (lookahead++ < 100) {
			if (ih == null) return -1;
			
	        InstructionTargeter[] ts = ih.getTargeters();
	        if (ts != null) { 
	            for (int j = ts.length - 1; j >= 0; j--) {
	                InstructionTargeter t = ts[j];
	                if (t instanceof LineNumberTag) {
	                	return ((LineNumberTag)t).getLineNumber();
	                }
	            }
	        }
	        ih = ih.getNext();
		}
		//System.err.println("no line information available for: " + ih);
        return -1;
	}
	
	// assumes that there is no already extant source line tag.  Otherwise we'll have to be better.
	public static void setSourceLine(InstructionHandle ih, int lineNumber) {
		ih.addTargeter(new LineNumberTag(lineNumber));
	}

	public static int makePublic(int i) {
		return i & ~(Modifier.PROTECTED | Modifier.PRIVATE) | Modifier.PUBLIC;
	}
	public static int makePrivate(int i) {
		return i & ~(Modifier.PROTECTED | Modifier.PUBLIC) | Modifier.PRIVATE;
	}
	public static BcelVar[] pushAndReturnArrayOfVars(
		ResolvedType[] proceedParamTypes,
		InstructionList il,
		InstructionFactory fact,
		LazyMethodGen enclosingMethod) 
	{
		int len = proceedParamTypes.length;
		BcelVar[] ret = new BcelVar[len];

		for (int i = len - 1; i >= 0; i--) {
			ResolvedType typeX = proceedParamTypes[i];
			Type type = BcelWorld.makeBcelType(typeX);
			int local = enclosingMethod.allocateLocal(type);
			
			il.append(InstructionFactory.createStore(type, local));
			ret[i] = new BcelVar(typeX, local);
		}		
		return ret;
	}

	public static boolean isConstantPushInstruction(Instruction i) {
		return (i instanceof ConstantPushInstruction) || (i instanceof LDC);
	}
	
	/**
     * Check if the annotations contain a SuppressAjWarnings annotation and
     * if that annotation specifies that the given lint message (identified
     * by its key) should be ignored.
     *
     */
    public static boolean isSuppressing(AnnotationX[] anns,String lintkey) {
    	if (anns == null) return false;
        boolean suppressed = false;
        // Go through the annotation types on the advice
        for (int i = 0;!suppressed && i<anns.length;i++) {
          // Check for the SuppressAjWarnings annotation
          if (UnresolvedType.SUPPRESS_AJ_WARNINGS.getSignature().equals(anns[i].getBcelAnnotation().getTypeSignature())) {
            // Two possibilities:
            // 1. there are no values specified (i.e. @SuppressAjWarnings)
            // 2. there are values specified (i.e. @SuppressAjWarnings("A") or @SuppressAjWarnings({"A","B"})
            List vals = anns[i].getBcelAnnotation().getValues();
            if (vals == null || vals.size()==0) { // (1)
                suppressed = true;
            } else { // (2)
            	// We know the value is an array value
            	ArrayElementValue array = (ArrayElementValue)((ElementNameValuePair)vals.get(0)).getValue();
            	ElementValue[] values = array.getElementValuesArray();
            	for (int j = 0; j < values.length; j++) {
            		// We know values in the array are strings
					SimpleElementValue value = (SimpleElementValue)values[j];
					if (value.getValueString().equals(lintkey)) {
						suppressed = true;
					}
				}
            }
          }
        }
        return suppressed;
    }
}