Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit d3c5eb7

Browse files
committed
[[ Bug 19967 ]] Add send script 'script' to 'object'
The current send command attempts to evaluate parameters in the current context before sending the message. In some cases this is not desirable, and furthermore there are issues with the evaluation of multiple parameters using the send command. Since we can't break this backwards compatibility with the send command, this patch adds a `send script` form of the command which simply forwards the script to be sent unmodified to the target object to be executed, using MCObject::domess.
1 parent 99d876d commit d3c5eb7

File tree

8 files changed

+204
-22
lines changed

8 files changed

+204
-22
lines changed

docs/dictionary/command/send.lcdoc

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Name: send
22

33
Type: command
44

5-
Syntax: send <message> [ to <object> [in <time> [seconds | ticks | milliseconds]] ]
5+
Syntax: send [script] <message> [ to <object> [in <time> [seconds | ticks | milliseconds]] ]
66

77
Summary:
88
Sends a <message> to an <object(glossary)>.
@@ -34,6 +34,9 @@ on timerIncrement
3434
send "timerIncrement" to me in 1 seconds
3535
end timerIncrement
3636

37+
Example:
38+
send script "answer the short name of me" to stack "Message Box"
39+
3740
Parameters:
3841
message:
3942
An expression that evaluates to a message name, possibly including
@@ -64,12 +67,34 @@ one <word> (for example, if it includes <parameter|parameters>, or if it
6467
is a multi-word command), it must be enclosed in <double quote|double
6568
quotes>.
6669

67-
Any parameters are evaluated before they are passed to the <send>
68-
<command>. For example, the following statement sends the <mouseUp>
69-
<message> with 2 as the first parameter:
70-
71-
send "mouseUp 1+1" to button "Example"
72-
70+
If the `script` form is used, the <message> is simply sent unmodified to the
71+
<object>. Otherwise, any parameters are evaluated before they are passed to the
72+
<send> <command>. For example, suppose there is a stack named "Stack" with
73+
script
74+
75+
on doAnswer pParam
76+
answer pParam
77+
end doAnswer
78+
79+
function myName
80+
return the short name of me
81+
end myName
82+
83+
and a button on the stack named "Button" with script
84+
85+
on mouseUp
86+
send "doAnswer myName()" to this stack
87+
send script "doAnswer myName()" to this stack
88+
end mouseUp
89+
90+
function myName
91+
return the short name of me
92+
end myName
93+
94+
clicking the button would result in an answer dialog first saying "Button" as the
95+
`myName` function would be evaluated in the button context, then "Stack" as
96+
using the `script` form would result in the `myName` function being evaluated in the
97+
stack context.
7398

7499
When the send command is used the stack containing the target handler
75100
temporarily becomes the defaultStack. All object references in the
@@ -78,6 +103,8 @@ defaultStack. Therefore references within the message that refer to
78103
"this card" or "this stack" will be referring to the card or stack where
79104
the target handler is located.
80105

106+
It is a parse error to specify a time when using the `script` form.
107+
81108
>*Important:* Specifying a <time> can affect the order in which
82109
> <statement|statements> are <execute|executed>. If you don't specify a
83110
> <time>, the <message> is sent immediately, and any <handler> it

docs/notes/feature-send_script.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Send script form of send command
2+
The syntax
3+
4+
send script <script> to <obj>
5+
6+
has been added to allow a chunk of script to be executed in the context
7+
of an object without any attempted evaluation of parameters that occurs
8+
with the original form of the send command.
9+
10+
For example, suppose there is a stack named "Stack" with script
11+
12+
on doAnswer pParam
13+
answer pParam
14+
end doAnswer
15+
16+
function myName
17+
return the short name of me
18+
end myName
19+
20+
and a button on the stack named "Button" with script
21+
22+
on mouseUp
23+
send "doAnswer myName()" to this stack
24+
send script "doAnswer myName()" to this stack
25+
end mouseUp
26+
27+
function myName
28+
return the short name of me
29+
end myName
30+
31+
clicking the button would result in an answer dialog first saying "Button" as the
32+
`myName` function would be evaluated in the button context, then "Stack" as
33+
using the `script` form would result in the `myName` function being evaluated in the
34+
stack context.

engine/src/cmds.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,7 @@ class MCMessage : public MCStatement
10991099
Boolean program;
11001100
Boolean reply;
11011101
Boolean send;
1102+
Boolean script;
11021103
public:
11031104
MCMessage(Boolean p_send) :
11041105
message(nullptr),
@@ -1107,7 +1108,8 @@ class MCMessage : public MCStatement
11071108
in(nullptr),
11081109
units(F_TICKS),
11091110
program(False),
1110-
reply(True)
1111+
reply(True),
1112+
script(False)
11111113
{
11121114
send = p_send;
11131115
}

