Public Information for Job 66378

Created By: jlmaddox
Created At: Wed, 07 Mar 2018 13:21:18 -0600

Input Dataset: 2015 September/GitHub

Last Submitted At: Wed, 07 Mar 2018 13:21:18 -0600
Last Finished At: Wed, 07 Mar 2018 14:45:58 -0600 (1h 24m 40s)

Source Code

p: Project = input; # for outputting method pair information out: output collection of string; # for outputting aggregate values like number of method pairs stat: output sum[string] of int; ######################################## ### Declarations ### ######################################## # maps the declaration name to its methods effects: map[string] of string; # maps the declaration to its superclass name supers: map[string] of string; # (OUTDATED) # Format: # [decl name] = # methodName:(!n or !y calls super method):Exceptions,That,Are,Thrown: # Arg,Type,Names:(!n or !y is abstract method)#nextMethodName # # when true, outputs places that seem to have superclass cycles OUTPUT_SUPER_CYCLES := false; INVALID_STR: string = "!INVALID!"; UNKNOWN_TYPE_STR: string = "!UNKNOWN!"; HAS_DUPES_STR: string = "!DUPLICATED!"; MDX_NAME: int = 0; MDX_ARGTYPESTR: int = 1; MDX_PRIMARY_MODIFIER: int = 2; MDX_METHOD_CALLS: int = 3; MOD_ABSTRACT: string = "A"; MOD_PRIVATE: string = "P"; MOD_STATIC: string = "S"; MOD_CTOR: string = "C"; MOD_OTHER: string = "O"; ######################################## ### Utility functions ### ######################################## safeSplit := function(str: string, reg: string) : array of string { temp: array of string; if (str == "") # empty string, empty list return new(temp, 0, INVALID_STR); return splitall(str, reg); }; mergeStrArraySet := function(a1: array of string, a2: array of string): array of string { strMap: map[string] of bool; foreach(i: int; def(a1[i])) { strMap[a1[i]] = true; } foreach(i: int; def(a2[i])) { strMap[a2[i]] = true; } return keys(strMap); }; # assume each array has no duplicates isEq := function(ar1: array of string, ar2: array of string): bool { if (len(ar1) != len(ar2)) { return false; } foreach(i: int; def(ar1[i])) { found: bool = false; foreach(j: int; def(ar2[j])) { if (ar1[i] == ar2[j]) { found = true; break; } } if (!found) { return false; } } return true; }; toList := function(arr: array of string): string { outStr: string = ""; first: bool = true; foreach(i: int; def(arr[i])) { if (first) { first = false; outStr = outStr + arr[i]; } else { outStr = outStr + "," + arr[i]; } } if (outStr == "") { outStr = ","; } return outStr; }; isConstructor := function (method: Method) : bool { return method.name == "<init>"; }; isStatic := function (method: Method) : bool { foreach(i: int; def(method.modifiers[i])) { if (method.modifiers[i].kind == ModifierKind.STATIC) { return true; } } return false; }; isAbstract := function(method: Method): bool { foreach(i: int; def(method.modifiers[i])) { if (method.modifiers[i].kind == ModifierKind.ABSTRACT) { return true; } } return false; }; isPrivate := function(method: Method): bool { foreach(i: int; def(method.modifiers[i])) { mod := method.modifiers[i]; if (mod.kind == ModifierKind.VISIBILITY && mod.visibility == Visibility.PRIVATE) { return true; } } return false; }; stripToTypeName := function(theType: string) : string { loc := strrfind(".", theType); # note: the double r is intentional if (loc != -1) { return substring(theType, loc + 1); # +1 to ignore the dot } return theType; }; XisSubsetY := function(sub: array of string, super: array of string) : bool { # todo: replace with contains() if it works on arrays # assume: super and sub are sets w/ no duplicates foreach (i: int; def(sub[i])) { found: bool = false; foreach(j: int; def(super[j])) if (sub[i] == super[j]) found = true; if (!found) return false; } return true; }; fileIsFilteredOut := function(root: ASTRoot) : bool { found := false; visit(root, visitor { before _ -> if (found) stop; before node: Modifier -> if (node.kind == ModifierKind.ANNOTATION && node.annotation_name == "Test") found = true; }); return found; }; ######################################## ### New getsnapshot() ### ######################################## # getsnapshot - medium precision version # note: does not check AST hashes, and takes ~35mins on big dataset getsnapshot_mp := function(cr: CodeRepository, t: time) : array of ChangedFile { filter := "SOURCE_JAVA_JLS"; snapshot: map[string] of ChangedFile; blacklist: set of string; visit(cr, visitor { before node: Revision -> { if (node.commit_date > t) stop; fKeys: map[ChangedFile] of string; rFinal: map[string] of ChangedFile; # apply filters and generate keys / hashes foreach (i:int; def(node.files[i])) { file := node.files[i]; if (!iskind(filter, file.kind)) continue; ast: ASTRoot = getast(file); if (len(ast.namespaces) != 1) continue; parts := splitall(file.name, "/"); fileName := parts[len(parts) - 1]; fKeys[file] = ast.namespaces[0].name + "." + fileName; } # process each file fList := keys(fKeys); foreach (i: int; def(fList[i])) { curr := fList[i]; if (def(rFinal[fKeys[curr]])) { add(blacklist, fKeys[curr]); } else { rFinal[fKeys[curr]] = curr; } } kList := keys(rFinal); foreach(i: int; def(kList[i])) { currKey := kList[i]; if (rFinal[currKey].change == ChangeKind.DELETED) { remove(snapshot, currKey); } else { snapshot[currKey] = rFinal[currKey]; } } } }); finalSnap: map[string] of ChangedFile; finalKeys := keys(snapshot); foreach (i:int; def(finalKeys[i])) { k := finalKeys[i]; if (!contains(blacklist, k)) # not blacklisted finalSnap[k] = snapshot[k]; } return values(finalSnap); }; ######################################## ### Resolution code ### ######################################## resolvOut: output sum[string] of int; declImp: map[string] of string; # [decl] = {list of imports, # separated} declPkg: map[string] of string; # [decl] = {package} declNme: map[string] of string; # [decl] = {decl.namee} IMPORT_DELIM := "#"; resolve := function(decl: string, ctx: string) : string { if (!def(declPkg[ctx])) { resolvOut["BAD_CONTEXT"] << 1; return INVALID_STR; # no such context } # See if it's an inner class inKey := ctx + "." + decl; inName := declPkg[inKey]; if (def(inName) && inName != HAS_DUPES_STR) { resolvOut["INNER_CLASS"] << 1; return inKey; } # See if it's an outer class oParts := safeSplit(ctx, "\\."); oPkgParts := safeSplit(declPkg[ctx], "\\."); oLB := len(oPkgParts); oUB := len(oParts) - 1; # oLB inclusive, oUB exclusive for (i: int = oLB; i < oUB; i++) { if (oParts[i] == decl) { # outer class, so try to reconstruct the key outKey := declPkg[ctx]; for (k: int = oLB; k <= i; k++) { outKey = outKey + "." + oParts[k]; } outName := declPkg[outKey]; if (def(outName) && outName != HAS_DUPES_STR) { resolvOut["OUTER_CLASS"] << 1; return outKey; } else { resolvOut["OUTER_CLASS_FAILED_TO_RESOLVE"] << 1; return INVALID_STR; } } } # set up to look at imports imps := safeSplit(declImp[ctx], IMPORT_DELIM); parts: array of string; # declaring it here prevents compiler breaking # Fully qualified imports foreach(i:int; def(imps[i])) { if (strfind("*", imps[i]) != -1) continue; parts = safeSplit(imps[i], "\\."); if (parts[len(parts) - 1] == decl) { if (def(declPkg[imps[i]]) && declPkg[imps[i]] != HAS_DUPES_STR) { resolvOut["NORM_IMPORT"] << 1; return imps[i]; } else { resolvOut["NORM_IMPORT_FAILED_TO_RESOLVE"] << 1; return INVALID_STR; } } } # Look for matches in the same package pkgKey := declPkg[ctx] + "." + decl; pkgName := declPkg[pkgKey]; if (def(pkgName) && pkgName != HAS_DUPES_STR) { resolvOut["SAME_PACKAGE"] << 1; return pkgKey; } # Finally look at wildcard imports foreach(i:int; def(imps[i])) { if (strfind("*", imps[i]) == -1) continue; # strreplace appears to cause an error in an unknown situation # working around that here #newImp := strreplace(imps[i], "*", decl, false); newImp := ""; parts = safeSplit(imps[i], "\\."); foreach (x: int; def(parts[x])) { toAdd := ""; if (parts[x] == "*") toAdd = decl; else toAdd = parts[x]; if (newImp == "") newImp = toAdd; else newImp = newImp + "." + toAdd; } newImpPkg := declPkg[newImp]; if (def(newImpPkg) && newImpPkg != HAS_DUPES_STR) { resolvOut["STAR_IMPORT"] << 1; return newImp; } } resolvOut["NO_RESOLUTION"] << 1; return INVALID_STR; }; # returns the declaration key # outers must be empty or in the format of ".out1.out2" preprocessDecl := function(decl: Declaration, root: ASTRoot, outers: string) : string { if (len(root.namespaces) != 1) { resolvOut["BAD_NAMESPACE"] << 1; return INVALID_STR; } key: string = root.namespaces[0].name + outers + "." + decl.name; if (def(declPkg[key])) { if (declPkg[key] != HAS_DUPES_STR) { resolvOut["DUPLICATED"] << 2; declImp[key] = HAS_DUPES_STR; declPkg[key] = HAS_DUPES_STR; declNme[key] = HAS_DUPES_STR; } else { resolvOut["DUPLICATED"] << 1; } return INVALID_STR; } imps := ""; foreach(i:int; def(root.imports[i])) { if (imps == "") imps = root.imports[i]; else imps = imps + IMPORT_DELIM + root.imports[i]; } declImp[key] = imps; declPkg[key] = root.namespaces[0].name; declNme[key] = decl.name; return key; }; ######################################## ### Method call analysis functions ### ######################################## MC_PARAM: string = "P"; # obj.m(); -> m; including exps such as ((Obj)o[0]).x(); MC_THIS: string = "T"; # this.m(); -> m MC_SUPER: string = "S"; # super.m(); -> m MC_DELIM: string = "!"; methodCallDebug: output sum[string] of int; getCallEffectPrefix := function(expr: Expression): string { if (len(expr.expressions) == 1) { source := expr.expressions[0]; if (source.kind == ExpressionKind.LITERAL) { if (source.literal == "this") return MC_THIS; else if (source.literal == "super") return MC_SUPER; # Boa doesn't see super as literal } else if (source.kind == ExpressionKind.VARACCESS) { return MC_PARAM; } else if (source.kind == ExpressionKind.METHODCALL) { return MC_PARAM; } else { methodCallDebug["OTHER_KIND=" + format("%s", source.kind)] << 1; return MC_PARAM; } } else { # length is 0 if (strfind("super.", expr.method) == 0) { return MC_SUPER; } else { return MC_THIS; } } methodCallDebug["MULTIPLE_SOURCE_EXPRS"] << 1; return MC_PARAM; }; getEffectMethodCall := function(method: Method, decl: Declaration): string { eList: map[string] of bool; visit(method, visitor { before node: Declaration -> stop; before node: Expression -> { if (node.kind == ExpressionKind.METHODCALL) { result := getCallEffectPrefix(node); if (result != INVALID_STR) { methodCallDebug["METHOD_MC_TYPE=" + result] << 1; eList[result + MC_DELIM + node.method] = true; } else { methodCallDebug["METHOD_MC_TYPE_INVALID"] << 1; } } } }); final: string = ""; finalList := keys(eList); foreach(i: int; def(finalList[i])) { if (i == 0) { final = finalList[i]; } else { final = format("%s,%s", final, finalList[i]); } } if (final == "") { final = ","; # empty list } return final; }; ######################################## ### Effect analysis functions ### ######################################## processOneMethod := function(method: Method, decl: Declaration, key: string) { finalStr: string = method.name; # argument types argStr: string = ""; foreach(i: int; def(method.arguments[i])) { tName: string = method.arguments[i].variable_type.name; if (argStr == "") { argStr = tName; } else { argStr = format("%s,%s", argStr, tName); } } if (argStr == "") argStr = ","; finalStr = format("%s:%s", finalStr, argStr); # add the primary access modifier with the following precedence accessModifier: string = MOD_OTHER; if (isAbstract(method)) { accessModifier = MOD_ABSTRACT; } else if (isPrivate(method)) { accessModifier = MOD_PRIVATE; } else if (isStatic(method)) { accessModifier = MOD_STATIC; } else if (isConstructor(method)) { accessModifier = MOD_CTOR; } finalStr = format("%s:%s", finalStr, accessModifier); methodCallEffect: string = getEffectMethodCall(method, decl); finalStr = format("%s:%s", finalStr, methodCallEffect); # add the method for later processing after intra-project deduplication if (effects[key] == "") { effects[key] = finalStr; } else { effects[key] = format("%s#%s", effects[key], finalStr); } }; processDecl := function(decl: Declaration, key: string) { # cache superclass if any supers[key] = INVALID_STR; foreach(i: int; def(decl.parents[i])) { if (decl.parents[i].kind == TypeKind.CLASS) { supers[key] = decl.parents[i].name; break; } } # process the methods effects[key] = ""; foreach(i: int; def(decl.methods[i])) { processOneMethod(decl.methods[i], decl, key); } }; # returns the overridden method info string if and only if it # 1. exists # 2. is not abstract, private, static, nor a constructor # otherwise returns INVALID_STR getOverridden := function(method: array of string, decl: string): string { argTypes: string = method[MDX_ARGTYPESTR]; currDeclKey: string = resolve(supers[decl], decl); prevSeen: set of string; add(prevSeen, decl); while (currDeclKey != INVALID_STR && !contains(prevSeen, currDeclKey)) { add(prevSeen, currDeclKey); if (effects[currDeclKey] == "") { # no methods to examine, go to next currDeclKey = resolve(supers[currDeclKey], currDeclKey); continue; } # for all methods methodList: array of string = splitall(effects[currDeclKey], "#"); foreach(i: int; def(methodList[i])) { tempDat: array of string = splitall(methodList[i], ":"); tempMethodName: string = tempDat[MDX_NAME]; tempArgTypes: string = tempDat[MDX_ARGTYPESTR]; tempIsValidForPair: bool = tempDat[MDX_PRIMARY_MODIFIER] == MOD_OTHER; # if method name and arguments match then return if (tempMethodName == method[MDX_NAME] && tempArgTypes == argTypes) { # overrides an abstract or similar method, so return no available overridden impl if (!tempIsValidForPair) { return INVALID_STR; } else { return methodList[i]; } } } # no matching method, go to next currDeclKey = resolve(supers[currDeclKey], currDeclKey); } if (OUTPUT_SUPER_CYCLES && currDeclKey != INVALID_STR && contains(prevSeen, currDeclKey)) { out << "ERROR: Superclass cycle " + p.name + "#" + method[MDX_NAME] + "#" + decl + "#" + currDeclKey; } return INVALID_STR; }; # matches the given method data with the method it overrides (if any) and then # outputs information on this pair in a single output line outputMethodResults := function(decl: string, mInfo: array of string) { # method is abstract, static, ctor, or private then skip it if (mInfo[MDX_PRIMARY_MODIFIER] != MOD_OTHER) { return; } superInfoFull: string = getOverridden(mInfo, decl); superInfo: array of string = splitall(superInfoFull, ":"); # no valid supermethod if (superInfoFull == INVALID_STR) { return; } stat["METHOD_PAIR_COUNT"] << 1; # valid pair has no explicit exception effects if (mInfo[MDX_METHOD_CALLS] == "," && superInfo[MDX_METHOD_CALLS] == ",") { return; } stat["METHOD_PAIR_WITH_EFFECT_COUNTER"] << 1; subCalls: array of string; sprCalls: array of string; diffInfo: string = "!y"; if (mInfo[MDX_METHOD_CALLS] == ",") { subCalls = new(subCalls, 0, ""); } else { subCalls = splitall(mInfo[MDX_METHOD_CALLS], ","); } if (superInfo[MDX_METHOD_CALLS] == ",") { sprCalls = new(sprCalls, 0, ""); } else { sprCalls = splitall(superInfo[MDX_METHOD_CALLS], ","); } if (isEq(subCalls, sprCalls)) { diffInfo = "!n"; } else if (len(subCalls) > len(sprCalls)) { if (XisSubsetY(sprCalls, subCalls)) { diffInfo = "!subissuperset"; } else { diffInfo = "!subhasmore"; } } else if (len(subCalls) < len(sprCalls)) { if (XisSubsetY(subCalls, sprCalls)) { diffInfo = "!subissubset"; } else { diffInfo = "!sprhasmore"; } } stat["CALL_PAIR_COUNT=" + diffInfo] << 1; }; outputMethodStats := function(mInfo: array of string) { stat["METHOD_COUNT"] << 1; if (mInfo[MDX_PRIMARY_MODIFIER] == MOD_ABSTRACT) { return; } # statistics for the method methodCallEffect: string = mInfo[MDX_METHOD_CALLS]; methodEffectArr: array of string; if (methodCallEffect == ",") methodEffectArr = new(methodEffectArr, 0, ""); else methodEffectArr = safeSplit(methodCallEffect, ","); callEffectCount: string = ""; if (len(methodEffectArr) == 0) { callEffectCount = "0"; } else if (len(methodEffectArr) == 1) { callEffectCount = "1"; } else if (len(methodEffectArr) == 2) { callEffectCount = "2"; } else { callEffectCount = "3+"; } if (mInfo[MDX_PRIMARY_MODIFIER] == MOD_PRIVATE) { stat["CALLS=" + callEffectCount + ";PRIVATE"] << 1; stat["METHOD_COUNT_PRIVATE"] << 1; } else if (mInfo[MDX_PRIMARY_MODIFIER] == MOD_STATIC) { stat["CALLS=" + callEffectCount + ";STATIC"] << 1; stat["METHOD_COUNT_STATIC"] << 1; } else if (mInfo[MDX_PRIMARY_MODIFIER] == MOD_CTOR) { stat["CALLS=" + callEffectCount + ";CTOR"] << 1; stat["METHOD_COUNT_CTOR"] << 1; } else { stat["CALLS=" + callEffectCount + ";OTHER"] << 1; stat["METHOD_COUNT_OTHER"] << 1; } stat["NON_ABSTRACT_METHOD_COUNT"] << 1; }; outputResults := function() { # also same for 'effects' types: array of string = keys(declPkg); foreach(i: int; def(types[i])) { curr: string = types[i]; # if type is not duplicated, output results for each method if (declPkg[curr] != HAS_DUPES_STR) { stat["CLASS_COUNT"] << 1; methods: array of string = safeSplit(effects[curr], "#"); foreach(j: int; def(methods[j])) { mInfo: array of string = splitall(methods[j], ":"); outputMethodStats(mInfo); outputMethodResults(curr, mInfo); } } } }; ######################################## ### Resolution startup ### ######################################## produce := function(snapshot: array of ChangedFile, filterOutList: array of bool) { file: ChangedFile; numOut: int = 0; # current size of outers OUTER_LEN: int = 3; # hard limit of "outer1.outer2.class" outers: array of string; outers = new(outers, OUTER_LEN, ""); prodVisit := visitor { before decl: Declaration -> { if (decl.kind == TypeKind.CLASS) { resolvOut["DECLARATION"] << 1; currOut: string = ""; for(i: int = 0; i < numOut && i < OUTER_LEN; i++) currOut = currOut + "." + outers[i]; key: string = preprocessDecl(decl, getast(file), currOut); if (key != INVALID_STR) processDecl(decl, key); } numOut++; if (numOut <= OUTER_LEN) { outers[numOut - 1] = decl.name; } } after node: Declaration -> numOut--; # don't look at declarations in a method before node: Method -> stop; }; foreach(i: int; def(snapshot[i])) { file = snapshot[i]; if (filterOutList[i]) continue; visit(snapshot[i], prodVisit); if (numOut != 0) { resolvOut["OUTER_NONZERO (bug?)"] << 1; numOut = 0; } } }; ######################################## ### Startup ### ######################################## countNodes := function (snapshot: array of ChangedFile) { currentNodeCountAST: int = 0; counterAST := visitor { before _ -> currentNodeCountAST++; }; foreach(i:int; def(snapshot[i])) { visit(snapshot[i], counterAST); } stat["PROJECT_LATEST_TOTAL_AST_NODES"] << currentNodeCountAST; }; # All repositories are Java code repositories. <1 per project on GitHub revisionVisitor := visitor { before node: CodeRepository -> { stat["CODE_REPOSITORY_COUNT"] << 1; clear(declImp); clear(declPkg); clear(declNme); clear(effects); clear(supers); #clear(statics); snapshot := getsnapshot_mp(node, now()); filterOutList: array of bool; filterOutList = new(filterOutList, len(snapshot), false); stat["CONSIDERED_SOURCE_FILES"] << len(snapshot); # set up statics for throws analysis, check for JUnit prescence foreach (i: int; def(snapshot[i])) { root := getast(snapshot[i]); if (fileIsFilteredOut(root)) filterOutList[i] = true; #else # visit(root, staticMethodVisitor); } # set up rest of the structures (for resolv and app) produce(snapshot, filterOutList); # count number of AST nodes countNodes(snapshot); } }; visit(p, revisionVisitor); outputResults();

