Skip to content

Commit acf1d0d

Browse files
committed
Merge branch 'gh-pages' of https://github.com/LivelyKernel/lively4-core into gh-pages
2 parents 77376a9 + 5bd4c2b commit acf1d0d

File tree

17 files changed

+1085
-1130
lines changed

17 files changed

+1085
-1130
lines changed

src/client/dependency-graph/graph.js

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,65 @@ const { types: t } = babelDefault.babel;
55

66
export class DependencyGraph {
77

8-
get inner() { return this._inner }
8+
get programPath() { return this.capabilities.programPath }
99

10-
constructor(code) {
11-
this._inner = code.toAST();
10+
constructor(capabilities) {
11+
this.capabilities = capabilities;
1212

1313
//Stores globally a `map` of properties (`String`) to objectbindings of the objects, that have a property of that name.
1414
this.memberAssignments = new Map();
15+
this.finishedEnrichment = false;
1516
this.enrich();
1617
}
18+
19+
getAexprAtCursor(location) {
20+
let aexprPath;
21+
this.programPath.traverse({
22+
CallExpression(path) {
23+
if (!range(path.node.loc).contains(location)) {
24+
path.skip();
25+
} else if (isAExpr(path)) {
26+
aexprPath = path;
27+
path.stop();
28+
}
29+
}
30+
});
31+
return aexprPath;
32+
}
33+
34+
getAllActiveExpressions () {
35+
const allAExpr = [];
36+
this.programPath.traverse({
37+
CallExpression(path) {
38+
if (isAExpr(path) ) {
39+
allAExpr.push(path);
40+
}
41+
}
42+
});
43+
return allAExpr;
44+
}
45+
46+
get hasActiveExpressionsDirective() {
47+
return this.programPath.node.directives.some(node => {
48+
return node.value.value === "enable aexpr";
49+
});
50+
}
51+
52+
ensureEnrichment() {
53+
if (this.finishedEnrichment) return true;
54+
if (!this.programPath || !this.hasActiveExpressionsDirective) return false;
55+
try {
56+
this.enrich();
57+
} catch (err) {
58+
console.error("Unable to process source code", err);
59+
return false;
60+
}
61+
return true;
62+
}
1763

1864
enrich() {
1965
let self = this;
20-
this._inner.traverseAsAST({
66+
this.programPath.traverse({
2167
enter(path) {
2268
path.node.extra = {
2369
// this in necessary due to possible circles
@@ -31,8 +77,8 @@ export class DependencyGraph {
3177
});
3278

3379
// adds the corresponding binding to every identifier
34-
this._inner.traverseAsAST({
35-
Scope(path) {
80+
this.programPath.traverse({
81+
Scopable(path) {
3682
Object.entries(path.scope.bindings).forEach(([_name, binding]) => {
3783
binding.referencePaths.forEach(path => {
3884
path.node.extra.binding = binding;
@@ -45,17 +91,18 @@ export class DependencyGraph {
4591

4692
this.enrichFunctionNodes();
4793

48-
this._inner.traverseAsAST({
94+
this.programPath.traverse({
4995
Expression(expr) {
5096
self.collectExpressionInformation(expr);
5197
}
5298
});
99+
this.finishedEnrichment = true;
53100
}
54101

55102
// Filters every memberassignment and registers it in `this.memberAssignments`
56103
extractMemberAssignments(){
57104
let self = this;
58-
this._inner.traverseAsAST({
105+
this.programPath.traverse({
59106
MemberExpression(expr) {
60107
if (expr.node.computed) return;
61108
if (!expr.parentPath.isAssignmentExpression()) return;
@@ -86,7 +133,7 @@ export class DependencyGraph {
86133

87134
enrichFunctionNodes(){
88135
// adds bindings definend outside of the current scope(e.g. Function) to the scope
89-
this._inner.traverseAsAST({
136+
this.programPath.traverse({
90137
'Function|ArrowFunctionExpression|Program'(path) {
91138
path.node.extra.leakingBindings = leakingBindings(path);
92139
const callExpressions = path.node.extra.callExpressions = [];
@@ -130,17 +177,18 @@ export class DependencyGraph {
130177
*
131178
*/
132179
collectExpressionInformation(path) {
133-
//debugger;
180+
if (path.node.extra.results) {
181+
return path.node.extra.results
182+
}
183+
134184
if (path.node.extra.returnVisited <= 0) {
185+
path.node.extra.resolvedObjects = [];
186+
path.node.extra.results = [];
135187
return [];
136188
}
137189

138190
path.node.extra.returnVisited -= 1;
139191

140-
if (path.node.extra.results) {
141-
return path.node.extra.results
142-
}
143-
144192
let results = [];
145193
let resolvedObjects = [];
146194

@@ -163,9 +211,11 @@ export class DependencyGraph {
163211
} else if (path.isAssignmentExpression() || path.isVariableDeclarator()) {
164212

165213
let val = this.assignedValue(path);
166-
this.collectExpressionInformation(val);
167-
results = val.node.extra.results;
168-
resolvedObjects = val.node.extra.resolvedObjects;
214+
if(val && val.node) {
215+
this.collectExpressionInformation(val);
216+
results = val.node.extra.results;
217+
resolvedObjects = val.node.extra.resolvedObjects;
218+
}
169219

170220
} else if (path.isFunction()) {
171221
results = [path];
@@ -276,6 +326,7 @@ export class DependencyGraph {
276326
* //b is equal to {x:2}
277327
*/
278328
shadowedBindings(bindings) {
329+
if(!bindings) return [];
279330

280331
/* this should be stored in the members map or in an extra property.
281332
* DOES NOT DETECT DEPENDENCIES THROUGH SIMPLE ASSIGNMENTS (a la `a = b`)
@@ -297,14 +348,13 @@ export class DependencyGraph {
297348
* DOES NOT care for execution order of the code
298349
* uses heuristics e.g. fixed recursion depth
299350
*/
300-
resolveDependencies(location) {
301-
351+
resolveDependenciesAtLocation(location) {
302352
let node;
303353
let dep;
304354
let dependencyGraph = this;
305-
this._inner.traverseAsAST({
355+
this.programPath.traverse({
306356
CallExpression(path) {
307-
if (isAExpr(path) && range(path.node.loc).contains(location)) {
357+
if (isAExpr(path) && range(path.node.loc).contains(location.node.loc)) {
308358
if (path.node.dependencies != null) {
309359
dep = path.node.dependencies;
310360
console.log("dependencies already collected: ", dep);
@@ -321,8 +371,37 @@ export class DependencyGraph {
321371
}
322372
return node.extra.dependencies;
323373
}
324-
325-
_resolveDependencies(path) {
374+
375+
resolveDependenciesForMember(memberName) {
376+
let deps = [];
377+
const self = this;
378+
let dependencyGraph = this;
379+
this.programPath.traverse({
380+
Function(path) {
381+
if(!path.node.key || path.node.key.name !== memberName) return;
382+
if (!path.node.extra.dependencies) {
383+
path.node.extra.dependencies = dependencyGraph.resolveDependencies(path);
384+
}
385+
deps.push(...(path.node.extra.dependencies || []), path.get("key"));
386+
},
387+
AssignmentExpression(path) {
388+
const left = path.node.left;
389+
if(t.isMemberExpression(left), t.isThisExpression(left.object) && left.property.name === memberName) {
390+
deps.push(path);
391+
}
392+
},
393+
UpdateExpression(path) {
394+
const argument = path.node.argument;
395+
if(t.isMemberExpression(argument), t.isThisExpression(argument.object) && argument.property.name === memberName) {
396+
deps.push(path);
397+
}
398+
}
399+
//Todo: Array stuff??
400+
});
401+
return deps;
402+
}
403+
404+
resolveDependencies(path) {
326405
const self = this;
327406
if ((path.node.extra.visited -= 1) <= 0) {
328407
return path.node.extra.dependencies || new Set();
@@ -343,7 +422,7 @@ export class DependencyGraph {
343422
if (t.isAssignmentExpression(callee)) {
344423
const value = this.assignedValue(callee);
345424
if (t.isFunction(value) || t.isArrowFunctionExpression(value)) {
346-
this._resolveDependencies(value).forEach(dep => dependencies.add(dep));
425+
this.resolveDependencies(value).forEach(dep => dependencies.add(dep));
347426
}
348427
}
349428

@@ -356,7 +435,7 @@ export class DependencyGraph {
356435
if (path.node.extra.objects.size) {
357436
for (const [objExpr, members] of path.node.extra.objects.entries()) {
358437
for (const member of members) {
359-
self.assignmentsOf(member, {bindings: new Set([objExpr.extra.binding])}).forEach(assignment => {
438+
self.assignmentsOf(member, {bindings: new Set(objExpr.extra.binding ?[objExpr.extra.binding]:[])}).forEach(assignment => {
360439
dependencies.add(assignment)
361440
});
362441
}

src/client/reactive/active-expression-rewriting/active-expression-rewriting.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Dependency {
7272
// #TODO: compute and cache isGlobal
7373
constructor(context, identifier, type) {
7474
this._type = type;
75-
75+
this.classFilePath = context.__classFilePath__;
7676
this.isTracked = false;
7777
}
7878

@@ -224,6 +224,10 @@ const DependenciesToAExprs = {
224224
this._depsToAExprs.removeAllLeftFor(aexpr);
225225
deps.forEach(dep => dep.updateTracking());
226226
},
227+
228+
getDependencyMapForFile(url) {
229+
return new Map(this._depsToAExprs.getAllLeft().filter(dep => dep.classFilePath && dep.classFilePath.includes(url)).map(dep => [dep, this.getAExprsForDep(dep)]));
230+
},
227231

228232
getAExprsForDep(dep) {
229233
return Array.from(this._depsToAExprs.getRightsFor(dep));
@@ -822,6 +826,11 @@ export function getGlobal(globalName) {
822826
}
823827
}
824828

829+
830+
export function getDependencyMapForFile(url) {
831+
return DependenciesToAExprs.getDependencyMapForFile(url);
832+
}
833+
825834
export function setGlobal(globalName) {
826835
TracingHandler.globalUpdated(globalName);
827836
}

src/client/reactive/active-expression/active-expression.js

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import EventTarget from '../utils/event-target.js';
33
import { shallowEqualsArray, shallowEqualsSet, shallowEqualsMap, shallowEquals, deepEquals } from '../utils/equality.js';
44
import { isString, clone, cloneDeep } from 'utils';
55

6+
import sourcemap from 'src/external/source-map.min.js';
7+
68
// #TODO: this is use to keep SystemJS from messing up scoping
79
// (BaseActiveExpression would not be defined in aexpr)
810
const HACK = {};
@@ -211,7 +213,7 @@ export class BaseActiveExpression {
211213
this.lastValue = NO_VALUE_YET;
212214
this.updateLastValue();
213215
}
214-
216+
215217
updateLastValue() {
216218
const { value, isError } = this.evaluateToCurrentValue();
217219
if (!isError) {
@@ -318,8 +320,10 @@ export class BaseActiveExpression {
318320
}
319321
const lastValue = this.lastValue;
320322
this.storeResult(value);
321-
322-
this.logEvent('changed value', value);
323+
this.findCallee().then(trigger => {
324+
this.logEvent('changed value', {value, trigger});
325+
})
326+
323327

324328
this.notify(value, {
325329
lastValue,
@@ -328,6 +332,31 @@ export class BaseActiveExpression {
328332
});
329333
}
330334

335+
async findCallee() {
336+
const stack = lively.stack();
337+
const frames = stack.frames;
338+
339+
for (let frame of frames) {
340+
if(!frame.file.includes("active-expression") && frame.file !== "<anonymous>") {
341+
return await frame.getSourceLoc();
342+
}
343+
}
344+
return undefined;
345+
}
346+
347+
async extractSourceMap(code) {
348+
var m = code.match(/\/\/# sourceMappingURL=(.*)(\n|$)/);
349+
if (!m) return false;
350+
var sourceMappingURL = m[1];
351+
return (await fetch(sourceMappingURL)).json();
352+
}
353+
354+
extractSourceURL(code) {
355+
var m = code.match(/\/\/# sourceURL=(.*)(\n|$)/);
356+
if (!m) return false;
357+
return m[1];
358+
}
359+
331360
setupMatcher(matchConfig) {
332361
// configure using existing matcher
333362
if (matchConfig && isString.call(matchConfig)) {
@@ -509,7 +538,7 @@ export class BaseActiveExpression {
509538
disable() {
510539
if (this._isEnabled) {
511540
this._isEnabled = false;
512-
this.emit('disable')
541+
this.emit('disable');
513542
}
514543
}
515544

0 commit comments

Comments
 (0)