
public class A
{
    public boolean t()
    {
	return true;
    }

    public void m()
    {
	System.out.println( " A.m() " );
    }
}

public class Aprime extends A
{
    public boolean t()
    {
	return false;
    }

    public void m()
    {
	System.out.println( " A'.m() " );
    }
}

public class BdoublePrime extends B
{
    public void m()
    {
	System.out.println( " B''.m() " );
    }
}


public abstract class B
{
    public abstract void m();

    public void n( A a )
    {
	System.out.println( " B.n( A ) " );
	a.m();
    }
}

public class Bprime extends B
{

    public int nonIdempotent( int a ) {
	return a;
    }

    // from LessSimple.java
    public void m() {
	int a = 5;
	int b = 4;
	int w = a + b;
	int x = 5 + 4;
	int y = x + 10;
	int z = w + 12;
	y = z * x;
	
	// what happens to z?
	z = nonIdempotent( y );

	System.out.println( " B'.m() " );
    }
}

// demonstrates interprocedural (and also intraprocedural) class analysis
public class ClassHierarchies
{
    public static void main(String[] args)
    {
	A a = new A();
	a.m();
	                         // INTRAPROCEDURAL example:
	A aPrime = new Aprime(); // although aPrime declared only as type A,
	aPrime.m();              // we should determine that it's of type A'
	                         // and statically call A'.m(), not A.m()
	B b = null;

	if ( aPrime.t() )        // and here statically call A'.t(), not A.t()
	    b = new Bprime();
	else
	    b = new BdoublePrime();

	                // INTERPROCEDURAL example:
	b.n( aPrime );  // b.n() should statically call A'.m() (see B.java)

	b.m(); // can't statically determine b's class because of branch,
	       // so we should not generate any static function call
    }
}

