1
+ //DCI
2
+
3
+ /*
4
+
5
+ SourceAccount {
6
+ foo() {
7
+ this['bar']();
8
+ }
9
+ bar() {}
10
+ }
11
+
12
+ var handler = {
13
+ get: function(target, propName) {
14
+ //TEMP
15
+ return 'test';
16
+
17
+ //This is all ONLY to check for the case where this['someMethod'] is called
18
+ //from within a role method.
19
+ //Role method calls that begin with the role name, e.g. DestinationAccount['deposit'](),
20
+ //are taken care of at compile time.
21
+
22
+ //the function that attempted to access a property on this object
23
+ var caller = arguments.callee.caller;
24
+
25
+ //if caller is a role method, then we want to check if propName is also a role method
26
+ if (caller.__isRoleMethod) {
27
+ var potentialRoleMethodName = propName;
28
+ var context = caller.__context,
29
+ roleName = caller.__roleName,
30
+ rolePlayer = target;
31
+
32
+ var role = context['__$' + roleName];
33
+ if (role[potentialRoleMethodName]) {
34
+ //bind() wraps the function with an outer function which ensures
35
+ //that the `this` keyword works within the called role method
36
+ return role[potentialRoleMethodName].bind(rolePlayer);
37
+ }
38
+ }
39
+ }
40
+ };
41
+
42
+ Object.prototype = new Proxy({}, handler);
43
+ Object.prototype.foo;
44
+
45
+ //var o = {};
46
+ //o.foo;
47
+
48
+ */
49
+
50
+
51
+ declare var global : Object ;
52
+
53
+ var isNodeJs = ( typeof window == 'undefined' && typeof global != 'undefined' ) ;
54
+ var globalNamespace = isNodeJs ? global : window ;
55
+
56
+ //Ensure that Function.prototype.bind() is available (used by getRoleMember() function below)
57
+ if ( ! isNodeJs ) {
58
+ //shim for Function.prototype.bind() for older browsers
59
+ if ( ! Function . prototype . bind ) {
60
+ Function . prototype . bind = function ( oThis ) {
61
+ if ( typeof this !== "function" ) {
62
+ // closest thing possible to the ECMAScript 5 internal IsCallable function
63
+ throw new TypeError ( "Function.prototype.bind - what is trying to be bound is not callable" ) ;
64
+ }
65
+
66
+ var aArgs = Array . prototype . slice . call ( arguments , 1 ) ,
67
+ fToBind = this ,
68
+ fNOP = function ( ) { } ,
69
+ fBound = function ( ) {
70
+ return fToBind . apply ( this instanceof fNOP && oThis
71
+ ? this
72
+ : oThis ,
73
+ aArgs . concat ( Array . prototype . slice . call ( arguments ) ) ) ;
74
+ } ;
75
+
76
+ fNOP . prototype = this . prototype ;
77
+ fBound . prototype = new fNOP ( ) ;
78
+
79
+ return fBound ;
80
+ } ;
81
+ }
82
+ }
83
+
84
+ //TODO Support DestinationAccount['de' + 'posit'](amount)
85
+ //
86
+ //DestinationAccount['de' + 'posit'](amount) could be rewritten to:
87
+ //__context.__$DestinationAccount['de' + 'posit'].call(__context.DestinationAccount, amount)
88
+ //
89
+ //would be better to give a nice error message if role method not found:
90
+ //DCI.getRoleMember(__context, __context.DestinationAccount, 'DestinationAccount', 'de' + 'posit').call(__context.DestinationAccount, amount)
91
+
92
+ //Gets a member on a role player - can be either a role method or a method or property of the role player object
93
+ export function getRoleMember ( context : Object , player : Object , roleName : string , memberName : string ) {
94
+ var roleMethod = context [ '__$' + roleName ] [ memberName ] ;
95
+ if ( roleMethod ) {
96
+ //bind the role player as `this` on the specified role method
97
+ return roleMethod . bind ( player ) ;
98
+ }
99
+ else {
100
+ //check for property on role player
101
+ if ( ! player [ memberName ] ) {
102
+ throw new Error ( 'Method or property "' + memberName + '" not found on role "' + roleName + '" nor on its current role player.' ) ;
103
+ }
104
+ return player [ memberName ] ;
105
+ }
106
+ }
107
+
108
+
109
+
110
+ //This function is for handling calls beginning with `this`; it calls a method on the current role player,
111
+ //which could be either a role method or a method on the data object.
112
+ //
113
+ // TODO test data object methods that aren't overridden by the role
114
+ //
115
+ export function callMethodOnSelf ( context : Object , player : Object , roleName : string , methodName : string , args : Array ) : any {
116
+ //if `this` is not equal to the current role player
117
+ if ( player != context [ roleName ] ) {
118
+ //If we're here, then it's a data object method, not a role method
119
+ //(Either that or the programmer used `this` inside a closure when they should have used `self`.
120
+ //In other words this code would also be reached if `this` is equal to `undefined`, `global`, or `window`.)
121
+ if ( ! player [ methodName ] ) {
122
+ var msg = 'Method "' + methodName + '" not found' ;
123
+ if ( player [ 'constructor' ] . name ) {
124
+ msg += ' on object of type "' + player [ 'constructor' ] . name + '".' ;
125
+ }
126
+ throw new Error ( msg ) ;
127
+ }
128
+ return player [ methodName ] . apply ( player , args ) ;
129
+
130
+ //Old version: commenting since it's inconsistent...this makes `this.someRoleMethod()` work
131
+ //even inside a closure like [1,2,3].forEach(...),
132
+ //but using `this` in other ways, like `this` as an argument, would not necessarily work.
133
+ //Better to just use `self`.
134
+ /*
135
+
136
+ //support functions within role methods for which `this` is unbound - but make sure we
137
+ //don't wrongly assume `this` refers to the current role
138
+ if (player != undefined && player != globalNamespace) {
139
+ //not a role method; call normally
140
+ if (!player[methodName]) {
141
+ var msg = 'Method "' + methodName + '" not found';
142
+ if (player['constructor'].name) {
143
+ msg += ' on object of type "' + player['constructor'].name + '".';
144
+ }
145
+ throw new Error(msg);
146
+ }
147
+ return player[methodName].apply(player, args);
148
+ }
149
+ //if we're here, then `this` probably *should* refer to the current role and the only
150
+ //reason it's undefined is most likely a nested function
151
+ //(e.g. a callback function passed to forEach() to loop over an array)
152
+ else {
153
+ player = context[roleName];
154
+ }
155
+ */
156
+ }
157
+
158
+ var roleMethod = context [ '__$' + roleName ] [ methodName ] ;
159
+ //if the method exists on the role, we always call it (role methods override data object methods)
160
+ if ( typeof roleMethod == 'function' ) {
161
+ return ( args ? roleMethod . apply ( player , args ) : roleMethod . call ( player ) ) ;
162
+ }
163
+ //otherwise, call the data object method
164
+ //(if the method is not found on the data object, this will throw Javascript's usual
165
+ //'[function name] is not defined' error)
166
+ else {
167
+ if ( ! player [ methodName ] ) {
168
+ var msg = 'Method "' + methodName + '" not found' ;
169
+ if ( player [ 'constructor' ] . name )
170
+ msg += ' on object of type "' + player [ 'constructor' ] . name + '" nor on any of the roles it is currently playing.' ;
171
+ throw new Error ( msg ) ;
172
+ }
173
+ return player [ methodName ] . apply ( player , args ) ;
174
+ }
175
+ }
0 commit comments