engine/src/cmdse.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -646,11 +646,27 @@ Parse_stat MCMessage::parse(MCScriptPoint &sp)
646646
{
647647
initpoint(sp);
648648

649-
if (sp.parseexp(False, True, &(&message)) != PS_NORMAL)
650-
{
651-
MCperror->add(PE_SEND_BADEXP, sp);
652-
return PS_ERROR;
653-
}
649+
MCScriptPoint oldsp(sp);
650+
if (send && sp.skip_token(SP_FACTOR, TT_PROPERTY, P_SCRIPT) == PS_NORMAL)
651+
{
652+
MCerrorlock++;
653+
if (sp.parseexp(False, True, &(&message)) != PS_NORMAL)
654+
{
655+
sp = oldsp;
656+
}
657+
else
658+
{
659+
script = True;
660+
}
661+
MCerrorlock--;
662+
}
663+
664+
if (!script && sp.parseexp(False, True, &(&message)) != PS_NORMAL)
665+
{
666+
MCperror->add(PE_SEND_BADEXP, sp);
667+
return PS_ERROR;
668+
}
669+
654670
if (sp.skip_token(SP_FACTOR, TT_TO) != PS_NORMAL
655671
&& sp.skip_token(SP_FACTOR, TT_OF) != PS_NORMAL)
656672
return PS_NORMAL;
@@ -684,7 +700,14 @@ Parse_stat MCMessage::parse(MCScriptPoint &sp)
684700
MCperror->add(PE_SEND_BADTARGET, sp);
685701
return PS_ERROR;
686702
}
687-
return gettime(sp, &(&in), units);
703+
704+
if (script && sp.skip_eol() != PS_NORMAL)
705+
{
706+
MCperror->add(PE_SEND_SCRIPTINTIME, sp);
707+
return PS_ERROR;
708+
}
709+
710+
return gettime(sp, &(&in), units);
688711
}
689712
return PS_NORMAL;
690713
}
@@ -740,11 +763,17 @@ void MCMessage::exec_ctxt(MCExecContext &ctxt)
740763
else
741764
{
742765
ctxt . SetLineAndPos(line, pos);
743-
744-
if (!send)
745-
MCEngineExecCall(ctxt, *t_message, t_target_ptr);
766+
if (!script)
767+
{
768+
if (!send)
769+
MCEngineExecCall(ctxt, *t_message, t_target_ptr);
770+
else
771+
MCEngineExecSend(ctxt, *t_message, t_target_ptr);
772+
}
746773
else
747-
MCEngineExecSend(ctxt, *t_message, t_target_ptr);
774+
{
775+
MCEngineExecSendScript(ctxt, *t_message, t_target_ptr);
776+
}
748777
}
749778
}
750779
}
@@ -758,7 +787,7 @@ void MCMessage::compile(MCSyntaxFactoryRef ctxt)
758787
message -> compile(ctxt);
759788
in -> compile(ctxt);
760789

