Skip to content

Commit 607ec6d

Browse files
Merge pull request livecode#6656 from montegoulding/resolvefile
[[ Resolve File ]] Implement resolve file relative to object
2 parents 36327e2 + 857a2fb commit 607ec6d

File tree

14 files changed

+364
-52
lines changed

14 files changed

+364
-52
lines changed

docs/lcb/notes/19137.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# [19137] `execute script` without an explict object now executes in the context of the host widget if called from a widget handler
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# LiveCode Builder Host Library
2+
## Engine library
3+
4+
* You can now use `resolve file <path expression> [relative to <script object expression>]`
5+
statement to resolve a relative file path to an absolute file path in the same
6+
way that it would be resolved in LiveCode Script.

engine/src/canvas.lcb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,11 @@ Example:
20962096
variable tImage
20972097
put image from file "images/logo.png" into tImage
20982098

2099+
Description:
2100+
If the file path is relative it will be resolved relative to
2101+
`this card of the defaultStack` in a library handler or the current
2102+
widget instance script object if in a widget handler.
2103+
20992104
Tags: Canvas
21002105
*/
21012106

engine/src/engine.lcb

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ public foreign handler MCEngineEvalMyResourcesFolder(out pFile as optional Strin
8585

8686
public foreign handler MCEngineEvalKeyIsDown(in pKey as UInt8, in pEvent as CBool, out rState as CBool) returns nothing binds to "<builtin>"
8787

88+
public foreign handler MCEngineExecResolveFilePath(in pFilePath as String) returns optional String binds to "<builtin>"
89+
public foreign handler MCEngineExecResolveFilePathRelativeToObject(in pFilePath as String, in pObject as ScriptObject) returns optional String binds to "<builtin>"
90+
8891
/**
8992
Summary: Resolves a string to a script object.
9093
Object: The string describing the script object.
@@ -451,17 +454,16 @@ Example:
451454

452455
Description:
453456
Executes the given fragment of LiveCode script in the context of the target
454-
script object, or `this card of the defaultStack` if none is specified. The list
455-
of arguments is accessible from the script fragment using the `paramCount()` and
456-
`param()` functions.
457+
script object. If no object is specified then execution occurs in the context of
458+
`this card of the defaultStack` in a library handler or the current widget
459+
instance script object if in a widget handler.
460+
461+
The list of arguments is accessible from the script fragment using the
462+
`paramCount()` and `param()` functions.
457463

458464
>*Note:* An error is thrown if this syntax is used in a context where access
459465
to script objects is not allowed.
460466

461-
>*Note:* The `execute script` command is subject to Anomaly 19137. It is
462-
recommended to always use targetted forms of the command, as the default target
463-
might change in a future release.
464-
465467
Tags: Script Engine
466468
*/
467469

@@ -662,4 +664,40 @@ begin
662664
MCEngineEvalKeyIsDown(mKey, mEvent, output)
663665
end syntax
664666

667+
/**
668+
Summary: Resolves a file path relative to a script object.
669+
FilePath: A string relative or full file path.
670+
Object: The string describing the script object.
671+
672+
The result: The resolved file path string.
673+
674+
Example:
675+
variable tObject as ScriptObject
676+
resolve script object "this stack"
677+
put the result into tObject
678+
variable tIconPath as String
679+
if tObject exists then
680+
resolve file "images/icon.png" relative to tObject
681+
put the result into tIconPath
682+
end if
683+
684+
Description:
685+
Use the <ResolveFilePath|resolve file path> statement to resolve a relative file
686+
path in LCB using the same file path resolution semantics used by LCS.
687+
If no object is specified the file path will be resolved relative to either
688+
`the defaultStack` or if in a widget handler the stack the current widget is on.
689+
690+
>*Note:* An error is thrown if this syntax is used in a context where access
691+
to script objects is not allowed.
692+
693+
Tags: Script Engine
694+
*/
695+
696+
syntax ResolveFilePath is statement
697+
"resolve" "file" <FilePath: Expression> [ "relative" "to" <Object: Expression> ]
698+
begin
699+
MCEngineExecResolveFilePath(FilePath)
700+
MCEngineExecResolveFilePathRelativeToObject(FilePath, Object)
701+
end syntax
702+
665703
end module

engine/src/module-canvas.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#include "module-canvas.h"
2626
#include "module-canvas-internal.h"
27+
#include "module-engine.h"
2728

2829
//////////
2930

@@ -1865,8 +1866,14 @@ void MCCanvasImageMakeWithPath(MCStringRef p_path, MCCanvasImageRef &r_image)
18651866
{
18661867
MCImageRep *t_image_rep;
18671868
t_image_rep = nil;
1868-
1869-
if (!MCImageGetRepForFileWithStackContext(p_path, MCWidgetGetHost(MCcurrentwidget)->getstack(), t_image_rep))
1869+
1870+
MCObject *t_object = MCEngineCurrentContextObject();
1871+
if (t_object == nullptr)
1872+
{
1873+
return;
1874+
}
1875+
1876+
if (!MCImageGetRepForFileWithStackContext(p_path, t_object->getstack(), t_image_rep))
18701877
{
18711878
MCCanvasThrowError(kMCCanvasImageRepReferencedErrorTypeInfo);
18721879
return;

engine/src/module-engine.cpp

Lines changed: 107 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
#include "module-engine.h"
4343
#include "widget.h"
4444
#include "libscript/script.h"
45+
#include "filepath.h"
46+
#include "osspec.h"
4547

4648
////////////////////////////////////////////////////////////////////////////////
4749

@@ -541,14 +543,35 @@ extern "C" MC_DLLEXPORT_DEF MCValueRef MCEngineExecSendToScriptObject(bool p_is_
541543
extern MCWidgetRef MCcurrentwidget;
542544
extern void MCWidgetExecPostToParentWithArguments(MCStringRef p_message, MCProperListRef p_arguments);
543545

544-
extern "C" MC_DLLEXPORT_DEF MCValueRef MCEngineExecSendWithArguments(bool p_is_function, MCStringRef p_message, MCProperListRef p_arguments)
546+
MCObject* MCEngineCurrentContextObject(void)
545547
{
546-
// PM-2017-10-31: [[ Bugfix 20625 ]] May have no default stack on startup
547-
if (!MCdefaultstackptr)
548-
return nil;
549-
MCObject *t_target = MCdefaultstackptr -> getcurcard();
548+
MCObject *t_object = nullptr;
550549
if (MCcurrentwidget)
551-
t_target = MCWidgetGetHost(MCcurrentwidget);
550+
{
551+
t_object = MCWidgetGetHost(MCcurrentwidget);
552+
}
553+
else if (MCdefaultstackptr)
554+
{
555+
t_object = MCdefaultstackptr->getcurcard();
556+
}
557+
558+
if (t_object == nullptr)
559+
{
560+
MCErrorCreateAndThrow(kMCGenericErrorTypeInfo, "reason", MCSTR("no default stack"), nil);
561+
return nullptr;
562+
}
563+
564+
return t_object;
565+
}
566+
567+
extern "C" MC_DLLEXPORT_DEF MCValueRef MCEngineExecSendWithArguments(bool p_is_function, MCStringRef p_message, MCProperListRef p_arguments)
568+
{
569+
MCObject *t_target = MCEngineCurrentContextObject();
570+
571+
if (t_target == nullptr)
572+
{
573+
return nullptr;
574+
}
552575

553576
return MCEngineDoSendToObjectWithArguments(p_is_function, p_message, t_target, p_arguments);
554577
}
@@ -598,22 +621,18 @@ extern "C" MC_DLLEXPORT_DEF void MCEngineExecPostToScriptObject(MCStringRef p_me
598621

599622
extern "C" MC_DLLEXPORT_DEF void MCEngineExecPostWithArguments(MCStringRef p_message, MCProperListRef p_arguments)
600623
{
601-
// PM-2017-10-31: [[ Bugfix 20625 ]] May have no default stack on startup
602-
if (!MCdefaultstackptr)
603-
return;
604-
605-
MCObject *t_target = MCdefaultstackptr -> getcurcard();
606-
if (MCcurrentwidget)
624+
if (MCcurrentwidget && !MCWidgetIsRoot(MCcurrentwidget))
607625
{
608-
if (!MCWidgetIsRoot(MCcurrentwidget))
609-
{
610-
MCWidgetExecPostToParentWithArguments(p_message, p_arguments);
611-
return;
612-
}
613-
t_target = MCWidgetGetHost(MCcurrentwidget);
626+
MCWidgetExecPostToParentWithArguments(p_message, p_arguments);
627+
return;
614628
}
615629

616-
MCEngineDoPostToObjectWithArguments(p_message, t_target, p_arguments);
630+
MCObject *t_target = MCEngineCurrentContextObject();
631+
632+
if (t_target != nullptr)
633+
{
634+
MCEngineDoPostToObjectWithArguments(p_message, t_target, p_arguments);
635+
}
617636
}
618637

619638
extern "C" MC_DLLEXPORT_DEF void MCEngineExecPost(MCStringRef p_message)
@@ -646,13 +665,12 @@ MCEngineDoExecuteScriptInObjectWithArguments(MCStringRef p_script, MCObject *p_o
646665
{
647666
if (p_object == nil)
648667
{
649-
if (!MCdefaultstackptr)
650-
{
651-
MCErrorCreateAndThrow(kMCGenericErrorTypeInfo, "reason", MCSTR("no default stack"), nil);
652-
return nullptr;
668+
p_object = MCEngineCurrentContextObject();
669+
670+
if (p_object == nullptr)
671+
{
672+
return nullptr;
653673
}
654-
655-
p_object = MCdefaultstackptr->getcurcard();
656674
}
657675

658676
MCExecContext ctxt(p_object, nil, nil);
@@ -1323,6 +1341,70 @@ MCEngineEvalKeyIsDown(uint8_t p_key, bool p_event, bool& r_down)
13231341

13241342
////////////////////////////////////////////////////////////////////////////////
13251343

1344+
1345+
static MCStringRef
1346+
MCEngineDoResolveFilePathRelativeToStack(MCStringRef p_filepath, MCStack *p_stack)
1347+
{
1348+
if (!MCPathIsAbsolute(p_filepath))
1349+
{
1350+
if (p_stack == nullptr)
1351+
{
1352+
MCObject *t_target = MCEngineCurrentContextObject();
1353+
1354+
if (t_target == nullptr)
1355+
{
1356+
return nullptr;
1357+
}
1358+
1359+
p_stack = t_target->getstack();
1360+
}
1361+
1362+
// else try to resolve from stack file location
1363+
MCAutoStringRef t_resolved;
1364+
if (p_stack->resolve_relative_path(p_filepath, &t_resolved))
1365+
{
1366+
return t_resolved.Take();
1367+
}
1368+
1369+
// else try to resolve from current folder
1370+
if (MCS_resolvepath(p_filepath, &t_resolved))
1371+
{
1372+
return t_resolved.Take();
1373+
}
1374+
}
1375+
1376+
return MCValueRetain(p_filepath);;
1377+
}
1378+
1379+
extern "C" MC_DLLEXPORT_DEF MCStringRef MCEngineExecResolveFilePathRelativeToObject(MCStringRef p_filepath, MCScriptObjectRef p_object)
1380+
{
1381+
if (!MCEngineEnsureScriptObjectAccessIsAllowed())
1382+
return nullptr;
1383+
1384+
MCStack *t_stack = nullptr;
1385+
if (p_object != nullptr)
1386+
{
1387+
MCObject *t_object = nullptr;
1388+
uint32_t t_part_id = 0;
1389+
if (!MCEngineEvalObjectOfScriptObject(p_object, t_object, t_part_id))
1390+
return nullptr;
1391+
1392+
t_stack = t_object->getstack();
1393+
}
1394+
1395+
return MCEngineDoResolveFilePathRelativeToStack(p_filepath, t_stack);
1396+
}
1397+
1398+
extern "C" MC_DLLEXPORT_DEF MCStringRef MCEngineExecResolveFilePath(MCStringRef p_filepath)
1399+
{
1400+
if (!MCEngineEnsureScriptObjectAccessIsAllowed())
1401+
return nullptr;
1402+
1403+
return MCEngineDoResolveFilePathRelativeToStack(p_filepath, nullptr);
1404+
}
1405+
1406+
////////////////////////////////////////////////////////////////////////////////
1407+
13261408
MC_DLLEXPORT_DEF MCTypeInfoRef kMCEngineScriptObjectDoesNotExistErrorTypeInfo = nil;
13271409
MC_DLLEXPORT_DEF MCTypeInfoRef kMCEngineScriptObjectNoContextErrorTypeInfo = nil;
13281410

engine/src/module-engine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ void MCEngineScriptObjectPreventAccess(void);
4141

4242
void MCEngineScriptObjectAllowAccess(void);
4343

44+
MCObject* MCEngineCurrentContextObject(void);
45+
4446
////////////////////////////////////////////////////////////////////////////////
4547

4648
#endif

tests/lcs/core/engine-lcb/_canvas.lcb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@ public handler CanvasTestImageFromResourceFile()
1515
return not (the pixels of tImage is empty)
1616
end handler
1717

18+
public handler TestCanvas_ImageFromFile(in pPath as String) returns Boolean
19+
variable tImage as Image
20+
put image from file pPath into tImage
21+
-- if image from file fails it will throw
22+
return true
23+
end handler
24+
1825
end library

tests/lcs/core/engine-lcb/_engine.lcb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,22 @@ end handler
5252
public handler TestEngine_Caller(in pCallback as String)
5353
send pCallback to the caller
5454
end handler
55+
56+
public handler TestEngine_ResolveFile(in pPath as String, in pRelativeObject as String) returns String
57+
if pRelativeObject is not empty then
58+
variable tObject as ScriptObject
59+
resolve script object pRelativeObject
60+
put the result into tObject
61+
if tObject exists then
62+
resolve file pPath relative to tObject
63+
return the result
64+
else
65+
throw "object not found:" && pRelativeObject
66+
end if
67+
else
68+
resolve file pPath
69+
return the result
70+
end if
71+
end handler
72+
5573
end library
6.1 KB
Loading

0 commit comments

Comments
 (0)