From 85040b29ee52a538d96b6bc1cf315de2fec17145 Mon Sep 17 00:00:00 2001 From: jjenista Date: Mon, 28 Jun 2010 18:44:51 +0000 Subject: [PATCH] forgot to add moved files back in, injecting stall site taints now also --- Robust/src/Analysis/Disjoint/Canonical.java | 2 +- .../Analysis/Disjoint/DisjointAnalysis.java | 105 ++++++--- .../Analysis/Disjoint/EffectsAnalysis.java | 3 +- Robust/src/Analysis/Disjoint/ReachGraph.java | 64 +++-- .../OoOJava/RBlockRelationAnalysis.java | 200 ++++++++++++++++ .../OoOJava/RBlockStatusAnalysis.java | 222 ++++++++++++++++++ Robust/src/Tests/disjoint/taintTest1/makefile | 2 +- .../src/Tests/disjoint/taintTest1/test.java | 17 +- 8 files changed, 551 insertions(+), 64 deletions(-) create mode 100644 Robust/src/Analysis/OoOJava/RBlockRelationAnalysis.java create mode 100644 Robust/src/Analysis/OoOJava/RBlockStatusAnalysis.java diff --git a/Robust/src/Analysis/Disjoint/Canonical.java b/Robust/src/Analysis/Disjoint/Canonical.java index d171bb69..dd97fd48 100644 --- a/Robust/src/Analysis/Disjoint/Canonical.java +++ b/Robust/src/Analysis/Disjoint/Canonical.java @@ -1432,7 +1432,7 @@ abstract public class Canonical { while( tItr.hasNext() ) { Taint t = tItr.next(); - if( !t.getSESE().equals( sese ) ) { + if( t.getSESE() == null || !t.getSESE().equals( sese ) ) { out.taints.add( t ); } } diff --git a/Robust/src/Analysis/Disjoint/DisjointAnalysis.java b/Robust/src/Analysis/Disjoint/DisjointAnalysis.java index 1fc3ba7f..a9e61789 100644 --- a/Robust/src/Analysis/Disjoint/DisjointAnalysis.java +++ b/Robust/src/Analysis/Disjoint/DisjointAnalysis.java @@ -1070,14 +1070,14 @@ public class DisjointAnalysis { rhs = fon.getLeft(); rg.assignTempXEqualToTempY( lhs, rhs ); - if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { - if(rblockStatus.isInCriticalRegion(fmContaining, fn)){ - // x gets status of y - if(rg.getAccessibleVar().contains(rhs)){ - rg.addAccessibleVar(lhs); - } - } - } + if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { + if(rblockStatus.isInCriticalRegion(fmContaining, fn)){ + // x gets status of y + if(rg.getAccessibleVar().contains(rhs)){ + rg.addAccessibleVar(lhs); + } + } + } } break; @@ -1098,21 +1098,34 @@ public class DisjointAnalysis { lhs = ffn.getDst(); rhs = ffn.getSrc(); fld = ffn.getField(); - if( shouldAnalysisTrack( fld.getType() ) ) { - rg.assignTempXEqualToTempYFieldF( lhs, rhs, fld ); + if( shouldAnalysisTrack( fld.getType() ) ) { + + // before graph transform, possible inject + // a stall-site taint if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { - effectsAnalysis.analyzeFlatFieldNode( rg, rhs, fld ); - + if(rblockStatus.isInCriticalRegion(fmContaining, fn)){ // x=y.f, stall y if not accessible // contributes read effects on stall site of y + if(!rg.isAccessible(rhs)) { + rg.taintStallSite(fn, rhs); + } + // after this, x and y are accessbile. - rg.addAccessibleVar(lhs); rg.addAccessibleVar(rhs); } } + + // transfer func + rg.assignTempXEqualToTempYFieldF( lhs, rhs, fld ); + + // after transfer, use updated graph to + // do effects analysis + if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { + effectsAnalysis.analyzeFlatFieldNode( rg, rhs, fld ); + } } break; @@ -1123,20 +1136,34 @@ public class DisjointAnalysis { rhs = fsfn.getSrc(); if( shouldAnalysisTrack( fld.getType() ) ) { - boolean strongUpdate = rg.assignTempXFieldFEqualToTempY( lhs, fld, rhs ); + // before transfer func, possibly inject + // stall-site taints if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { - effectsAnalysis.analyzeFlatSetFieldNode( rg, lhs, fld, strongUpdate ); - + if(rblockStatus.isInCriticalRegion(fmContaining, fn)){ // x.y=f , stall x and y if they are not accessible // also contribute write effects on stall site of x - + if(!rg.isAccessible(lhs)) { + rg.taintStallSite(fn, lhs); + } + + if(!rg.isAccessible(rhs)) { + rg.taintStallSite(fn, rhs); + } + // accessible status update rg.addAccessibleVar(lhs); rg.addAccessibleVar(rhs); } - + } + + // transfer func + boolean strongUpdate = rg.assignTempXFieldFEqualToTempY( lhs, fld, rhs ); + + // use transformed graph to do effects analysis + if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { + effectsAnalysis.analyzeFlatSetFieldNode( rg, lhs, fld, strongUpdate ); } } break; @@ -1153,20 +1180,29 @@ public class DisjointAnalysis { TypeDescriptor tdElement = rhs.getType().dereference(); FieldDescriptor fdElement = getArrayField( tdElement ); - rg.assignTempXEqualToTempYFieldF( lhs, rhs, fdElement ); - + // before transfer func, possibly inject + // stall-site taint if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { - effectsAnalysis.analyzeFlatFieldNode( rg, rhs, fdElement ); if(rblockStatus.isInCriticalRegion(fmContaining, fn)){ // x=y.f, stall y if not accessible // contributes read effects on stall site of y // after this, x and y are accessbile. - + if(!rg.isAccessible(rhs)) { + rg.taintStallSite(fn, rhs); + } + rg.addAccessibleVar(lhs); rg.addAccessibleVar(rhs); } - + } + + // transfer func + rg.assignTempXEqualToTempYFieldF( lhs, rhs, fdElement ); + + // use transformed graph to do effects analysis + if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { + effectsAnalysis.analyzeFlatFieldNode( rg, rhs, fdElement ); } } break; @@ -1189,21 +1225,34 @@ public class DisjointAnalysis { TypeDescriptor tdElement = lhs.getType().dereference(); FieldDescriptor fdElement = getArrayField( tdElement ); - rg.assignTempXFieldFEqualToTempY( lhs, fdElement, rhs ); - + // before transfer func, possibly inject + // stall-site taints if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { - effectsAnalysis.analyzeFlatSetFieldNode( rg, lhs, fdElement, - false ); if(rblockStatus.isInCriticalRegion(fmContaining, fn)){ // x.y=f , stall x and y if they are not accessible // also contribute write effects on stall site of x + if(!rg.isAccessible(lhs)) { + rg.taintStallSite(fn, lhs); + } + + if(!rg.isAccessible(rhs)) { + rg.taintStallSite(fn, rhs); + } // accessible status update rg.addAccessibleVar(lhs); rg.addAccessibleVar(rhs); } - + } + + // transfer func + rg.assignTempXFieldFEqualToTempY( lhs, fdElement, rhs ); + + // use transformed graph to do effects analysis + if( doEffectsAnalysis && fmContaining != fmAnalysisEntry ) { + effectsAnalysis.analyzeFlatSetFieldNode( rg, lhs, fdElement, + false ); } } break; diff --git a/Robust/src/Analysis/Disjoint/EffectsAnalysis.java b/Robust/src/Analysis/Disjoint/EffectsAnalysis.java index 9603b22e..4d3b851e 100644 --- a/Robust/src/Analysis/Disjoint/EffectsAnalysis.java +++ b/Robust/src/Analysis/Disjoint/EffectsAnalysis.java @@ -50,7 +50,8 @@ public class EffectsAnalysis { protected void add(Taint t, Effect e) { - if( t.getSESE().getIsCallerSESEplaceholder() ) { + if( t.getSESE() != null && + t.getSESE().getIsCallerSESEplaceholder() ) { return; } diff --git a/Robust/src/Analysis/Disjoint/ReachGraph.java b/Robust/src/Analysis/Disjoint/ReachGraph.java index 5e0e217e..824e6b3f 100644 --- a/Robust/src/Analysis/Disjoint/ReachGraph.java +++ b/Robust/src/Analysis/Disjoint/ReachGraph.java @@ -1288,41 +1288,49 @@ public class ReachGraph { Iterator isvItr = sese.getInVarSet().iterator(); while( isvItr.hasNext() ) { TempDescriptor isv = isvItr.next(); - VariableNode vn = getVariableNodeFromTemp( isv ); - - Iterator reItr = vn.iteratorToReferencees(); - while( reItr.hasNext() ) { - RefEdge re = reItr.next(); - - // these in-set taints should have empty - // predicates so they never propagate - // out to callers - Taint t = Taint.factory( sese, - null, - isv, - re.getDst().getAllocSite(), - ExistPredSet.factory() - ); - re.setTaints( Canonical.add( re.getTaints(), - t - ) - ); - } + // in-set var taints should NOT propagate back into callers + // so give it FALSE(EMPTY) predicates + taintTemp( sese, + null, + isv, + predsEmpty + ); } } - // this is useful for more general tainting - public void taintTemp( Taint taint, - TempDescriptor td, - ExistPredSet preds - ) { + public void taintStallSite( FlatNode stallSite, + TempDescriptor var ) { - VariableNode vn = getVariableNodeFromTemp( td ); + System.out.println( "Tainting stall site: "+stallSite+" and "+var ); + + // stall site taint should propagate back into callers + // so give it TRUE predicates + taintTemp( null, + stallSite, + var, + predsTrue + ); + } + + protected void taintTemp( FlatSESEEnterNode sese, + FlatNode stallSite, + TempDescriptor var, + ExistPredSet preds + ) { + + VariableNode vn = getVariableNodeFromTemp( var ); Iterator reItr = vn.iteratorToReferencees(); while( reItr.hasNext() ) { RefEdge re = reItr.next(); + + Taint taint = Taint.factory( sese, + stallSite, + var, + re.getDst().getAllocSite(), + preds + ); re.setTaints( Canonical.add( re.getTaints(), taint @@ -4958,6 +4966,10 @@ public class ReachGraph { return accessibleVars; } + public boolean isAccessible(TempDescriptor td) { + return accessibleVars.contains(td); + } + public void clearAccessibleVarSet(){ accessibleVars.clear(); } diff --git a/Robust/src/Analysis/OoOJava/RBlockRelationAnalysis.java b/Robust/src/Analysis/OoOJava/RBlockRelationAnalysis.java new file mode 100644 index 00000000..f47d0cf1 --- /dev/null +++ b/Robust/src/Analysis/OoOJava/RBlockRelationAnalysis.java @@ -0,0 +1,200 @@ +package Analysis.OoOJava; + +import IR.State; +import IR.TypeUtil; +import Analysis.CallGraph.CallGraph; +import IR.MethodDescriptor; +import IR.Flat.*; +import java.util.*; + +// This analysis computes relations between rblocks +// and identifies important rblocks. + +public class RBlockRelationAnalysis { + + // compiler data + State state; + TypeUtil typeUtil; + CallGraph callGraph; + + // an implicit SESE is automatically spliced into + // the IR graph around the C main before this analysis--it + // is nothing special except that we can make assumptions + // about it, such as the whole program ends when it ends + protected FlatSESEEnterNode mainSESE; + + // SESEs that are the root of an SESE tree belong to this + // set--the main SESE is always a root, statically SESEs + // inside methods are a root because we don't know how they + // will fit into the runtime tree of SESEs + protected Set rootSESEs; + + // simply a set of every reachable SESE in the program, not + // including caller placeholder SESEs + protected Set allSESEs; + + // per method-per node-rblock stacks + protected Hashtable< FlatMethod, + Hashtable< FlatNode, + Stack + > + > fm2relmap; + + + public RBlockRelationAnalysis( State state, + TypeUtil typeUtil, + CallGraph callGraph ) { + this.state = state; + this.typeUtil = typeUtil; + this.callGraph = callGraph; + + rootSESEs = new HashSet(); + allSESEs = new HashSet(); + + fm2relmap = + new Hashtable< FlatMethod, Hashtable< FlatNode, Stack > >(); + + + MethodDescriptor mdSourceEntry = typeUtil.getMain(); + FlatMethod fmMain = state.getMethodFlat( mdSourceEntry ); + + mainSESE = (FlatSESEEnterNode) fmMain.getNext( 0 ); + mainSESE.setfmEnclosing( fmMain ); + mainSESE.setmdEnclosing( fmMain.getMethod() ); + mainSESE.setcdEnclosing( fmMain.getMethod().getClassDesc() ); + + // add all methods transitively reachable from the + // source's main to set for analysis + Set descriptorsToAnalyze = + callGraph.getAllMethods( mdSourceEntry ); + + descriptorsToAnalyze.add( mdSourceEntry ); + + analyzeMethods( descriptorsToAnalyze ); + } + + + public FlatSESEEnterNode getMainSESE() { + return mainSESE; + } + + public Set getRootSESEs() { + return rootSESEs; + } + + public Set getAllSESEs() { + return allSESEs; + } + + public Stack getRBlockStacks( FlatMethod fm, + FlatNode fn ) { + if( !fm2relmap.containsKey( fm ) ) { + fm2relmap.put( fm, computeRBlockRelations( fm ) ); + } + return fm2relmap.get( fm ).get( fn ); + } + + + protected void analyzeMethods( Set descriptorsToAnalyze ) { + + Iterator mdItr = descriptorsToAnalyze.iterator(); + while( mdItr.hasNext() ) { + FlatMethod fm = state.getMethodFlat( mdItr.next() ); + + Hashtable< FlatNode, Stack > relmap = + computeRBlockRelations( fm ); + + fm2relmap.put( fm, relmap ); + } + } + + public Hashtable< FlatNode, Stack > + computeRBlockRelations( FlatMethod fm ) { + + Hashtable< FlatNode, Stack > seseStacks = + new Hashtable< FlatNode, Stack >(); + + // start from flat method top, visit every node in + // method exactly once, find SESE stack on every + // control path + Set flatNodesToVisit = new HashSet(); + flatNodesToVisit.add( fm ); + + Set visited = new HashSet(); + + Stack seseStackFirst = new Stack(); + seseStacks.put( fm, seseStackFirst ); + + while( !flatNodesToVisit.isEmpty() ) { + Iterator fnItr = flatNodesToVisit.iterator(); + FlatNode fn = fnItr.next(); + + Stack seseStack = seseStacks.get( fn ); + assert seseStack != null; + + flatNodesToVisit.remove( fn ); + visited.add( fn ); + + nodeActions( fn, seseStack, fm ); + + for( int i = 0; i < fn.numNext(); i++ ) { + FlatNode nn = fn.getNext( i ); + + if( !visited.contains( nn ) ) { + flatNodesToVisit.add( nn ); + + // clone stack and send along each control path + seseStacks.put( nn, (Stack)seseStack.clone() ); + } + } + } + + return seseStacks; + } + + protected void nodeActions( FlatNode fn, + Stack seseStack, + FlatMethod fm ) { + switch( fn.kind() ) { + + case FKind.FlatSESEEnterNode: { + FlatSESEEnterNode fsen = (FlatSESEEnterNode) fn; + + if( !fsen.getIsCallerSESEplaceholder() ) { + allSESEs.add( fsen ); + } + + fsen.setfmEnclosing( fm ); + fsen.setmdEnclosing( fm.getMethod() ); + fsen.setcdEnclosing( fm.getMethod().getClassDesc() ); + + if( seseStack.empty() ) { + rootSESEs.add( fsen ); + fsen.setParent( null ); + } else { + seseStack.peek().addChild( fsen ); + fsen.setParent( seseStack.peek() ); + } + + seseStack.push( fsen ); + } break; + + case FKind.FlatSESEExitNode: { + FlatSESEExitNode fsexn = (FlatSESEExitNode) fn; + assert !seseStack.empty(); + FlatSESEEnterNode fsen = seseStack.pop(); + } break; + + case FKind.FlatReturnNode: { + FlatReturnNode frn = (FlatReturnNode) fn; + if( !seseStack.empty() && + !seseStack.peek().getIsCallerSESEplaceholder() + ) { + throw new Error( "Error: return statement enclosed within SESE "+ + seseStack.peek().getPrettyIdentifier() ); + } + } break; + + } + } +} diff --git a/Robust/src/Analysis/OoOJava/RBlockStatusAnalysis.java b/Robust/src/Analysis/OoOJava/RBlockStatusAnalysis.java new file mode 100644 index 00000000..35693823 --- /dev/null +++ b/Robust/src/Analysis/OoOJava/RBlockStatusAnalysis.java @@ -0,0 +1,222 @@ +package Analysis.OoOJava; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; +import java.util.Stack; +import java.util.Map.Entry; + +import Analysis.CallGraph.CallGraph; +import IR.MethodDescriptor; +import IR.State; +import IR.TypeUtil; +import IR.Flat.FKind; +import IR.Flat.FlatMethod; +import IR.Flat.FlatNode; +import IR.Flat.FlatSESEEnterNode; +import IR.Flat.FlatSESEExitNode; + +public class RBlockStatusAnalysis { + + // compiler data + State state; + TypeUtil typeUtil; + CallGraph callGraph; + RBlockRelationAnalysis rra; + + // per method-per node-rblock stacks + protected Hashtable>> fm2statusmap; + + public RBlockStatusAnalysis(State state, TypeUtil typeUtil, CallGraph callGraph, + RBlockRelationAnalysis rra) { + this.state = state; + this.typeUtil = typeUtil; + this.callGraph = callGraph; + this.rra = rra; + + fm2statusmap = + new Hashtable>>(); + + MethodDescriptor mdSourceEntry = typeUtil.getMain(); + FlatMethod fmMain = state.getMethodFlat(mdSourceEntry); + + // add all methods transitively reachable from the + // source's main to set for analysis + Set descriptorsToAnalyze = callGraph.getAllMethods(mdSourceEntry); + + descriptorsToAnalyze.add(mdSourceEntry); + + analyzeMethods(descriptorsToAnalyze); + + // analyzeMethodsDebug(descriptorsToAnalyze); + } + + protected void analyzeMethods(Set descriptorsToAnalyze) { + + Iterator mdItr = descriptorsToAnalyze.iterator(); + while (mdItr.hasNext()) { + FlatMethod fm = state.getMethodFlat(mdItr.next()); + + Hashtable> fn2seseStatus = + computeRBlockStatus(fm); + + fm2statusmap.put(fm, fn2seseStatus); + } + } + + public Hashtable> computeRBlockStatus( + FlatMethod fm) { + + Hashtable> seseStacks = + new Hashtable>(); + + Hashtable> fn2seseStatus = + new Hashtable>(); + + LinkedList flatNodesToVisit = new LinkedList(); + flatNodesToVisit.add(fm); + + Stack seseStackFirst = new Stack(); + seseStacks.put(fm, seseStackFirst); + + while (!flatNodesToVisit.isEmpty()) { + Iterator fnItr = flatNodesToVisit.iterator(); + FlatNode fn = fnItr.next(); + + Hashtable prevResult = fn2seseStatus.get(fn); + + Hashtable currentResult = + new Hashtable(); + + for (int i = 0; i < fn.numPrev(); i++) { + FlatNode prevFlatNode = fn.getPrev(i); + Hashtable incoming = fn2seseStatus.get(prevFlatNode); + if (incoming != null) { + merge(currentResult, incoming); + } + } + + flatNodesToVisit.remove(fn); + + nodeActions(fn, fm, currentResult); + + // if we have a new result, schedule forward nodes for + // analysis + if (prevResult == null || !currentResult.equals(prevResult)) { + fn2seseStatus.put(fn, currentResult); + for (int i = 0; i < fn.numNext(); i++) { + FlatNode nn = fn.getNext(i); + flatNodesToVisit.addFirst(nn); + } + } + } + + return fn2seseStatus; + } + + private void merge(Hashtable current, + Hashtable incoming) { + + Iterator inIter = incoming.entrySet().iterator(); + while (inIter.hasNext()) { + Entry inEntry = (Entry) inIter.next(); + FlatSESEEnterNode seseContaining = (FlatSESEEnterNode) inEntry.getKey(); + Boolean isAfter = (Boolean) inEntry.getValue(); + + Boolean currentIsAfter = current.get(seseContaining); + if (currentIsAfter == null || currentIsAfter == Boolean.FALSE) { + current.put(seseContaining, isAfter); + } + } + + } + + public boolean isInCriticalRegion(FlatMethod fmContaining, FlatNode fn) { + FlatSESEEnterNode seseContaining = rra.getRBlockStacks(fmContaining, fn).peek(); + Hashtable> statusMap = + fm2statusmap.get(fmContaining); + Hashtable status = statusMap.get(fn); + + if(status.get(seseContaining).booleanValue()==true){ + System.out.println(fn+" is in the critical region in according to "+seseContaining); + } + + return status.get(seseContaining).booleanValue(); + } + + protected void nodeActions(FlatNode fn, FlatMethod fm, + Hashtable status) { + switch (fn.kind()) { + + case FKind.FlatSESEExitNode: { + FlatSESEExitNode fsexn = (FlatSESEExitNode) fn; + FlatSESEEnterNode fsen = fsexn.getFlatEnter(); + if (fsen.getParent() != null) { + status.put(fsen.getParent(), Boolean.TRUE); + } + } + break; + + default: { + if (!(fn instanceof FlatMethod)) { + Stack seseStack = rra.getRBlockStacks(fm, fn); + if (!seseStack.isEmpty()) { + FlatSESEEnterNode currentParent = seseStack.peek(); + if (!status.containsKey(currentParent)) { + status.put(currentParent, Boolean.FALSE); + } + } + } + + } + break; + } + + } + + /* + * DEBUG + */ + protected void analyzeMethodsDebug(Set descriptorsToAnalyze) { + + Iterator mdItr = descriptorsToAnalyze.iterator(); + while (mdItr.hasNext()) { + FlatMethod fm = state.getMethodFlat(mdItr.next()); + printStatusMap(fm); + + } + } + + protected void printStatusMap(FlatMethod fm) { + + Set flatNodesToVisit = new HashSet(); + flatNodesToVisit.add(fm); + + Set visited = new HashSet(); + + while (!flatNodesToVisit.isEmpty()) { + Iterator fnItr = flatNodesToVisit.iterator(); + FlatNode fn = fnItr.next(); + + flatNodesToVisit.remove(fn); + visited.add(fn); + + System.out.println("------------------"); + System.out.println("fn=" + fn); + System.out.println(fm2statusmap.get(fm).get(fn)); + + for (int i = 0; i < fn.numNext(); i++) { + FlatNode nn = fn.getNext(i); + + if (!visited.contains(nn)) { + flatNodesToVisit.add(nn); + + } + } + } + + } + +} diff --git a/Robust/src/Tests/disjoint/taintTest1/makefile b/Robust/src/Tests/disjoint/taintTest1/makefile index ec24f5e6..e3736144 100644 --- a/Robust/src/Tests/disjoint/taintTest1/makefile +++ b/Robust/src/Tests/disjoint/taintTest1/makefile @@ -5,7 +5,7 @@ SOURCE_FILES=$(PROGRAM).java BUILDSCRIPT=~/research/Robust/src/buildscript BSFLAGS= -mainclass Test -justanalyze -ooojava -disjoint -disjoint-k 1 -enable-assertions -DEBUGFLAGS= #-disjoint-write-dots final -disjoint-write-initial-contexts -disjoint-write-ihms -disjoint-debug-snap-method main 0 10 true +DEBUGFLAGS= -disjoint-write-dots final -disjoint-write-initial-contexts -disjoint-write-ihms -disjoint-debug-snap-method main 0 10 true all: $(PROGRAM).bin diff --git a/Robust/src/Tests/disjoint/taintTest1/test.java b/Robust/src/Tests/disjoint/taintTest1/test.java index f573d419..7c02d6b9 100644 --- a/Robust/src/Tests/disjoint/taintTest1/test.java +++ b/Robust/src/Tests/disjoint/taintTest1/test.java @@ -11,15 +11,18 @@ public class Test { Foo a = new Foo(); Foo b = new Foo(); - /* rblock r1 { - a.f = new Foo(); - } - */ - - rblock r1 { - doSomething( a, b ); + + rblock c1 { + a.f = new Foo(); + } + + Foo x = a.f; + + x.g = new Foo(); } + + } static void doSomething( Foo a, Foo b ) { -- 2.34.1