Output

Job Output Size: 2.17k

methodCallDebug[METHOD_MC_TYPE=P] = 453146007
methodCallDebug[METHOD_MC_TYPE=S] = 5824882
methodCallDebug[METHOD_MC_TYPE=T] = 138457741
methodCallDebug[MULTIPLE_SOURCE_EXPRS] = 3168374
methodCallDebug[OTHER_KIND=ARRAYINDEX] = 3368740
methodCallDebug[OTHER_KIND=ASSIGN] = 28347
methodCallDebug[OTHER_KIND=ASSIGN_ADD] = 162
methodCallDebug[OTHER_KIND=CAST] = 4187454
methodCallDebug[OTHER_KIND=CONDITIONAL] = 16535
methodCallDebug[OTHER_KIND=NEWARRAY] = 2016
methodCallDebug[OTHER_KIND=NEW] = 2641489
methodCallDebug[OTHER_KIND=OP_ADD] = 59061
methodCallDebug[OTHER_KIND=OP_DIV] = 2
methodCallDebug[OTHER_KIND=OP_INC] = 54
methodCallDebug[OTHER_KIND=OP_SUB] = 3
resolvOut[DECLARATION] = 20659999
resolvOut[DUPLICATED] = 90077
resolvOut[INNER_CLASS] = 89
resolvOut[NORM_IMPORT] = 16208538
resolvOut[NORM_IMPORT_FAILED_TO_RESOLVE] = 19107109
resolvOut[NO_RESOLUTION] = 74815024
resolvOut[OUTER_CLASS] = 293158
resolvOut[OUTER_CLASS_FAILED_TO_RESOLVE] = 1345
resolvOut[SAME_PACKAGE] = 25747751
resolvOut[STAR_IMPORT] = 1316814
stat[CALLS=0;CTOR] = 4846848
stat[CALLS=0;OTHER] = 39495367
stat[CALLS=0;PRIVATE] = 2250625
stat[CALLS=0;STATIC] = 2756307
stat[CALLS=1;CTOR] = 6412343
stat[CALLS=1;OTHER] = 23327386
stat[CALLS=1;PRIVATE] = 2458792
stat[CALLS=1;STATIC] = 3303843
stat[CALLS=2;CTOR] = 1461338
stat[CALLS=2;OTHER] = 12453185
stat[CALLS=2;PRIVATE] = 2099297
stat[CALLS=2;STATIC] = 2160358
stat[CALLS=3+;CTOR] = 2162519
stat[CALLS=3+;OTHER] = 29500109
stat[CALLS=3+;PRIVATE] = 8127777
stat[CALLS=3+;STATIC] = 4718488
stat[CALL_PAIR_COUNT=!n] = 397590
stat[CALL_PAIR_COUNT=!sprhasmore] = 743317
stat[CALL_PAIR_COUNT=!subhasmore] = 903136
stat[CALL_PAIR_COUNT=!subissubset] = 524519
stat[CALL_PAIR_COUNT=!subissuperset] = 1917266
stat[CALL_PAIR_COUNT=!y] = 516471
stat[CLASS_COUNT] = 20569922
stat[CODE_REPOSITORY_COUNT] = 380125
stat[CONSIDERED_SOURCE_FILES] = 20302663
stat[METHOD_COUNT] = 149294833
stat[METHOD_COUNT_CTOR] = 14883048
stat[METHOD_COUNT_OTHER] = 104776047
stat[METHOD_COUNT_PRIVATE] = 14936491
stat[METHOD_COUNT_STATIC] = 12938996
stat[METHOD_PAIR_COUNT] = 5975136
stat[METHOD_PAIR_WITH_EFFECT_COUNTER] = 5002299
stat[NON_ABSTRACT_METHOD_COUNT] = 147534582
stat[PROJECT_LATEST_TOTAL_AST_NODES] = 6619264814

Compilation

Status: Finished
Started: Wed, 07 Mar 2018 13:21:20 -0600
Finished: Wed, 07 Mar 2018 13:21:32 -0600 (12s)

Execution

Status: Finished
Started: Wed, 07 Mar 2018 13:21:49 -0600
Finished: Wed, 07 Mar 2018 14:45:58 -0600 (1h 24m 9s)