Closed rebolbot closed 8 years ago
Submitted by: BrianH
PROTECT doesn't protect the exported word of a module, it protects the original word. When a word is exported a new word is added to the context that is being imported into, then (a reference to) its value is copied over to the new context. That new word is not protected unless you protect it after it is initialized. This goes for the direct export of private modules, or twice for the two-stage export of regular modules, once into the runtime lib and then again into the target context. Added to all of that, there is no PROTECTED? function to tell you if a word is protected (see #1788), so you can't really detect and propagate the protected attribute to the new words anyways, even if you were presumptuous enough to assume that the user of your module wanted that.
Taken together, PROTECT is not really useful for exported words. The best way to use PROTECT is for local words and values that you want to rely on being unmodified, and then export accessor functions. You can also protect words and values in the runtime lib so that they can be relied on too (like PROTECT-SYSTEM in R2). But if you want to protect words and values in a target context (a user or other module context) then it would be better to export or provide a function that can be passed the target context as an argument and protect explicitly. Then the user of your exports can call that function if appropriate for them.
The security model of R3 depends on ownership and responsibility to a certain extent. If they are your words then you can protect them, but other scripts/modules have responsibility for their own words. Exporting is not making available, it is exporting. Once it is exported then other people can do what they want with their copies - it is someone else's responsibility. And you don't have to export - unless you explicitly hide your module's words then they are already accessible by default through a module reference. Exporting is only for global resources, or resources local to another context (private).
Note that this doesn't apply to the values, just the words. You can export read-only series and objects, for instance, and they will just be references to the original structures which will still be read-only. But the target context words that refer to them can be changed to refer to new values unless they are themselves protected.
Slight edit to make the real problem more clear.
Submitted by: oldes
I understand that protecting exported values in normal modules is not very useful, but while making extensions, it's quite common that you have a lot of constants which you need available and better protected so nobody can redefine its values by accident so protecting exported values makes sense.
Of course.. it can be done using words, but that's not easy in the current state (if you for example must deal with multiple enums)
To show real life example, let's say that you have extension where you have args of type:
typedef enum
{
UndefinedChannel, RedChannel = 0x0001, GrayChannel = 0x0001, CyanChannel = 0x0001, GreenChannel = 0x0002, MagentaChannel = 0x0002, BlueChannel = 0x0004, YellowChannel = 0x0004, AlphaChannel = 0x0008, OpacityChannel = 0x0008, MatteChannel = 0x0008, /* deprecated */ BlackChannel = 0x0020, IndexChannel = 0x0020, AllChannels = 0x002F,
/* special channel types */
TrueAlphaChannel = 0x0040, /* extract actual alpha channel from opacity / RGBChannels = 0x0080, / set alpha from grayscale mask in RGB / GrayChannels = 0x0080, SyncChannels = 0x0100, / channels should be modified equally */ DefaultChannels = ( (AllChannels | SyncChannels) &~ OpacityChannel)
} ChannelType;
The simplest way how to deal with it is to define the channel types as normal REBOL integer values, so you could use on REBOL side code like:
my-func (BlueChannel or RedChannel)
which would be impossible if the constants would be used just as words.
Submitted by: BrianH
You are presuming that the user of the exported words wants to treat them as constants in their code.
R3 doesn't have constants, and doesn't have enums, it just has values which may or not be modifiable, and value slots which may or not be write-protected. What you are talking about is write-protection. Since R3 isn't compiled there is no performance reason to write-protect the words, especially if they have immediate values assigned to them. It has the same performance to get a value from a protected word as it does to get one from an unprotected word.
The only reason that you would need to protect the words is to prevent them from being changed by accident or through malice in the client code - you can already protect them in your own code. Since it's not your code, it's not your decision to make. You can provide helper functions that they can choose to call that will protect the words, but it is up to them to decide whether they are necessary to call.
If you want to make something like an enum, make an object of the enum words, assign the values to those words, then write-protect the whole thing, and export that object by name. Then when someone wants to ensure that their enum words refer to the right values then they can bind them to the appropriate enum object. Enum name conflicts won't be a problem because different enum sets would have different contexts. And as a (currently theoretical) side effect, the enum object would not need to be task-local, it would be shared. And a definite positive effect of this approach is to not pollute the client context with a bunch of exported enum words.
Submitted by: oldes
Using objects probably makes a sense as some enums can be really huge.
Submitted by: oldes
CC - Data [ Version: alpha 110 Type: Issue Platform: All Category: Security Reproduce: Always Fixed-in:none ]