Skip to content

Commit 4e38e3a

Browse files
committed
feat(change_detector): add support for method calls
1 parent dcd905a commit 4e38e3a

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

modules/change_detection/src/parser/ast.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ export class MethodCall extends AST {
315315
}
316316

317317
export class FunctionCall extends AST {
318-
@FIELD('final receiver:AST')
318+
@FIELD('final target:AST')
319319
@FIELD('final closureMap:ClosureMap')
320320
@FIELD('final args:List')
321321
constructor(target:AST, closureMap:ClosureMap, args:List) {

modules/change_detection/src/record.js

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {ClosureMap} from 'change_detection/parser/closure_map';
66
var _fresh = new Object();
77

88
export const PROTO_RECORD_CONST = 'const';
9-
export const PROTO_RECORD_FUNC = 'func';
9+
export const PROTO_RECORD_PURE_FUNCTION = 'func';
10+
export const PROTO_RECORD_CLOSURE = 'closure';
11+
export const PROTO_RECORD_METHOD = 'method';
1012
export const PROTO_RECORD_PROPERTY = 'property';
1113

1214
/**
@@ -97,11 +99,20 @@ export class Record {
9799
this.mode = MODE_STATE_CONST;
98100
this.funcOrValue = protoRecord.funcOrValue;
99101

100-
} else if (protoRecord.recordType === PROTO_RECORD_FUNC) {
101-
this.mode = MODE_STATE_INVOKE_FUNCTION;
102+
} else if (protoRecord.recordType === PROTO_RECORD_PURE_FUNCTION) {
103+
this.mode = MODE_STATE_INVOKE_PURE_FUNCTION;
102104
this.funcOrValue = protoRecord.funcOrValue;
103105
this.args = ListWrapper.createFixedSize(protoRecord.arity);
104106

107+
} else if (protoRecord.recordType === PROTO_RECORD_METHOD) {
108+
this.mode = MODE_STATE_INVOKE_METHOD;
109+
this.funcOrValue = protoRecord.funcOrValue;
110+
this.args = ListWrapper.createFixedSize(protoRecord.arity);
111+
112+
} else if (protoRecord.recordType === PROTO_RECORD_CLOSURE) {
113+
this.mode = MODE_STATE_INVOKE_CLOSURE;
114+
this.args = ListWrapper.createFixedSize(protoRecord.arity);
115+
105116
} else if (protoRecord.recordType === PROTO_RECORD_PROPERTY) {
106117
this.mode = MODE_STATE_PROPERTY;
107118
this.funcOrValue = protoRecord.funcOrValue;
@@ -138,7 +149,13 @@ export class Record {
138149
case MODE_STATE_PROPERTY:
139150
return this.funcOrValue(this.context);
140151

141-
case MODE_STATE_INVOKE_FUNCTION:
152+
case MODE_STATE_INVOKE_METHOD:
153+
return this.funcOrValue(this.context, this.args);
154+
155+
case MODE_STATE_INVOKE_CLOSURE:
156+
return FunctionWrapper.apply(this.context, this.args);
157+
158+
case MODE_STATE_INVOKE_PURE_FUNCTION:
142159
return FunctionWrapper.apply(this.funcOrValue, this.args);
143160

144161
case MODE_STATE_CONST:
@@ -147,9 +164,6 @@ export class Record {
147164
case MODE_STATE_MARKER:
148165
throw new BaseException('MODE_STATE_MARKER not implemented');
149166

150-
case MODE_STATE_INVOKE_METHOD:
151-
throw new BaseException('MODE_STATE_INVOKE_METHOD not implemented');
152-
153167
case MODE_STATE_MAP:
154168
throw new BaseException('MODE_STATE_MAP not implemented');
155169

@@ -183,18 +197,17 @@ const MODE_STATE_MARKER = 0x0000;
183197

184198
/// _context[_protoRecord.propname] => _getter(_context)
185199
const MODE_STATE_PROPERTY = 0x0001;
186-
/// _context(_arguments)
187-
const MODE_STATE_INVOKE_FUNCTION = 0x0002;
188-
/// _getter(_context, _arguments)
200+
const MODE_STATE_INVOKE_PURE_FUNCTION = 0x0002;
189201
const MODE_STATE_INVOKE_METHOD = 0x0003;
202+
const MODE_STATE_INVOKE_CLOSURE = 0x0004;
190203

191204
/// _context is Map => _previousValue is MapChangeRecord
192-
const MODE_STATE_MAP = 0x0004;
205+
const MODE_STATE_MAP = 0x0005;
193206
/// _context is Array/List/Iterable => _previousValue = ListChangeRecord
194-
const MODE_STATE_LIST = 0x0005;
207+
const MODE_STATE_LIST = 0x0006;
195208

196209
/// _context is number/string
197-
const MODE_STATE_CONST = 0x0006;
210+
const MODE_STATE_CONST = 0x0007;
198211

199212
function isSame(a, b) {
200213
if (a instanceof String && b instanceof String) return a == b;

modules/change_detection/src/watch_group.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import {ProtoRecord, Record, PROTO_RECORD_CONST, PROTO_RECORD_FUNC, PROTO_RECORD_PROPERTY} from './record';
1+
import {ProtoRecord, Record, PROTO_RECORD_CONST, PROTO_RECORD_PURE_FUNCTION,
2+
PROTO_RECORD_PROPERTY, PROTO_RECORD_METHOD, PROTO_RECORD_CLOSURE} from './record';
23
import {FIELD, IMPLEMENTS, isBlank, isPresent, int, toBool, autoConvertAdd, BaseException} from 'facade/lang';
34
import {ListWrapper} from 'facade/collection';
4-
import {AST, AccessMember, ImplicitReceiver, AstVisitor, LiteralPrimitive, Binary, Formatter} from './parser/ast';
5+
import {AST, AccessMember, ImplicitReceiver, AstVisitor, LiteralPrimitive,
6+
Binary, Formatter, MethodCall, FunctionCall} from './parser/ast';
57

68
export class ProtoWatchGroup {
79
@FIELD('headRecord:ProtoRecord')
@@ -167,7 +169,7 @@ class ProtoRecordCreator {
167169
}
168170

169171
visitBinary(ast:Binary, dest) {
170-
var record = this.construct(PROTO_RECORD_FUNC, _operationToFunction(ast.operation), 2, dest);
172+
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, _operationToFunction(ast.operation), 2, dest);
171173

172174
ast.left.visit(this, new Destination(record, 0));
173175
ast.right.visit(this, new Destination(record, 1));
@@ -183,13 +185,31 @@ class ProtoRecordCreator {
183185

184186
visitFormatter(ast:Formatter, dest) {
185187
var formatter = this.protoWatchGroup.formatters[ast.name];
186-
var record = this.construct(PROTO_RECORD_FUNC, formatter, ast.allArgs.length, dest);
188+
var record = this.construct(PROTO_RECORD_PURE_FUNCTION, formatter, ast.allArgs.length, dest);
187189
for (var i = 0; i < ast.allArgs.length; ++i) {
188190
ast.allArgs[i].visit(this, new Destination(record, i));
189191
}
190192
this.add(record);
191193
}
192194

195+
visitMethodCall(ast:MethodCall, dest) {
196+
var record = this.construct(PROTO_RECORD_METHOD, ast.fn, ast.args.length, dest);
197+
ast.receiver.visit(this, new Destination(record, null));
198+
for (var i = 0; i < ast.args.length; ++i) {
199+
ast.args[i].visit(this, new Destination(record, i));
200+
}
201+
this.add(record);
202+
}
203+
204+
visitFunctionCall(ast:FunctionCall, dest) {
205+
var record = this.construct(PROTO_RECORD_CLOSURE, null, ast.args.length, dest);
206+
ast.target.visit(this, new Destination(record, null));
207+
for (var i = 0; i < ast.args.length; ++i) {
208+
ast.args[i].visit(this, new Destination(record, i));
209+
}
210+
this.add(record);
211+
}
212+
193213
createRecordsFromAST(ast:AST, memento){
194214
ast.visit(this, memento);
195215
}

modules/change_detection/test/change_detector_spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ export function main() {
6969
.toEqual(['address.city=Grenoble']);
7070
});
7171

72+
it("should support method calls", () => {
73+
var person = new Person('Victor');
74+
expect(executeWatch('m', 'sayHi("Jim")', person)).toEqual(['m=Hi, Jim']);
75+
});
76+
77+
it("should support function calls", () => {
78+
var td = new TestData(() => (a) => a);
79+
expect(executeWatch('value', 'a()(99)', td)).toEqual(['value=99']);
80+
});
81+
82+
it("should support chained method calls", () => {
83+
var person = new Person('Victor');
84+
var td = new TestData(person);
85+
expect(executeWatch('m', 'a.sayHi("Jim")', td)).toEqual(['m=Hi, Jim']);
86+
});
87+
7288
it("should support literals", () => {
7389
expect(executeWatch('const', '10')).toEqual(['const=10']);
7490
});
@@ -123,6 +139,10 @@ class Person {
123139
this.address = address;
124140
}
125141

142+
sayHi(m) {
143+
return `Hi, ${m}`;
144+
}
145+
126146
toString():string {
127147
var address = this.address == null ? '' : ' address=' + this.address.toString();
128148

@@ -140,6 +160,12 @@ class Address {
140160
}
141161
}
142162

163+
class TestData {
164+
constructor(a) {
165+
this.a = a;
166+
}
167+
}
168+
143169
class LoggingDispatcher extends WatchGroupDispatcher {
144170
constructor() {
145171
this.log = null;

0 commit comments

Comments
 (0)