custom load/store steps to implement promise flow

This commit is contained in:
Erik Krogh Kristensen
2020-01-13 20:06:51 +01:00
parent c50de3a7e8
commit d09bce5cd7
9 changed files with 322 additions and 329 deletions

View File

@@ -125,216 +125,159 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
*/
private module PromiseFlow {
/**
* A promise from which data-flow can flow into or out of.
*
* This promise can both be a promise created by e.g. `new Promise(..)` or `Promise.resolve(..)`,
* or the result from calling a method on a promise e.g. `promise.then(..)`.
*
* The 4 methods in this class describe that ordinary and exceptional flow can flow into and out of this promise.
* Gets the pseudo-field used to describe resolved values in a promise.
*/
private abstract class PromiseNode extends DataFlow::SourceNode {
/**
* Get a DataFlow::Node for a value that this promise is resolved with.
* The value is sent either to a chained promise, or to an `await` expression.
*
* The value is e.g. an argument to `resolve(..)`, or a return value from a `.then(..)` handler.
*/
DataFlow::Node getASentResolveValue() { none() }
/**
* Get the DataFlow::Node that receives the value that this promise has been resolved with.
*
* E.g. the `x` in `promise.then((x) => ..)`.
*/
DataFlow::Node getReceivedResolveValue() { none() }
/**
* Get a DataFlow::Node for a value that this promise is rejected with.
* The value is sent either to a chained promise, or thrown by an `await` expression.
*
* The value is e.g. an argument to `reject(..)`, or an exception thrown by the promise executor.
*/
DataFlow::Node getASentRejectValue() { none() }
/**
* Get the DataFlow::Node that receives the value that this promise has been rejected with.
*
* E.g. the `x` in `promise.catch((x) => ..)`.
*/
DataFlow::Node getReceivedRejectValue() { none() }
string resolveField() {
result = "$PromiseResolveField$"
}
/**
* A PromiseNode for a PromiseDefinition.
* E.g. `new Promise(..)`.
* Gets the pseudo-field used to describe rejected values in a promise.
*/
private class PromiseDefinitionNode extends PromiseNode {
string rejectField() {
result = "$PromiseRejectField$"
}
/**
* A flow step describing a promise definition.
*
* The resolved/rejected value is written to a pseudo-field on the promise.
*/
class PromiseDefitionStep extends DataFlow::AdditionalFlowStep {
PromiseDefinition promise;
PromiseDefinitionNode() { this = promise }
override DataFlow::Node getASentResolveValue() {
result = promise.getResolveParameter().getACall().getArgument(0)
PromiseDefitionStep() {
this = promise
}
override DataFlow::Node getASentRejectValue() {
result = promise.getRejectParameter().getACall().getArgument(0)
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = promise.getResolveParameter().getACall().getArgument(0) and
succ = this
or
result = promise.getExecutor().getExceptionalReturn()
prop = rejectField() and
(
pred = promise.getRejectParameter().getACall().getArgument(0) or
pred = promise.getExecutor().getExceptionalReturn()
) and
succ = this
}
}
/**
* A PromiseNode for a call that creates a promise.
* E.g. `Promise.resolve(..)` or `Promise.all(..)`.
* A flow step describing the a Promise.resolve (and similar) call.
*/
private class PromiseCreationNode extends PromiseNode {
class CreationStep extends DataFlow::AdditionalFlowStep {
PromiseCreationCall promise;
PromiseCreationNode() { this = promise }
override DataFlow::Node getASentResolveValue() {
exists(DataFlow::Node value | value = promise.getValue() |
not value instanceof PromiseNode and
result = value
or
result = value.(PromiseNode).getASentResolveValue()
)
CreationStep() {
this = promise
}
override DataFlow::Node getASentRejectValue() {
result = promise.getValue().(PromiseNode).getASentRejectValue()
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = promise.getValue() and
succ = this
}
}
/**
* A node referring to a PromiseNode through type-tracking.
* A load step loading the pseudo-field describing that the promise is either resolved or rejected.
* A resolved value is forwarding as the resulting value of the `await` expression,
* and a rejected value is thrown as a exception.
*/
private class TrackedPromiseNode extends PromiseNode {
PromiseNode base;
TrackedPromiseNode() {
this = trackPromise(DataFlow::TypeTracker::end(), base) and
not this instanceof PromiseDefinitionNode and
not this instanceof PromiseCreationNode
class AwaitStep extends DataFlow::AdditionalFlowStep {
DataFlow::Node operand;
AwaitExpr await;
AwaitStep() {
this.getEnclosingExpr() = await and
operand.getEnclosingExpr() = await.getOperand()
}
override DataFlow::Node getASentResolveValue() { result = base.getASentResolveValue() }
override DataFlow::Node getReceivedResolveValue() { result = base.getReceivedResolveValue() }
override DataFlow::Node getASentRejectValue() { result = base.getASentRejectValue() }
override DataFlow::Node getReceivedRejectValue() { result = base.getReceivedRejectValue() }
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
succ = this and
pred = operand.getALocalSource()
or
prop = rejectField() and
succ = await.getExceptionTarget() and
pred = operand.getALocalSource()
}
}
/**
* A flow step describing the data-flow related to the `.then` method of a promise.
*/
class ThenStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ThenStep() {
this.getMethodName() = "then"
}
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = getReceiver().getALocalSource() and
succ = getCallback(0).getParameter(0)
or
prop = rejectField() and
pred = getReceiver().getALocalSource() and
succ = getCallback(1).getParameter(0)
}
override predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) {
not exists(this.getArgument(1)) and
prop = rejectField() and
pred = getReceiver().getALocalSource() and
succ = this
}
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = getCallback([0..1]).getAReturn() and
succ = this
or
prop = rejectField() and
pred = getCallback([0..1]).getExceptionalReturn() and
succ = this
}
}
private DataFlow::SourceNode trackPromise(DataFlow::TypeTracker t, PromiseNode promise) {
t.start() and result = promise
or
exists(DataFlow::TypeTracker t2 | result = trackPromise(t2, promise).track(t2, t))
}
/**
* A PromiseNode that is a method call on an existing PromiseNode.
* E.g. `promise.then(..)`.
* A flow step describing the data-flow related to the `.catch` method of a promise.
*/
private abstract class ChainedPromiseNode extends PromiseNode, DataFlow::MethodCallNode {
PromiseNode base;
ChainedPromiseNode() { this = base.getAMethodCall(_) }
PromiseNode getBase() { result = base }
}
/**
* A PromiseNode for the `.then(..)` method on an existing promise.
*/
private class PromiseThenNode extends ChainedPromiseNode {
PromiseThenNode() { this = base.getAMethodCall("then") }
override DataFlow::Node getASentResolveValue() {
exists(DataFlow::Node ret | ret = this.getCallback(0).getAReturn() |
if ret instanceof PromiseNode
then result = ret.(PromiseNode).getReceivedResolveValue()
else result = ret
)
class CatchStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
CatchStep() {
this.getMethodName() = "catch"
}
override DataFlow::Node getASentRejectValue() {
not exists(this.getCallback(1)) and result = base.getASentRejectValue()
or
result = this.getCallback([0..1]).getExceptionalReturn()
override predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
pred = getReceiver().getALocalSource() and
succ = getCallback(0).getParameter(0)
}
override DataFlow::Node getReceivedResolveValue() { result = this.getCallback(0).getParameter(0) }
override predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = resolveField() and
pred = getReceiver().getALocalSource() and
succ = this
}
override DataFlow::Node getReceivedRejectValue() { result = this.getCallback(1).getParameter(0) }
}
/**
* A PromiseNode for the `.finally(..)` method on an existing promise.
*/
private class PromiseFinallyNode extends ChainedPromiseNode {
PromiseFinallyNode() { this = base.getAMethodCall("finally") }
override DataFlow::Node getASentResolveValue() { result = base.getASentResolveValue() }
override DataFlow::Node getASentRejectValue() {
result = base.getASentRejectValue()
or
result = this.getCallback(0).getExceptionalReturn()
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = rejectField() and
pred = getCallback([0..1]).getExceptionalReturn() and
succ = this
}
}
/**
* A PromiseNode for the `.catch(..)` method on an existing promise.
* A flow step describing the data-flow related to the `.finally` method of a promise.
*/
private class PromiseCatchNode extends ChainedPromiseNode {
PromiseCatchNode() { this = base.getAMethodCall("catch") }
override DataFlow::Node getASentResolveValue() {
exists(DataFlow::Node ret | ret = this.getCallback(0).getAReturn() |
if ret instanceof PromiseNode
then result = ret.(PromiseNode).getReceivedResolveValue()
else result = ret
)
or
result = base.getASentResolveValue()
class FinallyStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
FinallyStep() {
this.getMethodName() = "finally"
}
override DataFlow::Node getASentRejectValue() { result = this.getCallback(0).getExceptionalReturn() }
override DataFlow::Node getReceivedResolveValue() { none() }
override DataFlow::Node getReceivedRejectValue() { result = this.getCallback(0).getParameter(0) }
}
private ChainedPromiseNode getAChainedPromise(PromiseNode p) { result.getBase() = p}
/**
* A data flow edge from a promise resolve/reject to the corresponding handler (or `await` expression).
*/
private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
PromiseNode promise;
PromiseFlowStep() { this = promise }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = promise.getASentResolveValue() and
succ = getAChainedPromise(promise).getReceivedResolveValue()
or
pred = promise.getASentRejectValue() and
succ = getAChainedPromise(promise).getReceivedRejectValue()
or
pred = promise.getASentResolveValue() and
exists(DataFlow::SourceNode awaitNode |
awaitNode.asExpr().(AwaitExpr).getOperand() = promise.asExpr() and
succ = awaitNode
)
or
pred = promise.getASentRejectValue() and
exists(DataFlow::SourceNode awaitNode |
awaitNode.asExpr().(AwaitExpr).getOperand() = promise.asExpr() and
succ = awaitNode.asExpr().getExceptionTarget()
)
override predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) {
(prop = resolveField() or prop = rejectField()) and
pred = getReceiver().getALocalSource() and
succ = this
}
}
}

View File

@@ -223,6 +223,21 @@ abstract class Configuration extends string {
predicate hasFlowPath(SourcePathNode source, SinkPathNode sink) {
flowsTo(source, _, sink, _, this)
}
/**
* Holds if the `pred` should be stored in the object `succ` under the property `prop`.
*/
predicate isAdditionalStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
predicate isAdditionalLoadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
predicate isAdditionalCopyPropertyStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
}
/**
@@ -449,6 +464,24 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
) {
none()
}
/**
* Holds if the `pred` should be stored in the object `succ` under the property `prop`.
*/
cached
predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
cached
predicate load(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
cached
predicate copyProperty(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
}
/**
@@ -551,6 +584,13 @@ private predicate exploratoryFlowStep(
basicFlowStep(pred, succ, _, cfg) or
basicStoreStep(pred, succ, _) or
basicLoadStep(pred, succ, _) or
any(AdditionalFlowStep s).store(pred, succ, _) or
cfg.isAdditionalStoreStep(pred, succ, _) or
any(AdditionalFlowStep s).load(pred, succ, _) or
cfg.isAdditionalLoadStep(pred, succ, _) or
any(AdditionalFlowStep s).copyProperty(pred, succ, _) or
cfg.isAdditionalCopyPropertyStep(pred, succ, _) or
// the following two disjuncts taken together over-approximate flow through
// higher-order calls
callback(pred, succ) or
@@ -712,6 +752,12 @@ private predicate storeStep(
basicStoreStep(pred, succ, prop) and
summary = PathSummary::level()
or
any(AdditionalFlowStep s).store(pred, succ, prop) and
summary = PathSummary::level()
or
cfg.isAdditionalStoreStep(pred, succ, prop) and
summary = PathSummary::level()
or
exists(Function f, DataFlow::Node mid |
// `f` stores its parameter `pred` in property `prop` of a value that flows back to the caller,
// and `succ` is an invocation of `f`
@@ -767,6 +813,12 @@ private predicate loadStep(
basicLoadStep(pred, succ, prop) and
summary = PathSummary::level()
or
any(AdditionalFlowStep s).load(pred, succ, prop) and
summary = PathSummary::level()
or
cfg.isAdditionalLoadStep(pred, succ, prop) and
summary = PathSummary::level()
or
exists(Function f, DataFlow::PropRead read |
parameterPropRead(f, succ, pred, prop, read, cfg) and
reachesReturn(f, read, cfg, summary)
@@ -804,13 +856,31 @@ pragma[noinline]
private predicate flowThroughProperty(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration cfg, PathSummary summary
) {
exists(string prop, DataFlow::Node base, PathSummary oldSummary, PathSummary newSummary |
reachableFromStoreBase(prop, pred, base, cfg, oldSummary) and
loadStep(base, succ, prop, cfg, newSummary) and
exists(string prop, DataFlow::Node storeBase, DataFlow::Node loadBase, PathSummary oldSummary, PathSummary newSummary |
reachableFromStoreBase(prop, pred, storeBase, cfg, oldSummary) and
(storeBase = loadBase or existsCopyProperty(storeBase, loadBase, prop)) and
loadStep(loadBase, succ, prop, cfg, newSummary) and
summary = oldSummary.append(newSummary)
)
}
/**
* Holds if the property `prop` is copied from `fromNode` to `toNode` using at least 1 step.
*
* The recursion of this predicate has been unfolded once compared to a naive implementation in order to avoid having no constraint on `prop`.
* Therefore a caller of this predicate should also test whether the `toNode` and `fromNode` are equal.
*/
private predicate existsCopyProperty(DataFlow::Node fromNode, DataFlow::Node toNode, string prop) {
exists(DataFlow::AdditionalFlowStep step, DataFlow::Node mid |
step.copyProperty(fromNode, mid, prop) and
(
existsCopyProperty(mid, toNode, prop)
or
mid = toNode
)
)
}
/**
* Holds if `arg` and `cb` are passed as arguments to a function which in turn
* invokes `cb`, passing `arg` as its `i`th argument.

View File

@@ -1,5 +0,0 @@
import javascript
query predicate flowSteps(DataFlow::Node pred, DataFlow::Node succ) {
any(DataFlow::AdditionalFlowStep step).step(pred, succ)
}

View File

@@ -0,0 +1,49 @@
(async function () {
var source = "source";
var p1 = Promise.resolve(source);
sink(await p1); // NOT OK
var p2 = new Promise((resolve, reject) => resolve(source));
sink(await p2); // NOT OK
var p3 = new Promise((resolve, reject) => reject(source));
sink(await p3); // OK!
var p4 = new Promise((resolve, reject) => reject(source));
try {
var foo = await p4;
} catch(e) {
sink(e); // NOT OK!
}
Promise.resolve(source).then(x => sink(x)); // NOT OK!
Promise.resolve(source).then(x => foo(x), y => sink(y)); // OK!
new Promise((resolve, reject) => reject(source)).then(x => sink(x)); // OK!
new Promise((resolve, reject) => reject(source)).then(x => foo(x), y => sink(y)); // NOT OK!
Promise.resolve("foo").then(x => source).then(z => sink(z)); // NOT OK!
Promise.resolve(source).then(x => "foo").then(z => sink(z)); // OK!
new Promise((resolve, reject) => reject(source)).catch(x => sink(x)); // NOT OK!
Promise.resolve(source).catch(() => {}).then(a => sink(a)); // NOT OK!
var p5 = Promise.resolve(source);
var p6 = p5.catch(() => {});
var p7 = p6.then(a => sink(a)); // NOT OK!
new Promise((resolve, reject) => reject(source)).then(() => {}).catch(x => sink(x)); // NOT OK!
new Promise((resolve, reject) => reject(source)).then(() => {}, () => {}).catch(x => sink(x)); // OK!
Promise.resolve(source).catch(() => {}).catch(() => {}).catch(() => {}).then(a => sink(a)); // NOT OK!
Promise.resolve(source).finally(() => {}).then(a => sink(a)); // NOT OK!
new Promise(() => {throw source}).catch(x => sink(x)); // NOT OK!
})();

View File

@@ -0,0 +1,17 @@
import javascript
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PromiseFlowTestingConfig" }
override predicate isSource(DataFlow::Node source) {
source.getEnclosingExpr().getStringValue() = "source"
}
override predicate isSink(DataFlow::Node sink) {
any(DataFlow::InvokeNode call | call.getCalleeName() = "sink").getAnArgument() = sink
}
}
query predicate flow(DataFlow::Node source, DataFlow::Node sink) {
any(Configuration a).hasFlow(source, sink)
}

View File

@@ -1,56 +0,0 @@
(async function () {
function throws(resolve, reject) {
throw new Error()
}
new Promise(throws)
.catch((e) => console.log(e));
new Promise(throws)
.then((val) => console.log(val), (error) => console.log(error));
try {
await new Promise(throws);
} catch (e2) {
console.log(e2);
}
new Promise((resolve, reject) => reject(3))
.catch((e3) => console.log(e3));
try {
await new Promise((resolve, reject) => reject(4));
} catch(e4) {
console.log(e4);
}
new Promise(throws)
.then(() => {})
.catch((e5) => console.log(e5));
new Promise(throws)
.then(() => {})
.catch((e6) => console.log(e6))
.catch((e7) => console.log(e7));
var foo = await new Promise((resolve, reject) => resolve(8))
var bar = await new Promise((resolve, reject) => resolve(9)).then((x) => x + 2);
var p = Promise.resolve(3);
var baz = await p.then((val) => val * 2);
var p2 = new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resolve(13);
} else {
reject(14);
}
});
var quz = await p2.then(val => val * 4).catch(e => e * 3);
function returnsPromise() {
return Promise.resolve(3);
}
var a = await returnsPromise();
})();

View File

@@ -0,0 +1 @@
semmle-extractor-options: --experimental

View File

@@ -1,27 +1,34 @@
test_ResolvedPromiseDefinition
| flowsteps.js:40:11:40:28 | Promise.resolve(3) | flowsteps.js:40:27:40:27 | 3 |
| flowsteps.js:53:12:53:29 | Promise.resolve(3) | flowsteps.js:53:28:53:28 | 3 |
| flow.js:4:11:4:33 | Promise ... source) | flow.js:4:27:4:32 | source |
| flow.js:20:2:20:24 | Promise ... source) | flow.js:20:18:20:23 | source |
| flow.js:22:2:22:24 | Promise ... source) | flow.js:22:18:22:23 | source |
| flow.js:28:2:28:23 | Promise ... ("foo") | flow.js:28:18:28:22 | "foo" |
| flow.js:30:2:30:24 | Promise ... source) | flow.js:30:18:30:23 | source |
| flow.js:34:2:34:24 | Promise ... source) | flow.js:34:18:34:23 | source |
| flow.js:36:11:36:33 | Promise ... source) | flow.js:36:27:36:32 | source |
| flow.js:44:2:44:24 | Promise ... source) | flow.js:44:18:44:23 | source |
| flow.js:46:2:46:24 | Promise ... source) | flow.js:46:18:46:23 | source |
| promises.js:53:19:53:41 | Promise ... source) | promises.js:53:35:53:40 | source |
| promises.js:62:19:62:41 | Promise ... source) | promises.js:62:35:62:40 | source |
| promises.js:71:5:71:27 | Promise ... source) | promises.js:71:21:71:26 | source |
test_PromiseDefinition_getARejectHandler
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:6:12:6:32 | (e) => ... .log(e) |
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:9:38:9:66 | (error) ... (error) |
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:18:12:18:34 | (e3) => ... log(e3) |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:69:26:80 | y => sink(y) |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:66:42:73 | () => {} |
| flow.js:48:2:48:34 | new Pro ... ource}) | flow.js:48:42:48:53 | x => sink(x) |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:20:6:22:3 | (v) => ... v;\\n } |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
test_PromiseDefinition_getExecutor
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
| flowsteps.js:12:11:12:29 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:17:15:17:44 | (resolv ... ject(3) |
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) | flowsteps.js:21:23:21:52 | (resolv ... ject(4) |
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:2:3:4:3 | functio ... r()\\n } |
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) | flowsteps.js:36:31:36:61 | (resolv ... olve(8) |
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:31:38:61 | (resolv ... olve(9) |
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:43:24:49:3 | (resolv ... }\\n } |
| flow.js:7:11:7:59 | new Pro ... ource)) | flow.js:7:23:7:58 | (resolv ... source) |
| flow.js:10:11:10:58 | new Pro ... ource)) | flow.js:10:23:10:57 | (resolv ... source) |
| flow.js:13:11:13:58 | new Pro ... ource)) | flow.js:13:23:13:57 | (resolv ... source) |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:14:24:48 | (resolv ... source) |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:14:26:48 | (resolv ... source) |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:14:32:48 | (resolv ... source) |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:14:40:48 | (resolv ... source) |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:14:42:48 | (resolv ... source) |
| flow.js:48:2:48:34 | new Pro ... ource}) | flow.js:48:14:48:33 | () => {throw source} |
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:29:5:3 | functio ... e);\\n } |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:30:17:3 | (res, r ... e);\\n } |
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:31:35:5 | functio ... ;\\n } |
@@ -29,103 +36,70 @@ test_PromiseDefinition_getExecutor
test_PromiseDefinition_getAFinallyHandler
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
test_PromiseDefinition
| flowsteps.js:5:3:5:21 | new Promise(throws) |
| flowsteps.js:8:3:8:21 | new Promise(throws) |
| flowsteps.js:12:11:12:29 | new Promise(throws) |
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) |
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) |
| flowsteps.js:26:3:26:21 | new Promise(throws) |
| flowsteps.js:31:3:31:21 | new Promise(throws) |
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) |
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) |
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) |
| flow.js:7:11:7:59 | new Pro ... ource)) |
| flow.js:10:11:10:58 | new Pro ... ource)) |
| flow.js:13:11:13:58 | new Pro ... ource)) |
| flow.js:24:2:24:49 | new Pro ... ource)) |
| flow.js:26:2:26:49 | new Pro ... ource)) |
| flow.js:32:2:32:49 | new Pro ... ource)) |
| flow.js:40:2:40:49 | new Pro ... ource)) |
| flow.js:42:2:42:49 | new Pro ... ource)) |
| flow.js:48:2:48:34 | new Pro ... ource}) |
| promises.js:3:17:5:4 | new Pro ... );\\n }) |
| promises.js:10:18:17:4 | new Pro ... );\\n }) |
| promises.js:33:19:35:6 | new Pro ... \\n }) |
| promises.js:43:19:45:6 | Q.Promi ... \\n }) |
test_PromiseDefinition_getAResolveHandler
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:9:11:9:35 | (val) = ... og(val) |
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:27:11:27:18 | () => {} |
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:32:11:32:18 | () => {} |
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:69:38:80 | (x) => x + 2 |
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:50:27:50:40 | val => val * 4 |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:56:24:67 | x => sink(x) |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:56:26:66 | x => foo(x) |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:56:40:63 | () => {} |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:56:42:63 | () => {} |
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:6:16:8:3 | functio ... al;\\n } |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:18:17:20:3 | (v) => ... v;\\n } |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:26:20:28:3 | (v) => ... v;\\n } |
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:36:18:38:5 | functio ... ;\\n } |
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:46:18:48:5 | functio ... ;\\n } |
test_PromiseDefinition_getRejectParameter
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
| flowsteps.js:12:11:12:29 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:17:25:17:30 | reject |
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) | flowsteps.js:21:33:21:38 | reject |
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:2:28:2:33 | reject |
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) | flowsteps.js:36:41:36:46 | reject |
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:41:38:46 | reject |
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:43:34:43:39 | reject |
| flow.js:7:11:7:59 | new Pro ... ource)) | flow.js:7:33:7:38 | reject |
| flow.js:10:11:10:58 | new Pro ... ource)) | flow.js:10:33:10:38 | reject |
| flow.js:13:11:13:58 | new Pro ... ource)) | flow.js:13:33:13:38 | reject |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:24:24:29 | reject |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:24:26:29 | reject |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:24:32:29 | reject |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:24:40:29 | reject |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:24:42:29 | reject |
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:48:3:53 | reject |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:36:10:38 | rej |
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:50:33:55 | reject |
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:48:43:53 | reject |
test_PromiseDefinition_getResolveParameter
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
| flowsteps.js:8:3:8:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
| flowsteps.js:12:11:12:29 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:17:16:17:22 | resolve |
| flowsteps.js:21:11:21:53 | new Pro ... ect(4)) | flowsteps.js:21:24:21:30 | resolve |
| flowsteps.js:26:3:26:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
| flowsteps.js:31:3:31:21 | new Promise(throws) | flowsteps.js:2:19:2:25 | resolve |
| flowsteps.js:36:19:36:62 | new Pro ... lve(8)) | flowsteps.js:36:32:36:38 | resolve |
| flowsteps.js:38:19:38:62 | new Pro ... lve(9)) | flowsteps.js:38:32:38:38 | resolve |
| flowsteps.js:43:12:49:4 | new Pro ... }\\n }) | flowsteps.js:43:25:43:31 | resolve |
| flow.js:7:11:7:59 | new Pro ... ource)) | flow.js:7:24:7:30 | resolve |
| flow.js:10:11:10:58 | new Pro ... ource)) | flow.js:10:24:10:30 | resolve |
| flow.js:13:11:13:58 | new Pro ... ource)) | flow.js:13:24:13:30 | resolve |
| flow.js:24:2:24:49 | new Pro ... ource)) | flow.js:24:15:24:21 | resolve |
| flow.js:26:2:26:49 | new Pro ... ource)) | flow.js:26:15:26:21 | resolve |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:15:32:21 | resolve |
| flow.js:40:2:40:49 | new Pro ... ource)) | flow.js:40:15:40:21 | resolve |
| flow.js:42:2:42:49 | new Pro ... ource)) | flow.js:42:15:42:21 | resolve |
| promises.js:3:17:5:4 | new Pro ... );\\n }) | promises.js:3:39:3:45 | resolve |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:10:31:10:33 | res |
| promises.js:33:19:35:6 | new Pro ... \\n }) | promises.js:33:41:33:47 | resolve |
| promises.js:43:19:45:6 | Q.Promi ... \\n }) | promises.js:43:39:43:45 | resolve |
test_PromiseDefinition_getACatchHandler
| flowsteps.js:5:3:5:21 | new Promise(throws) | flowsteps.js:6:12:6:32 | (e) => ... .log(e) |
| flowsteps.js:17:3:17:45 | new Pro ... ect(3)) | flowsteps.js:18:12:18:34 | (e3) => ... log(e3) |
| flow.js:32:2:32:49 | new Pro ... ource)) | flow.js:32:57:32:68 | x => sink(x) |
| flow.js:48:2:48:34 | new Pro ... ource}) | flow.js:48:42:48:53 | x => sink(x) |
| promises.js:10:18:17:4 | new Pro ... );\\n }) | promises.js:23:18:25:3 | (v) => ... v;\\n } |
flowSteps
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:6:13:6:13 | e |
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:9:39:9:43 | error |
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:13:12:13:13 | e2 |
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:28:13:28:14 | e5 |
| flowsteps.js:2:3:4:3 | exceptional return of function throws | flowsteps.js:33:13:33:14 | e6 |
| flowsteps.js:17:15:17:44 | exceptional return of anonymous function | flowsteps.js:18:13:18:14 | e3 |
| flowsteps.js:17:43:17:43 | 3 | flowsteps.js:18:13:18:14 | e3 |
| flowsteps.js:21:23:21:52 | exceptional return of anonymous function | flowsteps.js:22:11:22:12 | e4 |
| flowsteps.js:21:51:21:51 | 4 | flowsteps.js:22:11:22:12 | e4 |
| flowsteps.js:27:11:27:18 | exceptional return of anonymous function | flowsteps.js:28:13:28:14 | e5 |
| flowsteps.js:32:11:32:18 | exceptional return of anonymous function | flowsteps.js:33:13:33:14 | e6 |
| flowsteps.js:33:12:33:34 | exceptional return of anonymous function | flowsteps.js:34:13:34:14 | e7 |
| flowsteps.js:36:31:36:61 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
| flowsteps.js:36:60:36:60 | 8 | flowsteps.js:36:13:36:62 | await n ... lve(8)) |
| flowsteps.js:38:31:38:61 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
| flowsteps.js:38:60:38:60 | 9 | flowsteps.js:38:70:38:70 | x |
| flowsteps.js:38:69:38:80 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
| flowsteps.js:38:76:38:80 | x + 2 | flowsteps.js:38:13:38:81 | await n ... x + 2) |
| flowsteps.js:40:27:40:27 | 3 | flowsteps.js:41:27:41:29 | val |
| flowsteps.js:41:26:41:41 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
| flowsteps.js:41:35:41:41 | val * 2 | flowsteps.js:41:13:41:42 | await p ... al * 2) |
| flowsteps.js:43:24:49:3 | exceptional return of anonymous function | flowsteps.js:50:49:50:49 | e |
| flowsteps.js:45:12:45:13 | 13 | flowsteps.js:50:27:50:29 | val |
| flowsteps.js:47:11:47:12 | 14 | flowsteps.js:50:49:50:49 | e |
| flowsteps.js:50:27:50:40 | exceptional return of anonymous function | flowsteps.js:50:49:50:49 | e |
| flowsteps.js:50:34:50:40 | val * 4 | flowsteps.js:50:13:50:59 | await p ... e * 3) |
| flowsteps.js:50:49:50:58 | exceptional return of anonymous function | flowsteps.js:1:2:56:1 | exceptional return of anonymous function |
| flowsteps.js:50:54:50:58 | e * 3 | flowsteps.js:50:13:50:59 | await p ... e * 3) |
| flowsteps.js:53:28:53:28 | 3 | flowsteps.js:55:11:55:32 | await r ... omise() |
| promises.js:4:13:4:18 | source | promises.js:6:26:6:28 | val |
| promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:20:7:20:7 | v |
| promises.js:10:30:17:3 | exceptional return of anonymous function | promises.js:23:19:23:19 | v |
| promises.js:14:11:14:20 | res_source | promises.js:18:18:18:18 | v |
| promises.js:16:11:16:20 | rej_source | promises.js:20:7:20:7 | v |
| promises.js:16:11:16:20 | rej_source | promises.js:23:19:23:19 | v |
| promises.js:34:17:34:22 | source | promises.js:36:28:36:30 | val |
| promises.js:44:17:44:22 | source | promises.js:46:28:46:30 | val |
| promises.js:53:35:53:40 | source | promises.js:54:28:54:30 | val |
| promises.js:62:35:62:40 | source | promises.js:63:28:63:30 | val |
| promises.js:71:21:71:26 | source | promises.js:71:34:71:36 | val |
flow
| flow.js:2:15:2:22 | "source" | flow.js:5:7:5:14 | await p1 |
| flow.js:2:15:2:22 | "source" | flow.js:8:7:8:14 | await p2 |
| flow.js:2:15:2:22 | "source" | flow.js:17:8:17:8 | e |
| flow.js:2:15:2:22 | "source" | flow.js:20:41:20:41 | x |
| flow.js:2:15:2:22 | "source" | flow.js:26:79:26:79 | y |
| flow.js:2:15:2:22 | "source" | flow.js:28:58:28:58 | z |
| flow.js:2:15:2:22 | "source" | flow.js:32:67:32:67 | x |
| flow.js:2:15:2:22 | "source" | flow.js:34:57:34:57 | a |
| flow.js:2:15:2:22 | "source" | flow.js:38:29:38:29 | a |
| flow.js:2:15:2:22 | "source" | flow.js:40:82:40:82 | x |
| flow.js:2:15:2:22 | "source" | flow.js:44:89:44:89 | a |
| flow.js:2:15:2:22 | "source" | flow.js:46:59:46:59 | a |
| flow.js:2:15:2:22 | "source" | flow.js:48:52:48:52 | x |

View File

@@ -7,4 +7,4 @@ import PromiseDefinition_getAResolveHandler
import PromiseDefinition_getRejectParameter
import PromiseDefinition_getResolveParameter
import PromiseDefinition_getACatchHandler
import Flowsteps
import flow