removing crowed source run, crashes, coverage, profiling and associated debugger analysis
This commit is contained in:
Родитель
94db69c47c
Коммит
6a5c6f4a35
49
ast/help.ts
49
ast/help.ts
|
@ -153,24 +153,6 @@ module TDev {
|
|||
data: string; // groupid for group invitation codes
|
||||
}
|
||||
|
||||
export interface JsonStoreApp
|
||||
{
|
||||
kind: string; // 'storeapp'
|
||||
time: number; // seconds since 1970 of last activity on this app (most likely: increase of users / launches)
|
||||
url:string;
|
||||
scriptid: string;
|
||||
storeid: string;
|
||||
title: string;
|
||||
publisher: string;
|
||||
iconurl: string;
|
||||
languages: string[];
|
||||
users: number;
|
||||
launches: number;
|
||||
userid: string; // creator of scriptid
|
||||
username: string;
|
||||
userscore: number;
|
||||
userhaspicture: boolean;
|
||||
}
|
||||
|
||||
export interface JsonScript extends JsonPublication
|
||||
{
|
||||
|
@ -230,37 +212,6 @@ module TDev {
|
|||
publicationkind:string; //
|
||||
}
|
||||
|
||||
export interface JsonRunBucket extends JsonPubOnPub {
|
||||
compilerversion: string; // the version of the compiler
|
||||
publicationid: string; // script id
|
||||
publicationname: string; // script name
|
||||
publicationkind: string; // should be "script"
|
||||
hash: string; // the hash of the bucket (probably not interesting to anyone)
|
||||
runs: number; // the "weight" of the bucket #1, the total number of runs bound to it
|
||||
cumulativeruns: number; // the "weight" of the bucket #2, the total number of encounters
|
||||
error: string; // the error message
|
||||
}
|
||||
|
||||
export interface JsonStackFrame {
|
||||
id: string; // the id of the ast node
|
||||
action: string; // the name of the action
|
||||
}
|
||||
|
||||
export interface JsonStackTrace {
|
||||
pack: JsonStackFrame[]; // the array of stack frames
|
||||
path: number[]; // the trace in a form of indexes into pack
|
||||
}
|
||||
|
||||
export interface JsonRun extends JsonPubOnPub {
|
||||
bucketid: string; // id of the bucket
|
||||
userplatform?: string[]; // platform
|
||||
anonymous: boolean; // is this run anonymous?
|
||||
error: string; // the error message
|
||||
runmap: string[]; // the run map
|
||||
stack: JsonStackTrace; // the stack trace
|
||||
clientversion: string; // the long release version of the client
|
||||
compilerversion: string; // the version of the compiler for this run
|
||||
}
|
||||
|
||||
export interface JsonComment extends JsonPubOnPub
|
||||
{
|
||||
|
|
|
@ -1,657 +0,0 @@
|
|||
///<reference path='refs.ts'/>
|
||||
|
||||
module TDev {
|
||||
|
||||
export interface IStringSet {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export function setClone(s: IStringSet):IStringSet {
|
||||
if (!s) return {};
|
||||
|
||||
var res:IStringSet = {};
|
||||
Object.keys(s).forEach(key => key && (res[key] = true));
|
||||
return res;
|
||||
}
|
||||
|
||||
export function setEmpty(s: IStringSet) {
|
||||
if (!s) return true;
|
||||
return Object.keys(s).length === 0;
|
||||
}
|
||||
|
||||
export function setSize(s: IStringSet) {
|
||||
if (!s) return 0;
|
||||
return Object.keys(s).length;
|
||||
}
|
||||
|
||||
export function mkSet(...vals: string[]): IStringSet {
|
||||
var ret:IStringSet = {};
|
||||
vals.forEach(k => ret[k] = true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function setUnion(s0: IStringSet, s1: IStringSet) {
|
||||
if (!s0) return setClone(s1);
|
||||
if (!s1) return setClone(s0);
|
||||
|
||||
var res:IStringSet = {};
|
||||
Object.keys(s0).forEach(key => key && (res[key] = true));
|
||||
Object.keys(s1).forEach(key => key && (res[key] = true));
|
||||
return res;
|
||||
}
|
||||
|
||||
export function setIntersection(s0: IStringSet, s1: IStringSet):IStringSet {
|
||||
if (!s0 || !s1) return {};
|
||||
|
||||
var res:IStringSet = {};
|
||||
Object.keys(s0).forEach(key => key && s1[key] && (res[key] = true));
|
||||
return res;
|
||||
}
|
||||
|
||||
export function setDifference(s0: IStringSet, s1: IStringSet):IStringSet {
|
||||
if (!s0) return {};
|
||||
if (!s1) return setClone(s0);
|
||||
|
||||
var res:IStringSet = {};
|
||||
Object.keys(s0).forEach(key => key && !s1[key] && (res[key] = true));
|
||||
return res;
|
||||
}
|
||||
|
||||
export function setToArray(s: IStringSet) {
|
||||
if (!s) return [];
|
||||
|
||||
return Object.keys(s).filter(k => !!k);
|
||||
}
|
||||
|
||||
export function setFromArray(s: string[]):IStringSet {
|
||||
if (!s) return {};
|
||||
|
||||
var ret:IStringSet = {};
|
||||
s.forEach(k => ret[k] = true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export class FullRunMapper
|
||||
extends AST.NodeVisitor {
|
||||
private fullRunMap: IStringSet = TDev.mkSet();
|
||||
|
||||
constructor(public runMap: RunBitMap, public stackTrace: IPackedStackTrace) {
|
||||
super()
|
||||
}
|
||||
public visitAstNode(node: AST.AstNode) {
|
||||
if (!this.runMap && !this.stackTrace) return;
|
||||
if (!node) return;
|
||||
return this.visitChildren(node);
|
||||
}
|
||||
|
||||
public visitDecl(d: AST.Decl) {
|
||||
return this.visitAstNode(d);
|
||||
}
|
||||
|
||||
private definitelyVisitedCache = {};
|
||||
private definitelyVisited(id: string): boolean {
|
||||
if (!id) return;
|
||||
if (this.definitelyVisitedCache[id] !== undefined) return this.definitelyVisitedCache[id];
|
||||
|
||||
var ret = (this.runMap && this.runMap.contains(id)) || (this.stackTrace && this.stackTrace.pack.some(node => node.id === id));
|
||||
this.definitelyVisitedCache[id] = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private definitelyVisitedThisOrChildrenCache = {};
|
||||
private definitelyVisitedThisOrChildren(s: AST.Stmt): boolean {
|
||||
if (!s) return;
|
||||
|
||||
var id = s.stableId;
|
||||
|
||||
if (this.definitelyVisitedThisOrChildrenCache[id] !== undefined) return this.definitelyVisitedThisOrChildrenCache[id];
|
||||
// the depth level is not big, so we can just use recursion
|
||||
var ret = this.definitelyVisited(s.stableId) || s.children().some((child: AST.Stmt) => this.definitelyVisitedThisOrChildren(child));
|
||||
this.definitelyVisitedThisOrChildrenCache[s.stableId] = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// three state return value: true if visited else false; undefined for "don't know"
|
||||
private visitedCodeBlockStart(cb: AST.Block) {
|
||||
if (this.runMap &&
|
||||
cb &&
|
||||
cb.children() &&
|
||||
(cb.children().length > 0)) {
|
||||
var firstNonComment = cb.children().filter(stmt => (stmt.nodeType() !== "comment") && !!stmt.stableId)[0];
|
||||
|
||||
if (!firstNonComment) return; // the bb contains only comments, not interested
|
||||
|
||||
Util.assert(!!firstNonComment.stableId); // these guys should be already jsonified
|
||||
|
||||
return (this.definitelyVisited(firstNonComment.stableId));
|
||||
} else return;
|
||||
}
|
||||
|
||||
private visitedCodeBlockEnd(cb: AST.Block) {
|
||||
if (this.runMap &&
|
||||
cb &&
|
||||
cb.children() &&
|
||||
(cb.children().length > 0)) {
|
||||
var lastNonComment = cb.children().filter(stmt => stmt.nodeType() !== "comment" && !!stmt.stableId).peek();
|
||||
|
||||
if (!lastNonComment) return; // the bb contains only comments, not interested
|
||||
|
||||
Util.assert(!!lastNonComment.stableId); // these guys should be already jsonified
|
||||
|
||||
return (this.definitelyVisited(lastNonComment.stableId));
|
||||
} else return;
|
||||
}
|
||||
|
||||
private visitedCodeBlockMiddle(cb: AST.Block, stmt: AST.Stmt) {
|
||||
if (this.runMap &&
|
||||
cb &&
|
||||
cb.children() &&
|
||||
(cb.children().length > 0)) {
|
||||
|
||||
var stmtIndex = -1;
|
||||
var lastVisitedIndex = -1;
|
||||
|
||||
cb.children().forEach((v, ix) => {
|
||||
if (this.definitelyVisitedThisOrChildren(v)) lastVisitedIndex = ix;
|
||||
if (v.stableId === stmt.stableId) stmtIndex = ix;
|
||||
});
|
||||
|
||||
Util.assert(stmtIndex !== -1 && lastVisitedIndex !== -1);
|
||||
|
||||
return (stmtIndex <= lastVisitedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private visitedStmt(s: AST.Stmt) {
|
||||
if (!this.runMap) return;
|
||||
|
||||
var visitedStart = this.visitedCodeBlockStart(s.parentBlock());
|
||||
var visitedEnd = this.visitedCodeBlockEnd(s.parentBlock());
|
||||
|
||||
if (!visitedStart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (visitedStart && visitedEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.visitedCodeBlockMiddle(s.parentBlock(), s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public visitStmt(s: AST.Stmt) {
|
||||
super.visitStmt(s);
|
||||
if (this.visitedStmt(s)) this.fullRunMap[s.stableId] = true;
|
||||
}
|
||||
|
||||
public visitExprHolder(node: AST.ExprHolder) { // don't go below ExprHolder level
|
||||
}
|
||||
|
||||
static doit(rm: RunBitMap, st: IPackedStackTrace, theScript: AST.App) {
|
||||
var visitor = new FullRunMapper(rm, st);
|
||||
visitor.dispatch(theScript);
|
||||
return visitor.fullRunMap;
|
||||
}
|
||||
}
|
||||
|
||||
export class FullRunMapDebuggingAnnotator
|
||||
extends AST.NodeVisitor {
|
||||
|
||||
constructor(public fullRunMap: IStringSet) { super(); }
|
||||
|
||||
public visitApp(script: AST.App) {
|
||||
super.visitApp(script);
|
||||
if (!this.fullRunMap) script.annotatedBy = AST.AnnotationMode.None;
|
||||
else script.annotatedBy = AST.AnnotationMode.Coverage;
|
||||
}
|
||||
|
||||
public visitExprStmt(s: AST.ExprStmt) {
|
||||
if (!s || !s.stableId) return;
|
||||
if (this.fullRunMap[s.stableId]) s.expr.debuggingData = { visited: true };
|
||||
else s.expr.debuggingData = {};
|
||||
}
|
||||
|
||||
public visitAnyIf(s: AST.If) {
|
||||
if (!s) return;
|
||||
var firstTrue = coalesce(s)(s => s.thenBody)(then_ => then_.firstNonComment())(fnc => fnc.stableId)();
|
||||
var firstFalse = coalesce(s)(s => s.elseBody)(else_ => else_.firstNonComment())(fnc => fnc.stableId)();
|
||||
|
||||
var visitedTrue = this.fullRunMap[firstTrue];
|
||||
var visitedFalse = this.fullRunMap[firstFalse];
|
||||
|
||||
if (visitedTrue && visitedFalse) s.rawCondition.debuggingData = { visited: true };
|
||||
else if (visitedTrue) s.rawCondition.debuggingData = { alwaysTrue: true };
|
||||
else if (visitedFalse) s.rawCondition.debuggingData = { alwaysFalse: true };
|
||||
|
||||
super.visitAnyIf(s);
|
||||
}
|
||||
|
||||
public visitAstNode(node: AST.AstNode) {
|
||||
if (!node) return;
|
||||
return this.visitChildren(node);
|
||||
}
|
||||
|
||||
public visitInlineActions(node: AST.InlineActions) {
|
||||
if (!node) return;
|
||||
return this.visitExprStmt(node);
|
||||
}
|
||||
}
|
||||
|
||||
export function getNormalCoverageAsync(scriptId: string, theScript: AST.App, compilerVersion: string) : Promise {
|
||||
var unionPromise = Cloud.getCoverageDataAsync(scriptId, compilerVersion).then(a => a.length > 0 ? a[0] : null);
|
||||
var intersectionPromise = Cloud.getCoverageDataAsync(scriptId, compilerVersion, true).then(a => a.length > 0 ? a[0] : null);
|
||||
|
||||
var unwrapCoverage = (c) => c && FullRunMapper.doit(RunBitMap.fromJSON(c.astnodes), null, theScript);
|
||||
return unionPromise.then(union => {
|
||||
var unwrappedU = unwrapCoverage(union);
|
||||
return intersectionPromise.then(intersection => {
|
||||
var unwrappedI = unwrapCoverage(intersection);
|
||||
return { union: unwrappedU, intersection: unwrappedI };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function accumulateRunMaps(runs: IRun[], theScript: AST.App, existingUAcc ?: IStringSet, existingIAcc ?: IStringSet) {
|
||||
var uacc = existingUAcc;
|
||||
var iacc = existingIAcc;
|
||||
|
||||
runs.forEach(run => {
|
||||
var full = FullRunMapper.doit(run.runmap, run.stack, theScript);
|
||||
uacc = setUnion(uacc, full);
|
||||
iacc = (!iacc) ? full : setIntersection(iacc, full);
|
||||
});
|
||||
|
||||
return { union: uacc, intersection: iacc };
|
||||
}
|
||||
|
||||
export function accumulateStacks(runs: IRun[], theScript: AST.App, existingTops?: IStringSet, existingStacks?: IStringSet) {
|
||||
var tops:IStringSet = existingTops ? existingTops : {};
|
||||
var stacks = existingStacks;
|
||||
|
||||
runs.forEach(run => {
|
||||
var stack = run.stack;
|
||||
if (!stack || !stack.pack || !stack.path) return;
|
||||
|
||||
var full = setFromArray(stack.pack.map(node => node.id).filter(id => !!id));
|
||||
var top = stack.pack[stack.path[0]].id;
|
||||
stacks = setUnion(stacks, full);
|
||||
tops[top] = true;
|
||||
});
|
||||
|
||||
return { tops: tops, full: stacks };
|
||||
}
|
||||
|
||||
export class ScriptDebuggingBugginessAnnotator extends AST.NodeVisitor {
|
||||
private bugSkeleton: IStringSet;
|
||||
private bugShell: IStringSet;
|
||||
private bugInnerSkeleton: IStringSet;
|
||||
public maxRating = 9;
|
||||
public topRatedId: string;
|
||||
public topRatedRate: number = 0;
|
||||
public localTopRate: number = 0;
|
||||
|
||||
public topRateContainer = { critical: 0 };
|
||||
|
||||
constructor(
|
||||
public normalPortrait: { union: IStringSet; intersection: IStringSet; },
|
||||
public bugPortrait: { union: IStringSet; intersection: IStringSet; },
|
||||
public stacky: { tops: IStringSet; full: IStringSet },
|
||||
public additional: IStringSet = {},
|
||||
public errorMessage: string = null)
|
||||
{
|
||||
super();
|
||||
|
||||
this.bugSkeleton = setEmpty(normalPortrait.intersection) ? {} : setDifference(bugPortrait.intersection, normalPortrait.intersection) || {};
|
||||
this.bugInnerSkeleton = setEmpty(normalPortrait.union) ? {} : setDifference(this.bugSkeleton, normalPortrait.union);
|
||||
this.bugShell = setEmpty(normalPortrait.union) ? {} : setDifference(bugPortrait.union, normalPortrait.union);
|
||||
}
|
||||
|
||||
public rate(id: string): number {
|
||||
var ret = 0;
|
||||
var countSet = (s: IStringSet) => ret += (s[id] ? 1 : 0);
|
||||
|
||||
countSet(this.bugPortrait.union || {});
|
||||
countSet(this.bugPortrait.intersection || {});
|
||||
countSet(this.stacky.tops || {});
|
||||
countSet(this.stacky.full || {});
|
||||
countSet(this.bugShell);
|
||||
countSet(this.bugSkeleton);
|
||||
countSet(this.bugInnerSkeleton);
|
||||
countSet(this.additional);
|
||||
countSet(this.additional);
|
||||
|
||||
if (ret > this.maxRating) ret = this.maxRating;
|
||||
if (ret > this.localTopRate) this.localTopRate = ret;
|
||||
if (ret > this.topRatedRate) {
|
||||
this.topRatedRate = ret;
|
||||
this.topRatedId = id;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public visitApp(script: AST.App) {
|
||||
super.visitApp(script);
|
||||
|
||||
script.annotatedBy = AST.AnnotationMode.Crash;
|
||||
this.topRateContainer.critical = this.topRatedRate;
|
||||
}
|
||||
|
||||
public visitAction(node: AST.Action) {
|
||||
this.localTopRate = 0;
|
||||
super.visitAction(node);
|
||||
if (!node || !this.localTopRate) return;
|
||||
|
||||
node.debuggingData = { critical: this.localTopRate, max: this.topRateContainer };
|
||||
}
|
||||
|
||||
public visitAstNode(node: AST.AstNode) {
|
||||
if (!node) return;
|
||||
return this.visitChildren(node);
|
||||
}
|
||||
|
||||
public updateRatingForGlobals(node: AST.AstNode, rate: number) {
|
||||
var varFinder = new AST.VariableFinder();
|
||||
varFinder.traverse(node, true);
|
||||
varFinder.readGlobals.forEach((g: AST.GlobalDef) => {
|
||||
var dd = g.debuggingData;
|
||||
if (dd && dd.critical) (dd.critical < rate) && (dd.critical = rate);
|
||||
else g.debuggingData = { critical: rate, max: this.topRateContainer };
|
||||
});
|
||||
}
|
||||
|
||||
public visitAnyIf(node: AST.If) {
|
||||
if (!node) return;
|
||||
super.visitAnyIf(node);
|
||||
|
||||
var rate = this.rate(node.stableId);
|
||||
node.rawCondition.debuggingData = { critical: rate, max: this.topRateContainer };
|
||||
this.updateRatingForGlobals(node.rawCondition, rate);
|
||||
}
|
||||
|
||||
public visitFor(node: AST.For) {
|
||||
if (!node) return;
|
||||
super.visitFor(node);
|
||||
|
||||
var rate = this.rate(node.stableId);
|
||||
node.upperBound.debuggingData = { critical: rate, max: this.topRateContainer };
|
||||
this.updateRatingForGlobals(node.upperBound, rate);
|
||||
}
|
||||
|
||||
public visitForeach(node: AST.Foreach) {
|
||||
if (!node) return;
|
||||
super.visitForeach(node);
|
||||
|
||||
var rate = this.rate(node.stableId);
|
||||
node.collection.debuggingData = { critical: rate, max: this.topRateContainer };
|
||||
this.updateRatingForGlobals(node.collection, rate);
|
||||
}
|
||||
|
||||
public visitWhile(node: AST.While) {
|
||||
if (!node) return;
|
||||
super.visitWhile(node);
|
||||
|
||||
var rate = this.rate(node.stableId);
|
||||
node.condition.debuggingData = { critical: rate, max: this.topRateContainer };
|
||||
this.updateRatingForGlobals(node.condition, rate);
|
||||
}
|
||||
|
||||
public visitInlineActions(node: AST.InlineActions) {
|
||||
if (!node) return;
|
||||
return this.visitExprStmt(node);
|
||||
}
|
||||
|
||||
public visitExprStmt(s: AST.ExprStmt) {
|
||||
if (!s || !s.stableId) return;
|
||||
|
||||
var rate = this.rate(s.stableId);
|
||||
var message = this.stacky && this.stacky.tops && this.stacky.tops[s.stableId] ? this.errorMessage : undefined;
|
||||
|
||||
s.expr.debuggingData = { critical: rate, max: this.topRateContainer, errorMessage: message };
|
||||
|
||||
this.updateRatingForGlobals(s.expr, rate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class StmtHashTable extends Hashtable {
|
||||
constructor() {
|
||||
super((stmt: AST.Stmt) => Hashtable.stringHash(stmt.stableId), (lhv: AST.Stmt, rhv: AST.Stmt) => lhv.stableId === rhv.stableId);
|
||||
}
|
||||
|
||||
public push(stmt: AST.Stmt) {
|
||||
super.set(stmt, true);
|
||||
}
|
||||
}
|
||||
|
||||
export class SmallBackSlicer {
|
||||
public slice: StmtHashTable = new StmtHashTable(); // really a hashset of Stmts
|
||||
public visitedCFNodes: StmtHashTable = new StmtHashTable(); // really a hashset of Stmts
|
||||
public relevants: StmtHashTable = new StmtHashTable(); // Stmt => IStringSet
|
||||
public vars: StmtHashTable = new StmtHashTable() // Stmt => VariableFinder
|
||||
|
||||
private steps = 0;
|
||||
|
||||
constructor(public roots: AST.Stmt[], public universum: IStringSet) { }
|
||||
|
||||
static varsFrom(stmt: AST.AstNode) {
|
||||
if (stmt instanceof AST.If) stmt = (<AST.If>stmt).rawCondition;
|
||||
if (stmt instanceof AST.For) stmt = (<AST.For>stmt).upperBound;
|
||||
if (stmt instanceof AST.While) stmt = (<AST.While>stmt).condition;
|
||||
if (stmt instanceof AST.Foreach) stmt = (<AST.Foreach>stmt).conditions;
|
||||
|
||||
var ret = new AST.VariableFinder();
|
||||
ret.traverse(stmt, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private defs(stmt: AST.Stmt): IStringSet {
|
||||
var vars: AST.VariableFinder = this.vars.get(stmt);
|
||||
if (!vars) this.vars.set(stmt, vars = SmallBackSlicer.varsFrom(stmt));
|
||||
return setFromArray(vars.writtenGlobals.map(v => v.getName()).concat(vars.writtenLocals.map(v => v.getName())));
|
||||
}
|
||||
|
||||
private refs(stmt: AST.Stmt): IStringSet {
|
||||
var vars: AST.VariableFinder = this.vars.get(stmt);
|
||||
if (!vars) this.vars.set(stmt, vars = SmallBackSlicer.varsFrom(stmt));
|
||||
return setFromArray(vars.readGlobals.map(v => v.getName()).concat(vars.readLocals.map(v => v.getName())));
|
||||
}
|
||||
|
||||
private majorStepBack(from: AST.Stmt[]): AST.Stmt[]{
|
||||
var retSet = new StmtHashTable();
|
||||
from.forEach(stmt => this.stepBack(stmt).forEach(pred => (pred) && (retSet.push(pred))));
|
||||
return retSet.keys();
|
||||
}
|
||||
|
||||
static mayBeControlFlowDependency(from: AST.Stmt, stmt: AST.Stmt) {
|
||||
var cf = stmt instanceof AST.If || stmt instanceof AST.For || stmt instanceof AST.Foreach || stmt instanceof AST.While;
|
||||
return cf && AST.PredecessorsFinder.enclosingStmt(from) === stmt; // can use object equality here
|
||||
}
|
||||
|
||||
private stepBack(from: AST.Stmt): AST.Stmt[]{
|
||||
var total: AST.Stmt[];
|
||||
total = [];
|
||||
total = AST.PredecessorsFinder.find(from);
|
||||
var refs = this.relevants.get(from);
|
||||
|
||||
var ignoreIxs: number[] = [];
|
||||
total.forEach((stmt, ix) => {
|
||||
var check = this.relevants.get(stmt);
|
||||
|
||||
var defs = this.defs(stmt);
|
||||
var newRefs = this.refs(stmt);
|
||||
|
||||
var rel1 = setIntersection(refs, defs);
|
||||
var rel2 = setDifference(refs, defs);
|
||||
|
||||
var rel3 = check || mkSet();
|
||||
|
||||
var cf = SmallBackSlicer.mayBeControlFlowDependency(from, stmt) && !(this.visitedCFNodes.get(stmt));
|
||||
if (!setEmpty(rel1) || cf) {
|
||||
if (cf) this.visitedCFNodes.set(stmt, true);
|
||||
this.addToSlice(stmt);
|
||||
rel3 = setUnion(rel3, newRefs);
|
||||
}
|
||||
|
||||
var ret = setUnion(rel2, rel3);
|
||||
this.relevants.set(stmt, ret);
|
||||
if (setSize(check) === setSize(ret)) ignoreIxs.push(ix);
|
||||
});
|
||||
|
||||
ignoreIxs.forEach(ix => delete total[ix]);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private addToSlice(stmt: AST.Stmt) {
|
||||
if (stmt && this.universum && this.universum[stmt.stableId]) this.slice.set(stmt, true);
|
||||
}
|
||||
|
||||
public doit() {
|
||||
var current = this.roots;
|
||||
this.roots.forEach(root => {
|
||||
this.relevants.set(root, this.refs(root));
|
||||
this.addToSlice(root);
|
||||
});
|
||||
var limitDepth = 100;
|
||||
var i = 0;
|
||||
while (current.length > 0 && i < limitDepth) {
|
||||
++i;
|
||||
current = current.filter(stmt => !!this.universum[stmt.stableId]);
|
||||
current = this.majorStepBack(current);
|
||||
}
|
||||
|
||||
return setFromArray(this.slice.keys().map((stmt:AST.Stmt) => stmt.stableId));
|
||||
}
|
||||
}
|
||||
|
||||
export class ScriptBugginessFeatureSurvey implements EditorSpy {
|
||||
static surveyName = "BucketColours";
|
||||
|
||||
private manager: IEditorSurveyManager;
|
||||
private eventCounter = 0;
|
||||
|
||||
public addTo(manager: IEditorSurveyManager) {
|
||||
manager.addSpy(ScriptBugginessFeatureSurvey.surveyName, this);
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onAddThisSpy(manager: IEditorSurveyManager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onRemoveThisSpy() {
|
||||
delete this.manager;
|
||||
this.eventCounter = 0;
|
||||
}
|
||||
|
||||
public removeSelf() {
|
||||
if (this.manager) this.manager.removeSpy(ScriptBugginessFeatureSurvey.surveyName);
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onRun(_) {
|
||||
this.removeSelf();
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onLeaveDebugMode() {
|
||||
this.removeSelf();
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onExit() {
|
||||
this.removeSelf();
|
||||
}
|
||||
|
||||
private scoreDD(debuggingData) {
|
||||
if (debuggingData && debuggingData.critical && debuggingData.max) {
|
||||
var scoreF = debuggingData.critical / debuggingData.max.critical;
|
||||
return Math.ceil(scoreF * 5) * 20; // level to 20-40-60-80-100 %
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
private check() {
|
||||
if (this.eventCounter > 10) {
|
||||
this.removeSelf();
|
||||
tick(Ticks.coverageBucketSurveyExceededSuccessfully);
|
||||
}
|
||||
this.eventCounter++;
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onEdit(node: AST.AstNode) {
|
||||
if (node instanceof AST.ExprStmt) {
|
||||
this.check();
|
||||
var dd: any = (<AST.ExprStmt>node).expr.debuggingData;
|
||||
if (!dd || !dd.max) return; // this is a new statement
|
||||
var scorr = this.scoreDD(dd);
|
||||
tickArg(Ticks.coverageBucketSurveyStatementEdit, scorr + "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onView(decl: AST.Decl) {
|
||||
if (decl instanceof AST.Action) {
|
||||
this.check();
|
||||
var dd: any = (<AST.Action>decl).debuggingData;
|
||||
if (!dd || !dd.max) return; // this is a new action
|
||||
var scorr = this.scoreDD(dd);
|
||||
tickArg(Ticks.coverageBucketSurveyActionEdit, scorr + "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onAddNear(node: AST.AstNode) {
|
||||
if (node instanceof AST.ExprStmt) {
|
||||
this.check();
|
||||
var dd: any = (<AST.ExprStmt>node).expr.debuggingData;
|
||||
if (!dd || !dd.max) return; // this is a new statement
|
||||
var scorr = this.scoreDD(dd);
|
||||
tickArg(Ticks.coverageBucketSurveyStatementEdit, scorr + "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onDelete(node: AST.Block) {
|
||||
this.check();
|
||||
node.stmts.forEach(stmt => {
|
||||
if (stmt instanceof AST.ExprStmt) {
|
||||
var dd: any = (<AST.ExprStmt>stmt).expr.debuggingData;
|
||||
if (!dd || !dd.max) return; // this is a new statement
|
||||
var scorr = this.scoreDD(dd);
|
||||
tickArg(Ticks.coverageBucketSurveyStatementEdit, scorr + "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onEnterDebugMode() {
|
||||
this.check();
|
||||
tick(Ticks.coverageBucketSurveyDebugger);
|
||||
}
|
||||
|
||||
/* override */
|
||||
public onAddBreakpoint(node: AST.Stmt) {
|
||||
this.check();
|
||||
if (node instanceof AST.ExprStmt) {
|
||||
var dd: any = (<AST.ExprStmt>node).expr.debuggingData;
|
||||
if (!dd || !dd.max) return; // this is a new statement
|
||||
var scorr = this.scoreDD(dd);
|
||||
tickArg(Ticks.coverageBucketSurveyBreakpoint, scorr + "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -561,13 +561,6 @@ module TDev
|
|||
return Meta.packageScriptAsync(this.currentRt, id, options);
|
||||
}
|
||||
|
||||
static scriptProfiledCache: { [scriptId: string]: Promise/*of Profiled*/; } = {}
|
||||
static getScriptProfiledAsync(scriptId: string): Promise { // of Profiled
|
||||
var p = EditorHost.scriptProfiledCache[scriptId];
|
||||
if (!p) EditorHost.scriptProfiledCache[scriptId] = p = Cloud.getProfiledAsync(scriptId, TDev.AST.Compiler.version);
|
||||
return p;
|
||||
}
|
||||
|
||||
resetAnnotators() {
|
||||
new ProfilingResultsAnnotator(null).visitApp(Script);
|
||||
new ScriptDebuggingAnnotator(null, null, null).visitApp(Script);
|
||||
|
@ -584,14 +577,6 @@ module TDev
|
|||
profilingData.maximumEps = this.currentRt.eventQ.maximumEps;
|
||||
profilingData.averageEps = this.currentRt.eventQ.averageEps;
|
||||
}
|
||||
EditorHost.getScriptProfiledAsync(scriptId)
|
||||
.then(profiled=> {
|
||||
var p = profiled.count < 20 ? 1 - .90 * profiled.count / 20 : .1; // probability of uploading profiling information; this is after the probability of getting instrumented for profiling; the combined probability converges to 1%
|
||||
if (Random.normalized() < p)
|
||||
return Cloud.postProfilingDataAsync(scriptId, profilingData)
|
||||
.then(() => profiled.count++);
|
||||
})
|
||||
.done(undefined, () => { }); // ignore network errors
|
||||
}
|
||||
if (profilingData.show) {
|
||||
this.resetAnnotators();
|
||||
|
@ -599,27 +584,9 @@ module TDev
|
|||
}
|
||||
}
|
||||
|
||||
static scriptCoveredCache: { [scriptId: string]: Promise/*of Covered*/; } = { }
|
||||
static getScriptCoveredAsync(scriptId: string): Promise { // of Covered
|
||||
var p = EditorHost.scriptCoveredCache[scriptId];
|
||||
if (!p) EditorHost.scriptCoveredCache[scriptId] = p = Cloud.getCoveredAsync(scriptId, TDev.AST.Compiler.version);
|
||||
return p;
|
||||
}
|
||||
|
||||
public attachCoverageInfo(coverageData: CoverageData, showCoverage: boolean): void {
|
||||
if (!coverageData) return;
|
||||
|
||||
var scriptId = this.currentRt.currentScriptId;
|
||||
if (scriptId)
|
||||
EditorHost.getScriptCoveredAsync(scriptId)
|
||||
.then(covered => {
|
||||
var p = covered.count < 90 ? .5 - .49 * covered.count / 90 : .01; // probability of uploading coverage information
|
||||
if (Random.normalized() < p)
|
||||
return Cloud.postCoverageDataAsync(scriptId, coverageData)
|
||||
.then(() => covered.count++);
|
||||
})
|
||||
.done(undefined, () => { }); // ignore network errors
|
||||
|
||||
if (showCoverage) {
|
||||
this.resetAnnotators();
|
||||
this.attachDebuggingInfo(RunBitMap.fromJSON(coverageData.astnodes), null, null);
|
||||
|
@ -1623,7 +1590,6 @@ module TDev
|
|||
crashOnInvalid: /crashOnInvalid/.test(document.URL),
|
||||
commonSubexprElim: /commonSubexprElim/.test(document.URL),
|
||||
constantPropagation: /constantPropagation/.test(document.URL),
|
||||
coverage: true,
|
||||
azureSite: Azure.getDestinationAppUrl(app),
|
||||
};
|
||||
Object.keys(opts).forEach((k) => {
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
///<reference path='collab.ts'/>
|
||||
///<reference path='declrender.ts'/>
|
||||
///<reference path='debugger.ts'/>
|
||||
///<reference path='debuggerAnalysis.ts'/>
|
||||
///<reference path='libraryExtractor.ts'/>
|
||||
///<reference path='editor.ts'/>
|
||||
///<reference path='tutorial.ts'/>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -92,7 +92,6 @@ module TDev.TestMgr
|
|||
blockChaining: Browser.compilerBlockChaining || /blockChaining/.test(document.URL),
|
||||
commonSubexprElim: /commonSubexprElim/.test(document.URL),
|
||||
constantPropagation: /constantPropagation/.test(document.URL),
|
||||
coverage: false,
|
||||
crashOnInvalid: /crashOnInvalid/.test(document.URL),
|
||||
};
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
47
rt/cloud.ts
47
rt/cloud.ts
|
@ -1,8 +1,5 @@
|
|||
///<reference path='refs.ts'/>
|
||||
|
||||
|
||||
module TDev {
|
||||
export module Cloud {
|
||||
module TDev.Cloud {
|
||||
|
||||
export var lite = false;
|
||||
|
||||
|
@ -518,10 +515,6 @@ module TDev {
|
|||
{
|
||||
return Util.httpPostJsonAsync(getPrivateApiUrl("bug"), bug);
|
||||
}
|
||||
export function postRunReportAsync(id: string, run: any): Promise // of ???
|
||||
{
|
||||
return Util.httpPostJsonAsync(getPrivateApiUrl(id + "/runs"), run);
|
||||
}
|
||||
export function postTicksAsync(ticks:any) : Promise // of void
|
||||
{
|
||||
return Util.httpPostJsonAsync(getPrivateApiUrl("ticks"), ticks);
|
||||
|
@ -608,41 +601,6 @@ module TDev {
|
|||
client.send();
|
||||
});
|
||||
}
|
||||
|
||||
export function postProfilingDataAsync(id: string, body: any /*ProfilingData*/): Promise // of void
|
||||
{
|
||||
return Util.httpPostJsonAsync(getPrivateApiUrl(id + "/profile?v=1"), body);
|
||||
}
|
||||
|
||||
export function postCoverageDataAsync(id: string, body: any /*CoverageData*/): Promise // of void
|
||||
{
|
||||
return Util.httpPostJsonAsync(getPrivateApiUrl(id + "/coverage"), body);
|
||||
}
|
||||
|
||||
export function getCoverageDataAsync(id: string, compilerversion: string, intersection: boolean = false): Promise // of CoverageData[]
|
||||
{
|
||||
return Util.httpGetJsonAsync(getPrivateApiUrl(id + "/coverage?compilerversion=" + encodeURIComponent(compilerversion) + (intersection ? "&view=intersection" : "")));
|
||||
}
|
||||
|
||||
export interface Covered {
|
||||
count: number;
|
||||
}
|
||||
export function getCoveredAsync(id: string, compilerversion: string): Promise // of Covered
|
||||
{
|
||||
return Util.httpGetJsonAsync(getPrivateApiUrl("me/covered/" + id + "?compilerversion=" + encodeURIComponent(compilerversion)));
|
||||
}
|
||||
|
||||
export function getProfileDataAsync(id: string, compilerversion: string): Promise // of ProfileData[]
|
||||
{
|
||||
return Util.httpGetJsonAsync(getPrivateApiUrl(id + "/profile?compilerversion=" + encodeURIComponent(compilerversion)));
|
||||
}
|
||||
export interface Profiled {
|
||||
count: number;
|
||||
}
|
||||
export function getProfiledAsync(id: string, compilerversion: string): Promise // of Profiled
|
||||
{
|
||||
return Util.httpGetJsonAsync(getPrivateApiUrl("me/profiled/" + id + "?compilerversion=" + encodeURIComponent(compilerversion)));
|
||||
}
|
||||
export function postPendingProgressAsync() {
|
||||
if (!getUserId() || !getAccessToken() || isOffline() || dbg) return Promise.as();
|
||||
var data = loadPendingProgress();
|
||||
|
@ -657,7 +615,4 @@ module TDev {
|
|||
var req = { kind: "comment", text: text, userplatform: Browser.platformCaps };
|
||||
return Cloud.postPrivateApiAsync(id + "/comments", req)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -715,8 +715,6 @@ module TDev {
|
|||
public attachScriptStackTrace(bug: BugReport): void { }
|
||||
/* overriden in EditorHost */
|
||||
public debugModeEnabled(): boolean { return false; }
|
||||
/* overriden in EditorHost */
|
||||
public publishRunHelpLink(title: string): any { return undefined; }
|
||||
|
||||
public exceptionHandler(e:any)
|
||||
{
|
||||
|
@ -731,10 +729,6 @@ module TDev {
|
|||
var stack = PackedStackTrace.buildFrom(this.currentRt.getStackTrace());
|
||||
TDev.RT.App.logException(e);
|
||||
|
||||
var askRunReport =
|
||||
!!e.isUserError &&
|
||||
!!Runtime.theRuntime.currentScriptId /* && this.currentRt.currentAuthorId != Cloud.getUserId() */; // the commented piece is questionable, put to remove by Nikolai
|
||||
|
||||
var actions = this.exceptionActions(e) || {}
|
||||
|
||||
var debugAction = actions.debug || Util.doNothing;
|
||||
|
@ -747,70 +741,7 @@ module TDev {
|
|||
d.addFirst(div("floatingFrown", ":("))
|
||||
}
|
||||
|
||||
if (askRunReport) {
|
||||
var futureModalDialog: ModalDialog;
|
||||
var debugBtn: any = (this.debugModeEnabled() && debugAction) ? HTML.mkButton(lf("debug"), () => {
|
||||
debugAction(); this.attachDebuggingInfo(runMap, stack, msg); futureModalDialog.dismiss()
|
||||
}) : null;
|
||||
var sendRunReport = (anonymous: boolean, comment?: string) => {
|
||||
var run: IRun = {
|
||||
clientfile: (<any>window).mainJsName,
|
||||
compilerversion: this.currentRt.compiled._compilerVersion,
|
||||
kind: "run",
|
||||
publicationid: this.currentRt.currentScriptId,
|
||||
error: error,
|
||||
stack: stack,
|
||||
runmap: runMap.isEmpty() ? undefined : runMap,
|
||||
userplatform: Browser.platformCaps,
|
||||
anonymous: anonymous
|
||||
};
|
||||
var rt = Runtime.theRuntime;
|
||||
Cloud.postRunReportAsync(rt.currentScriptId, run).done(
|
||||
json => {
|
||||
HTML.showProgressNotification(dbg ? ("crash report submitted: /" + json.id) : "thank you for your run report");
|
||||
if (!!comment) {
|
||||
var jsonComment = {
|
||||
kind: "comment",
|
||||
text: comment,
|
||||
userplatform: Browser.platformCaps
|
||||
};
|
||||
Util.httpPostJsonAsync(Cloud.getPrivateApiUrl(json.id + "/comments"), jsonComment).done();
|
||||
}
|
||||
},
|
||||
e => HTML.showProgressNotification(dbg ? ("crash report send failed: " + e) : "thank you for your run report")
|
||||
);
|
||||
}
|
||||
|
||||
var hiddenContent = div(null);
|
||||
var commentArea = HTML.mkAutoExpandingTextArea();
|
||||
commentArea.textarea.placeholder = "describe how you crashed the script";
|
||||
|
||||
var defDS = hiddenContent.style.display;
|
||||
hiddenContent.style.display = "none";
|
||||
hiddenContent.setChildren([commentArea.div, debugBtn]);
|
||||
var showHidden = (v: boolean) => hiddenContent.style.display = v ? defDS : "none";
|
||||
|
||||
var commentBox = HTML.mkCheckBox(
|
||||
lf("show additional options"),
|
||||
showHidden
|
||||
);
|
||||
|
||||
futureModalDialog = ModalDialog.askManyWithAdditionalElts(
|
||||
lf("the script crashed"), this.publishRunHelpLink("about posting crash"),
|
||||
lf("Do you want to post some information to help improve the script? (The collected information include a stack trace, a coverage map, and a message.)"),
|
||||
lf("error message"),
|
||||
error,
|
||||
{
|
||||
"post": () => sendRunReport(false, commentArea.textarea.value || null),
|
||||
"post anonymously": () => sendRunReport(true, commentArea.textarea.value || null),
|
||||
cancel: () => { },
|
||||
},
|
||||
commentBox,
|
||||
hiddenContent
|
||||
);
|
||||
futureModalDialog.fullYellow();
|
||||
frown(futureModalDialog)
|
||||
} else if(this.canEditCode() && !!e.syntaxErrorDeclName) {
|
||||
if(this.canEditCode() && !!e.syntaxErrorDeclName) {
|
||||
var dial = ModalDialog.buttons(
|
||||
lf("errors in the code?"),
|
||||
lf("the script appears to have some errors. fix each error marked with a red :( symbol and try to run again"),
|
||||
|
|
Загрузка…
Ссылка в новой задаче