forked from LibertyEiffel/Liberty
/
liberty_interpreter_feature_call.e
712 lines (646 loc) · 19.8 KB
/
liberty_interpreter_feature_call.e
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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
-- This file is part of Liberty Eiffel.
--
-- Liberty Eiffel is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, version 3 of the License.
--
-- Liberty Eiffel is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with Liberty Eiffel. If not, see <http://www.gnu.org/licenses/>.
--
class LIBERTY_INTERPRETER_FEATURE_CALL
inherit
LIBERTY_FEATURE_VISITOR
insert
LIBERTY_TAGS
creation {LIBERTY_INTERPRETER}
make, make_precursor
feature {ANY}
name: FIXED_STRING
target: LIBERTY_INTERPRETER_OBJECT
parameters: TRAVERSABLE[LIBERTY_INTERPRETER_OBJECT]
position: LIBERTY_POSITION
definition_type: LIBERTY_ACTUAL_TYPE is
do
Result := bound_feature.definition_type
end
returned_static_type: LIBERTY_ACTUAL_TYPE
feature {LIBERTY_INTERPRETER_EXTERNAL_PLUGINS}
bound_feature: LIBERTY_FEATURE
feature {LIBERTY_INTERPRETER}
call is
do
check not prepare end
if logging.is_trace then
log_call
end
prepare := True
bound_feature.accept(Current)
prepare := False
if options.is_invariant_checked then
check_invariant
end
if options.is_require_checked then
check_precondition
end
debug ("interpreter.call.steps")
debug_step(once"Calling the feature")
end
bound_feature.accept(Current)
if parameters = Void and then options.is_ensure_checked then
-- Can happen only when semi-evaluated built-in externals are used
-- In that case the postcondition must not depend on the arguments...
prepare_postcondition
end
if options.is_ensure_checked then
check_postcondition
end
if options.is_invariant_checked then
check_invariant
end
end
feature {LIBERTY_FEATURE_ACCELERATOR}
accelerate_call (a: LIBERTY_FEATURE_ACCELERATOR) is
do
bound_feature.accelerate_call(a)
end
feature {LIBERTY_INTERPRETER, LIBERTY_FEATURE_ACCELERATOR, LIBERTY_INTERPRETER_EXTERNAL_TYPE_ANY_BUILTINS}
evaluate_parameters is
local
i: INTEGER; p: FAST_ARRAY[LIBERTY_INTERPRETER_OBJECT]
val: LIBERTY_INTERPRETER_OBJECT
formal_type, actual_type: LIBERTY_ACTUAL_TYPE
do
if parameters = Void then
interpreter.set_evaluating_parameters(Current)
if bound_feature.parameters.count /= actuals.count then
interpreter.fatal_error("Bad parameters count: expected " + bound_feature.parameters.count.out
+ " but got " + actuals.count.out, position)
end
check
bound_feature.parameters.lower = actuals.lower
end
from
create p.with_capacity(actuals.count)
i := actuals.lower
until
i > actuals.upper
loop
actuals.item(i).accept(interpreter.expressions)
val := interpreter.expressions.eval_as_right_value
formal_type ::= bound_feature.parameters.item(i).result_type.known_type
actual_type ::= val.type.known_type
if actual_type.is_conform_to(formal_type) then
p.add_last(val)
elseif actual_type.converts_to(formal_type) then
p.add_last(interpreter.object_converter.convert_object(val, formal_type))
elseif formal_type = bound_feature.current_type and then formal_type.may_promote_current and then formal_type.converts_to(actual_type) then
-- Special Current promotion for kernel numeric types
check
not_a_precursor: feature_name /= Void
end
make(interpreter, interpreter.object_converter.convert_object(target, actual_type),
actual_type.feature_definition(feature_name), actuals, position)
check
bound_feature.parameters.count = actuals.count
end
i := actuals.lower - 1 -- try again
else
interpreter.fatal_error("Bad object type: " + actual_type.full_name + " does not conform or convert to " + formal_type.full_name, actuals.item(i).position)
p.add_last(val)
end
i := i + 1
end
parameters := p
interpreter.unset_evaluating_parameters(Current)
prepare_parameter_map(bound_feature)
debug ("interpreter.call.internals")
show_parameter_map(std_output)
std_output.put_new_line
end
if options.is_ensure_checked then
prepare_postcondition
end
end
end
feature {LIBERTY_INTERPRETER, LIBERTY_INTERPRETER_EXTERNAL_BUILTINS, LIBERTY_INTERPRETER_EXTERNAL_PLUGINS}
returned_object: LIBERTY_INTERPRETER_OBJECT
set_returned_object (a_returned_object: like returned_object) is
do
if returned_static_type = Void then
check a_returned_object = Void end
else
returned_object := a_returned_object
debug ("interpreter.call.internals")
std_output.put_string(once " >>> Feature ")
std_output.put_string(name)
std_output.put_string(once " @")
std_output.put_string(to_pointer.out)
std_output.put_string(once ": setting Result to ")
interpreter.object_printer.print_object(std_output, returned_object, 2)
end
end
ensure
returned_object = a_returned_object
end
local_static_type (local_name: FIXED_STRING): LIBERTY_ACTUAL_TYPE is
do
Result := local_types.reference_at(local_name)
end
set_local_value (local_name: FIXED_STRING; value: LIBERTY_INTERPRETER_OBJECT) is
do
local_map.put(value, local_name)
end
local_value (local_name: FIXED_STRING): LIBERTY_INTERPRETER_OBJECT is
do
if local_map = Void then
interpreter.fatal_error("Locals map not ready!", position)
end
Result := local_map.fast_reference_at(local_name)
end
parameter (parameter_name: FIXED_STRING): LIBERTY_INTERPRETER_OBJECT is
do
if parameter_map = Void then
interpreter.fatal_error("Parameters map not ready!", position)
end
Result := parameter_map.fast_reference_at(parameter_name)
end
writable_feature_static_type (a_feature_name: LIBERTY_FEATURE_NAME): LIBERTY_ACTUAL_TYPE is
do
Result ::= target.type.feature_definition(a_feature_name).result_type.known_type
end
set_writable_feature (a_name: LIBERTY_FEATURE_NAME; a_value: LIBERTY_INTERPRETER_OBJECT) is
local
struct: LIBERTY_INTERPRETER_OBJECT_STRUCTURE
do
struct ::= target
struct.put_attribute(a_name.name, a_value)
end
writable_feature (a_name: LIBERTY_FEATURE_NAME): LIBERTY_INTERPRETER_OBJECT is
local
struct: LIBERTY_INTERPRETER_OBJECT_STRUCTURE
do
struct ::= target
Result := struct.attribute_object(a_name.name)
end
raised_exception: LIBERTY_INTERPRETER_EXCEPTION
raise (a_exception: like raised_exception) is
do
raised_exception := a_exception
ensure
raised_exception = a_exception
end
feature {ANY}
show_stack (o: OUTPUT_STREAM) is
do
o.put_string(once "Feature {")
o.put_string(target.type.full_name)
o.put_string(once "}.")
o.put_string(name)
o.put_string(once " @")
o.put_line(to_pointer.out)
if not position.is_unknown then
o.put_character('%T')
position.show(o)
end
o.put_new_line
o.put_string(once "Current = ")
interpreter.object_printer.print_object(o, target, 0)
if returned_static_type /= Void then
o.put_new_line
o.put_string(once "Result = ")
interpreter.object_printer.print_object(o, returned_object, 0)
end
show_parameter_map(o)
show_local_map(o)
end
feature {}
show_parameter_map (o: OUTPUT_STREAM) is
do
show_map(parameter_map, once "Parameters", o)
end
show_local_map (o: OUTPUT_STREAM) is
do
show_map(local_map, once "Locals", o)
end
show_map (map: DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING]; tag: STRING; o: OUTPUT_STREAM) is
local
i: INTEGER; obj: LIBERTY_INTERPRETER_OBJECT
do
if map = Void then
o.put_new_line
o.put_string(tag)
o.put_line(once " map not yet computed")
elseif not map.is_empty then
o.put_new_line
o.put_string(tag)
o.put_line(once ":")
from
i := map.lower
until
i > map.upper
loop
o.put_string(once " ")
o.put_string(map.key(i))
o.put_string(once " = ")
obj := map.item(i)
interpreter.object_printer.print_object(o, obj, 1)
i := i + 1
end
end
end
feature {LIBERTY_FEATURE_ATTRIBUTE}
visit_liberty_feature_attribute (v: LIBERTY_FEATURE_ATTRIBUTE) is
local
t: LIBERTY_INTERPRETER_OBJECT_STRUCTURE
fn: LIBERTY_FEATURE_NAME
actual_type: LIBERTY_ACTUAL_TYPE
do
if prepare then
evaluate_parameters
else
if t ?:= target then
t ::= target
if t.has_attribute(name) then
set_returned_object(t.attribute_object(name))
else
create fn.make(name)
if t.type.has_feature(fn) then
-- at creation time
actual_type ::= t.type.feature_definition(fn).result_type.known_type
set_returned_object(interpreter.default_object(actual_type, t.position))
t.put_attribute(name, returned_object)
else
interpreter.fatal_error("No such attribute: " + name, position)
end
end
else
--|*** TODO: not good. Native objects may have attributes too (e.g. string)
interpreter.fatal_error("No such attribute: " + name, position)
end
end
end
feature {LIBERTY_FEATURE_CONSTANT}
visit_liberty_feature_constant (v: LIBERTY_FEATURE_CONSTANT) is
do
if prepare then
evaluate_parameters
else
v.expression.accept(interpreter.expressions)
set_returned_object(interpreter.expressions.eval_as_right_value)
end
end
feature {LIBERTY_FEATURE_DEFERRED}
visit_liberty_feature_deferred (v: LIBERTY_FEATURE_DEFERRED) is
do
evaluate_parameters
interpreter.fatal_error("Deferred feature called", position)
end
feature {LIBERTY_FEATURE_DO}
visit_liberty_feature_do (v: LIBERTY_FEATURE_DO) is
do
if prepare then
evaluate_parameters
prepare_local_map(v)
else
v.block_instruction.accept(interpreter.instructions)
end
end
feature {LIBERTY_FEATURE_EXTERNAL}
visit_liberty_feature_external (v: LIBERTY_FEATURE_EXTERNAL) is
do
if prepare then
inspect
v.external_def.out
when "built_in" then
-- nothing
when "plug_in" then
evaluate_parameters
else
not_yet_implemented
end
else
inspect
v.external_def.out
when "built_in" then
interpreter.builtins.call(Current)
when "plug_in" then
interpreter.plugins.call(Current, v.alias_def, position)
else
not_yet_implemented
end
end
end
feature {LIBERTY_FEATURE_ONCE}
visit_liberty_feature_once (v: LIBERTY_FEATURE_ONCE) is
local
tags: LIBERTY_TAGS
do
if prepare then
evaluate_parameters
prepare_local_map(v)
else
if tags.once_flag.is_set(v) then
set_returned_object(tags.once_flag.value(v))
else
v.block_instruction.accept(interpreter.instructions)
tags.once_flag.add(returned_object, v)
end
end
end
feature {LIBERTY_FEATURE_REDEFINED}
visit_liberty_feature_redefined (v: LIBERTY_FEATURE_REDEFINED) is
do
v.redefined_feature.accept(Current)
end
feature {LIBERTY_FEATURE_UNIQUE}
visit_liberty_feature_unique (v: LIBERTY_FEATURE_UNIQUE) is
do
if prepare then
evaluate_parameters
else
not_yet_implemented
end
end
feature {}
make (a_interpreter: like interpreter; a_target: like target; a_feature_definition: LIBERTY_FEATURE_DEFINITION; a_actuals: like actuals; a_position: like position) is
require
a_interpreter /= Void
a_target /= Void
a_feature_definition /= Void
a_actuals /= Void
a_position /= Void
do
feature_name := a_feature_definition.feature_name
name := feature_name.full_name
interpreter := a_interpreter
target := a_target
actuals := a_actuals
position := a_position
bound_feature := a_feature_definition.the_feature.bound(a_target.type)
if bound_feature.result_type /= Void then
returned_static_type ::= bound_feature.result_type.known_type
end
create {ARRAY_DICTIONARY[TUPLE[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING, LIBERTY_POSITION], LIBERTY_EXPRESSION]} old_values.with_capacity(0)
if returned_static_type /= Void then
returned_object := interpreter.default_object(returned_static_type, position)
end
ensure
interpreter = a_interpreter
target = a_target
actuals = a_actuals
end
make_precursor (a_interpreter: like interpreter; a_target: like target; a_precursor: LIBERTY_FEATURE; a_actuals: like actuals; a_position: like position) is
require
a_interpreter /= Void
a_target /= Void
a_actuals /= Void
a_position /= Void
do
name := name_precursor
interpreter := a_interpreter
target := a_target
actuals := a_actuals
position := a_position
bound_feature := a_precursor
if bound_feature.result_type /= Void then
returned_static_type ::= bound_feature.result_type.known_type
end
create {ARRAY_DICTIONARY[TUPLE[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING, LIBERTY_POSITION], LIBERTY_EXPRESSION]} old_values.with_capacity(0)
ensure
interpreter = a_interpreter
target = a_target
actuals = a_actuals
end
feature_name: LIBERTY_FEATURE_NAME
name_precursor: FIXED_STRING is
once
Result := "Precursor".intern
end
interpreter: LIBERTY_INTERPRETER
actuals: TRAVERSABLE[LIBERTY_EXPRESSION]
parameter_types: DICTIONARY[LIBERTY_ACTUAL_TYPE, FIXED_STRING]
parameter_map: DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING]
local_types: DICTIONARY[LIBERTY_ACTUAL_TYPE, FIXED_STRING]
local_map: DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING]
empty_types: DICTIONARY[LIBERTY_ACTUAL_TYPE, FIXED_STRING] is
once
create {AVL_DICTIONARY[LIBERTY_ACTUAL_TYPE, FIXED_STRING]} Result.make
end
empty_map: DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING] is
once
create {AVL_DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING]} Result.make
end
prepare_parameter_map (f: LIBERTY_FEATURE) is
require
parameters /= Void
local
i: INTEGER; p: LIBERTY_PARAMETER
actual_type: LIBERTY_ACTUAL_TYPE
do
if f.parameters.count /= actuals.count then
interpreter.fatal_error("Bad number of arguments: expected " + f.parameters.count.out
+ " but got " + actuals.count.out, position)
end
if actuals.is_empty then
parameter_types := empty_types
parameter_map := empty_map
else
check
f.parameters.lower = actuals.lower
parameters.lower = actuals.lower
end
create {HASHED_DICTIONARY[LIBERTY_ACTUAL_TYPE, FIXED_STRING]} parameter_types.with_capacity(actuals.count)
create {HASHED_DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING]} parameter_map.with_capacity(actuals.count)
from
i := actuals.lower
until
i > actuals.upper
loop
p := f.parameters.item(i)
actual_type ::= p.result_type.known_type
parameter_types.add(actual_type, p.name)
parameter_map.add(parameters.item(i), p.name)
i := i + 1
end
end
ensure
parameter_types /= Void
parameter_map /= Void
end
prepare_local_map (f: LIBERTY_FEATURE_ROUTINE) is
local
i: INTEGER; l: LIBERTY_LOCAL; def: LIBERTY_INTERPRETER_OBJECT
actual_type: LIBERTY_ACTUAL_TYPE
do
if f.locals.is_empty then
local_types := empty_types
local_map := empty_map
else
create {HASHED_DICTIONARY[LIBERTY_ACTUAL_TYPE, FIXED_STRING]} local_types.with_capacity(f.locals.count)
create {HASHED_DICTIONARY[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING]} local_map.with_capacity(f.locals.count)
from
i := f.locals.lower
until
i > f.locals.upper
loop
l := f.locals.item(i)
actual_type ::= l.result_type.known_type
local_types.add(actual_type, l.name)
def := interpreter.default_object(actual_type, l.position)
local_map.add(def, l.name)
i := i + 1
end
end
ensure
local_types /= Void
local_map /= Void
end
feature {LIBERTY_INTERPRETER}
has_old_value (a_expression: LIBERTY_EXPRESSION): BOOLEAN is
do
Result := old_values.fast_has(a_expression)
end
old_value (a_expression: LIBERTY_EXPRESSION): LIBERTY_INTERPRETER_OBJECT is
require
has_old_value(a_expression)
local
t: TUPLE[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING, LIBERTY_POSITION]
do
t := old_values.fast_at(a_expression)
if t.second /= Void then
interpreter.fatal_error(t.second, t.third)
end
Result := t.first
end
add_old_value (a_expression: LIBERTY_EXPRESSION; a_value: LIBERTY_INTERPRETER_OBJECT; a_fatal_error: FIXED_STRING; a_fatal_position: LIBERTY_POSITION) is
do
old_values.add([a_value, a_fatal_error, a_fatal_position], a_expression)
debug ("interpreter.call.internals")
std_output.put_string(once " >>> Feature ")
std_output.put_string(name)
std_output.put_string(once " @")
std_output.put_string(to_pointer.out)
std_output.put_string(once ": adding old value: ")
interpreter.object_printer.print_object(std_output, a_value, 2)
end
ensure
old_values.fast_at(a_expression).first = a_value
old_values.fast_at(a_expression).second = a_fatal_error
old_values.fast_at(a_expression).third = a_fatal_position
a_fatal_error = Void implies old_value(a_expression) = a_value
end
feature {}
check_invariant is
require
options.is_invariant_checked
do
debug ("interpreter.call.steps")
debug_step(once "Checking invariant")
end
interpreter.assertions.validate(target.type.the_invariant, once "Invariant")
debug ("interpreter.call.steps")
debug_step(once "Done checking invariant")
end
end
check_precondition is
require
options.is_require_checked
do
debug ("interpreter.call.steps")
debug_step(once "Checking precondition")
end
interpreter.assertions.validate(bound_feature.precondition, once "Precondition")
debug ("interpreter.call.steps")
debug_step(once "Done checking precondition")
end
end
prepare_postcondition is
require
options.is_ensure_checked
do
debug ("interpreter.call.steps")
debug_step(once "Preparing postcondition (gathering old values)")
end
interpreter.postcondition_browser.gather_old(bound_feature.postcondition)
debug ("interpreter.call.steps")
debug_step(once "Done preparing postcondition (gathering old values)")
end
end
check_postcondition is
require
options.is_ensure_checked
do
debug ("interpreter.call.steps")
debug_step(once "Checking postcondition")
end
interpreter.assertions.validate(bound_feature.postcondition, once "Postcondition")
debug ("interpreter.call.steps")
debug_step(once "Done checking postcondition")
end
end
options: LIBERTY_INTERPRETER_OPTIONS
old_values: DICTIONARY[TUPLE[LIBERTY_INTERPRETER_OBJECT, FIXED_STRING, LIBERTY_POSITION], LIBERTY_EXPRESSION]
prepare: BOOLEAN
debug_step (step: STRING) is
do
std_output.put_new_line
std_output.put_string(once " >>> ")
std_output.put_string(name)
std_output.put_string(once " @")
std_output.put_string(to_pointer.out)
std_output.put_string(once ": ")
std_output.put_line(step)
end
logging: LOGGING
log_call is
require
logging.is_trace
local
i: INTEGER; log: OUTPUT_STREAM
formals: TRAVERSABLE[LIBERTY_PARAMETER]
do
log := logging.trace
log.put_string(once "Calling feature {")
log.put_string(bound_feature.current_type.full_name)
log.put_string(once "}.")
log.put_string(name)
formals := bound_feature.parameters
if not formals.is_empty then
log.put_character(' ')
log.put_character('(')
from
i := formals.lower
until
i > formals.upper
loop
if i > formals.lower then
log.put_string(once ", ")
end
log.put_string(formals.item(i).result_type.full_name)
i := i + 1
end
log.put_character(')')
end
if returned_static_type /= Void then
log.put_string(once ": ")
log.put_string(returned_static_type.full_name)
end
if position.is_unknown then
log.put_new_line
else
log.put_character(' ')
position.show(log)
end
end
invariant
interpreter /= Void
actuals /= Void
target /= Void
name /= Void
not interpreter.gathering_old_values implies ((returned_static_type = Void) = (returned_object = Void))
end -- class LIBERTY_INTERPRETER_FEATURE_CALL