-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathmirror.grace
416 lines (387 loc) · 16.9 KB
/
mirror.grace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
dialect "none"
import "intrinsic" as intrinsic
import "basicTypesBundle" as basicTypesBundle
import "equalityBundle" as equalityBundle
use intrinsic.annotations
use basicTypesBundle.open
use equalityBundle.open
type Mirror = EqualityObject & interface {
methods → Sequence⟦MethodMirror⟧ // mirrors on all of subject's methods
methodNames → Sequence⟦String⟧ // the names of the subject's non-confidential methods
confidentialMethodNames → Sequence⟦String⟧ // the names of the subject's confidential methods
allMethodNames → Sequence⟦String⟧ // the names of all of the subject's methods
onMethod(nm:String) → MethodMirror // a mirror on the method with name nm
request(nm:String)withArgs(args:Sequence⟦Object⟧) → Unknown
ilk → String // a uid for the subject's object constructor
definitionModule → String // the name of the module containing the subject's constructor
definitionLine → String // the line number of the start of the subject's constructor
subject → Object // the object on which I reflect
whenNoMethodDo(action:Function3) → Done
// if subject is sent a request named nm, with argument list args, and
// there is no method nm, action will be applied to (nm, args, subject)
}
type MethodMirror = EqualityObject & interface {
name → String // canonical name of the method
numberOfParams → Number // the number of parameters of the method
numberOfTypeParams → Number // the number of type parameters
partCount → Number // the number of parts to name
paramCounts → Sequence⟦Number⟧ // the number of parameters to each part
paramNames → Sequence⟦String⟧ // the names of the parameters
paramTypes → Sequence⟦Type⟧ // the types of the parameters
returnType → Type
typeParamNames → Sequence⟦String⟧// the names of the type parameters
requestWithArgs(args:Sequence⟦Object⟧) → Unknown
// executes the method with args; ordonary arguments first, followed by type args
subject → Object // the object on whose method I reflect
isConfidential → Boolean // true if this method is confidential
isPublic → Boolean // true if this method is public
}
def MMhash = "MM".hash
def OMhash = "OM".hash
def thisModule = self
class reflect(subj) {
// answers a Mirror on subj
use equality
def subject is public = subj
method asString { "a mirror on {subject}" }
method methods → Sequence⟦MethodMirror⟧ {
native "js" code ‹
var meths = [];
var current = this.data.subject;
for (var k in current.methods) {
if (! k.includes("$") && current.methods.hasOwnProperty(k)) {
meths.push(request(var_thisModule, "methodMirror(2)",
2, current, new GraceString(k)));
}
}
return new GraceSequence(meths);
›
}
method methodNames {
// Answers a sorted sequence of strings, being the names of the subject's
// public methods. Don't use a set, to limit dependencies
native "js" code ‹
var methNames = [];
var current = this.data.subject;
for (var k in current.methods) {
if (! k.includes("$") && current.methods.hasOwnProperty(k) &&
! current.methods[k].confidential) {
methNames.push(canonicalMethodName(k));
}
}
return new GraceSequence(methNames.sort().map(
nm => new GraceString(nm)));
›
}
method confidentialMethodNames {
// answers a sorted sequecne of strings (not a set) being the names of
// the subject's confidential methods
native "js" code ‹
var methNames = [];
var current = this.data.subject;
for (var k in current.methods) {
if (! k.includes("$") && current.methods.hasOwnProperty(k) &&
current.methods[k].confidential) {
methNames.push(canonicalMethodName(k));
}
}
return new GraceSequence(methNames.sort().map(
nm => new GraceString(nm)));
›
}
method allMethodNames {
// answers a sorted sequecne of strings, being the names of all of
// the subject's methods
native "js" code ‹
var methNames = [];
var current = this.data.subject;
for (var k in current.methods) {
if (! k.includes("$") && current.methods.hasOwnProperty(k)) {
methNames.push(canonicalMethodName(k));
}
}
return new GraceSequence(methNames.sort().map(
nm => new GraceString(nm)));
›
}
method onMethod(nm) {
// answers a MethodMirror on the subject's method named nm
methodMirror(subject, nm)
}
method request(nm:String) withArgs(args:Sequence⟦Object⟧) → Unknown {
def methodMirror = methodMirror(subject, nm)
methodMirror.requestWithArgs(args)
}
method whenNoMethodDo(handlerBlock) {
// sets up handlerBlock (a function with 3 parameters) to be applied when
// a requested method is not found. The parameters of handlerBlock are
// name:String — the canonical name of the requested method
// args:Sequence⟦Object⟧ — the arguments of the original request
// receiver:Object — the object on which the request was originally made
// (which will be the subject of this mirror)
native "js" code ‹this.data.subject.noSuchMethodHandler = var_handlerBlock;›
}
method ilk {
native "js" code ‹return new GraceString(this.data.subject.classUid);›
}
method definitionModule {
native "js" code ‹return new GraceString(this.data.subject.definitionModule);›
}
method definitionLine {
native "js" code ‹return new GraceNum(this.data.subject.definitionLine || 0);›
}
method hash {
def subjectHash = native "js" code ‹
result = selfRequest(this.data.subject, "myIdentityHash");
› // in native code so that we can make it a _self_ request
intrinsic.hashCombine(OMhash, subjectHash);
}
method == (other) {
native "js" code ‹
if (this.classUid !== var_other.classUid) {
return GraceFalse; // return false if other is not a Mirror
}
if (this.data.subject !== var_other.data.subject) {
return GraceFalse; // return false if we don't have the same subject
}
return GraceTrue;
›
}
}
class methodMirror(theSubject, aMethodName) {
// Answers a MethodMirror on theSubject's method with name aMethodName
// For flexibility, we allow aMethodName to be canonical or numeric
use equality
def subject is public = theSubject
native "js" code ‹
const rawName = var_aMethodName._value;
if (rawName.indexOf("(") === -1) {
this.numericName = rawName;
this.canonicalName = rawName;
} else if (rawName.match(/\(\d+\)/)) {
this.numericName = rawName;
this.canonicalName = canonicalMethodName(rawName);
} else {
this.numericName = numericMethodName(rawName);
this.canonicalName = rawName;
};
if (! this.data.subject.methods[this.numericName]) {
var exceptionMsg = new GraceString("no method " +
this.canonicalMethodName + " in mirror for ");
const objDescription = request(this.data.subject, "asString", [0]);
exceptionMsg = request(exceptionMsg, "++(1)", [1], objDescription);
throw new GraceExceptionPacket(NoSuchMethodErrorObject, exceptionMsg);
};
this.theFunction = this.data.subject.methods[this.numericName];
›
method name {
native "js" code ‹return new GraceString(this.canonicalName)›
}
method asString { "mirror on method '{name}' of {subject}" }
method partCount {
native "js" code ‹
const count = this.numericName.split("(").length;
if (count === 1) return new GraceNum(1);
return new GraceNum(count - 1);
›
}
method paramCounts {
native "js" code ‹
const len = this.theFunction.paramCounts ? this.theFunction.paramCounts.length : 0;
const countArray = new Array(len);
for (var i = 0; i < len; i++) {
countArray[i] = new GraceNum(this.theFunction.paramCounts[i]);
}
return new GraceSequence(countArray);
›
}
method isConfidential {
native "js" code ‹
return (this.theFunction.confidential ? GraceTrue : GraceFalse);
›
}
method isPublic {
native "js" code ‹
return (this.theFunction.confidential ? GraceFalse : GraceTrue);
›
}
method numberOfParams {
native "js" code ‹
return new GraceNum(this.theFunction.paramNames.length);
›
}
method numberOfTypeParams {
native "js" code ‹
return new GraceNum(this.theFunction.typeParamNames.length);
›
}
method paramNames {
native "js" code ‹
const graceNames = this.theFunction.paramNames.map(s => new GraceString(s));
return new GraceSequence(graceNames);
›
}
method paramTypes {
native "js" code ‹
var types;
const paramTypes = this.theFunction.paramTypes;
if (typeof paramTypes == "function") {
const nrTypeParams = this.theFunction.typeParamNames.length;
const paramTypesBlock = new GraceBlock(
{definitionModule: "mirror"}, 247, nrTypeParams);
paramTypesBlock.guard = function(...args) {
for (let i=0; i<args.length; i++) {
if (! isGraceType(args[i])) return false;
}
return true;
};
paramTypesBlock.real = (...args) =>
new GraceSequence(paramTypes.apply(this.theFunction, args));
const applyMeth = (argcv, ...args) => {
if (paramTypesBlock.guard.apply(paramTypesBlock, args))
return paramTypesBlock.real.apply(this.theFunction, args);
for (let i=0; i<args.length; i++) {
if (! isGraceType(args[i])) {
raiseException(RequestErrorObject,
"argument " + (i+1) + " to block (from methodMirror.paramTypes) is not a type.");
}
}
};
applyMeth.methodName = "apply(" + nrTypeParams + ")";
applyMeth.paramCounts = [nrTypeParams];
applyMeth.paramNames = this.theFunction.typeParamNames;
applyMeth.definitionLine = this.theFunction.definitionLine;
applyMeth.definitionModule = this.theFunction.definitionModule;
paramTypesBlock.methods[applyMeth.methodName] = applyMeth;
return paramTypesBlock;
}
if (paramTypes) {
types = this.theFunction.paramTypes;
} else {
types = Array(this.theFunction.paramNames.length).fill(type_Unknown);
}
return new GraceSequence(types);
›
}
method returnType {
native "js" code ‹
const returnType = this.theFunction.returnType;
if (typeof returnType == "function") {
const nrTypeParams = this.theFunction.typeParamNames.length;
const returnTypeBlock = new GraceBlock(
{definitionModule: "mirror"}, 285, nrTypeParams);
returnTypeBlock.guard = function(...args) {
for (let i=0; i<args.length; i++) {
if (! isGraceType(args[i])) return false;
}
return true;
};
returnTypeBlock.real = (...args) =>
returnType.apply(this.theFunction, args);
const applyMeth = (argcv, ...args) => {
if (returnTypeBlock.guard.apply(returnTypeBlock, args))
return returnTypeBlock.real.apply(this.theFunction, args);
for (let i=0; i<args.length; i++) {
if (! isGraceType(args[i])) {
raiseException(RequestErrorObject,
"argument " + (i+1) + " to block (from methodMirror.returnType) is not a type.");
}
}
};
applyMeth.methodName = "apply(" + nrTypeParams + ")";
applyMeth.paramCounts = [nrTypeParams];
applyMeth.paramNames = this.theFunction.typeParamNames;
applyMeth.definitionLine = this.theFunction.definitionLine;
applyMeth.definitionModule = this.theFunction.definitionModule;
returnTypeBlock.methods[applyMeth.methodName] = applyMeth;
return returnTypeBlock;
}
if (returnType) {
return returnType;
} else {
return type_Unknown;
}
›
}
method typeParamNames {
native "js" code ‹
const jsNames = this.theFunction.typeParamNames
if (jsNames) {
return new GraceSequence(jsNames.map(s => new GraceString(s)));
}
return new GraceSequence([]);
›
}
method requestWithArgs(argList) {
native "js" code ‹
var paramcv = this.theFunction.paramCounts || [0];
var np = (this.theFunction.paramNames) ?
this.theFunction.paramNames.length : 0;
var ntp = (this.theFunction.typeParamNames) ?
this.theFunction.typeParamNames.length : 0;
var providedLen = request(var_argList, "size", [0])._value;
if ((providedLen !== np) && (providedLen != (np + ntp))) {
const tParamPart = (ntp > 0) ? (" and " + ntp + " type arguments") : "";
throw new GraceExceptionPacket(RequestErrorObject,
new GraceString("method '" + this.canonicalName + "' requires " +
np + " arguments" + tParamPart +
", but was given " + providedLen + "."));
}
const requestArgs = [this.data.subject, this.numericName, paramcv];
const argsIter = request(var_argList, "iterator", [0]);
while (Grace_isTrue(request(argsIter, "hasNext", [0]))) {
const arg = request(argsIter, "next", [0]);
requestArgs.push(arg);
}
return selfRequest.apply(null, requestArgs);
›
}
method hash {
def subjectHash = native "js" code ‹
result = selfRequest(this.data.subject, "myIdentityHash");
›
intrinsic.hashCombine(MMhash,
intrinsic.hashCombine(name.hash, subjectHash));
}
method == (other) {
native "js" code ‹
if (this.classUid !== var_other.classUid) {
return GraceFalse; // return false if other is not a methodMirror
}
if (this.data.subject !== var_other.data.subject) {
return GraceFalse; // return false if we don't have the same subject
}
if (this.numericName !== var_other.numericName) {
return GraceFalse; // return false if not the same method
}
return GraceTrue;
›
}
}
method loadModule(moduleName) {
// loads moduleName dynamically
native "js" code ‹return loadDynamicModule(safeJsString(var_moduleName));›
}
method initialModuleName {
// the name of the "main" module being run from the command line or ide
native "js" code ‹return new GraceString(minigrace.initialModuleName);›
}
method numberOfParameters(numericName) → Number {
def parts = numericName.split "("
var result := 0
(2..parts.size).do { i →
def part_split = parts.at(i).split ")"
def n = part_split.first.asNumber
if (n.isNaN) then {
intrinsic.constants.ProgrammingError.raise
"ill-formed numeric method name {numericName}"
} else {
result := result + n
}
}
result
}
method isTheSame(left, right) → Boolean {
def leftMirror = reflect(left)
leftMirror.request "isMe(_)" withArgs [right]
}