761-
if (eventtype != nil)
790+
if (*eventtype != nil)
762791
eventtype -> compile(ctxt);
763792
else
764793
MCSyntaxFactoryEvalConstantNil(ctxt);
@@ -771,12 +800,12 @@ void MCMessage::compile(MCSyntaxFactoryRef ctxt)
771800
{
772801
message -> compile(ctxt);
773802

774-
if (target != nil)
803+
if (*target != nil)
775804
target -> compile_object_ptr(ctxt);
776805
else
777806
MCSyntaxFactoryEvalConstantNil(ctxt);
778807

779-
if (in != nil)
808+
if (*in != nil)
780809
{
781810
in -> compile(ctxt);
782811
MCSyntaxFactoryEvalConstantInt(ctxt, units);

engine/src/exec-engine.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ MC_EXEC_DEFINE_EXEC_METHOD(Engine, StopUsingStack, 1)
105105
MC_EXEC_DEFINE_EXEC_METHOD(Engine, StopUsingStackByName, 1)
106106
MC_EXEC_DEFINE_EXEC_METHOD(Engine, Dispatch, 4)
107107
MC_EXEC_DEFINE_EXEC_METHOD(Engine, Send, 2)
108+
MC_EXEC_DEFINE_EXEC_METHOD(Engine, SendScript, 2)
108109
MC_EXEC_DEFINE_EXEC_METHOD(Engine, SendInTime, 4)
109110
MC_EXEC_DEFINE_EXEC_METHOD(Engine, Call, 2)
110111
MC_EXEC_DEFINE_EXEC_METHOD(Engine, LockErrors, 0)
@@ -1468,6 +1469,32 @@ void MCEngineExecCall(MCExecContext& ctxt, MCStringRef p_script, MCObjectPtr *p_
14681469
MCEngineSendOrCall(ctxt, p_script, p_target, false);
14691470
}
14701471

1472+
void MCEngineExecSendScript(MCExecContext& ctxt, MCStringRef p_script, MCObjectPtr *p_target)
1473+
{
1474+
MCObject *optr;
1475+
if (p_target == nil)
1476+
optr = ctxt . GetObject();
1477+
else
1478+
optr = p_target -> object;
1479+
1480+
Boolean oldlock = MClockmessages;
1481+
MClockmessages = False;
1482+
1483+
Boolean added = False;
1484+
if (MCnexecutioncontexts < MAX_CONTEXTS)
1485+
{
1486+
MCexecutioncontexts[MCnexecutioncontexts++] = &ctxt;
1487+
added = True;
1488+
}
1489+
1490+
if (optr->domess(p_script, false) == ES_ERROR)
1491+
ctxt . Throw();
1492+
1493+
if (added)
1494+
MCnexecutioncontexts--;
1495+
MClockmessages = oldlock;
1496+
}
1497+
14711498
void MCEngineExecSendInTime(MCExecContext& ctxt, MCStringRef p_script, MCObjectPtr p_target, double p_delay, int p_units)
14721499
{
14731500
MCNewAutoNameRef t_message;

engine/src/exec.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3792,6 +3792,7 @@ extern MCExecMethodInfo *kMCEngineExecStopUsingStackMethodInfo;
37923792
extern MCExecMethodInfo *kMCEngineExecStopUsingStackByNameMethodInfo;
37933793
extern MCExecMethodInfo *kMCEngineExecDispatchMethodInfo;
37943794
extern MCExecMethodInfo *kMCEngineExecSendMethodInfo;
3795+
extern MCExecMethodInfo *kMCEngineExecSendScriptMethodInfo;
37953796
extern MCExecMethodInfo *kMCEngineExecSendInTimeMethodInfo;
37963797
extern MCExecMethodInfo *kMCEngineExecCallMethodInfo;
37973798
extern MCExecMethodInfo *kMCEngineExecLockErrorsMethodInfo;
@@ -3924,6 +3925,7 @@ void MCEngineExecStopUsingStackByName(MCExecContext& ctxt, MCStringRef p_name);
39243925

39253926
void MCEngineExecDispatch(MCExecContext& ctxt, int handler_type, MCNameRef message, MCObjectPtr *target, MCParameter *params);
39263927
void MCEngineExecSend(MCExecContext& ctxt, MCStringRef script, MCObjectPtr *target);
3928+
void MCEngineExecSendScript(MCExecContext& ctxt, MCStringRef script, MCObjectPtr *target);
39273929
void MCEngineExecSendInTime(MCExecContext& ctxt, MCStringRef script, MCObjectPtr target, double p_delay, int p_units);
39283930
void MCEngineExecCall(MCExecContext& ctxt, MCStringRef script, MCObjectPtr *target);
39293931

engine/src/parseerrors.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,9 @@ enum Parse_errors
17741774

17751775
// {PE-0574} folders: bad folder expression
17761776
PE_FOLDERS_BADPARAM,
1777+
1778+
// {PE-0575} send: can't send script in time
1779+
PE_SEND_SCRIPTINTIME,
17771780
};
17781781

17791782
extern const char *MCparsingerrors;

tests/lcs/core/engine/send.livecodescript

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,30 @@ on TestSendParams
3333
TestAssert "send script with multiple params", the result is "2"
3434
end TestSendParams
3535

36+
/*
37+
It is known that there is code in the wild relying on the following
38+
(undocumented) form of the send command, so we add a test here to ensure
39+
backwards compatibility until such a point as it is deemed possible or
40+
necessary to break.
41+
*/
42+
on TestSendLegacy
43+
local tStack
44+
create stack
45+
put it into tStack
46+
set the defaultStack to the short name of tStack
47+
48+
local tButton
49+
create button
50+
put it into tButton
51+
52+
local tSendScript
53+
put "delete tButton" into tSendScript
54+
55+
send tSendScript to tStack
56+
TestAssert "send script as chunk of script legacy", \
57+
there is not a button 1 of tStack
58+
end TestSendLegacy
59+
3660
private function MessageExists pId
3761
repeat for each line tLine in the pendingMessages
3862
if item 1 of tLine is pId then
@@ -71,4 +95,38 @@ on TestSendDeleteMe
7195
send "--execute this" & return & "delete me" to it
7296

7397
TestAssert "execute 'delete me' using send", there is not a stack it
74-
end TestSendDeleteMe
98+
end TestSendDeleteMe
99+
100+
on TestSendScript
101+
local tStack
102+
create stack
103+
put it into tStack
104+
set the defaultStack to the short name of tStack
105+
create button
106+
107+
local tSendScript
108+
put "delete button 1 of me" into tSendScript
109+
110+
send script tSendScript to tStack
111+
TestAssert "send script to object", \
112+
there is not a button 1 of tStack
113+
end TestSendScript
114+
115+
on TestSendScriptEvaluation
116+
local tVar
117+
put "Something" into tVar
118+
119+
local tStack
120+
create stack
121+
put it into tStack
122+
set the script of tStack to "on setVar pValue; set the cVar of me to pValue; end setVar"
123+
-- tVar should be treated as UQL in target context
124+
send script "setVar tVar" to tStack
125+
TestAssert "send script param evaluated in target context", \
126+
the cVar of tStack is "tVar"
127+
128+
-- tVar should be evaluated in the current context
129+
send "setVar tVar" to tStack
130+
TestAssert "send param evaluated in current context", \
131+
the cVar of tStack is "Something"
132+
end TestSendScriptEvaluation

0 commit comments

Comments
 (0)