compile.sh0000775000176100017620000000016413120354112011233 0ustar yueyueif [ ! -d "build" ]; then mkdir build fi javac $(find src -name "*.java") -d build jar -cvf mahjong.jar -C build . doop-mahjong.patch0000664000176100017620000007737213120354033012670 0ustar yueyuediff -urN doop-r160113-bin/logic/3-object-sensitive+2-heap/analysis.logic doop-mahjong/logic/3-object-sensitive+2-heap/analysis.logic --- doop-r160113-bin/logic/3-object-sensitive+2-heap/analysis.logic 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/logic/3-object-sensitive+2-heap/analysis.logic 2017-06-15 10:41:11.273935834 +1000 @@ -0,0 +1,105 @@ +/** + * @author Tian Tan + * @author Yue Li + */ + +#include "macros.logic" +#include "../context-sensitive.logic" +#include "../library.logic" + +// In this analysis, both the real context and the real heap context +// are triplets of HeapAllocationRefs. Keeping mapping +// functions is the way to handle analyses where HContext = Context +// (since the language considers them different types). +ContextFromRealContext[?heap1, ?heap2, ?heap3] = ?ctx -> + Context(?ctx), HeapAllocationRef(?heap1), HeapAllocationRef(?heap2), HeapAllocationRef(?heap3). +lang:skolem(`ContextFromRealContext). +RealContext1FromContext[?ctx] = ?heap -> + Context(?ctx), HeapAllocationRef(?heap). +RealContext2FromContext[?ctx] = ?heap -> + Context(?ctx), HeapAllocationRef(?heap). +RealContext3FromContext[?ctx] = ?heap -> + Context(?ctx), HeapAllocationRef(?heap). + +RealContext1FromContext[?ctx] = ?heap1, +RealContext2FromContext[?ctx] = ?heap2, +RealContext3FromContext[?ctx] = ?heap3 <- + ContextFromRealContext[?heap1, ?heap2, ?heap3] = ?ctx. + +HContextFromRealHContext[?heap1, ?heap2] = ?hctx -> + HContext(?hctx), HeapAllocationRef(?heap1), HeapAllocationRef(?heap2). +lang:skolem(`HContextFromRealHContext). + +RealHContext1FromHContext[?hctx] = ?heap -> + HContext(?hctx), HeapAllocationRef(?heap). +RealHContext2FromHContext[?hctx] = ?heap -> + HContext(?hctx), HeapAllocationRef(?heap). + +RealHContext1FromHContext[?hctx] = ?heap1, +RealHContext2FromHContext[?hctx] = ?heap2<- + HContextFromRealHContext[?heap1, ?heap2] = ?hctx. + +// Creating special immutable heap allocation constant +ImmutableHeapValue[] = ?immHeap <- + HeapAllocationValue(?immHeap, "<>"). + + +// Create initial objects with their heapcontexts. +HContextFromRealHContext[?heap1, ?heap2] = ?hctx, +HContext(?hctx), +SystemThreadGroup(?hctx, ?alloc) <- + MySystemThreadGroup(?heap1, ?heap2, ?alloc). + +HContextFromRealHContext[?heap1, ?heap2] = ?hctx, +HContext(?hctx), +MainThreadGroup(?hctx, ?alloc) <- + MyMainThreadGroup(?heap1, ?heap2, ?alloc). + +HContextFromRealHContext[?heap1, ?heap2] = ?hctx, +HContext(?hctx), +MainThread(?hctx, ?alloc) <- + MyMainThread(?heap1, ?heap2, ?alloc). + +/** + * Merge optimization hack + */ + +// For this analysis, we only need two of the parameters that may +// influence the new context object. +MyMergeBasis(?hctx, ?heap) <- + MergeBasis(_, _, ?hctx, ?heap). + +// We create new context objects sparingly, because of the high cost. +// We also cache them, so they can be looked up without a join. +Context(?calleeCtx), +ContextFromRealContext[RealHContext1FromHContext[?hctx], + RealHContext2FromHContext[?hctx], + ?heap] = ?calleeCtx, +OptimizeMerge[?hctx, ?heap] = ?calleeCtx <- + MyMergeBasis(?hctx, ?heap). + + +/** + * Reachable + */ +ReachableContext(?ctx, ?method), +ContextFromRealContext[?initheap, ?initheap, ?initheap] = ?ctx, +Context(?ctx) + <- + MainMethodDeclaration(?method), + HeapAllocationValue(?initheap, "<>"). + +ReachableContext(?ctx, ?method), +ContextFromRealContext[?startupheap, ?startupheap, ?startupheap] = ?ctx, +Context(?ctx) + <- + ImplicitReachable(?method), + HeapAllocationValue(?startupheap, "<>"). + +ReachableContext(?ctx, ?clinit), +ContextFromRealContext[?clinitheap, ?clinitheap, ?clinitheap] = ?ctx, +Context(?ctx) + <- + InitializedClass(?class), + ClassInitializer[?class] = ?clinit, + HeapAllocationValue(?clinitheap, "<>"). diff -urN doop-r160113-bin/logic/3-object-sensitive+2-heap/declarations.logic doop-mahjong/logic/3-object-sensitive+2-heap/declarations.logic --- doop-r160113-bin/logic/3-object-sensitive+2-heap/declarations.logic 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/logic/3-object-sensitive+2-heap/declarations.logic 2017-06-15 10:41:11.357936251 +1000 @@ -0,0 +1,16 @@ +#include "macros.logic" +#include "../context-sensitive-declarations.logic" + +// For this analysis, contexts are triplets of heap objects, so this is +// what the initial system objects should have. +MySystemThreadGroup(?heap1, ?heap2, ?alloc) -> + HeapAllocationRef(?heap1), HeapAllocationRef(?heap2), + HeapAllocationRef(?alloc). + +MyMainThreadGroup(?heap1, ?heap2, ?alloc) -> + HeapAllocationRef(?heap1), HeapAllocationRef(?heap2), + HeapAllocationRef(?alloc). + +MyMainThread(?heap1, ?heap2, ?alloc) -> + HeapAllocationRef(?heap1), HeapAllocationRef(?heap2), + HeapAllocationRef(?alloc). diff -urN doop-r160113-bin/logic/3-object-sensitive+2-heap/delta.logic doop-mahjong/logic/3-object-sensitive+2-heap/delta.logic --- doop-r160113-bin/logic/3-object-sensitive+2-heap/delta.logic 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/logic/3-object-sensitive+2-heap/delta.logic 2017-06-15 10:41:11.421936568 +1000 @@ -0,0 +1,29 @@ +#include "macros.logic" +#include "../library/common-delta.logic" + +/** + * Special calling contexts + * + * Note: the type is necessary (java.lang.String), but never used. It + * could be anything. It also needs to be an existing type, otherwise + * the sanity checks will barf. + */ +PlusHeapAllocationRef(?heap, "<>", "java.lang.String"). +PlusHeapAllocationRef(?heap, "<>", "java.lang.String"). +PlusHeapAllocationRef(?heap, "<>", "java.lang.String"). +PlusHeapAllocationRef(?heap, "<>", "java.lang.String"). + +/** + * Special objects + */ ++MySystemThreadGroup(?heap, ?heap, ?alloc), + PlusHeapAllocationRef(?heap, "<>", "java.lang.String"), + PlusHeapAllocationRef(?alloc, "<>", "java.lang.ThreadGroup"). + ++MyMainThreadGroup(?heap, ?heap, ?alloc), + PlusHeapAllocationRef(?heap, "<>", "java.lang.String"), + PlusHeapAllocationRef(?alloc, "<>", "java.lang.ThreadGroup"). + ++MyMainThread(?heap, ?heap, ?alloc), + PlusHeapAllocationRef(?heap, "<>", "java.lang.String"), + PlusHeapAllocationRef(?alloc, "<>", "java.lang.Thread"). diff -urN doop-r160113-bin/logic/3-object-sensitive+2-heap/macros.logic doop-mahjong/logic/3-object-sensitive+2-heap/macros.logic --- doop-r160113-bin/logic/3-object-sensitive+2-heap/macros.logic 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/logic/3-object-sensitive+2-heap/macros.logic 2017-06-15 10:41:11.525937084 +1000 @@ -0,0 +1,69 @@ +#include "../library/fact-macros.logic" + +// For this analysis, every heap context recorded on allocation +// corresponds to the calling context of the allocator method. +// Subtle point: this may need to be created because even though it +// exists as a Context it may not exist as an HContext. +#define RecordMacro(ctx, heap, hctx) \ + HContext(hctx), \ + HContextFromRealHContext[RealContext2FromContext[ctx], \ + RealContext3FromContext[ctx]] = hctx + +#define RecordImmutableMacro(ctx, heap, immCtx) \ + HContext(immCtx), \ + HContextFromRealHContext[ImmutableHeapValue[], ImmutableHeapValue[]] = immCtx + + +// For this analysis the context of a method call corresponds to the +// identity of the receiver object, that of the receiver object of +// the caller and so on. Again, this may trigger creation of +// a new object. +#define MergeMacro(callerCtx, invocation, hctx, heap, calleeCtx) \ + Context(calleeCtx), \ + ContextFromRealContext[RealHContext1FromHContext[hctx], \ + RealHContext2FromHContext[hctx], \ + heap] = calleeCtx + +#define MergeBasisMacro(callerCtx, invocation, hctx, heap) \ + MergeBasis(callerCtx, invocation, hctx, heap) + +#define OptimizeMergeMacro(callerCtx, invocation, hctx, heap, calleeCtx) \ + OptimizeMerge[hctx, heap] = calleeCtx + +// For this analysis, static calls just keep the same context as the +// caller. *Not* creating a new one, but pretending to, nonetheless, +// so the engine doesn't barf. +#define MergeStaticMacro(callerCtx, invocation, calleeCtx) \ + Context(calleeCtx), \ + ContextFromRealContext[RealContext1FromContext[callerCtx], \ + RealContext2FromContext[callerCtx], \ + RealContext3FromContext[callerCtx]] = calleeCtx + +// MergeThreadStart, MergeStartup, and MergeFinalizerRegisterContext +// have the same logic as plain Merge for this analysis. +#define MergeThreadStartMacro(hctx, heap, callerCtx, newCtx) \ + Context(newCtx), \ + ContextFromRealContext[RealHContext1FromHContext[hctx], \ + RealHContext2FromHContext[hctx], \ + heap] = newCtx + +#define MergeStartupMacro(hctx, heap, calleeCtx) \ + Context(calleeCtx), \ + ContextFromRealContext[RealHContext1FromHContext[hctx], \ + RealHContext2FromHContext[hctx], \ + heap] = calleeCtx + +// This is exactly equivalent to the regular merge logic, but written +// differently. At finalization, we create a new hctx, based on the +// callerCtx, and then use this new hctx as we would in regular Merge. +// The macro below does this, without referring to the new hctx (i.e., +// using knowledge of how it is created). This is necessary because since +// the new hctx is created in the same rule, it will not yet have values +// for its inverse functions (RealHContextFromHContext), so the rule will never +// fire if done naively. The signature of the macro (which does not accept a +// hctx) is a hint for avoiding this problem. +#define MergeFinalizerRegisterContextMacro(callerCtx, inmethod, heap, newCtx) \ + Context(newCtx), \ + ContextFromRealContext[RealContext2FromContext[callerCtx], \ + RealContext3FromContext[callerCtx], \ + heap] = newCtx diff -urN doop-r160113-bin/logic/library/fact-declarations.logic doop-mahjong/logic/library/fact-declarations.logic --- doop-r160113-bin/logic/library/fact-declarations.logic 2013-01-16 07:31:46.000000000 +1100 +++ doop-mahjong/logic/library/fact-declarations.logic 2017-06-15 10:41:15.545957018 +1000 @@ -489,3 +489,33 @@ MethodInvocationRef(?invocation), VarRef(?to), VarRef(?field). + + +MahjongHeapAbstraction[?heap] = ?mergedHeap -> + HeapAllocationRef(?heap), + HeapAllocationRef(?mergedHeap). + + +ClassForName:Log(?invocation, ?type) -> + MethodInvocationRef(?invocation), + Type(?type). + +ClassNewInstance:Log(?invocation, ?type) -> + MethodInvocationRef(?invocation), + Type(?type). + +ConstructorNewInstance:Log(?invocation, ?constructor) -> + MethodInvocationRef(?invocation), + MethodSignatureRef(?constructor). + +MethodInvoke:Log(?invocation, ?method) -> + MethodInvocationRef(?invocation), + MethodSignatureRef(?method). + +FieldGet:Log(?invocation, ?field) -> + MethodInvocationRef(?invocation), + FieldSignatureRef(?field). + +FieldSet:Log(?invocation, ?field) -> + MethodInvocationRef(?invocation), + FieldSignatureRef(?field). diff -urN doop-r160113-bin/logic/library/mahjong.logic doop-mahjong/logic/library/mahjong.logic --- doop-r160113-bin/logic/library/mahjong.logic 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/logic/library/mahjong.logic 2017-06-15 10:41:15.937958962 +1000 @@ -0,0 +1,17 @@ +/** + * Mahjong heap abstraction. + * + * @author Tian Tan + * @author Yue Li + */ + +HeapAllocation:Merge[?heap] = ?mergedHeap <- + ObjectToBeMerged(?heap), + MahjongHeapAbstraction[?heap] = ?mergedHeap. + +ObjectToBeMerged(?heap) <- + MahjongHeapAbstraction[?heap] = ?mergedHeap, + NumberOfObjectsMergedTo[?mergedHeap] > 1. + +NumberOfObjectsMergedTo[?mergedHeap] = n <- + agg<> MahjongHeapAbstraction[_] = ?mergedHeap. diff -urN doop-r160113-bin/logic/library/reflection.logic doop-mahjong/logic/library/reflection.logic --- doop-r160113-bin/logic/library/reflection.logic 2013-01-16 07:31:46.000000000 +1100 +++ doop-mahjong/logic/library/reflection.logic 2017-06-15 10:41:16.781963147 +1000 @@ -112,6 +112,7 @@ ReachableContext(?ctx, ?inmethod), Config:DynamicClass(?type, ?invocation). +#ifndef ENABLE_REFLECTION_LOG /** * Class.forName invocations with string constant parameters */ @@ -124,6 +125,8 @@ java:lang:Class:forName:ActualParam[?invocation] = ?param, VarPointsTo(_, ?constant, ?ctx, ?param), // recursive ClassNameStringConstant:Type[?constant] = ?type. +#endif // #ifndef ENABLE_REFLECTION_LOG + java:lang:Class:forName:ActualParam[?invocation] = ?param -> MethodInvocationRef(?invocation), @@ -161,6 +164,8 @@ AssignReturnValue[?invocation] = ?to, VirtualMethodInvocation:Base[?invocation] = ?from. + +#ifndef ENABLE_REFLECTION_LOG /** * evaluate */ @@ -184,6 +189,8 @@ ReifiedMethod[?signature] = ?heap, ObjectShouldBeRefined(?heap). #endif +#endif // #ifndef ENABLE_REFLECTION_LOG + /************************************************************* * Class.getConstructors @@ -221,6 +228,7 @@ AssignReturnValue[?invocation] = ?to, VirtualMethodInvocation:Base[?invocation] = ?from. +#ifndef ENABLE_REFLECTION_LOG /** * evaluate */ @@ -265,6 +273,8 @@ ReifiedConstructor[?signature] = ?heap, ObjectShouldBeRefined(?heap). #endif +#endif // #ifndef ENABLE_REFLECTION_LOG + /************************************************************* * Method.invoke @@ -293,10 +303,18 @@ VirtualMethodInvocation:Signature[?invocation] = ?invoke, VirtualMethodInvocation:Base[?invocation] = ?base. +#ifdef ENABLE_REFLECTION_LOG +ReflectiveMethodInvocation(?ctx, ?instruction, ?signature) <- + java:lang:reflect:Method:invoke(?instruction, _), + MethodInvoke:Log(?instruction, ?signature), + VirtualMethodInvocation:In(?instruction, ?inmethod), + ReachableContext(?ctx, ?inmethod). +#else ReflectiveMethodInvocation(?ctx, ?instruction, ?signature) <- java:lang:reflect:Method:invoke(?instruction, ?from), VarPointsTo(_, ?method, ?ctx, ?from), // recursive ReifiedMethod[?signature] = ?method. +#endif ReflectiveBaseVar[?invocation] = ?base <- java:lang:reflect:Method:invoke(?invocation, _), @@ -340,12 +358,22 @@ * *************************************************************/ +#ifdef ENABLE_REFLECTION_LOG +ReflectiveAssignHeapAllocation(?invocation, ?type, ?ctx, ?to), +ReflectiveSpecialMethodInvocation(?ctx, ?invocation, ?signature) <- + java:lang:reflect:Constructor:newInstance(?invocation, ?to, _), + ConstructorNewInstance:Log(?invocation, ?signature), + VirtualMethodInvocation:In(?invocation, ?inmethod), + ReachableContext(?ctx, ?inmethod), + MethodSignature:Type[?signature] = ?type. +#else ReflectiveAssignHeapAllocation(?invocation, ?type, ?ctx, ?to), ReflectiveSpecialMethodInvocation(?ctx, ?invocation, ?signature) <- java:lang:reflect:Constructor:newInstance(?invocation, ?to, ?base), VarPointsTo(_, ?constructor, ?ctx, ?base), // recursive ReifiedConstructor[?signature] = ?constructor, MethodSignature:Type[?signature] = ?type. +#endif ReflectiveBaseVar[?invocation] = ?to <- java:lang:reflect:Constructor:newInstance(?invocation, ?to, _). @@ -403,13 +431,25 @@ MethodDescriptorRef:Value(?descriptor:"void()"), SimpleNameRef:Value(?simplename:""). +#ifdef ENABLE_REFLECTION_LOG +ReflectiveAssignHeapAllocation(?invocation, ?type, ?ctx, ?to), +ReflectiveSpecialMethodInvocation(?ctx, ?invocation, ?constructor) + <- + java:lang:Class:newInstance(?invocation, ?to, _), + ClassNewInstance:Log(?invocation, ?type), + ReifiedClass[?type] = ?class, + VirtualMethodInvocation:In(?invocation, ?inmethod), + ReachableContext(?ctx, ?inmethod), + OptClassToConstructor(?constructor, ?class). +#else ReflectiveAssignHeapAllocation(?invocation, ?type, ?ctx, ?to), ReflectiveSpecialMethodInvocation(?ctx, ?invocation, ?constructor) <- java:lang:Class:newInstance(?invocation, ?to, ?var), VarPointsTo(_, ?class, ?ctx, ?var), - OptClassToConstructor(?constructor, ?class), - MethodSignature:Type[?constructor] = ?type. + MethodSignature:Type[?constructor] = ?type, + OptClassToConstructor(?constructor, ?class). +#endif ReflectiveBaseVar[?invocation] = ?to <- java:lang:Class:newInstance(?invocation, ?to, _). @@ -450,6 +490,7 @@ AssignReturnValue[?invocation] = ?to, VirtualMethodInvocation:Base[?invocation] = ?from. +#ifndef ENABLE_REFLECTION_LOG /** * evaluate */ @@ -472,6 +513,8 @@ ReifiedField[?signature] = ?heap, ObjectShouldBeRefined(?heap). #endif +#endif // #ifndef ENABLE_REFLECTION_LOG + /************************************************************* * Field.get @@ -514,10 +557,18 @@ FieldSignatureRef(?signature), Context(?ctx), VarRef(?to). +#ifdef ENABLE_REFLECTION_LOG +ReflectiveLoadField(?invocation, ?ctx, ?to, ?signature) <- + java:lang:reflect:Field:get(?invocation, ?to, ?field), + FieldGet:Log(?invocation, ?signature), + VarPointsTo(_, _, ?ctx, ?field). +#else ReflectiveLoadField(?invocation, ?ctx, ?to, ?signature) <- java:lang:reflect:Field:get(?invocation, ?to, ?field), VarPointsTo(_, ?fieldHeap, ?ctx, ?field), ReifiedField[?signature] = ?fieldHeap. +#endif + /** * Load of static field. @@ -596,11 +647,19 @@ FieldSignatureRef(?signature), Context(?ctx), VarRef(?var). +#ifdef ENABLE_REFLECTION_LOG +ReflectiveStoreField(?invocation, ?signature, ?ctx, ?from) <- + java:lang:reflect:Field:set(?invocation, ?fieldVar), + FieldSet:Log(?invocation, ?signature), + VarPointsTo(_, _, ?ctx, ?fieldVar), + java:lang:reflect:Field:set:from[?invocation] = ?from. +#else ReflectiveStoreField(?invocation, ?signature, ?ctx, ?from) <- java:lang:reflect:Field:set(?invocation, ?fieldVar), VarPointsTo(_, ?fieldHeap, ?ctx, ?fieldVar), ReifiedField[?signature] = ?fieldHeap, java:lang:reflect:Field:set:from[?invocation] = ?from. +#endif /** * Store of static field. diff -urN doop-r160113-bin/logic/library/reflective.logic doop-mahjong/logic/library/reflective.logic --- doop-r160113-bin/logic/library/reflective.logic 2013-01-16 07:31:46.000000000 +1100 +++ doop-mahjong/logic/library/reflective.logic 2017-06-15 10:41:17.021964337 +1000 @@ -241,7 +241,11 @@ * to initialize classes separate from the VarPointsTo rule. */ InitializedClass(?type) <- +#ifdef ENABLE_REFLECTION_LOG + ClassForName:Log(_, ?type). +#else ReflectiveAssignClassConstant(_, _, ?type). +#endif /** * TODO This doesn't make any sense without a 'to' variable. @@ -263,12 +267,28 @@ Context(?callerCtx), VarRef(?base). /** - * TODO it's unfortunate this code is so similar to normal LoadArrayIndex. + * Original predicate name LoadHeapArrayIndex is replaced by ReflectiveLoadHeapArrayIndex. + * The original implementation causes many argument passing of reflective calls fail. */ -LoadHeapArrayIndex(?calleeCtx, ?to, ?basehctx, ?baseheap) <- +ReflectiveLoadHeapArrayIndex(?calleeCtx, ?to, ?basehctx, ?baseheap) -> + Context(?calleeCtx), + VarRef(?to), + HContext(?basehctx), + HeapAllocationRef(?baseheap). + +ReflectiveLoadHeapArrayIndex(?calleeCtx, ?to, ?basehctx, ?baseheap) <- ReflectiveLoadArrayIndex(?calleeCtx, ?to, ?callerCtx, ?base), VarPointsTo(?basehctx, ?baseheap, ?callerCtx, ?base). +// Use types of objects to filter reflective interprocedural assignments. +// Fix the reflective argument passing bug. +VarPointsTo(?hctx, ?heap, ?ctx, ?to) <- + ReflectiveLoadHeapArrayIndex(?ctx, ?to, ?basehctx, ?baseheap), + ArrayIndexPointsTo(?hctx, ?heap, ?basehctx, ?baseheap), + Var:Type[?to] = ?type, + HeapAllocation:Type[?heap] = ?heaptype, + AssignCompatible(?type, ?heaptype). + /************************************************************* * diff -urN doop-r160113-bin/logic/library/statistics-simple.logic doop-mahjong/logic/library/statistics-simple.logic --- doop-r160113-bin/logic/library/statistics-simple.logic 2013-01-16 07:31:46.000000000 +1100 +++ doop-mahjong/logic/library/statistics-simple.logic 2017-06-15 10:41:17.245965448 +1000 @@ -58,6 +58,25 @@ Stats:Simple:ReachableVirtualMethodInvocation(?from), Stats:Simple:InsensCallGraphEdge(?from, ?to). +Stats:Simple:InsensAllCallGraphEdge(?from, ?to) -> + CallGraphEdgeSourceRef(?from), MethodSignatureRef(?to). +Stats:Simple:InsensAllCallGraphEdge(?from, ?to) <- + Stats:Simple:InsensCallGraphEdge(?from, ?to). +Stats:Simple:InsensAllCallGraphEdge(?from, ?to) <- + InsensReflectiveCallGraphEdge(?from, ?to). + +InsensReflectiveCallGraphEdge(?caller, ?callee) <- + ReflectiveCallGraphEdge(_, ?caller, _, ?callee). + +Stats:Simple:AllCallGraphEdge(?callerctx, ?caller, ?calleectx,?callee) -> + Context(?callerctx), CallGraphEdgeSourceRef(?caller), + Context(?calleectx), MethodSignatureRef(?callee). + +Stats:Simple:AllCallGraphEdge(?callerctx, ?caller, ?calleectx,?callee) <- + CallGraphEdge(?callerctx, ?caller, ?calleectx,?callee). +Stats:Simple:AllCallGraphEdge(?callerctx, ?caller, ?calleectx,?callee) <- + ReflectiveCallGraphEdge(?callerctx, ?caller, ?calleectx,?callee). + /*************************************************** * Application methods ***************************************************/ diff -urN doop-r160113-bin/logic/library/string-constants.logic doop-mahjong/logic/library/string-constants.logic --- doop-r160113-bin/logic/library/string-constants.logic 2013-01-16 07:31:46.000000000 +1100 +++ doop-mahjong/logic/library/string-constants.logic 2017-06-15 10:41:17.789968146 +1000 @@ -46,9 +46,16 @@ AssignHeapAllocation(?heap, ?var, ?inmethod), HeapAllocation:Merge[?heap] = ?mergeHeap. +#ifdef ENABLE_MAHJONG AssignContextInsensitiveHeapAllocation(?heap, ?var, ?inmethod) <- AssignHeapAllocation(?heap, ?var, ?inmethod), + ! ObjectToBeMerged(?heap), HeapAllocation:ContextInsensitive(?heap). +#else +AssignContextInsensitiveHeapAllocation(?heap, ?var, ?inmethod) <- + AssignHeapAllocation(?heap, ?var, ?inmethod), + HeapAllocation:ContextInsensitive(?heap). +#endif /************************************************************* * String constants diff -urN doop-r160113-bin/logic/library.logic doop-mahjong/logic/library.logic --- doop-r160113-bin/logic/library.logic 2013-01-16 07:31:46.000000000 +1100 +++ doop-mahjong/logic/library.logic 2017-06-15 10:41:18.193970149 +1000 @@ -45,6 +45,10 @@ #include "client/client-extensions-catalogue.logic" #endif +#ifdef ENABLE_MAHJONG +#include "library/mahjong.logic" +#endif + /** * Declaring class */ diff -urN doop-r160113-bin/mahjong.py doop-mahjong/mahjong.py --- doop-r160113-bin/mahjong.py 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/mahjong.py 2017-06-15 10:41:23.933998612 +1000 @@ -0,0 +1,42 @@ +#!/usr/bin/python + +__author__ = "Tian Tan and Yue Li" + +import os, sys + +MAHJONG_CP = 'mahjong.jar' +MAHJONG_OPT = '-mahjong' +MAHJONG_OUTPUT = 'MahjongHeapAbstraction.facts' + +def splitArgs(args): + analysis = args[0] + outDir = args[1] + ptaArgs = args[2:] + i = ptaArgs.index(analysis) + opts = [opt for opt in ptaArgs[:i] if opt != MAHJONG_OPT] + jars = ptaArgs[i+1:] + argMap = { 'outDir':outDir, 'opts':opts, 'jars':jars } + return argMap + +def runCIPTA(argMap): + print + print 'Running pre-analysis (context-insensitive analysis) ...' + cmd = './run %s context-insensitive %s' % (' '.join(argMap['opts']), ' '.join(argMap['jars'])) + os.system(cmd) + +def runMahjong(argMap): + print + print 'Running Mahjong ...' + cmd = 'java -cp %s mahjong.main.DoopMain ' % MAHJONG_CP +\ + '-db last-analysis -out %s' % argMap['outDir'] + os.system(cmd) + +def run(args): + argMap = splitArgs(args) + mahjongOutput = os.path.join(argMap['outDir'], MAHJONG_OUTPUT) + if not os.path.isfile(mahjongOutput): + runCIPTA(argMap) + runMahjong(argMap) + +if __name__ == '__main__': + run(sys.argv[1:]) diff -urN doop-r160113-bin/reflection.py doop-mahjong/reflection.py --- doop-r160113-bin/reflection.py 1970-01-01 10:00:00.000000000 +1000 +++ doop-mahjong/reflection.py 2017-06-15 10:41:24.181999842 +1000 @@ -0,0 +1,80 @@ +#!/usr/bin/python + +__author__ = "Tian Tan and Yue Li" + +import os, sys +import re + +INVOCATIONREF = 'MethodInvocationRef.facts' +PATTERN = re.compile('<(?P[^:]+): \S+ (?P[^(]+).*') +REFCALL = { + 'Class.forName':'java.lang.Class.forName', + 'Class.newInstance':'java.lang.Class.newInstance', + 'Constructor.newInstance':'java.lang.reflect.Constructor.newInstance', + 'Field.get*':'java.lang.reflect.Field.get', + 'Field.set*':'java.lang.reflect.Field.set', + 'Method.invoke':'java.lang.reflect.Method.invoke', +} + +def convertToDotFormat(caller): + if caller.startswith('<'): + match = PATTERN.match(caller) + d = match.groupdict() + return '%s.%s' % (d['clsName'], d['methName']) + else: + return caller + +def isPotentialCall(call, caller, invo): + invoCaller, invoCallee, _ = invo.split('/') + return caller == convertToDotFormat(invoCaller) and \ + REFCALL[call] == invoCallee + +def findInvocations(call, caller, invos): + return [invo for invo in invos if isPotentialCall(call, caller, invo)] + +def filterReflectiveInvocation(invos): + refInvos = set() + for invo in invos: + for _, call in REFCALL.items(): + if call in invo and \ + 'java.lang.Class.newInstance0' not in invo: # skip wrapper call + refInvos.add(invo.strip()) + return refInvos + +def convertReflectionLog(refLog, factsDir, outDir): + flog = open(refLog) + + finvo = open(os.path.join(factsDir, INVOCATIONREF)) + refInvos = filterReflectiveInvocation(finvo.readlines()) + finvo.close() + + outputDict = { + 'Class.forName':open(os.path.join(outDir, 'ClassForName.log'), 'w'), + 'Class.newInstance':open(os.path.join(outDir, 'ClassNewInstance.log'), 'w'), + 'Constructor.newInstance':open(os.path.join(outDir, 'ConstructorNewInstance.log'), 'w'), + 'Field.get*':open(os.path.join(outDir, 'FieldGet.log'), 'w'), + 'Field.set*':open(os.path.join(outDir, 'FieldSet.log'), 'w'), + 'Method.invoke':open(os.path.join(outDir, 'MethodInvoke.log'), 'w'), + } + for line in flog: + call, target, caller, _, _, _ = line.split(';') + # skip generated accessors + if 'GeneratedConstructorAccessor' in target or \ + 'GeneratedMethodAccessor' in target: + continue + # skip non side-effect calls + if call not in outputDict.keys(): + continue + output = outputDict[call] + for invo in findInvocations(call, caller, refInvos): + output.write('%s\t%s\n' % (invo, target)) + flog.close() + for _, output in outputDict.items(): + output.close() + +def run(args): + [refLog, factsDir, outDir] = args + convertReflectionLog(refLog, factsDir, outDir) + +if __name__ == '__main__': + run(sys.argv[1:]) diff -urN doop-r160113-bin/run doop-mahjong/run --- doop-r160113-bin/run 2013-01-16 07:38:09.000000000 +1100 +++ doop-mahjong/run 2017-06-15 10:41:24.562001726 +1000 @@ -147,6 +147,7 @@ CPPFLAGS_FIELD_BASED_STATIC="" CPPFLAGS_FIELD_BASED_DYNAMIC="" CPPFLAGS_EXCEPTIONS_EXPERIMENTAL="" +CPPFLAGS_MAHJONG="" cache="false" logMemStats="false" sanity="false" @@ -166,6 +167,8 @@ singleRun="false" noColour="false" # skolemGraph="false" +mahjong="" +refllog="" originalCommandLine="$*" @@ -334,6 +337,17 @@ phantom="true" shift 1 ;; + "-refl-log") + shift 1 + refllog="${refllog} $1" + shift 1 + CPPFLAGS_REFLECTION="-DENABLE_REFLECTION_LOG" + ;; + "-mahjong") + mahjong="true" + CPPFLAGS_MAHJONG="-DENABLE_MAHJONG" + shift 1 + ;; *) echo "invalid option: $1" usage @@ -382,7 +396,7 @@ ;; esac -CPPFLAGS="${CPPFLAGS_EXCEPTIONS} ${CPPFLAGS_EXCEPTIONS_EXPERIMENTAL} ${CPPFLAGS_PADDLE_COMPAT} ${CPPFLAGS_CLIENT_ANALYSES} ${CPPFLAGS_JRE} ${CPPFLAGS_OS} ${CPPFLAGS_STRING_CONSTANTS} ${CPPFLAGS_STRING_BUFFERS} ${CPPFLAGS_CONTEXT} ${CPPFLAGS_REFLECTION} ${CPPFLAGS_FIELD_BASED_STATIC} ${CPPFLAGS_FIELD_BASED_DYNAMIC}" +CPPFLAGS="${CPPFLAGS_EXCEPTIONS} ${CPPFLAGS_EXCEPTIONS_EXPERIMENTAL} ${CPPFLAGS_PADDLE_COMPAT} ${CPPFLAGS_CLIENT_ANALYSES} ${CPPFLAGS_JRE} ${CPPFLAGS_OS} ${CPPFLAGS_STRING_CONSTANTS} ${CPPFLAGS_STRING_BUFFERS} ${CPPFLAGS_CONTEXT} ${CPPFLAGS_REFLECTION} ${CPPFLAGS_FIELD_BASED_STATIC} ${CPPFLAGS_FIELD_BASED_DYNAMIC} ${CPPFLAGS_MAHJONG}" # Requires DOOP_HOME environment variable to be set if [ ! "x$clientcode" = "x" ]; then @@ -668,6 +682,87 @@ # timing $bloxbatch -db $database -addBlock -file tmp/spanning.logic # fi + # Read Mahjong heap abstraction + #if [ ! "x$mahjong" = "x" ]; then + if test "$mahjong" = "true"; then + #printf "loading ${C_YELLOW}Mahjong heap abstraction${C_RESET} from %s ...\n" $mahjong + echo "loading Mahjong heap abstraction ... " + cat > tmp/mahjong.import < tmp/classforname.import < tmp/classnewinstance.import < tmp/constructornewinstance.import < tmp/fieldget.import < tmp/fieldset.import < tmp/methodinvoke.import <^.f.vnvF6›Ì¼Ì;Ff Í0Fçü”TF~ŸÌ¼T¿ÒܤԢ�Ĥ Wp~iQrª[&˜ã–™š“¢—•X–ÈÃÀÂÀÊÈ ˜›˜‘•Ÿ—®_P’¨–ed‡‰•–dæèCLÆ¢ÈÀt0!P?�dòdÁ|V­í ŒÁÒì@’ ,È $9€4'PK� Çb¤ËPKXËJmahjong/pta/Type.class;õo×>^.f.vnvF6›Ì¼Ì;Ff Í0Fçü”TF~ŸÌ¼T¿ÒܤԢ�Ĥ Wp~iQrª[&ˆÃRY�ª—•X–ÈÃÀÂÀÊÈ �›˜‘•Ÿ—®_P’¨’dd‡ •–dæèCÌÅ¢ÈÀt0!P;�dòdÁ|V­í ŒÁÒì@’ ,È $9€4'PK3å"%¤ÉPKXËJmahjong/pta/Obj.classMO»nÂ@œÃÆB��¨(è€"nÒ�Ò ¥BP€èÏp2gù�¬s¤|��B™ƒDÊ;»3³�û¾]®ÞÐ ààÙGÇÇ‹€7Ó¹6ïÎh¼pçÅ^ t:WË*‹T¹‘QJÆ�•YÊŒYo4^$òS†©ÌãpmJ�ÇÓ‡aóuü5dò�”�F†–¥!XU¹SÚŽk¬¢äÕNiÁE� ÿ7Pèÿ1•Ñiø¸ÅžâQãìs l7£Çj@Äúä qb›½;Ùv¹M€àé®¶khÿPK'úÉÂØPKXËJmahjong/pta/PTAProvider.class…NÁ ‚@œ-ËÔ‚Nõ yÉK7#è"AAýÀ–›­l®,«ס裢UˆŒ‚ÞéÍÌ›™w\ofØðlô Uˆó+ç¥w™®ZÖ©÷•þù–kÂäàœ©·–›Æ4„¸‹Ò–Å œIZÂìOB¼¡‡oõ¥!W€�ìCÑP&†: ?PKÐãÖ‚ ÔPKXËJmahjong/pta/doop/Options.classuTÛSgÿ- Ùͺ(  ±� C(¢µ Z®6mä"Šjë&ù ‹ÉnÜÝtèL�écŸûÖq¦“¬£Á)3ô½Rgz9gD“íCÎù¾sÎ÷;¿sÙüùÏï‡&ðTÅy̆!cŽÅ<‹‹,nËø\%=« ­â |É— ‹; –X/+Xa½ªà.ë5÷Xß—±.ã�ŠÓüø+Ö,6Uô`+Œ¯ñPÁ7 ¾UñºJ÷¬Œœ„P>»¢»Û"™ý;=UÔÍBj͵ ³0%!œÓsÛ¢ÐËe ²UqëwÙpæE¶R� mÎ Ã4Ü›^—œ³òB©ŒaŠ¥J)+ì{z¶H–pA¸ó�”ÝñaߤÎqHO¼5‚ñ5‚™;æ¦9ï]Cä�a¶!§qPɲ|Ä\uÞ¹ßͺ)}TO0¾É9Ú˺íåT|ˇD¦¤oïXd(»z*oYåÔrÙ5,Ó¡ :Ö\=÷äŽ^öЦ™˜³mfúºÊ|+Rgƒb×py)ÓÃëôß!ÓhG¯�Úx<=ÐЃ MûBº¯Éº ’]"­&ö!%¯ÑöÝ$\fù@øAïM£�7çHó›öÄ+^¼ yÆN’´Q�Ð Á ¡ÄÈk𡻉vY>ö…6C÷’÷‡6CG šÁ®øB·7CÇHNøC·7C_"è²\õ…5CÇI^ó‡5C_&è$Y†ð‰tÛ^4ƒÒ—ØÝï3Æ ‚¾Š0&1U�—8Y€<¯ oìCɦ£‰Ãçø5�„kPFW"ú'¦GÈRƒVÅ3vu´ºNVa°ëT««³Š%v}ÐêŠTq…]]žëÄH°†î*bèÙ8Ài¢t&Ò[ÃY/öl }ûèÿãY›Týwkä…·Ð\ݺH^‡B}è¤êú©¾ó¸ANSÛoÒéñV1ƒ˜…Ž91 ø�±*Å’�Ðj”˜ð©`¥Kp—ì.þ®6©!©I@TÓ;[<6`ü2wgî9çÞ™;wöÏß_¿l# ÖŰ!†MCØ’±­À�W2^+�Åü�‚ÞÊx§ ,ì{I;2>0¨YÃЬt�Û¶f3„3Gù¬£YÜ1-š«w³Pêøøk&û)wÀ0’«ò ž¨q£’8q,ݨ$Ó¦a;ÜpNy­¡±Ë •‹ Ó¹sþ­j´îðDÙ4ë‰îð�ÜÖˆ¥˜ÅêgnðŠF1f»¡‡·n«gºV+ßÂç»á™;º¡;”É\´G±S†�´YÖåtCûÒ8/jVžk´6[Oa,û¿÷†£×7ë)t¢W î4,¢Ä= ©ƒ/*:({�:õED6¨±HÏÙ¨1@õÅSú£7OÂ8FDqè{DTʵT6²ÂP9h\ Y–˜ÙÅxñ•KøâWðVš\B¢o¹ÐDðì�ðÓ8AÄIz0§(è4IÍ�w ÏHLÈ.‘—‘U®*ÄYÊ÷[~Àõ, â&ºL³ç^¼°ËS;yË×¼ÃAD»ÞÆ,YáõÇÂ×I&h+PÉ ºŠ—PÖ¥$ÖÜTxLv >ZŽ ý?é¼%ˆßÍHÿPK\é½DªŠPKXËJ!mahjong/pta/doop/ObjManager.class�UÿSWÿ<@N�S£1Mc�!‘/"µ­mZ¬iEIQÓbMI¿$xÂ)ÜÑã.�üWíLÔ1™ÉäçNÿ¦N÷¤  uú¼·û>»ûÙ}»ïþüûÕŸ ®â*’ Ux�Á>T°¤bI)|4Œ�U‚-‡ �`_°Š{ ¾Rðµ´YS�‘ÈucØPiw_ÂHئ‚¬‚‡ ¾aв†!¬L�7›¢ÉØ}Ñ[Üàa1(¶+5‚¹þœ§[¯¥H‘fÉëƒÛŽ%–Ïž®´Ä7*©¼méF%Ý«Y%»;Z,WçÕ“ ›§Ê¦ÙHíÚÇ‘.bdé_Ñ Ý^e˜‰öZ­s›¯ñ¦HÇö|³,dº!¶�zQX»¼X#�Z²·EÖu†ÛÑ^†gùŠ{§/®£"”(ÙÔj¼^,óˆ!~‹,1LD»J”Ó›¶Ëm¹wú“”Ú©®ôp�uSó¦c•Ä}]æìiQÆÐp9†ùKS·4|€Y†ñÇMÞ¬ÒUj¸Ž÷†wÖ>Ý-<ÚP°­a�Bk¦i7m‹7¶„]5ËÍ ߆ð]PA>„˜†]|¯`OÃcü   á ~$«óÕc˜î["+MæÉ¤û\×ð~–´Ñpw$쩆gà ×$Í0û_Õf¸1¨jt�Þ¨¼·{}š®ƒKìã%÷æ¯]Ø´ C¿:ÂzÁ¹¸×:m¤¢ˆÃd´· c{rìË4,¼T ›ae0ïnçûŽQ²uÓHeL£éÔÝ¡;›Þ‡}ÓÚà¥*•6:ÐV¦nvvvNF®àüÒ¢äT·ÌœTF—üü‚€Ç€¢ü²Ì”Ô"½¬Ä²DF~×¼äœüâ̼tßÔ’Œüv.F¥ÜÄŒ¬ü¼tý‚’Dý >}4Í*†Œ <žyy©EÎ9‰ÅũŌ  óôs�ºü“²R“K# ÀÀÈÀÄÌ@Èä±0°i6 ÉÄ\@5Œ PKºX#_¨ãPKXËJmahjong/pta/doop/DoopObj.classmRßoÒ`=”°ûÓM'nsCÛ‚«?öb0¾¸˜4!ÛĸøTF‡%Ð’Z–ø?ù ‰ÄÅDÿÿ(ãù:t}èý¾Þ{î9÷Üö×ïï?â°ˆØ) ˆ]î˰—Ǿ†jð°¦LZjêZØé»CO Ôì»®=pƒžÝŠ#?è5”øã(© Ý÷ý�•QìÚmæXËø]ád#o$ ¾ð?~)`‹L‹ýŽù†ô¯Â.éWš~à�‡/j»�3¹ž;G¤6L h|»rÝ0ÓÆ”€v2©¤Ìš�Ã+°@±Ž£3ïµ/uô£0�tú’TG�tèXÒ±Œ«X£¯ê£Ú5V¯…Ùá�ÅtØx¬á‰Ž§x¦£„%�ÍYý.ùí©�Ζ’ rk×ÔN{=/¢§ w0öNÎnάë)¢ñßDÝ©ça4tc�ç)_á]sÞB#e�Ø…ü�…~¸ ÞJrÉÉ•$g)Éçø”±�›¼½…Ê n}ƒ°jd¬úY«2�b•r¹PO³µÖ©R¹„ÖºD~‚Ââé�ñ»Av©Z ë2yËä­aƒˆ lNU*<…Ô¶¾Bùô¯]M’[Œ·IµÍ|žƒî0nán 4;­2VRY óP‹ñÏ ¶ÿPK½fÑ�ï–PKXËJmahjong/pta/doop/DoopType.classmPËJÃP=“G1¶1¶Õ]¸k#˜…î7!PºiéþÖ„šÒ&!¤ÿÉ…‚EpáøQâÜEª‹{fî™sf†ùø|{p�Ž�*44%´Ê8(ã�PÉ’`(–ÁÌÅZ¸ ÍÜQ–†Ñì’ „>�Aíö<91.\„F·÷ßLc¯Ò»à6”æÝ~'c^òL*Mh›(¡l¢‚*¡½÷ó˜ÝI&\Ÿ¥î·ž`ý®¯0Á @ç«(°e'ÎlÙŒ£ÊÏÀ&gç¬�LÝy9§(ÎÑê3S¼WnÓkŒu¶[œíq¥Æ¿¢Á1Gâ¨;/Pl¥œl2Z¹a[ªìÝ;÷Î9çž™ùùûëwO°§#�¢†ãÐqO†û ”ð@§¬,³Š U 5 u†¸ÃGbÏòň!y8ä§Ü |Ë6Û|Ü nÇ8Ü<ÁPºÞm^,mî ÌŽïYΠÑí6ZŠöÝÀñØC¬i9–ßbPÊ•w ê¾{,¤�åˆ7Á¨'¼.ïÙTQ‚¥òMÒÊ•ÒÛÞPô}’Xîø¼ÿ�¦á¡‘ì",MÄ ÷=Á}qzL�Ð Û–‹ Ãf¹rÅÕ¾kÛÄo¹¡rÿh5g.ËÍî󛣵M«wÜÀë‹W–tš~éºc)ßæoW ¬a�!õWð5Ÿœ�?– ¬"¥á¡� ì¸�¬� 7�‰ÜœS`X�ŠNðÚeÎýlò…G¯Œ‚?[°ý? b§ÜÄDÍC>WÐS�H“`X WI¨Ò/å*}t.ÔISö e@¦zVý†ÈÑ9”ÏP«¢©Á�¡¸BÛ@Ä*Q&CÊ d‰VRQOvwªŸ Ö¦ˆEЮÿ ÆÚhí°XŸbIÁûúY8K&„G)¦ˆr�ש–F�ª›T]û…‚†[³Ù·TU^Ï¥—-ŠÛÈÍä‹T—t©4EœáìrìXØÙžáîÐw‘ÝE>üþPKf’ ç$æPKXËJ mahjong/pta/doop/DoopField.classmPMOÂ@}Ó µR¨€Jôà­­‰=èMãEBÒ„xÁp/¶©K %¥ø«¡r%‘_:ö_�ïŒÊMFs ’èv9GÙ]0ž2£ÆQî÷xŒíp£jž®]„–íü7S¦Ëì>ê‹Â\ï¥é¼/¢ixVH è84P�f Š¡; &)Ûçyà…¬õ6Bówñ‡ã-F8áç«ü'¬¢gVÑŽ£ÌGÇgç¬(Ó}¹§+HîÑ ò S„ÝÒ¦0ÖM¶78kr¥Î·uƒcŽÄQu_!?ml•’l36JöTzÞ’v­rá½oPK¶š+ÏPKXËJ2mahjong/pta/doop/DoopPTAProvider$ObjIterator.class�TYSAþ&�lXˆáPä1` \Ê95 jéÛ¦ÂbØÅÝ …?ÅÿàTÉ¥Uʳ?ʲ'DŽ ånÕÌôô7Ý_÷tϯßß~Ä[õè© !ª¢½*úÐïÅ€JºG•ÐðXê†*qÃ:,W# F%0&c ž¨Ç„‚§ &Uø¥•zL)˜V0«`ŽA1W7⎰ê|›G³Žž‰Êî˜VŒ¡"©§ îd-Á0Z2va/¡ÛN^Îp#M:–n¤cãô3xœuÝô2´'6ùú†Iú-‡G×Ls+:CÃòÊä²enëkBºõŒé†îŒ3tKÃCoʦÍ5¢X“Ð ±˜Ý\Ö _ÍÐŽ²ÎíE±ã0¸ƒ¡÷4rBm0tÉðÒê¹­J:<õa�oå×ê<ˆ”SðŒA‹†°¦3ܶ…Ϳ͒ˆ@_.”‘«.‹&¾�¾Ì²š4³VJÌé9öö{¤ ­¸§¡-rõ\A\Ã<^Pz–¦æ$4,`QÖ5ÜA£†v<ÐÀC†êËW­á%^iHb…ÁWxë ^K…SSÀ“¡íÜ΢™Ì¦Ög3bSÎìNJl9ºiH ]%¶tZ½2 •—$_aüW³HåXúVr¥C‘´•‚Rþy*%l;ÐßK•>q“Ú-áŸâ¶ ël¼VÉPþ1+¬O �àÕ– t%™òêg!7CÅ›ý/ûÉ~òÿØS²¸ÁÓ¹6v§å½>/ÒF Mÿ8ÌÐy}\Í �ž´0zý\ôSÉÒú.=¡.4ù|²Îi]N•=iÚHƒ›d 9Ù‡+|w8r„2ù<ÇPöáÝ#=Ã}« t¡ÝhBíRGä-µÓÌhVÃ_á=F׳sžœ¦ŸF*æ<žç=† ~FðôX%à ´ ®#TEÜǨvÑNÍ|'¸µûþw‡¨ý™‹IZö£ŒÆ!(FFˆ[ �´ãëòMÊ^Í»kÉÓ+'wu»Ì‚>PNÂyh+Ù–qz‘C°½B,éNs!orîF(ÇÄE:ú|^º…>ÌÀõPK^o‰!ÁPKXËJ#mahjong/pta/doop/FieldManager.class…Q]KA=WW·Ý4µÌÊ2|*?¢} zP|IÁêÁì©Q‡mEweû]•PÐèGEwE¨Lh`9çÞ;3Ÿ¯ïN�3¡cCGÚ@›&BȘœméØÖ±CˆV×Q5B¸Pl´3¯/ ‰¦ãÊËɨ+ýkÑ2cö|)”l(9"šq/¬¡pm«¥|ǵ+ÅæHÜ <&ÆJXçŽö+„ƒ¥Êoêª;�=ÅB£åØ®PŸ{�þ*Õ÷¼±Uçô¾®°¥_]Ò¬ÆUÌ–7ñ{òÜ FNÍø¹ã8èà Bviù™:†,vÙ^ 1Ä ¹?ÚŸU ùÿF%DzÞÄUj2KÞ£Ql#Ï¢#XaÞ<%ø2œå‰1Rš‚9!¬rŒÎHZ0ä\zÄlˆ1ý†P§\êV�áš÷¨9q<Ç·B½ç¦�£ï”Þ‰D_¤OžÑßsøìrñf&ÚN'êm’øè´lÞÆý�—°úwaó^ÁÞßµa†—pV8b�Óý‚n0øií‡�Öœ¸‰aÜÂDIB3 úþíŒöÿïpîAbø}Dw›ôS¶_a³>31ò÷ºLí8½Ãië9ë<¿OíIrz�M~;Éþ©½÷J…îaØGoŒÖWè X÷à²çÔú÷q:^áluþ™}œMèÛõgl¯1¸:veȶ‹sÏ!^¯Ð'³Ö?ÔdÅvœ•×8¿º‹á¿jÑø=fŽ$¤0„4&q‡â»Bƒô.4ÜÃ�XÅOXÃ/4žÃ}¼Á:þÆ3zXÞA{¥Z®0ËÕ¼¸«y±ûwðÁóZ:+á»@%2]U¡ËV.óvayѬK²Š�O.ß?ÃU³0,˜¢.‡il–ÿPK Ïå‹� PKXËJ-mahjong/pta/doop/ObjManager$TypeManager.class…’ßNAÆ¿³Ývé²ÚEj­¥Ý«‰ñ¦/ &Mª^”˜à•Ó2)Û´³ÍvJÂ[A"6ÑÄð¡Œgp„âz3çœÙßùó��Ÿ¿¾ýð u x˜çcÍE�¬›`Ã…�Mó­è ä LÈé£pR~AXo�ÄÑ RýÆX‹Æa�ºƒwB‰¾Œ› n‡*Ô;„b5•¬}$ػѡ$Ú¡’ï§£®Œ÷EwÈ7n/–BË––#B¥ÚˆcÑ .ÕÑq¨úÍÚ•Úû'cÙüxyÅÝeO7<%x-¥d¼;“‰œÞ¤›ö±üòBK¾ö•ÐÓ˜Ç=Ïïñaä$IÛóóï°·Mãž|š%.{<7"öà3|Ö‰;q—ä¹[’ï)À½¸¯÷ã9<¨às’ûó ö:Q,u<äÀà qb9ubsâq¦¤ ›UøP¡Ë¡[UDѧà�*þ„?3êÙºU\†~QñWüM`Ú:™(ôÊ-“Å Vµº«««ü]Å?ðžŠ.©ãŸø—‚÷Uüǘ¯*þƒã*N GHE ܤLÍáíz ÕD½¤_ëÛ!sÔÐjÔZ3ä+Íñ…";}šáïsW|»ÊÌd¾eéYëÛ¦û U‘£Š\‘ÇìW…MØU¡‡@ñÈ9LÝC„–p4aÐZ]ë·h,!Uä '¡rPU1ŽÒq5úU1^ª¢HLP… »U1QL˜™6§%Ì(ÇQC¤2—ΤÖ'ÂF°_Ï NÚòD0dÚ85Åå$$ÅmBPëVÅdQ¬Š)Òª^¼'01åÍÚ Év3¤nf˜»G †â^¾:ýZ"®ܾ]”2U ¸W¨¸Sñ|T%8ή‘Y¼,RS­•3·Ù¶Ý=$Z’-“¦±£–Å«e„ìeqù§ŠéØÊ~‘).ÃڌʦÓZ”èÏ`¸—G"F܈iÑ6Ýè‹â…vqZ‘˜ÉǬ"AÕ§‹Ùª˜#JUQ&rr¼\;CÌU„Gå¢B•ÂËâZ»|µ@u·'âÛV^_ÅfnÄk;ƒýÑ�^ÛŽëáøF-Ö¡Iñ Éäí.¯&xÜÖ½¡«£Y`]÷._p¶±+ª×W­ÒµhC(ñkîÚ \L“½cŸ×²¢cƒÀÊn�,·>ÊôöõPÀ+_if‹ytËnÀÅÔönog·wh«˜Û å-èöh±˜¶kHLƒœ´„úÎá2º½&£ÜêJ×i á׌Hl¨×˜ùÙ‹øõxœÎÏöiüúVP"oŸ¼dná,âêØÊy·9/áüÃóVþ´ ~\òrÌw—¼›O^‘ùTÉë Ç g�Kn ´b¢ârº‘{yâlr°ËA©¨„c?ù¶qœIû€*äa Q�I¨¡Eó¸>s°ÀÔ°=©a 5äñ9k¸†J©Á+5TQCZø4Øå¿m’ÂSø ?“—r=„~,7ñ)MŸ^ñ4ì/by•‡�ŸƒçáÜ'-.Øgb!…ާ@=í>“qœåBLNÂá/š%”4yá“P]ã0þIB‘À!Lhó‚K`Šù2QPÛ¤öŠÊªALÞ{âíý)�Űq\ZPŽÕDh ‘i55 çŠ\ø>¦“� –fñ}Ï'ÃGPLœ¦´‹Ú ,Ä,ÌN!2›© ÜÄÙÍ|»…Ô[I¿ƒë·1±og3ÚczR‚œ\Ì´:!­¾t·ò[5ié-´”�ï(\礒KafUÊŒjw�kZ´e\Cg—­ÊÌ7º°œÙ5ˆÆ6oºIZ–ÞÅÙÝìB÷°ôîe|ïcß½—mó~¬Äƒ¦¥ˆcX)ÍË‘_Òì–Ò°õIÃ*,� ‰ 5KäV b%ë $ï0û!›h Ï/&ÔêtÏ·šåC ÚØ‚GˆËã,ø'˜`'æç]2hWñwuê(Kžã˜¬k\­h;ˆö})¡v>ÁÚ·¶^Sä”W™$Šs“ç^A…«µò Ö #m�Ó$=ÊçcôUf¡µi»yˆç$O±§PÈrr­s­·Ú¥œtº6¤'ç¹6¦'›\ç[“áGÝT*šC·Kù7— ]Že„=— .í¾^,2Mb�X"2M^Kžâ?$Ï�ðcüä¿PKu  êPK XËJ mahjong/fpg/PKXËJ$mahjong/fpg/FieldPointstoGraph.class�VkSg~6 ¬„PD/ Lk½…TD¡&Þµº$²vãÙûýò¹¿¡êL-ƒÎtüÜéOêtú¼›Õ�dAê0¼—óžóœçœ÷œ7û׿Oþp?‡Ñ‰¸Œ·Ã ^‡œøÎÖcçš©7]�¼#¶—š!‰m.‹áŠŒ«2®‰åu1ÜÓî¦nɸF+âõhÄ»aÜÁ]ª˜ge¤ÄœjšŒŒŒ9 [ò–¹¤§5KBû䢚�7�¹XÞQc3ÉÑïhH¨™ºáØISBã伺¤Æ Žž‹M©yÖ%ô9Cu –&![~:\†9=;?´Ñù¸®åÒk5šS�0âþÑ­ËIK�-K¨Ö Ý‘°'²n½W$„âfZ1è†v±°8«YIu6GIÜæ¬Q–°;Ò»A>ÂTÍåHÇ–°�ªeœ©ÐU)ó‹CB½Ypܨí錄‘*¥jäã/UòKªpÖ�’¯£Õ0>fÕî'^ÅÎ?øÆ¬j»ê3îE2é7~ƒ—¯¶f9Ô�Ðê“ÞõÖ„£¦XdÞ{&ã3I '6åȶ¡3ã5;‘‘¬‘ RÉ©‹³iµËÐîw“°³äÏÍIÒÒó9ÍÅ 'Ì‚•ÒÆuÁ±­”Û1ÏZj>{HdRA²¬´RV'T;ËØìÇÝbØ‹}2tóX�Ð4fšÄ ”ædÍ´ÝX‹\eM8¡ÀD^Æ=lxPhÂiKPpï1ƒe½Z¶ç¥*XÆïãÃR0‚¡*Š®Þ‡x ã#ã!sU‘TŸñ¶ªô+ø ÔÎç§™ü\¬:G ×u›S©BL-åHFDzÛÖibÑ{³óçXvªcr·£¬aŸË‡Âø ŸKØ™¬tÀË/é||ÔTJËÓã©Ò-ûó,ë‹LÁH9ºiÄâ¦aÝw¥¹šc˘Ö5•½¤-ª|æ >Ûû"‰Àë3yç9† ú%‰.h˼¦fNŒ=~AU‹•2 ‡®í Úò:¹`KóÇí¬ªÓMº ªé´„ŸFá�ë–íˆv÷µ®£FB#ÓtQ;™Õ­´Œ¯|�ï5 §ùÊ–ŒucÉ\Ðb“n¯²OÔŒšbºdýbi'ãGænÒ4 låœahV<§Ú¶ÆÇÿïµñzxÅŽ›P�tN³»Š†k“�p,ÞâÐz†Éå¼öj‡E—ÛöVŸÆÕ\.¡;Ú�ŒŸ$to*öäÆz|˜:ø‘ù‰Ã§‰«€x¬ø!óš»îâwÙ~+×=8È1B‰åZcÑHѧ\_Að1B¥eM´ï1jûV!Kˆ>Á`uPvõžàQ$ôrì„ÌqBhá÷Q+v¡�Üvâu´c”+š¡ÏóßÁYâ\ýµ¿½€¨u…‚v?H0âí„4LÕÐ* •ú=^�‡¸‹Ñ]Ñ®�³±EØ‘öÖž¡Á ê¼ÁÝa_€š�­Ø�7=€ç ç¶2ýÞ¢±ä°r„»n_�š—ƒ[r”»bºû=�ÖJ&MüZ�0H]6´gy�×)’pʳ¤Á/Øîn¼RYŶ ®zì^×”?rIõºõQ#àX�§Yc¤ç�Û¼E)]íí¤–<÷1ÊBœ£}ý+h.N-¥¢kpãºÈÒ›æz†ÆÃ/Œmš g'Ë¢ µ‡ú=¦-\>E+yn/òt�Š™XÅŽ@5ók;Éھ̪¹Jg×\æ[ ýƒ¨Œ“Ôñ%P3ð ôù¸I·Hà6 Ü!�»åË—Ø#pÞ#p ï´=ÃÎ)Îíϰë"çÝ܆¢ýí¡ì)e´èew£ {ؼ¬éNæyÔe2†ïD7Rûü€oy~Þµ àu&1ÅÛ(®HþPKkÞý•• PKXËJ$mahjong/HeapAbstraction$DFAMap.class�T[OAþ¦ ]ºlí‚ *ˆ7À^€RAA[A„ˆE0}rºŒ°Xv›Ý) ¿À˜¨ñÙ_‰ˆhb|öGÏ,B[âËÌœs¾sÎw.»¿~û` ³Π_1:nH ©#e I¥I·"ƒAC6�ňs긩cTǘŽ[ ÑåW|‘WÅ5¾Á³U锳¤È3Ä–œ—˪/&Ž[ Åu¾ºæ¹+ÙŠäÙÇ¥µü¡‚W¥·ÎI;;7�Ÿ¤0Q¹ê}# 1ó‚W¦K�ô¹-ÏU ‚ã:r’¡'Ù•zÊÐ4ã- EÖqÅ£êzIøOx©LšV_Hß‚Ò2¤’5Sõ2Ä—$·_SQ‘ nÛ" Æ#Ä9J˜°=æ‚ë ¦Ìƒ@ iöí{äÿ—€ŽÛ ã�«n˜'6ÄXòª¾-æE½ãfX ÍÄ9t™¸ˆnèTâ8CÛÑ<çy°JŒMXhÓ1aâîêÈ›(àžŽISÊõ>¦M<À e©WCïé a°Â”eNꇰ%­DÃâ´¤ª/~lïhζçJî¸ÁC±ÉЙ,ž šO½`èj÷hº95݉S–­î´æÈêù›´5ݧ˜imV„ ›¢Uª2ÌSC³ñZ H õê¬Uá }ö ú;DЮ¦ F·’ÎZ–š=½›H¢} Ëy’rÐH:Ó™hé]4¥¿£ùù.¢;зÉ@#¢ÓAoÃ[zÑ&¸¿$w�îÑôgè™=´Dð Ý$h_Ë|�±÷ЪáÙÈŒà'â[!/¼�8ïÇ{’? ÑC ¬ßjÁ%*j?[/9(Ñtf‘­CvQº‰�*ìÚ?Xí/–mŸÄ’õzH —C jÜPèu•dX-PKB6¶t›gPK XËJ mahjong/main/PKXËJmahjong/main/DoopMain.class�X‡{çÿX:é|cÀ`0D ÙØVj¼õJä@\šq–ÎöÁéNÑ�Hœ¶éLštït¯4Ýi“´µ nÒ½Òtïñ·ôéï=É–, ƒý<ßx¿wüÞñ½÷Ù/ÿïêKã¿pNÃkq^Ã=˜”á ²½Åqo÷i¸hÜ*¦4hHÉ�S†éfTÌŠ %¼U\ŠÀÖ°9wdåªÈjØŠ#È©ð4l‡¯"¯a. ý¡®xÔ<‡Gêñ&¼¹oÁ£õx+Þ&ÃÛ…öŽz¼�Fð.±óX�kx7žˆàIïÑÐ…éÓ‡„óòýˆ†�âc²ú¸ŠOÈü”‚µÉÁqJ¿‚uý®ãù†ãŸ3ì¼¹–¢QŽ ËRS>n9–ßKZ¼íœ‚º~7m*X?l9æh>3eæ&Œ)›”ºŒa9 šâ†/—�„m83‰¤Ÿ³œ™ÔN™Yߢ5Ÿ¤º\žì‘©¼e§‡ÆO)8¯!8œ1f/º$LggC–i§Ç]Ëñ=ß=•3²³= î©!v#”P½ÁÍûÙ¼?hYÏLOXºŠŸ=+î4Î’¾á[žo¥<�E$yß²#F6p;š´fÃÏç({¤‚áø „¬o$Ʀ.öTz%›ÊY¾9bæfÌ4©fʧ´‚X¥ÁÂÖréŽm’7c²ZÍNÛÈL¥�½µpì=pLAw|EhœáõÏ c®Zy!(�¤›Ï¥L±ÆzpÝìk©K긽:ѧâS:>�Ïèø,>§c§T|^ÇðEÂëϙ̃3›5�l̘òüœ‘’Z‹uuuÅT|IÇ—ñ´Ž¯à[–1œ&óɯØúªŽ¯áë:Î଎×cXÁ~qET�¤ªMøn¬ÕC¼- ¥:+HÇ7î7ñ-zy\´5VÀèšæì ˜oëø¬žVÐ\¹4c”0|£ÏðLaxVA[Ÿ\%AÊë‹+”t'!EÝă•S­ŒÃøÄÉñœ{ÙJ›9ÑùöúwD¸¾+Ãs¬Q³pMb>ïIw¬µëà´§©x^Ç øžŠïëøæU,èXÄWu,኎â/â%?RÐÓyó?:~,a:ÜšŽ¹A佘ÅÜÌš1öݔ!¡íôX•U ì"ÌŸÌŸ*訿VÞ%Ýk=‹ Ïu}áËŽ˜þ¬›öÖ‡ñ³üœÓ/ðK¿Â¯uü/ëø-zU¼¢ãwR$¿6/‡\úibù2Èáì(¯ Q×róNz¥±*ØZ•×±BËeãÊ9�U—¨Ù ‡¯%È6�1ý�©qße–ãmÕÒ«Ê>9çùf†ý� ’Þ¬n 0�LOeà dÁ(;JÕú°QlHås9Óñ¥�X¶myÁg鬂=ñWkç"ß”r3lÚÕ�tò[+­”]‰n�åý‚ßáÙ´‚ã5 •´ ´§í~ßvC_«À×x�6¯ÎÓrZV3”]èàø@…ÎZ»JëKÔ!Û5˜õ²¼Ʀ¥†ÊË!8§“ëV…’Á»dÎ%M¿2Ì$­fxÂ^±¶¯b.P+�Ür�#>)ÒòAvRÔJñ~ˆÎ1‹eë*�ý®m›Á]¦ÂMµèt6H¼ÍÕ†’£gßœ‘›Þ?S€"½GÃñ§Šd £F©ïÎ?+h�ߨç2l¤ä—ÞÝr§óNàH¢Ï’w^>Ãs:5íæ�ëzwüU˜Å`(e»Ò:ÖW ¡"Þ‘Q#xO>‘s*þ¢ã¯ø§Ž»�¤‘’Ï–sÙ½d&†ƒç»£!;77§ >SÚ©ø7ÝvÝKy–�~ÆqÌ\¿mxžäð•ò¨õúìiÃIÛ¦·· xýWà*Á‰¹¬ys‡“×—m«>íç7)Éxõ¨øCþðú_Ÿ»XG |µ¯Á6Å1(x]°ëæ¾§lQ®ù°âx‚”œΡö(Ïs¡àŽá€¸‘Ì'ÑG1aݵºˆ5%V�Ç@3êh©Ÿ«:Ò(8¸"ø,ÕiœM ®Þ?�ºE„F^@¸Q�GdѾ%h“ ¨ï5‡æ¡w‡I<ªîjR±®™›[„y}Ý‹h˜\Kù Éy4ž_ÂÆIÙ4nZÀæîhs¸9ºˆ¦°#X8­\íÂfì&Ì=üÝ‹v´"�} Zœ!jÃ()÷b?ÒèX埚Å@Í‘å|RÌ2X[†—°urÿšO¶­x²]øPKÖÓh PKXËJmahjong/HeapAbstraction$1.class;õo×>nvvvNF®àüÒ¢äT·ÌœTF�ÔÄǤ⒢Ää’Ìü<½¬Ä²DF~×¼äœüâ̼tßÔ’Œüv.FÉÜÄŒ¬ü¼t}4=*†Œ <žyy©EÎ9‰ÅũŌ  côs�Šý“²R“KÄqèP``d`b€f dòXX�4 �äb. F�PKwN´º ÑPKXËJmahjong/HeapAbstraction.class�X xÇþG–´¶¼ÆÂƒN8L�%ƒ1w1‡ÁÁÅ6I ¸�¤éZ^ÛyWH+i›ôJÏ´é] é™4ô mhÁ`hSz¤GJ›¦mzß÷Ýô.Iùgµ¶eI>¾ÂÇîìÌ›ÿ½÷¿7ožxìò¹G¬Õ%Ø€´1 GòÁ�à îðÁ›™|‘��ûàÇKäèNwù fÖ^êÃËðr¯�˯,ÅjÜ]Aà×ûpÞ o,Žx“‚7—bÞ"ÞZ�wÊé#>¬ÁQ©ç˜\½Ï‡ûñ.)òîR¼ï•�÷)x¿œ~@N?èC-> E’�ã >(g>¤àÃ>„1P>ZŠ�áˆDÿ¸㤂OH£?Y�" J®ÅpFÁYCRÿ9¹|Þ‡|Júðiùùˆ}FÎ]PðYŸSðyµÅ0ôdS\K¥ô”€wKó¦6-!PÔ“è˜×Ú¯õí3�Þz~Ö7Çôx÷�f̰R–yCRKôE|Ý=Z³µÌäa�êy-m™ýš¥ÕÐY¦°Já­šúôè~=),(¾õ@:6 Åu#ª;’ÜYÕŒ6=Ù«w ”·îÓ´ú´‹×ÓX¹Úë54+�ÔV�]]?¢#Aø]û"™õ¸Æ©Í¦×5#ÒH ïú˜³'óºv·€»ÉìÖ¥-1CoO÷wéÉ�ZWœ3UQ³?‘¶ôŒµT¨G-›ÒéÁÚ<ÃWåÎ07wB[ÒOíñmºÄ­&ưŒ ³Ëˆ™FsÌèîÐ-Ê.›h=_Ÿ„/%Û›õa¾·'7jdBÚ´©+e%tª©É$T¤v¯@Y‡¥E÷óËajå¸ãÂ(ø‚‚G™JQÓГÖNÓ&wq0‹Ç&3×í}‘|ÊÍÂ’ë³fÇá¥1mŠÁªLéRÉÍz"©§tÃÒ¬ØÝ_«2R›·[&ÈävÚ73®õwuk5Ù,Õ¬˜Ì!$צv™³×HÇã5+í8åì™Ôm SáÀŒ¤jÍ _ðl5,Y,V'HÛ±kì-DÍ”8Ö¿BðËå§ÔXvvNè²jiѨžJÕ4,[&°68^Úem.\Ð|f:Õ›c2Ÿ+sv/•>¨hÆ *Úñ%[°U`ΈRn‡ÀµS+‹*¶¡EÅ.ìVñe|EÅ p;7�2ÇàGÓÉ$Sމ><ܦ¥úH©Šça»‚ÇT|ã†BÚô5_Çã~LKú—hÓ­>³;UîÅ7üx‚¯oúAÌoáÛ žTñ|WÅ÷ð}?PñCüˆI5& *~Œ‹*~‚Çýøi¹‚Ÿùñs¿À/Uü ¿VñüVÅïð{ÀyäUüIÊÿááWñW<¥âoø{–ÝÙT±ÎXºŠàŸ*þ…«øþ«âžVñ ž`%λTüO /ØF:4ѳgý¸,m»¢°ê!\Š(’@Ô^ž“l*4t•{…Û/<Ü"¼ªP¤\Ù˜C&põÄeŽ”‹bÏÇ-ª(¡ýÂ'JåçU¨‰21M`Ö8ŒÒ¨{™ë'‹¦\… LÛBò|�ÚÜbéI�ù)0£À±d³”ç½¼W·¶ÄRûäUI/ÙMÌs»eUå, _±|²SŽ¥F >]­/Ü*G ÏÊf«”ÅßÒbFj»~x®)UÂSfWK¦dqlä˜W�ÉÊáã/ÉéÓRíú!Ë>óÜï6ì�Ê`áè›i‹­ðŽ�ºÂغ#ù‡Û�ŠÝ¡ÛJZ¨”,¤u‰RÜ›­h¸}§ ÕØyžS:Üè•9ªvewK…2g°æµiŒûÒÜ–h²¬ª,T«È³²9–LY¹¤ïHH -S‰‡g¥m•’÷‚�׆I¶ÉÌ-&6 k¦pÊònÛ AZ¶0c)BôYþšX<µ 1¹ìWÑ Þÿ=àOØ£Ð1cÀܯ׷Ú}#­gøwkiÿè—"æ°Æ´šæþ4W\̶ÖÈô>Û4£;®§j2’Ù.°À±^GÆÛhý-fTN¼·6µ‰e·ƒ‡›LÍe‹<%x3M,‡ùØ€�ܨ��-G²ÕµßìKùvÉ­ö¸ *Çìoù¼‘3-Üéâ{~è \¡ði…Îý'|žÓ{÷œ�rÅ')$pŸUðÈ Ü¸%Xˆ™¨Á<,âêÍè ˜„­çWßå„-i Âw¥mu�€L³—ƒ„ªEBŽu;ù��´r/eÜ|ï *Í(;�i¡S(äïýóðK#§·…‡À‹êfgQy3‡PåÂÌj_2„ÙC„êÎá*p<Ç…,#–¢”Ï>—sÿ ÌÅ*Tc5c-MZÃÑuX�u$2BâÚ#;ù�] C`;�ôJ>ÂuÁã>±$|s×¹—ÔÉ—‡æÜÏ ªOØ›o²##½ê„B˜iš‰½ü ±O•Ø·8Ø�rJá;A˜itôjŽa—»±.£#àÄ5C˜'°Îð a¾ÀQ,–£‚ ,\ç¥jï j†°ˆÅüA5‡÷ø•'m¼ê³¸v‹‹ÐéÀ%oµƒ�š‚�Ï(CÔ�rô2ö}ä&FFö3­âäÄ ¥&nÇÛ…p_¦v·*¸-ô4\—è¡Oþ$rbºšo™oUçfHëZCásX"C³Ô…pn~¤QŒTâ‡ú‹½½ƒ¢¬�|†ìB½À –]@Ã(Ž×–¸ÓÙõûdQrè]ARË Ò¹¼-,“d…´d¥ 's 9ÈD=ÄÀ& š°È( Õ…m&©ºódŽîC~—¬†™-Bþh gï¯Â*¥Õíç±fOHœÁZfMn4ŽD³a�7`@Ïຣ¸ÍTŠ#¸]ŽÁâP Øl�ñ_(æãÔ„ ÀaäËZª3}çñ+'�_¹ûäHøW3ðàðázfi#éÙÀçFÜŠM$a3£ w±îÜÃÊóvÖ’#¬4÷‘k™ ó์jqýL†gT`pð Ú/ÁÅÔ •¼**«�XzBƒhÌ ßZ1ó+™'z "Wt’²í·hX¢Ü´•±†Ö7q%F©}Þ1K\%f#€WÙ¹äÆ«¹÷5x-^g�ÞF�ÞáŒ>‚ÎèN?PK(PO´ âPK XËJ mahjong/util/PKXËJmahjong/util/UnionFindSet.class­WëSWÿÝ$°‰(*⻈I�§ø(Aª¨´<ªA)öå’,°vÓd£Å¾ßï÷ûk§­_ü 3:€ÎtúÙé¿d§¿»‰$@ŒÓIöî½çž{Îïüι÷&þ½÷€üêÅœUÐã… gËQ‡p5'ÎU`ÏT`ž•²AÙR0ìå`Ä /ž“ÍyÙ\�MD*ŒVà".UC`Ü‹ËxA6/Và%¼¬à©pEªj²™�肘]Á¤€:`šz2×R)=%PÒgÚÉyEçÛ�’ªÁíªÖ’¶�xË�– ”GŒ)S³ÓI] sål×h_hpN›ž±Ì©Œð¢iXf¿aÆ"º-g»›¡n*5“)ݦ1ÀA—av·@­?ÏfØŠÇõ¨M¡À%OØŠé“aêÃé¹ =9ªMÄ)ÙWx‘ãÑYY’–@ŽdãŽLÌP/´V¸,P±µè,ƒr\0ŒÜO{üÊé #¶È]ÔÖc6É X¬ ”IK>Ì€é22q¸pûd£jJ·{�ÔŒe˜vF¾Ë(Ì‹@ó:S]yÒGô;Ä—I´,Ëèõ¯Ÿ¯úLªj 1“Ÿö"¶òÕÈ~\››ˆiõ¦~­¾­C ¦³Ì«¿«¯síLw!ν+�Œêý†,˜­ù®›¥¶ŠLq"ÇÙ9-5Íü«8€ƒ¾–²“ZbH·§­XªªÓ>œ®R`¨˜Á¬‚¸Š9˜*,WQ�Ã*hØ¿*žD§ŠW‘T‘‚­"-{WqMÅk˜Wp]ÅëxÇöª¼©â-¼­àïâ=ïãªø“´›QÅ'8©âSi»Ÿ©ø_p{­ †!®fM n}ìmN±^òâK|%ϧ¯¹‰µhTOpYg‘zåWëdÚtj•ek¦Òsz’‰Ú^¨˜y&MZÉ>-:-pÀ_Ô‚sR¤ŒëL³{JÆå•°R©úÖÖV�úMTü@nM[+3¨o¼æÙF9 ¹A»D}|3û´@Ñ—ÎêóNn·®8$dVÔ‹£SÊòÖµ9f}…rFÚq^!™êox j‰D|þq2ÝŸí@Ý'Ù„[IyÙ–k_!4ËŠ’¶©¤•NæT�,Že§Å,màfç:”°4£5�Vÿ&¬JÑU-ž–Wm®ä˜ð¡b¼¸ÃŠìSgC¸iÂ;µ¹ëo­HÁ·*¾Ã�*ºpZE›<×å´ óª5«· :‡6ÏGmR“3s¹‘‚Ÿî eͦÿäcÉÈ­ç43×SõÍ|€$œi­·pt>¡?ÞdÆeñµ�µ³a-�¶N~~8¼©xXÐÅõp�[l ºøáÝÃv‡œÑ|x­°_FYŽpìçè<Üì�àDð>\ã pß…§1x%ÀJ]ïÀ³Eà.ÊnSY Àv‡T@5<¨âgo§Éš¬Ã µq4ëæÝHí¤c«q å.ü ï*†ÉÑ„±#$lœè¯�¬Ij$HSWl�ç!š4ó[S£fãka|­ËñŲñµm>>ÅswÞšAUÓèa:–®+ázˆ–Œë3"Ï37QÖó1.“ w®ñÃz!2úØË7|e�÷Ú~Î|�Ÿð««×Ù.ôQ§Oó‘½1<�±ÿPKÉ`%UÔÆ PKXËJmahjong/util/Numberable.class]OMKÃ@œ—l›¦&Uƒ'ÞÚ íÅ›"‚"ª*¼mëÒ$¦©m’^¼xQðàðG‰oÓ‚’]xó>fæí~ÿ|~8DèB éÂÄ–ƒm;û8Îâü„`¶Ú·q6»WgꪘŽÔâFŽRîX•÷ÏKZŸ°Ée´¢Új^ÈtI[ƒD>É^*³Iïz”¨q~Ô¾#4†¹?\Êǵ“;œ‹±ºˆuᯖèIW«=X°=Ôàv§2JflVäqÚû#‚ê"±ÏÐG€´G‡«=Fb´: 7Nøíí²é Ò›8k¢ÎX•¼Ãx­H¦zh¬©]|�zçàŽx�0µÀø'±©½`ÔNµ­_Žƒ_PKV-ÿ™’PKXËJmahjong/util/Triple.class�S[OQþNwÛmË"PnŠ€ Û ÷‹\JEPÔôaôi)kY,-–­oþ~�/h6]«ðjÓ:�:©ùDâVL·ê”‹õ³0}€ ÜKPŸ…ÐAûÎ ¸‹öÝðUŠ4‚Ú_'O–V•ÖŽÔD*}‚P*s%5zõùzÉvÉ\-t³•>×6\!ÆvùcÎ>ºÕ�>##Ak8õ¡÷)"ÒÙå‡  QêCzü�›„†CÔú�~?ä¡Û&Ë!³$ŸOû"_ÞRXž¹¥rJsKÒ˜çˆ×Q ‘�áS¼%R…Ö\ú;´#̧¿AÛd¢ ýÏ=ÂóR$êdZBªDšúªr¬Kq½²¶Yƒf’„ŽÕ˜Æ24•1òÍP~aQCjEü”ÂèûR,¢`�ËŸÛ:C<Ñt =%e4Ó.qŃÊÿPõa ™S´þk\B¾‚išï õx–òÏQ¯�£„xÿPK–ïóõPKXËJ!mahjong/util/UnionFindSet$1.class;õo×>nvvvNF®àüÒ¢äT·ÌœTFÁмÌü<·Ì¼”àÔ½¬Ä²DF~×¼äœüâ̼tßÔ’Œüv.FéÜÄŒ¬ü¼týÒ’Ì}d]*†Œ <žyy©EÎ9‰ÅũŌ  ƒôs�Êý“²R“K$qêP``d`b€f dòXX�4 �äb. F�PK7À3¢ÒPKXËJmahjong/util/Pair.class�S]OA=ÓÝînËÒÒZ ~# l·¥åË/¨1!!4ÙÆ}ZÊZJ‹íÖÿÂ/ð…Mä#�¾ú“Œ1ޙݨi÷Á‡�;÷ΜsÏœ™ýþëã X�CF>>L«(ÆQÂŒŠYçTÌÇ¡!¯a�Ç»¼xOÃ} xþ�!úÊmw<†ôƾýÆ.5ìf½ôtgß©yK 1Ë­7m¯Ûväju–JJÇ©µš»"ŸãyÙmº^…aÊègè¯äžòIk—“nÓÙêî8íª½Ó Ê€Á{p^¾M«;Þš/.cäÂä)FΣ­V K¹2mÏîìù­$#·N+Îë®Ýè0 ‡Hͽ`´<»v°i =ä!‘x-Ëk»Íz¯¿Jm*åêìb?_u.¤X ;EÜjuÛ5gÍåÄžÙn»ÈwéÈbQÇR:2H1 õbU,é(ãCêÐÞÛoÑB×s%N¡ct)#½‚W»nc×i3°²Že¬è˜Äc†HaŒJžM »Èÿ”À F¿9'sO²ÆË×ÿû�׊}täðûš6ú]í7:ÐMvM†]^?nÑÿ ÓOAš›zâ"»D1óO>LûèD¡ù(Uæ)J“æ9˜™?EÄ,œBzO%†Ë4¦m‚Æ$bD� 2Ÿê í¸ŠkÑuÊŨù‘wàŠ(fßt#"õBFÈMÊÆèxL@Фƒ¯f£Ÿ!oK¼—µ-s¼u�èIo0ŽÛö 8êLþ”c˜ù¯P69C�¾ ¨ÇæD”I"Så·�¥éDèà¬YDi‡Jœ:îÐl‚MÑ „ô+ì‡�Ao+h¼Ep.§ø Úö9béøLÑT§YzÐO¥ MÐp†ä_CBv�ÌŸ&;Šâ޹$9ÍßPK€Èšo¦!PKXËJ%mahjong/util/UnionFindSet$Entry.class•“_oAÅÏÀ®v‹Z­µÒ*Vº ЊO6MLm m5T}r¡“v+,fYLüN>ØDÒÄ?€Êx†"4uù#ÉÞaîž9÷wgv~ýþñ@ ¥tdâ «*X*dUÈ%ÃcEM6dS Y9±?Û…†íök'²î?ˆW�#×ö;žl3ýd{Òõ"Û®ï}0Ê®+½­†Ýn˶Àr¥iŸ´hÒñ�Fá�ë´ÜÇ=¬J?Ý[A�•Ñš ÙÌÿjží~eÖõ��vš¸ £+¼ ÇuüM�bfŒîßVWß²ÚVë�}ÎTWîuš5éص3Ñ ©”"a×ë²MŒ"AÒc*ô[]-׬×ää5“M§Øá!hi8YWÔO§ ú†mÐäÅ4&“:Övÿï˜Æ˜®�ŸPµÕñêrÇQç6{Q�WfæpË@ WaHâº�¸©^¬ëx"�šÀ-`^ÆâG9†‹7'£ÐæGj°ÄKã�ÕÈC6\á,ƒ1óY>$LSA3§3Öosöœc˜ã¼•í"b�!jåºYVa+Ü…vÊ—$aœC„q—åöp û4}E‹×ýBwÌp—Ze»ÈQpŒXß¡}XD{É—”.â^_šbý�â²²ïYô[€85ðMS¬²†õŽÆÚ®´_ÂrI(Èü>H8Ô<¼­�CçÎ N/‹ÙÑJo[a†c’ÿtä¹m!¶ÀŸûPK€ _3tPK XËJmahjong/automata/PKXËJ.mahjong/automata/DFAEquivalenceChecker$1.class�Œ; AD«uuuÜØÈ@ÄÈEðâ ÄÀ´c³ÇÜßá <€‡GL ¬¦ªi¨~Ï×ý`�žßG—m•+Ù¦ZÃõv¹¹UiÍZŒ’U"ê"ù,ãš ƒ�QÚ©‰÷R&öì# L¯œdÖÄW¥½rÉÑOÄxNè|¥¹(¤ „j¤ÙýN™¨’0ùŽ@h૦¸ËCËmm—çÀuèS;oPKR1Æ«õPKXËJmahjong/automata/DFAState.class�VkSÔf~²»A.‚ŠVð‚º¨«mµ¶xC(Y¤íR­öúÂd“u7¡Úû½ý þ€ö‹쌠ؙŽýÔ™NS§Ï›Ö½@­Ã웜“sžsÎsNùëŸG¿x ?%Ѓá$qI#òUñZñ@=–ÄëO¢ R¼¬b2ÁkVj¦äq¥“@3I¼�«ò¸–À;¸.ïn¨xW¢¿×Œ÷ø&ð„ŠY1gv©¬ -»$VDÆsM+“3Ü!-9sÁ®W2ª~z6[‹K޽�)º"3=»4tžMŽç=WÁáí¬gn ß\µ�[î”(VǦ‚Ï.TkªÆLÃÊmª„ç:AýèØpÎn€ž\åqQ^qòÌ_¹¡ y±"N0Û³¦mºçLÕ”^#¦¯’¤À¯-kÚÆ¯0k”fĬEÍxê¿™y 6dŒÄ‚áÒÁ˜sÙŽŽTº®!ýµºÆmh‘8a'Žlï²Ù‹V‘ÏÏ”„]6]Ó±œLý?Æ7ò¿²ÑÒêüƒ¦×êž¡­ÑTš½k¥bn™ašŒ›ž°H[wØK! s(-{ï:9·dÚ º6²ðm-�;-Q˜Í‹þ Ãþ“§$}u 7pV1ÇêsŽWš3ÆL™OëFÎÇ¥±†cÈk8Œ~ {±OC/ö‘¡ rNYŒ†8¨ÂÐ0� GpZGôlɉ†A<¯á ^ѰSAû%ÇqËnI§ wÑÉ—Ûš°ÔŽe^¬vœÓP€­ÂÑPÄM %˜*Ê\xVð13¯š ¯‚ÜÄlϲTÜÒp§Y­†Oð)cÕR]¥Úà›»Z…ëÏGÔÏ3 ÚÕ �õ*¶œE¢ `_ÕTZœ—¡>Ãç Ž6šŠz•Ü¥_(ˆ‹bѺ­ ¯ xÞ³ç仑 o˜Ãî-â²¼‚,òLj[€­ó–å9%×ȳ¼:›Dz˜®Sâ¼Ç9®‰ìmD¦¡\¶s� àDj;ˆ\·Õ¼ŒËw}b”°+ÂòŒéyùæM4|5¾Ôð¾Ópçøo¤b`Ú+β‘ÉúogUÌ ™yO*’ŠÈEÖq–=ò©MضQ±D¹l°ø¿ŸlkˆLý¸°ó–Qî‡êóÚÊÑ_ˆÏô0¹½oºþ鈰¬œÉå¦âGî›§ª‡íÞÞ9É=üˆ£Sn(xŽRû)÷>!÷QæÎÙ”ñÇ5åßsóðÚÂgÇ�¢œ¦4‰(ÿ€>ý=ºŠˆ>°Š¨>¸Š˜þâ× iê¯4Q óÜ‹&ž=ˆa¡öbƒïd°^¦„ %wX`?%E&®ßGôÞ&L“¯<º§”iè«ué]Nð<‰¨“.é°ŒVº¨ƒëhŽâZ%éÀš5ßøE~™ÕGRk# †‘NQâæ#MR#ŸöÒ%rݺ¬k- V‘Ðc’ÀûHÜõ=%V'™’Ÿ‚*qºˆÒÃÕþ25Q(=âž�¯ÓA–pXøñØÝ�?�¼ƒxôîÀc$§ô5hƒü­£5‚ zÐ�s‹óŒp‘ÝF;FÐ�Q?J µ™¸ôÃ0ÓaþÇtm‡‚GhÖÑáÑ¡à!:×±3‚ÇèZCw…”] L0Äe6|{�ådMµ'ä"ÁO‡|v袹s×/dd »bϽ͌n/ûÙ©ˆ´Œ©L=Š ¾ÁE|ëwKÁ×øß�¬iÊå7hó&ÞBî_PK7Ç“8ý\ PKXËJmahjong/automata/NFA.class�VùWWþ&&$D·RE T\Á¬,*Vª]‡d€‘ÉLL&Vk÷}ß7éjmÏé´UÜÎééÏ=þK]¾7H ­œÃ[îÜûÝïÞwß}ùëŸhÃÏ~Ü�îr4 G d<àG ºeô£ ÝbèCŸúe p‡ÅpÄOƒÁjÂÛÂþa!{Ä�Gñ˜�ã ±RÅjX¬b~*𰩆„“ŒÃ,Õð )¶'dœ’‘’P×Ôø ­Úš„½ uì¤eŽF“¶>Ù.ÁsªU‚w$9*¡aæ3·Ñ.]3â‡,Ý´Ó¶Õ�R“cÔ.ëÐMÝÞ)¡-4 ëNÖMÇ$”tZq©êÕM­?“ÖRGÕaƒ’òQÍvX¦I3ÔÔ{R=­F3¶nD5›Ž+ø16Þ§&}if—Vƒú¨©Ú™ ­:f1Ü)€²ŽR¶›“šÍΊB­}¹ÄùMíÌ ½Mó„.Nèí³Cè¹»âAôt»ÚÙÀM'HÚÊØûã£Zz`DÂÊ":³n¹£RGÆ‚��¾’[xZ] $_rôlR£I�¡&†ãjãÌÁ7®ß"¡»H’n§™¿5Q4‰,µJßÌFãúÍV… ‘ŠeÍ×3Ü÷†ŽÐʤbZ—.ÊÔ×ßµg�@Q°û4b•‚ÕXâÍa÷¨é1â³Ì ¨*°‘QpO*8ƒ³T(`ÀºVðÎ)xZ ÛÁ<÷Z/¯PŸf�YñtUž âÙ*Ïñ¼‚𢂗𲂕‚ÍVl“ñŠ‚WñšŒ×¼�7¼…Í ÞF³‚wЮà]¼§`“�½�‚8Gȃئà#|,ãŸâ3Ÿã|U)|AL'_°Ð¦éªÛJ¨äÌt�¡¦¡òcÐbŒ¼â¶LóÞ'3é1 kÜ#ÈÓ�®°|‘YOïO$í³Î!ðT¼I+) ½¨²/f™¶ª›¼›uÅ{5Φù%¾šCå˜è¨_KXš£šÊÔXLK2’‘œF~fÓ�Ê6ÿf�d̘­[f´Ó2Ó™„–)±RûÕ³ÖšWUÐ[1ïe¥�©éœ\ãðöîÚ“W—óÞi†æøg²ËÓº9jh¶eæ.W‘“Ík0¾¤Ãê¨Å¥&Î×)Ž›‡ãÍéT2‚hÙã.Ò8Êx45!aém?+erÅÔ.ÎóÖû =D ¯~ÑäJiq=ÆVD'‹æ¦ûÝp¸­è±ä@æ&Çèd§¡¦ÓÙ(—:GØ^¼§óœ[Cÿ³Žd|«à~T° »ìÁ^¾9wºyÚ×¢½N«dQGÔ˜m¥xï‰ÜNÆ%ÆÝkYãÞBå€ij)‡¨xo埼‹—íT=ª7Ø×³†íyjÌK§}.Cç ïécÖåü¶M³¿vª†1¨óÌdüÄì®âᥛ_�¿Çøx±D´f®<âÁpf¾œüõ¶!ŽMÜm¤Ü˹*|R8ržpóx¥HB˜c5V‹9Ö£œ •XÊ/4ÓP\@©£qä&JŽ_EiïM”q–ûšÃ—ᙂo¨y åØÈÉÿ'ý‘–ëP<˜À ±ªð`(Ü2…ÊpK¤ùª€ëzpéß[‘I‡´à†ŸãJøH�áÔ3”b 8n'“>DùÛ´-Ô¬€÷oÔËX'#ºÑC¡ZOÚo6îåœ%Î¥‚åäL´eް�cÛŒê2Wµä7,(ÔÜÊ‘o𛋦R$su82…ê &õ2¼^3?­¯¡vrZT— ¯Ò1ê@-v0¹»œJà©-s™o¡¾°.�VJ<"È/B.¹„ï/3`Y^û�Rx»y>ê.Å�®u�Cñ<äkX˜å4…E…”z˜ñPpÐAóBR\FÄÜA¬,£­.f•‹Y*Mr±¸íÑó$§ÑüŒ Q‹Q—‘”‘ò¡iº>Äed|P,™gŠWÖ‡Œª{v-Öá9Bð¹2žçÃRÜ,”Þ"ãù>,G\,¿ ·Z¯Š×mbîE>Èx±`}I$¼L̽\{E\¸½¯Ák}xîÔëe¼Aðß)ìÜÕ€*¼¹nÜ#ÖÞÚno€÷–wïzß%^ï–ñ÷J¨Ê KXÝ“RGFõôp+‡­] -? 'ÒFÎÐwgÕÌH›„꜡Z¯š‘Pß3ªŽ©­y#‘låk¢‰á´j䳚„¾¹«í³†QÍh/XÊjëþÁÑ¶Ž¶Â”š7ô”Êù�];¢Â%xM˹¹v©ŠK‘rÊ�5Éc‰\ÂÐâÔÙžH'Œ kƒ ¹Þ|X‚»S�k@"­õåSƒZ¶_LrÆ;¬4!¡98ϱfG4„Q;˜O$ã;’IS²*(,T›sæÄRU䨣±ãŒ¨iÚÌß{™V—Y™ï£^ Œé¯„kƒ%áš§�pœQZ1“p(x>é[X¯;¥�ãž ³p9䨙"ª/­}Qº*:PV½““?$xY˜›çÆ|Ì7ž7æŠ-Ä:5ïϪi–}BOKèq0XH‹ƒŽ Ü¢&ýI55W›¬@4mX/!TZv³‘挬¦¦Z£æ�€gË‹jhÚZ2Þ"a_¹øT@ZÜ~¥Ñ^bkŸÙjMWK¸~! a*ê=˜×òŽqÁñEõ|6¦u%D£¨§x—3ôì‰+…¼‚íèR° ›ìÔÕØ"ãý >€Êø�‚ã> �²(Ø&œÖ„ÚSô´±[Í�˜]»dŽ!QІvh—qZÁýx€½V×ÙûØùz5cD�çê½xÐ�3õ2ÆýèV0�³ &1¥`çœPôY(>¢à’¡h–Ýó¸ïIähù!œcÏRðQ<¬àcø¸‚Oàœ‚Oâ‡q„æ>åǧ…¹Ï(ø,>§à|Þ�ûê=xÔ�/(ø"¾$ãË ¾‚Çd|UÁ×ðußÀ7YDsò.°|KÁ·ñzò]?¾'D¿Oú~ôü�Qï�9ùø©‚Ÿáç ¢èçIXék;�Œ™i0©r�…§Å ¦»¤9SR5.*ÎtêÉ$å¸EÙMjr‰ôpR3Ä~]kï¦YÊ »nΉË#…�„+ÊËΞ’ÐX©)ˆJn¬|D²Ôi�ç5ÿ’`)B~�_Òa'@‡ÅuäWl¦j,¦e»Ý�í9ÞåÓfпt.ŸÒ²âŒÒ³»ÔA­VdŽÕÆô´¡&Ò¹}Ú‰2ø˜ý�óäÅ\ªØ{‰'‘Û•Ê'̃žÊÜæZòŽ©¨ždÅԚωýZ²`™Nsså�ýÿn¡ {­ÆÎ�¬Ôî«éRgRÍÑ» çøoN¶‰+êo$\^é*%fúOdÄ£f2IÆuS™îºË&(¿¼ Pf*%vÑÇú)(¨äèÊyKö׳ôÝcèQ±A�‚U`µ³Ö+q:–Òšy÷¿’sY”h&o�-¿¨Ö!êò·.sJXi�ê^üQ^9qÂèï$¬ .”_y(©쇢¬~/¤þ aƒÓZ ûùÒÚMFÔþÙT¡”¿�Êø£‚?áo v‹3ôàùÿwqŠÝˆ§+8&¢ˆ“ée8‹qN¤ÇôãZk�yoá�¬Íœ9µ©âHÆãÜ‚=º~<ϲUö¤ÓZÖÜQ𣳓k볎ön5Oj¹&KpvÆYÇ<‚ÚÊ šûï¼-“•e›ç¯vªÉd4a~8TëyƒE»ˆy!¹+>¬åöÉøûÆ¢ü䶫̇5,•�ü„w£AÜÏH¹ÄíŒ_ã[Mz^œ ãkøtÌ+¤·ã©|ïàÌUœsñ÷‚ФPø \$ªNs†i|‹OvàR¾QƒU¨Ãj®tb§-¿™ßõUü]zîðYx&áuáaȽӨˆL æTA—Ï´´KÐDŠK[ÇQj�Ä¿¡iø&P{n’ ɺ3DV ëiÄ5hŠ:xÏkp¡ˆt<|Q‹fÒ!F*ÌXEh‰emY’êÉSM®»Ò^92‰ ]xNNcé@XèŸÀE}¡HË–msOc9-_¼ÍÃ… îI\8ðj…IMb¥D齂ºD~oó’·>à�Ä¥BoËÌHH†ZÞq4N`Õ69 ‹QÀ3Å€Z®Üûäý§Í o:PoF×Ãì60¿«˜¿uÌîz3»íŒÝ5¼Âv0vÛ1ÊlŽÑ¿[)yzp'ú°‡ÒkPõ$½2öÊØ'£GF¯Œ>û�'Ðû¸™zÞƒùi8 ¯™ìƒf*í¸l%±FBd`—™bòˆµÉ…³¸|WˆD¯ë3Ù§!„´à$š«è©¥Â*…S¯VÐ °Š®c=E©®­8Äz:b"ö@z‚«T¯ãÃ[®�®‡E"Ð#& pËsl�™b¦IEæÀ:URÂ7ÐèQ,g?»„�Ͳpˆïó¶…v)Gl ¶Ë-+W.le�…ÃJÄ™UͶr=^Ôíb_kû!‡#-ãh-Ö¯µ;ŒÐ¨ß'š­-°Š¿bwÈ!VÑ¢×œŽ“ùØb™�‘™]½Àl9,‡"áq¬/e>êw‰ÃÀf¾�¨eþv†ÂÖpÀ=� ÜOX쯹AfŠþ♑(úAˆÝŠÜï®*Vy£¥’©ÙÉ=»‹}¡‹¾›�ÛÃõ½fMÔBú¶šÕÛG¸ƒ¦p 5C&áÏø'þ‚òwSÝyž…›p¦Nâ¥&õJ¼ ¯¶©7£E½wÛÔÛ soÇÝÿPKV�&¿wPKXËJmahjong/automata/DFA.class�VkWWÝ3 LHA|⃪ø ´¾C-ÈC¬´A)ÚV‡d�$“ •¾kßï÷C@ýâl+íêê·®ÕÕÐÿRWÛ}'CB¦­k%wî¹çœ}ö9÷Ü;óÛ_wp7ÜØŽ“5؆~1œrsxBÌN+¹Q�“ `PÁ�5yáŒÎ*xÒƒ0†Å0èF-Îyp#î©B?ëÁ%\Nš€À1D…�.†qá³ ¡ˆ»1‰)±–P�T�’P�55SÏJ¨ MjÓZ gÆ�°n%Ô„ã±”fæ2º„ÖRmG(©ML©X@Ë™FR3µ@O_WX@� W-‘ÛÀò•v ›+8Ð<ªkQK ¡Žx*n—Ðä­àÓr^‚³Ûˆê‚x<¥æ’czfXKp¥&¦›‹ÑW{[–%VKedj@KÛömå6ÿ–žÊ]Å k#F2�3õEÙám9¯À "O$cÚ¹me Ju¸=ÅRxâÙ%Råz\`Ú)ýêb¤Ó•Œ ª4Wûâz"¬ÄKAZ‚ËÈ™Ìqhœ½P‘ȲjwþóŽjÃ3é|½Ý Þ�éY¾ë¡ñò© @îXÊÔ⩬].WG$a·�;lä2½/.:ÃEà6�¨Â‡*öÀ«bšU<*ÄÝh–°¶‘�8¥GCñ¬ÉíWq6`Qݯe'HJEr˜–ÐøÀT<§â*2*fð<·@Å xQÅKxYBý Ã0³fFKèæ„ÍÖUã•z¼ZWW=ü*^Ã5¯«xo æo©hÌ�☊#8¬âmüÞUñ*ÞÃ~| âC!øðS[‰Ã[)%4*†Æ&õˆÉCXÌòlNϱtµÅž4 -•°Î*w¶zW‰g{“isÆ:<”�i#‘ �Å“¹Ô¾›õä6-n'·�Gh�çÀŠYzø¹Dßêi-‘§tc‰²›ñ7R´q¤;¡eiµ¦„�µtãc|ò€\x/Uk‘ˆž&­vo¨¬,%ý9žKY;•Í%õL°¤™ŠŒXžq#Ó«E&$lóVDñ½+ç%tžÅJ�ÖE­c¢z»WÊc¥²‹{uȺ˜ä”>cÕ~ÝJ±X8—.öÓ2Ùî-»ÊDq[~ªâ3|%aG1n<5mLé��–‹jltm\‹˜F†¼=É¢¤à² ÆTŽ;®žJ¥ôŒµMb�_šš�—?2ýZ*šÐ³;óŽÁ%fa3OÅ‚r´.¥‡RæCVömY®íïÒ¸u_—°ë?å÷Ee;~ClãpB$ì¤$cÿ»ËdÞzœowŸ*u>ø9¶RòQ/óéöÍCòùï@¾MIÂ^Ž«à°üœØÌ�”-\mC€ÖÂï(%‰Ïzßpü …îN1�µ¢½›¡�‰‡œÚ)9 ylbûè¾E¸ª"\U9\á„Û·Nwº�Õä,̯ÝCõè<”�Ÿ`ò\2F|÷PÃ5÷8ü ðH¸�.NT ¿ v@äк€U2—÷ØB½ZçP¿€Õü£sh¹‹5ÀÖʸù÷O¢°�Ǻ;¨¼¿Ãú¼ßíBûYyà \8DçÃhâM¾‡‰ïÃ1G§Ð�1Ît<�|tYIªpÜG“‚ƒ¢[f~‡è›Ïr«]4Qž-l[µµØÃñHÁt‹mê$±rË~Ž|³Ø–ívCÔÒÒÿ-çM8· Yä]B³*ÈžN�$õ¼wŒfN>[}þ9l¸—og9¯ÜËJ6ʸŽUb¶IåžµÔðM„ΰDgù�F†ÉüË4b¬�\{� c¸ã¬“\¨ƒ \Å8›Ë³»h7H'¥®‚‹—’hUP Ÿ-Šžn î¼l{Ÿ ¦»P£f»FÒßê·ºåVYØ(“è)ôãA«(Àê{¨ýMüÍã‘ï±¾xØ,“õ7°Ç7¢žsz­è}øÒb%ás|�/ЈQû _ ÍE<�gþPK"XI Я PKXËJ8mahjong/automata/DFAEquivalenceChecker$CombinedDFA.classµVQSWþn€,IV‚€RÛTC m­I©4‚Z´ •J«e³¹ÀÂf7&§}êôgôøJg´ŸýQ�ž³YˆÂ§efÏ=÷Þs¾ó�{νáÍ?ý àŠaôá‹ ‹,‹/XÌ„H|ÆÜfm–ÅWaäp'‚nÌE0�»aÜÃ}Ö¾Vð€÷ò,n±È²È(XT°$Ð^Zצúóems˶6ÒZݱ˚£¥ïÌÏfûÓAgÓ¨Å&⾆sOëÆ3Í”–.s›Rß–Ur f ËpfVZvògá¿:ö�Øåì’èΖ\¬—‹²º¬MZ mH§àhެ ôÄÇò[Ú3-]w 3]�q Œ KsêU²�8ºŸõ èÂefØÙ’¿4ÐøçÖ0>ܪÐê¼!ÍRfìs�s¤èÛ ZÅMCÁ7a»îÌ•6dmi] uZ´£YRåfÏà‘õáËùv…JÝa·ßnù× Ÿž‚oÔû–%«9S«Õ¸@¿ÿ/ýÑ"flÊí¤°¦ë²V‹MMR“Grv¹H-U"�¹VéÅÞr;V�fˆi±ö^ g¨~3ÞuŽw0¹Á“ßþëàgnôpÁ®Wu9oðe½ä{‚óQñb*’ˆ«¸Æb£*†ð�Š‹¸ÄÚw*ú1 b *–Ùà{ÍÎD25¾�Žfù‚nq ÑNLbÚ³¾L#¯vPÍÛw|L¯Ó{ Óa�z0‘ÜG›Ÿí'þ¶?ÛOñ™g;âT!ÂûÇŒ 覛çç˜p›0€qîgéæøPK�ßóïé— PKXËJ,mahjong/automata/DFAEquivalenceChecker.classÍX‹Uþn²»“ìNèhaZ}¤ínÒ�¦Å“XIó )IZÙ´4¨Àdw’L²;³ìÌV j­ŠŠo+ò(ŠbE+R•MÒ(ß"¾_ÿ‹¨õ»“mvCg÷jýýÜÝÜ{çÜsÎ=ç»çœ{'¯ýûÜ+nÁßÃhÅ~w„Qƒ!Ù + #ˆ †¡`ïÂ�’’P0F‡$ù°lî’ÍISp·ìß-U¼'‚&¼7‚{po#W1ähBj˜ £S˜˜Ž`iÙdX�ÈI.'ù0Žâ}�Tô�¤½_6�Í¥òãRù‡äè„TùaQðQ ¨ƒ–eäzÓºãŽ@¤×ÎŒ›–‘êèu›–éî¨�Å zí”!°jˆ #ù̸‘ÕÇÓ¤¨¦ÓÞ<ª§ Ëh� eô©iÛšl×ó®�Ñ]½�úºü©ñ»®žœÖ³žBϼ�`ºId *‘¨£~3¥»\pØ…%#’Fœ1rÍeþt MëGõö¼k¦Û{ítÚHº¦mq}¨O˜“–îæsTÿЕSß]FMn·¯bzï]{ø•`4¦õÌxJo¾ènsÇN�éË3©òjñ2Ã7gè™ö„×uùY°CàÎ+nÁ ÀšâJåÔÜÑ!p¦´šgâ!‹`˜VŠ ú+½|CªM•íž Ño–l¦‘NuÉÔ'ì|.i ˜2ÖúZs³T¥¢Ûv¼u¤ä'T<‚O h¡‘\ŸbоÉl¦“ŠOã3*>‹Ï \½Lþ næ¤ÜçU|'U|�ªøSñ8+[ìtTIÛJ•ß!¿ïYñ÷"´XøÖŪ×U˜¢Ú�Ô]ï@®$_q¢ò’a¼†ß¬öÃþ°¼½Î“#V¡‡$ˆYÚôòÿéRî÷DÞòŸ™`9ù Ó™‘4açúõ$ƒo}¬*«ôvçÏt¦mÓb¹¼Â]¿l/Ë2ŒÑèGg4šKegÍ2á‹åH5¥;#Æ„5`y�ïæ ÊÍù­À�þ±[:÷Ãz6›>Öã Êûâ‘ÿº´,a3jSã@ñI&Z†wJÛ[å6_0— ø„$§—amåY&|Æ´dy+×q +Õêi²Q|µïÄb5(BÁÚáaø;�]U1¬’®A[�Ä•ƒµ Qe"­»¼© Üê h5,—L¬K™ŽkZòü &í|Ñóý¥ ¸SVÀ€c>h”h·HÚñ+]ýªHÕC'0Á¢à÷*þ€?«Àí*úЯb/z6–’´ŽÚ3Fû�wÃåEHŸÐ“L&îL$SzRðWÖ¬!ÛžÉÓ×˳ª¨`ñµO·RiÃi^ä,?´ˆªiMvU=–5.orqÉê²ñKg{õt:Á’Ò¥ào›WäKFu>l`V´B~B|óå-šmŸ6°ìƒ-³g9àíš­|Ù@‚”�|m÷ØÅŠ«œ9»€š±–Ömbµ#mstÓ³u ŒÍ¢®3ÔZ@}§²M¶uZPS´ºÂwi¡DÆäÓ,ÔH(àªS8(ûUçé¬×ê ˆžÇÕ�a9j”£H›žÃ5ó¸V M‹\Îaµä”ÑBç°˜Çu58}áE-XÀõ�õ-mZý,´34ºÆsm×°½�n4ÑÁÙÞ„-Äa6r~ºÑŒalÆ!RïÅV˜ˆÁA'ˆà#ìOÇg Éså¼�_�«æ…êìR°Ûûݪàí :p»ùÞÀ(Màe�­„þU¢['ÿY±mkFÚæ±Nðuà07^¼›:ZÀs²õ ¥g7Õ°Y/PÀ†]ÁÒÜF9·IÎ5 6›v‡Ö„V~ôkÏœ¾p:PòØîÅ:ôÒ³>´¡ŸôìÆí|Ú‡A~G°Ÿþß�{0„ûø”ÄÏÏ«PûO( ÞùŽüƒúnó´öDÃ2{‹µ‰<‘–Ö9lñL|i)®B^°í£s¾‚ÀV¾¾¾I �¬E�ãzÀíejÁ9Ä ˆË µ´¶•?+ZHSžGS‹ì½À x�ǧbà�]Be-Wc J<Öãfî¨Ì‘íèá¬ô<ñ/ô·²–0IÁA2KÑZ ò­#­…Â5ø#þ‚?A#x ºZèü'Œ)od#‹û½Ñ8†ÿPK”_Ž0}PKXËJ META-INF/þÊPKXËJn›‰¦CD=META-INF/MANIFEST.MFPK XËJÂmahjong/PK XËJ èmahjong/pta/PKXËJ� Çb¤Ëmahjong/pta/Field.classPKXËJ3å"%¤Éûmahjong/pta/Type.classPKXËJ'úÉÂØãmahjong/pta/Obj.classPKXËJXÄÙÇgþmahjong/pta/PTAProvider.classPK XËJmahjong/pta/doop/PKXËJÐãÖ‚ Ô#?mahjong/pta/doop/ObjManager$1.classPKXËJ»œég.%0mahjong/pta/doop/Options.classPKXËJ\é½DªŠ&ª mahjong/pta/doop/DoopPTAProvider.classPKXËJãáóã´!¨ mahjong/pta/doop/ObjManager.classPKXËJºX#_¨ã(Úmahjong/pta/doop/DoopPTAProvider$1.classPKXËJ½fÑ�ï–Ømahjong/pta/doop/DoopObj.classPKXËJ˜4 .Ñmahjong/pta/doop/DoopType.classPKXËJf’ ç$æ&Žmahjong/pta/doop/DoopItemManager.classPKXËJ¶š+Ï mahjong/pta/doop/DoopField.classPKXËJ^o‰!Á2mahjong/pta/doop/DoopPTAProvider$ObjIterator.classPKXËJÈ~0g\^#mahjong/pta/doop/FieldManager.classPKXËJ Ïå‹� 2­mahjong/pta/doop/DoopPTAProvider$FPTIterator.classPKXËJ�Bò=η-&$mahjong/pta/doop/ObjManager$TypeManager.classPKXËJu  êO&mahjong/pta/doop/DataBase.classPK XËJ ¹2mahjong/fpg/PKXËJkÞý•• $ã2mahjong/fpg/FieldPointstoGraph.classPKXËJB6¶t›g$Ê8mahjong/HeapAbstraction$DFAMap.classPK XËJ ·;mahjong/main/PKXËJÖÓh â;mahjong/main/DoopMain.classPKXËJwN´º Ñ6Dmahjong/HeapAbstraction$1.classPKXËJ(PO´ â#Emahjong/HeapAbstraction.classPK XËJ uOmahjong/util/PKXËJÉ`%UÔÆ  Omahjong/util/UnionFindSet.classPKXËJV-ÿ™’ÁUmahjong/util/Numberable.classPKXËJ–ïóõWmahjong/util/Triple.classPKXËJ7À3¢Ò!YZmahjong/util/UnionFindSet$1.classPKXËJ€Èšo¦!J[mahjong/util/Pair.classPKXËJ€ _3t%5^mahjong/util/UnionFindSet$Entry.classPK XËJ»`mahjong/automata/PKXËJR1Æ«õ.ê`mahjong/automata/DFAEquivalenceChecker$1.classPKXËJ7Ç“8ý\ ñamahjong/automata/DFAState.classPKXËJôý+Õ ;gmahjong/automata/NFA.classPKXËJV�&¿w!®mmahjong/automata/DFAFactory.classPKXËJ"XI Я ¼vmahjong/automata/DFA.classPKXËJ�ßóïé— 8Ô|mahjong/automata/DFAEquivalenceChecker$CombinedDFA.classPKXËJ”_Ž0},#�mahjong/automata/DFAEquivalenceChecker.classPK--O úˆREADME.txt0000664000176100017620000001103213120364717010752 0ustar yueyueMAHJONG is implemented as a standalone tool for building heap abstraction to significantly improve the efficiency of points-to analysis while achieving almost the same precision for an important class of type-dependent clients, e.g., call graph construction. To demonstrate the usefulness of MAHJONG to points-to analysis, we have integrated MAHJONG with DOOP (version r160113), a state-of-the-art context-sensitive points-to analysis framework for Java. This tutorial introduces how to install and use MAHJONG together with DOOP. ---------------- | Requirements | ---------------- To use MAHJONG, you need to get JDK 1.8 (or later) and Python 2.x installed on your system. ------------------------- | Building instructions | ------------------------- We have provided a pre-compiled jar of MAHJONG, i.e., mahjong.jar in the unpack directory. To build MAHJONG by yourself, you just need to switch to the unpack directory and run script: $ ./compile.sh ------------------------------------------------- | Instructions of Integrating MAHJONG with DOOP | ------------------------------------------------- 1. Download DOOP (r160113) from http://doop.program-analysis.org/download.html. 2. Unzip DOOP, then set environment variable DOOP_HOME to the directory where you unpack DOOP by: $ export DOOP_HOME=unpack_directory/doop-r160113-bin/ 3. Put doop-mahjong.patch in $DOOP_HOME and then apply it to DOOP by: $ cd $DOOP_HOME $ patch -p1 < doop-mahjong.patch 4. Put mahjong.jar in $DOOP_HOME. Now, MAHJONG has been integrated with DOOP. In addition, to run DOOP, users should follow the instructions in $DOOP_HOME/README to setup the applications and library (to be analyzed) and configure the corresponding paths. ---------- | Usages | ---------- After integration, you can run the points-to analyses provided by DOOP with the heap abstraction constructed by MAHJONG. To enable MAHJONG, you just need to use this option (and the usages of DOOP remain the same): -mahjong For example, let us analyze luindex benchmark of DaCapo using the 3-object-sensitive points-to analysis with the heap abstraction constructed by MAHJONG: $ ./run -jre1.6 -mahjong 3-object-sensitive+2-heap jars/dacapo/luindex.jar Then MAHJONG is run and will automatically construct the heap abstraction (by testing the equivalence of automata to merge the type-consistent objects), which will be used by later precise points-to analysis. To save user's time, MAHJONG will cache its heap abstractions for the programs that have been analyzed. If it is the first time to run MAHJONG for a program, MAHJONG will construct heap abstraction for it and cache the heap abstraction. Otherwise, the cached heap abstraction will be directly used without running MAHJONG again. MAHJONG may affect the reflection resolution of DOOP by merging some string constants, thus we suggest you to use the reflection analysis results generated by TamiFlex. We have provided the option for DOOP (after integrated with MAHJONG) to do this: -refl-log For example, let us analyze luindex again. This time, we let DOOP use the reflection analysis results created by TamiFlex (suppose they are in the file "$DOOP_HOME/refl.log"): $ ./run -jre1.6 -mahjong -refl-log refl.log 3-object-sensitive+2-heap jars/dacapo/luindex.jar ----------------------------------------------- | Reproducibility of the Results in Our Paper | ----------------------------------------------- Generally, using this release version to analyze the programs in our paper ("Efficient and Precise Points-to Analysis: Modeling the Heap by Merging Equivalent Automata", PLDI'17) will generate the client results with smaller numbers than those in the paper. This is due to some dynamically loaded classes missed by DOOP. DOOP builds closed-world without considering dynamic class loading, so some classes that are dynamically loaded during runtime will be missed by DOOP during analysis. In the evaluation of our paper, to build a more sound closed-world, we manually adds these classes (discovered by TamiFlex but missed by DOOP) to DOOP’s closed-world. But we do not do this in the release version. As a result, DOOP in the evaluation of the paper reaches more code and genetates client results with larger numbers than this release version. To reproduce the results in our paper, please download the artifact from MAHJONG website. ----------- | Licence | ----------- GPL v3 Please feel free to contact the authors if you have any concerns. src/0000775000176100017620000000000013120354112010032 5ustar yueyuesrc/mahjong/0000775000176100017620000000000013120354112011455 5ustar yueyuesrc/mahjong/pta/0000775000176100017620000000000013120354112012241 5ustar yueyuesrc/mahjong/pta/Obj.java0000664000176100017620000000062613120354112013622 0ustar yueyuepackage mahjong.pta; import mahjong.util.Numberable; /** * A class used to represent abstract objects in points-to analysis. * To avoid the name collision between this class and java.lang.Object, * it is named "Obj". * * @author Tian Tan * @author Yue Li */ public abstract class Obj extends Numberable { public abstract String getName(); public abstract Type getType(); } src/mahjong/pta/PTAProvider.java0000664000176100017620000000074213120354112015246 0ustar yueyuepackage mahjong.pta; import java.util.Iterator; import mahjong.util.Triple; /** * @author Tian Tan * @author Yue Li */ public interface PTAProvider { /** * * @return an iterator for each object in the points-to set */ public Iterator objIterator(); /** * * @return an iterator for every field points-to relation, * including instance field and array objects */ public Iterator> fptIterator(); } src/mahjong/pta/Field.java0000664000176100017620000000024013120354112014123 0ustar yueyuepackage mahjong.pta; import mahjong.util.Numberable; /** * @author Tian Tan * @author Yue Li */ public abstract class Field extends Numberable {} src/mahjong/pta/Type.java0000664000176100017620000000023713120354112014027 0ustar yueyuepackage mahjong.pta; import mahjong.util.Numberable; /** * @author Tian Tan * @author Yue Li */ public abstract class Type extends Numberable {} src/mahjong/pta/doop/0000775000176100017620000000000013120354112013202 5ustar yueyuesrc/mahjong/pta/doop/DoopType.java0000664000176100017620000000065013120354112015611 0ustar yueyuepackage mahjong.pta.doop; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ public class DoopType extends Type { private final String typeName; private final int id; DoopType(String typeName, int id) { this.typeName = typeName; this.id = id; } @Override public int getID() { return id; } @Override public String toString() { return typeName; } } src/mahjong/pta/doop/DoopItemManager.java0000664000176100017620000000107213120354112017060 0ustar yueyuepackage mahjong.pta.doop; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * @author Tian Tan * @author Yue Li */ abstract class DoopItemManager { private Map name2item = new HashMap<>(); protected int count = 0; T get(String name) { T item = name2item.get(name); if (item == null) { item = createItem(name); name2item.put(name, item); } return item; } protected abstract T createItem(String name); Collection getAllItems() { return name2item.values(); } } src/mahjong/pta/doop/FieldManager.java0000664000176100017620000000041513120354112016363 0ustar yueyuepackage mahjong.pta.doop; import mahjong.pta.Field; /** * @author Tian Tan * @author Yue Li */ class FieldManager extends DoopItemManager { @Override protected Field createItem(String name) { return new DoopField(name, ++count); } } src/mahjong/pta/doop/Options.java0000664000176100017620000000341613120354112015504 0ustar yueyuepackage mahjong.pta.doop; /** * @author Tian Tan * @author Yue Li */ public class Options { private String dbPath; private String cachePath; private String app; private String outPath; private boolean isDebug = false; public String getDbPath() { return dbPath; } public void setDbPath(String dbPath) { this.dbPath = dbPath; } public String getCachePath() { return cachePath; } public void setCachePath(String cachePath) { this.cachePath = cachePath; } public String getApp() { return app; } public void setApp(String app) { this.app = app; } public String getOutPath() { return outPath; } public void setOutPath(String outPath) { this.outPath = outPath; } public boolean isDebug() { return this.isDebug; } public void setIsDebug(boolean isDebug) { this.isDebug = isDebug; } public static Options parse(String[] args) { Options opt = new Options(); for (int i = 0; i < args.length; ++i) { if (args[i].equals("-db")) { i = shift(args, i); opt.setDbPath(args[i]); } else if (args[i].equals("-cache")) { i = shift(args, i); opt.setCachePath(args[i]); } else if (args[i].equals("-app")) { i = shift(args, i); opt.setApp(args[i]); } else if (args[i].equals("-out")) { i = shift(args, i); opt.setOutPath(args[i]); } else if (args[i].equals("-debug")) { opt.setIsDebug(true); } else { throw new RuntimeException("Unexpected options: " + args[i]); } } return opt; } private static int shift(String[] args, int index) { if (args.length == index + 1) { System.err.println("error: option " + args[index] + " requires an argument"); System.exit(1); } return index + 1; } } src/mahjong/pta/doop/DoopField.java0000664000176100017620000000062313120354112015713 0ustar yueyuepackage mahjong.pta.doop; import mahjong.pta.Field; /** * @author Tian Tan * @author Yue Li */ public class DoopField extends Field { private final String sig; private final int id; DoopField(String sig, int id) { this.sig = sig; this.id = id; } @Override public int getID() { return id; } @Override public String toString() { return sig; } } src/mahjong/pta/doop/DoopPTAProvider.java0000664000176100017620000000452613120354112017035 0ustar yueyuepackage mahjong.pta.doop; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import mahjong.pta.Field; import mahjong.pta.Obj; import mahjong.pta.PTAProvider; import mahjong.util.Triple; /** * @author Tian Tan * @author Yue Li */ public class DoopPTAProvider implements PTAProvider { private static final String ARR_FIELD = "@ARRAY"; private final DataBase db; private final ObjManager objManager; private final FieldManager fieldManager; public DoopPTAProvider(DataBase db) { this.db = db; this.objManager = new ObjManager(db); this.fieldManager = new FieldManager(); } @Override public Iterator objIterator() { return new ObjIterator(); } @Override public Iterator> fptIterator() { return new FPTIterator(); } private class ObjIterator implements Iterator { private Iterator> objIter; private ObjIterator() { objIter = db.query("OBJ").iterator(); } @Override public boolean hasNext() { return objIter.hasNext(); } @Override public Obj next() { if (hasNext()) { List obj = objIter.next(); return objManager.get(obj.get(0)); } else { throw new NoSuchElementException(); } } } private class FPTIterator implements Iterator> { private Iterator> fptIter; private Iterator> aptIter; private FPTIterator() { fptIter = db.query("IFPT").iterator(); aptIter = db.query("APT").iterator(); } @Override public boolean hasNext() { return fptIter.hasNext() || aptIter.hasNext(); } @Override public Triple next() { if (fptIter.hasNext()) { List fpt = fptIter.next(); Obj baseObj = objManager.get(fpt.get(0)); Field field = fieldManager.get(fpt.get(1)); Obj obj = objManager.get(fpt.get(2)); return new Triple(baseObj, field, obj); } else if (aptIter.hasNext()) { List apt = aptIter.next(); Obj array = objManager.get(apt.get(0)); Field field = fieldManager.get(ARR_FIELD); Obj obj = objManager.get(apt.get(1)); return new Triple(array, field, obj); } else { throw new NoSuchElementException(); } } } } src/mahjong/pta/doop/DataBase.java0000664000176100017620000001206013120354112015510 0ustar yueyuepackage mahjong.pta.doop; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * The class representing database of Doop. * * @author Tian Tan * @author Yue Li */ public class DataBase { public static final Map QUERY = new HashMap<>(); private static final String CMD = "bloxbatch -db %s -query %s"; private static final String SEP = ", "; private Map>> queryResults = new HashMap<>(); private final File dbDir; private final File cacheDir; private final String program; static { QUERY.put("OBJ", "_(obj)<-Stats:Simple:InsensVarPointsTo(obj,_)."); QUERY.put("OBJ_TYPE", "_[obj]=type<-" + "HeapAllocation:Type[obj]=type," + "Stats:Simple:InsensVarPointsTo(obj,_)."); QUERY.put("IFPT", "_(baseheap,field,heap)<-" + "InstanceFieldPointsTo(_,heap,field,_,baseheap)."); QUERY.put("APT", "_(array,heap)<-ArrayIndexPointsTo(_,heap,_,array)."); } public DataBase(File dbDir) { this.dbDir = null; this.cacheDir = null; this.program = null; init(dbDir); } public DataBase(File dbDir, File cacheDir, String program) { this.dbDir = dbDir; this.cacheDir = cacheDir; this.program = program; } /** Return the results of the give query. */ List> query(String query) { if (this.cacheDir == null) { return queryResults.get(query); } else { return queryDoopWithCache(query); } } /** * Query Doop database and store the query results to queryResults. * @param dbDir the directory of Doop database. */ private void init(File dbDir) { System.out.println("Querying Doop database ..."); for (String query: QUERY.keySet()) { queryDoop(dbDir, query); } } private void queryDoop(File dbDir, String query) { List> results = new LinkedList<>(); try { Process proc = null; String cmd = String.format(CMD, dbDir.getAbsolutePath(), QUERY.get(query)); Runtime rt = Runtime.getRuntime(); proc = rt.exec(cmd); BufferedReader reader = new BufferedReader(new InputStreamReader( proc.getInputStream())); String line = null; while ((line = reader.readLine()) != null) { results.add(line2list(line)); } if (proc != null) { proc.waitFor(); } reader.close(); queryResults.put(query, results); } catch (InterruptedException e) { throw new RuntimeException("Exception during query: " + QUERY.get(query)); } catch (IOException e) { throw new RuntimeException("Query " + query + " fails, " + "caused by " + e.getMessage()); } } /** Query Doop and cache the results for later uses. */ private List> queryDoopWithCache(String query) { BufferedReader reader = getCacheReader(query); List> result = new LinkedList<>(); try { Process proc = null; if (reader == null) { String cmd = String.format(CMD, dbDir.getAbsolutePath(), QUERY.get(query)); Runtime rt = Runtime.getRuntime(); proc = rt.exec(cmd); reader = new BufferedReader(new InputStreamReader( proc.getInputStream())); } String line = null; while ((line = reader.readLine()) != null) { result.add(line2list(line)); } if (proc != null) { proc.waitFor(); // bloxbatch is invoked which means that the cache file // does not exist. So we are going to create the cache. createCache(query, result); } reader.close(); } catch (InterruptedException e) { throw new RuntimeException("Exception during query: " + QUERY.get(query)); } catch (IOException e) { throw new RuntimeException("Query " + query + " fails, " + "caused by " + e.getMessage()); } return result; } private BufferedReader getCacheReader(String query) { BufferedReader reader = null; File path = getCacheFile(query); if (path.exists()) { try { reader = new BufferedReader(new FileReader(path)); } catch (IOException e) { throw new RuntimeException("Read cache file fails: " + path); } } return reader; } private File getCacheFile(String query) { File cacheFile = null; if (cacheDir != null) { String fileName = String.format("%s.%s", program, query); String filePath = String.format("%s%s%s", cacheDir.getAbsolutePath(), File.separator, fileName); cacheFile = new File(filePath); } return cacheFile; } private void createCache(String query, List> content) throws IOException { File path = getCacheFile(query); PrintWriter writer = new PrintWriter(new FileWriter(path)); content.forEach(list -> { writer.println(String.join(SEP, list)); }); writer.close(); } private List line2list(String line) { return Arrays.asList(line.trim().split(SEP)); } } src/mahjong/pta/doop/ObjManager.java0000664000176100017620000000161013120354112016050 0ustar yueyuepackage mahjong.pta.doop; import java.util.HashMap; import java.util.Map; import mahjong.pta.Obj; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ class ObjManager extends DoopItemManager { private final Map typeMap; private final TypeManager typeManager = new TypeManager(); ObjManager(DataBase db) { typeMap = new HashMap<>(); db.query("OBJ_TYPE").forEach(list -> { String objName = list.get(0); String typeName = list.get(1); typeMap.put(objName, typeName); }); } @Override protected Obj createItem(String name) { String typeName = typeMap.get(name); return new DoopObj(name, typeManager.get(typeName), ++count); } private class TypeManager extends DoopItemManager { @Override protected Type createItem(String name) { return new DoopType(name, ++count); } } } src/mahjong/pta/doop/DoopObj.java0000664000176100017620000000132513120354112015402 0ustar yueyuepackage mahjong.pta.doop; import mahjong.pta.Obj; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ public class DoopObj extends Obj { private final String objName; private final Type type; private final int id; private final String rep; DoopObj(String objName, Type type, int id) { this.objName = objName; this.type = type; this.id = id; this.rep = String.format("%s(%d)", objName, id); //this.rep = objName; } @Override public int getID() { return id; } @Override public String getName() { return objName; } @Override public Type getType() { return type; } @Override public String toString() { return rep; } } src/mahjong/HeapAbstraction.java0000664000176100017620000000774613120354112015405 0ustar yueyuepackage mahjong; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; import mahjong.automata.DFA; import mahjong.automata.DFAEquivalenceChecker; import mahjong.automata.DFAFactory; import mahjong.automata.DFAState; import mahjong.fpg.FieldPointstoGraph; import mahjong.pta.Obj; import mahjong.pta.Type; import mahjong.util.UnionFindSet; /** * @author Tian Tan * @author Yue Li */ public class HeapAbstraction { private final FieldPointstoGraph fpg; private final DFAFactory dfaFactory; private final DFAEquivalenceChecker dfaEqChecker; /** This map would be manipulated by multiple threads * thus it should be concurrent hashmap. */ private Map canMerged; public HeapAbstraction(FieldPointstoGraph fpg) { this.fpg = fpg; this.dfaFactory = new DFAFactory(fpg); this.dfaEqChecker = new DFAEquivalenceChecker(); } public Map computeMergedObjectMap() { UnionFindSet uf = modelHeap(); Map mom = convertToMap(uf.getDisjointSets()); return mom; } /** * Modeling the heap by checking the equivalence of automata. */ private UnionFindSet modelHeap() { canMerged = new ConcurrentHashMap<>(); Set allObjs = fpg.getAllObjs(); UnionFindSet uf = new UnionFindSet<>(allObjs); // group the objects by their types Map> groupedObjs = allObjs .stream() .collect(Collectors.groupingBy( obj -> obj.getType(), Collectors.toSet())); groupedObjs.entrySet() .parallelStream() .forEach(entry -> { Set objs = entry.getValue(); DFAMap dfaMap = new DFAMap(); for (Obj o1 : objs) { if (canBeMerged(o1, dfaMap)) { for (Obj o2 : objs) { if (canBeMerged(o2, dfaMap)) { if (o1.getID() <= o2.getID() && !uf.isConnected(o1, o2)) { if (canBeMerged(o1, o2, dfaMap)) { uf.union(o1, o2); } } } } } } }); return uf; } /** * * @param o1 * @param o2 * @return whether o1 and o2 can be merged. */ private boolean canBeMerged(Obj o1, Obj o2, DFAMap dfaMap) { if (o1 == o2) { return true; } DFA dfa1 = dfaMap.retrieveDFA(o1); DFA dfa2 = dfaMap.retrieveDFA(o2); return dfaEqChecker.isEquivalent(dfa1, dfa2); } /** * * @param o * @return whether o can be merged with other objects. */ private boolean canBeMerged(Obj o, DFAMap dfaMap) { if (!canMerged.containsKey(o)) { boolean result = true; // Check whether the types of objects pointed (directly/indirectly) // by o are unique. DFA dfa = dfaMap.retrieveDFA(o); for (DFAState s : dfa.getStates()) { if (dfa.outputOf(s).size() > 1) { // Types pointed (directly/indirectly) by o are not unique. result = false; break; } } canMerged.put(o, result); } return canMerged.get(o); } private static Map convertToMap(Collection> objModel) { Map map = new HashMap<>(); objModel.forEach(objs -> { Obj rep = selectRepresentative(objs); objs.forEach(obj -> map.put(obj, rep)); }); return map; } private static Obj selectRepresentative(Set objs) { return objs.stream().findFirst().get(); } /** * During equivalence check, each thread holds a DFAMap which contains * the DFA of the objects of the type. After comparison, the DFAMap * and its containing DFA will be released to save memory space. * */ private class DFAMap { private final Map dfaMap = new HashMap<>(); private DFA retrieveDFA(Obj o) { if (!dfaMap.containsKey(o)) { DFA dfa = dfaFactory.getDFA(o); dfaMap.put(o, dfa); } return dfaMap.get(o); } } } src/mahjong/fpg/0000775000176100017620000000000013120354112012231 5ustar yueyuesrc/mahjong/fpg/FieldPointstoGraph.java0000664000176100017620000000465113120354112016647 0ustar yueyuepackage mahjong.fpg; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import mahjong.pta.Field; import mahjong.pta.Obj; import mahjong.pta.PTAProvider; /** * @author Tian Tan * @author Yue Li */ public class FieldPointstoGraph { private PTAProvider provider; private Map>> pointsTo = new HashMap<>(); private Map>> pointedBy = new HashMap<>(); public FieldPointstoGraph(PTAProvider provider) { this.provider = provider; provider.objIterator().forEachRemaining(this::insertObj); provider.fptIterator().forEachRemaining(triple -> { Obj baseObj = triple.getFirst(); Field field = triple.getSecond(); Obj obj = triple.getThird(); insertFPT(baseObj, field, obj); }); } public PTAProvider getPTAProvider() { return provider; } public Set getAllObjs() { return pointsTo.keySet(); } public Set outFieldsOf(Obj baseObj) { return pointsTo.get(baseObj).keySet(); } public Set inFieldsOf(Obj obj) { return pointedBy.get(obj).keySet(); } public Set pointsTo(Obj baseObj, Field field) { return pointsTo.get(baseObj).get(field); } public Set pointedBy(Obj obj, Field field) { return pointedBy.get(obj).get(field); } public boolean hasFieldPointer(Obj obj, Field field) { return pointsTo.get(obj).containsKey(field); } private void insertObj(Obj obj) { if (!pointsTo.containsKey(obj)) { pointsTo.put(obj, new HashMap<>()); } if (!pointedBy.containsKey(obj)) { pointedBy.put(obj, new HashMap<>()); } } /** * Insert field points-to relation. * @param baseObj the base object * @param field a field of `baseObj' * @param obj the object pointed by `field' */ private void insertFPT(Obj baseObj, Field field, Obj obj) { insertPointsTo(baseObj, field, obj); insertPointedBy(baseObj, field, obj); } private void insertPointsTo(Obj baseObj, Field field, Obj obj) { Map> fpt = pointsTo.get(baseObj); if (!fpt.containsKey(field)) { fpt.put(field, new HashSet<>()); } fpt.get(field).add(obj); } private void insertPointedBy(Obj baseObj, Field field, Obj obj) { Map> fpb = pointedBy.get(obj); if (!fpb.containsKey(field)) { fpb.put(field, new HashSet<>()); } fpb.get(field).add(baseObj); } } src/mahjong/main/0000775000176100017620000000000013120354112012401 5ustar yueyuesrc/mahjong/main/DoopMain.java0000664000176100017620000000650113120354112014754 0ustar yueyuepackage mahjong.main; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.Map; import mahjong.HeapAbstraction; import mahjong.fpg.FieldPointstoGraph; import mahjong.pta.Obj; import mahjong.pta.doop.DataBase; import mahjong.pta.doop.DoopPTAProvider; import mahjong.pta.doop.Options; /** * @author Tian Tan * @author Yue Li */ public class DoopMain { private static final char SEP = '\t'; private static final char EOL = '\n'; public static void main(String[] args) throws FileNotFoundException { run(args); } public static void run(String[] args) throws FileNotFoundException { Options opt = Options.parse(args); FieldPointstoGraph fpg = buildFPG(opt.getDbPath()); System.out.print("Creating heap abstraction ... "); long start = System.currentTimeMillis(); HeapAbstraction heapAbs = new HeapAbstraction(fpg); Map mom = heapAbs.computeMergedObjectMap(); long end = System.currentTimeMillis(); DoopMain.outputElapsedTime(start, end); outputStatistics(mom); System.out.printf("Writing Mahjong heap abstraction to %s ...\n", opt.getOutPath()); File outputFile = new File(opt.getOutPath(), "MahjongHeapAbstraction.facts"); writeMergedObjectMap(mom, outputFile); } public static FieldPointstoGraph buildFPG(String dbPath) { File dbDir = new File(dbPath); DataBase db = new DataBase(dbDir); System.out.print("Building FPG (Field Points-to Graph) ... "); long start = System.currentTimeMillis(); DoopPTAProvider fptProvider = new DoopPTAProvider(db); FieldPointstoGraph fpg = new FieldPointstoGraph(fptProvider); long end = System.currentTimeMillis(); outputElapsedTime(start, end); return fpg; } public static FieldPointstoGraph buildFPG(String dbPath, String cachePath, String app) { File dbDir = new File(dbPath); File cacheDir = new File(cachePath); DataBase db = new DataBase(dbDir, cacheDir, app); System.out.print("Building FPG (Field Points-to Graph) ... "); long start = System.currentTimeMillis(); DoopPTAProvider fptProvider = new DoopPTAProvider(db); FieldPointstoGraph fpg = new FieldPointstoGraph(fptProvider); long end = System.currentTimeMillis(); outputElapsedTime(start, end); return fpg; } public static void outputElapsedTime(long start, long end) { System.out.printf("elapsed time: %.2fs\n", (end - start) / 1000F); } public static void outputStatistics(Map mom) { int nObj = (int) mom.keySet().stream().distinct().count(); int nObjMahjong = (int) mom.values().stream().distinct().count(); System.out.println("-----------------------------------------------------------"); System.out.printf("%d objects in the allocation-site heap abstraction.\n", nObj); System.out.printf("%d objects in the Mahjong heap abstraction.\n", nObjMahjong); System.out.println("-----------------------------------------------------------"); } private static void writeMergedObjectMap(Map mom, File outputFile) throws FileNotFoundException { PrintWriter writer = new PrintWriter(outputFile); mom.forEach((heap, mergedHeap) -> { writer.write(heap.getName()); writer.write(SEP); writer.write(mergedHeap.getName()); writer.write(EOL); }); writer.close(); } } src/mahjong/util/0000775000176100017620000000000013120354112012432 5ustar yueyuesrc/mahjong/util/Triple.java0000664000176100017620000000206713120354112014541 0ustar yueyuepackage mahjong.util; import java.util.Objects; /** * @author Tian Tan * @author Yue Li */ public class Triple { private final T1 first; private final T2 second; private final T3 third; public Triple(T1 first, T2 second, T3 third) { this.first = first; this.second = second; this.third = third; } public T1 getFirst() { return first; } public T2 getSecond() { return second; } public T3 getThird() { return third; } @Override public int hashCode() { return Objects.hash(first, second, third); } @Override public boolean equals(Object o) { if (o instanceof Triple) { Triple anoTriple = (Triple) o; return Objects.equals(first, anoTriple.first) && Objects.equals(second, anoTriple.second) && Objects.equals(third, anoTriple.third); } return false; } @Override public String toString() { return "<" + Objects.toString(first) + ", " + Objects.toString(second) + ", " + Objects.toString(third) + ">"; } } src/mahjong/util/UnionFindSet.java0000664000176100017620000000340513120354112015644 0ustar yueyuepackage mahjong.util; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; /** * @author Tian Tan * @author Yue Li */ public class UnionFindSet { private Map entries = new HashMap<>(); private int nrsets; // number of disjoint sets public UnionFindSet(Collection elems) { elems.forEach(elem -> entries.put(elem, new Entry(elem))); nrsets = entries.size(); } public boolean union(E e1, E e2) { Entry root1 = findRoot(entries.get(e1)); Entry root2 = findRoot(entries.get(e2)); if (root1 == root2) { return false; } else { // union by rank if (root1.rank < root2.rank) { root1.parent = root2; } else if (root1.rank > root2.rank) { root2.parent = root1; } else { root2.parent = root1; ++root2.rank; } --nrsets; return true; } } public boolean isConnected(E e1, E e2) { Entry root1 = findRoot(entries.get(e1)); Entry root2 = findRoot(entries.get(e2)); return root1 == root2; } public E find(E e) { Entry ent = findRoot(entries.get(e)); return ent.elem; } public int numberOfSets() { return nrsets; } public Collection> getDisjointSets() { return entries.keySet() .stream() .collect(Collectors.groupingBy(this::find, Collectors.toSet())) .values(); } private Entry findRoot(Entry ent) { if (ent.parent != ent) { // path compression ent.parent = findRoot(ent.parent); } return ent.parent; } private class Entry { private final E elem; private Entry parent; private int rank; private Entry(E elem) { this.elem = elem; this.parent = this; this.rank = 0; } } } src/mahjong/util/Pair.java0000664000176100017620000000152313120354112014171 0ustar yueyuepackage mahjong.util; import java.util.Objects; /** * @author Tian Tan * @author Yue Li */ public class Pair { private final T1 first; private final T2 second; public Pair(T1 first, T2 second) { this.first = first; this.second = second; } public T1 getFirst() { return first; } public T2 getSecond() { return second; } @Override public int hashCode() { return Objects.hash(first, second); } @Override public boolean equals(Object o) { if (o instanceof Pair) { Pair anoPair = (Pair) o; return Objects.equals(first, anoPair.first) && Objects.equals(second, anoPair.second); } return false; } @Override public String toString() { return "<" + Objects.toString(first) + ", " + Objects.toString(second) + ">"; } } src/mahjong/util/Numberable.java0000664000176100017620000000056213120354112015354 0ustar yueyuepackage mahjong.util; /** * Every instance of this class has an unique ID number. * * @author Tian Tan * @author Yue Li */ public abstract class Numberable { public abstract int getID(); @Override public final int hashCode() { return getID(); } @Override public final boolean equals(Object obj) { return this == obj; } } src/mahjong/automata/0000775000176100017620000000000013120354112013270 5ustar yueyuesrc/mahjong/automata/DFAEquivalenceChecker.java0000664000176100017620000000573313120354112020204 0ustar yueyuepackage mahjong.automata; import java.util.Collection; import java.util.Set; import java.util.Stack; import java.util.stream.Collectors; import java.util.stream.Stream; import mahjong.pta.Field; import mahjong.pta.Type; import mahjong.util.Pair; import mahjong.util.UnionFindSet; /** * @author Tian Tan * @author Yue Li */ public class DFAEquivalenceChecker { /** * Check the equivalence of input automata by Hopcroft-Karp algorithm * with minor modifications. * @param dfa1 * @param dfa2 * @return whether dfa1 and dfa2 are equivalent */ public boolean isEquivalent(DFA dfa1, DFA dfa2) { CombinedDFA dfa = new CombinedDFA(dfa1, dfa2); Set combinedStates = dfa.getStates(); UnionFindSet uf = new UnionFindSet<>(combinedStates); Stack> stack = new Stack<>(); DFAState s1 = dfa1.getStartState(); DFAState s2 = dfa2.getStartState(); uf.union(s1, s2); stack.push(new Pair<>(s1, s2)); while (!stack.isEmpty()) { Pair pair = stack.pop(); DFAState q1 = pair.getFirst(); DFAState q2 = pair.getSecond(); Stream.concat(dfa.outEdgesOf(q1).stream(), dfa.outEdgesOf(q2).stream()) .forEach(field -> { DFAState r1 = uf.find(dfa.nextState(q1, field)); DFAState r2 = uf.find(dfa.nextState(q2, field)); if (r1 != r2) { uf.union(r1, r2); stack.push(new Pair<>(r1, r2)); } }); } Collection> mergedStateSets = uf.getDisjointSets(); return validate(dfa, mergedStateSets); } /** * * @param dfa * @param mergedStateSets * @return true if every state set contains no different output * (i.e., types) */ private boolean validate(CombinedDFA dfa, Collection> mergedStateSets) { for (Set set : mergedStateSets) { int minSize = set.stream() .mapToInt(s -> dfa.outputOf(s).size()) .min() .getAsInt(); long unionSize = set.stream() .flatMap(s -> dfa.outputOf(s).stream()) .distinct() .count(); if (unionSize > minSize) { return false; } } return true; } private class CombinedDFA { DFA dfa1, dfa2; private CombinedDFA(DFA dfa1, DFA dfa2) { this.dfa1 = dfa1; this.dfa2 = dfa2; } private Set getStates() { return Stream .concat(dfa1.getAllStates().stream(), dfa2.getAllStates().stream()) .collect(Collectors.toSet()); } private DFAState nextState(DFAState s, Field f) { return dfa1.containsState(s) ? dfa1.nextState(s, f) : dfa2.nextState(s, f); } private Set outEdgesOf(DFAState s) { return dfa1.containsState(s) ? dfa1.outEdgesOf(s) : dfa2.outEdgesOf(s); } private Set outputOf(DFAState s) { return dfa1.containsState(s) ? dfa1.outputOf(s) : dfa2.outputOf(s); } } } src/mahjong/automata/NFA.java0000664000176100017620000000333713120354112014545 0ustar yueyuepackage mahjong.automata; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.Stack; import mahjong.fpg.FieldPointstoGraph; import mahjong.pta.Field; import mahjong.pta.Obj; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ public class NFA { private static final Obj deadState = null; private Obj q0; private FieldPointstoGraph fpg; public NFA(Obj q0, FieldPointstoGraph fpg) { this.q0 = q0; this.fpg = fpg; } /** * This method on-the-fly computes set of states. * @return Set of states. Does not contains dead state. */ public Set getStates() { Set states = new HashSet<>(); Stack stack = new Stack<>(); stack.push(q0); while (!stack.isEmpty()) { Obj s = stack.pop(); if (!states.contains(s)) { states.add(s); outEdgesOf(s).forEach(field -> { nextStates(s, field).stream() .filter(obj -> !states.contains(obj)) .forEach(stack::push); }); } } return states; } public Obj getStartState() { return q0; } public Obj getDeadState() { return deadState; } public Set nextStates(Obj obj, Field f) { if (isDeadState(obj) || !fpg.hasFieldPointer(obj, f)) { return Collections.singleton(deadState); } else { return fpg.pointsTo(obj, f); } } public boolean isDeadState(Obj obj) { return obj == deadState; } public Set outEdgesOf(Obj obj) { if (isDeadState(obj)) { return Collections.emptySet(); } else { return fpg.outFieldsOf(obj); } } public Type outputOf(Obj obj) { if (isDeadState(obj)) { return null; } else { return obj.getType(); } } } src/mahjong/automata/DFA.java0000664000176100017620000000401713120354112014527 0ustar yueyuepackage mahjong.automata; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.Set; import mahjong.pta.Field; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ public class DFA { private Set states, allStates; private DFAState q0; private static final DFAState deadState = new DFAState(Collections.emptySet(), Collections.emptySet()); public DFA(DFAState q0) { this.q0 = q0; } /** * * @return Set of states. Does not contains dead state. */ public Set getStates() { if (states == null) { computeStates(); } return states; } /** * * @return Set of all states including dead state. */ public Set getAllStates() { if (allStates == null) { computeStates(); } return allStates; } private void computeStates() { Queue queue = new LinkedList<>(); queue.add(q0); states = new HashSet<>(); while (!queue.isEmpty()) { DFAState s = queue.poll(); if (!states.contains(s)) { states.add(s); s.getNextMap().values().forEach(queue::add); } } allStates = new HashSet<>(states); allStates.add(deadState); } public DFAState getStartState() { return q0; } public DFAState getDeadState() { return deadState; } public boolean isDeadState(DFAState s) { return deadState == s; } public DFAState nextState(DFAState s, Field f) { if (isDeadState(s)) { return getDeadState(); } Map nextMap = s.getNextMap(); if (nextMap.containsKey(f)) { return nextMap.get(f); } return getDeadState(); } public Set outputOf(DFAState s) { return s.getOutput(); } public Set outEdgesOf(DFAState s) { Map nextMap = s.getNextMap(); return nextMap.keySet(); } public boolean containsState(DFAState s) { return getAllStates().contains(s); } } src/mahjong/automata/DFAFactory.java0000664000176100017620000000552213120354112016061 0ustar yueyuepackage mahjong.automata; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.stream.Collectors; import mahjong.fpg.FieldPointstoGraph; import mahjong.pta.Field; import mahjong.pta.Obj; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ public class DFAFactory { private final FieldPointstoGraph fpg; private Map, DFAState> stateMap; private Set states, visited; public DFAFactory(FieldPointstoGraph fpg) { this.fpg = fpg; buildAllDFA(); } public DFA getDFA(Obj obj) { DFAState q0 = stateMap.get(Collections.singleton(obj)); return new DFA(q0); } private void buildAllDFA() { stateMap = new HashMap<>(); states = new HashSet<>(); visited = new HashSet<>(); fpg.getAllObjs().forEach(this::buildDFA); } /** * Perform subset construction algorithm to convert an NFA * to a DFA. If a set of NFA states are merged to an existing * DFA state, then reused the existing DFA state instead of creating * an equivalent new one. * @param obj the start state (object) of the DFA */ private void buildDFA(Obj obj) { Set q0Set = Collections.singleton(obj); if (!stateMap.containsKey(q0Set)) { NFA nfa = new NFA(obj, fpg); DFAState startState = getDFAState(q0Set, nfa); Queue worklist = new LinkedList<>(); states.add(startState); worklist.add(startState); while (!worklist.isEmpty()) { DFAState s = worklist.poll(); if (!visited.contains(s)) { visited.add(s); Set fields = fields(nfa, s.getObjects()); fields.forEach(f -> { Set nextNFAStates = move(nfa, s.getObjects(), f); DFAState nextState = getDFAState(nextNFAStates, nfa); if (!states.contains(nextState)) { states.add(nextState); worklist.add(nextState); } addTransition(s, f, nextState); }); } } } } private DFAState getDFAState(Set objs, NFA nfa) { if (!stateMap.containsKey(objs)) { Set output = objs.stream() .map(nfa::outputOf) .collect(Collectors.toSet()); stateMap.put(objs, new DFAState(objs, output)); } return stateMap.get(objs); } private Set move(NFA nfa, Set objs, Field f) { return objs.stream() .map(obj -> nfa.nextStates(obj, f)) .flatMap(nextStates -> nextStates.stream()) .collect(Collectors.toSet()); } private Set fields(NFA nfa, Set objs) { return objs.stream() .map(nfa::outEdgesOf) .flatMap(fields -> fields.stream()) .collect(Collectors.toSet()); } private void addTransition(DFAState s, Field f, DFAState nextState) { s.addTransition(f, nextState); } } src/mahjong/automata/DFAState.java0000664000176100017620000000302413120354112015525 0ustar yueyuepackage mahjong.automata; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import mahjong.pta.Field; import mahjong.pta.Obj; import mahjong.pta.Type; /** * @author Tian Tan * @author Yue Li */ public class DFAState { private final Set objs; private final Set output; private final Map nextMap; private boolean hasHashCode = false; private int hashCode; public DFAState(Set objs, Set output) { this.objs = objs; this.output = output; this.nextMap = new HashMap<>(); } public Set getObjects() { return objs; } public Set getOutput() { return output; } void addTransition(Field f, DFAState nextState) { nextMap.put(f, nextState); } Map getNextMap() { return nextMap; } /** * Cache hash code. */ @Override public int hashCode() { if (!hasHashCode) { hashCode = objs.hashCode(); hasHashCode = true; } return hashCode; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (! (other instanceof DFAState)) { return false; } DFAState anoDFAState = (DFAState) other; return getObjects().equals(anoDFAState.getObjects()); } @Override public String toString() { return getObjects().stream() .map(obj -> obj == null ? "null" :String.valueOf(obj.getID())) .sorted() .collect(Collectors.toSet()) .toString(); } }