Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Updated TIP 500 in light of implementation experience. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
7ff5dde719885f7394c8adce89ad344a |
User & Date: | dkf 2018-05-19 08:37:09.396 |
Context
2018-05-19
| ||
10:48 | Added an example check-in: e6ade010a0 user: dkf tags: trunk | |
08:37 | Updated TIP 500 in light of implementation experience. check-in: 7ff5dde719 user: dkf tags: trunk | |
2018-05-17
| ||
12:58 | TIP #491 Done check-in: a23088053b user: jan.nijtmans tags: trunk | |
Changes
Changes to tip/500.md.
︙ | ︙ | |||
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 | mechanism does not need to be totally proof against prying (and introspection mechanisms are desirable) as TclOO deliberately does not provide security mechanisms; such mechanisms are the purpose of Tcl's interpreters. All that is desired is a way of ensuring that code does not inadvertently trample other code, allowing a superclass to evolve its implementation without needing to take into account every detail of its subclasses. # Detailed Proposal: Methods For methods, as they actually exist in storage that is totally controlled by TclOO (and which is not addressable other than via TclOO-created commands or introspectors) the main complexity is working out what lookups are possible in a particular case while ensuring that method lookups are fast. Determining what the context class (or instance) is easy enough, as that's present pretty much directly in the `Tcl_ObjectContext`, but rapidly determining whether the object that is invoked is able to be seen in that way is the non-trivial part. (As with most things in TclOO, appropriate caching is the key to accelerating this sort of thing with a reasonable memory overhead.) A key objective is that if one invokes `$otherobj ClassPrivateMethod` from inside a method that is part of the class *but a different instance* or possibly even a *different subclass* of that class, then that method will be found. That makes this a useful mechanism that is not provided by the TclOO system in Tcl 8.6. ## Method Invoke Semantics | > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > | > | > > | < > | > > > | > | > > > > > > > > | < > > > | | | | < < < < < < < < < < < | > | | | 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 | mechanism does not need to be totally proof against prying (and introspection mechanisms are desirable) as TclOO deliberately does not provide security mechanisms; such mechanisms are the purpose of Tcl's interpreters. All that is desired is a way of ensuring that code does not inadvertently trample other code, allowing a superclass to evolve its implementation without needing to take into account every detail of its subclasses. # Private Declarations: Syntax and Immediate Semantics This TIP introduces a new definition command, **private**, that is present when doing both **oo::define** and **oo::objdefine**. Much as with the **self** definition command (or the **oo::define** and **oo::objdefine** commands themselves), it takes either single arguments or a list of arguments (that it then builds a script of by turning them into a list) and evaluates the result in a special context. The special context is the same as the outer definition context, except that some definition commands instead create _private definitions_ in that context. The following commands are private-definition aware: * **forward** — the forwarded method it creates is a _private method_. * **method** — the procedure-like method it creates is a _private method_. * **self** (in **oo::class**) — the definitions on the class instance inside will be _private definitions_ (if the concept applies). * **variable** — the variable resolution rules created or manipulated (as this is a slot) will be for _private variables_. If provided with no arguments, the **private** definition command will return whether it was invoked from a context that should create private definitions. Otherwise it returns the result of evaluating its script. The meaning of a private method and a private variable is defined below. # Detailed Proposal: Methods For methods, as they actually exist in storage that is totally controlled by TclOO (and which is not addressable other than via TclOO-created commands or introspectors) the main complexity is working out what lookups are possible in a particular case while ensuring that method lookups are fast. Determining what the context class (or instance) is easy enough, as that's present pretty much directly in the `Tcl_ObjectContext`, but rapidly determining whether the object that is invoked is able to be seen in that way is the non-trivial part. (As with most things in TclOO, appropriate caching is the key to accelerating this sort of thing with a reasonable memory overhead.) A key objective is that if one invokes `$otherobj ClassPrivateMethod` from inside a method that is part of the class *but a different instance* or possibly even a *different subclass* of that class, then that method will be found. That makes this a useful mechanism that is not provided by the TclOO system in Tcl 8.6. ## Method Invoke Semantics When a method is invoked, a public (or unexported, if via the **my** command) method is usually preferred. However, if the class of the object on which the method is called is also the class that defined the currently executing method (specifically, is the creator of the method that defines the current stack frame at the point of the call), bearing in mind that the determination of this also respects the inheritance and mixin hierarchies, then private methods on that context class will also be found. Similarly, if the currently executing method was defined by an object and that object is the object to which the method invoke is directed, private methods on that object are also found (the author expects these to be a rarely used feature). Searches for private methods precede the equivalent public and unexported methods in the method resolution order, but share a common naming space (i.e., they're stored in the same hash tables) so that there are no ambiguities. Since every method has at most one declaring class or object (and not both simultaneously) there is at most one private method that can be on any call chain. External calls (e.g., from the top level of Tcl, from a **namespace eval**, from a procedure) will never have a private method on thir call chains. Once the call chain is created, the execution is handled as prior to this TIP; the implementation of the first element on the call chain is executed, whatever that is, and that can dispatch to later items on the call chain using **next** and **nextto**. A private method on a class (or object) may not have the same name as another method on that class; all methods of a class (or object) are part of a single space of names. When resolving a method call of a subclass of the class that has the private method, even if the method names match, the private method does not participate in the call chain; only an exact match from exactly the right context counts. In particular: oo::class create Top { method Foo {} { puts "This is Top::Foo for [self]" } } oo::class create Super { superclass Top private method Foo {} { puts "This is Super::Foo for [self]" } method bar {other} { puts "This is Super::bar for [self]" my Foo [self] Foo $other Foo } } oo::class create Sub { superclass Super method Foo {} { puts "This is Sub::Foo for [self]" next } private method bar {other} { error "This should be unreachable" } method grill {} { puts "This is Sub::grill for [self]" my Foo } } Sub create obj1 Sub create obj2 obj1 bar obj2 obj1 grill is expected to print out: This is Super::bar for ::obj1 This is Super::Foo for ::obj1 This is Super::Foo for ::obj1 This is Super::Foo for ::obj2 This is Sub::grill for ::obj1 This is Sub::Foo for ::obj1 This is Top::Foo for ::obj1 Note that the calls via **my**, **self** and through the public handle of another object all pick up the private method from _Super_, and the call to **next** from _Sub_ does not pick up the method on _Super_ but instead goes straight to _Top_. ## Creation and Introspection Private methods are created calling **oo::define** (and **oo::objdefine**, and via the constructor of **oo::class**) like this: oo::define TheClass { private method foo {...} { ... } private forward bar SomeOtherCommand } At the C API level, private methods can be directly created by setting the *flags* parameter (called *isPublic* in older versions of the API) of `Tcl_NewMethod` and `Tcl_NewInstanceMethod` to the constant `TCL_OO_METHOD_PRIVATE`. This lets custom types of methods be declared private by their C code directly. Introspection is done via appropriate options to **info class methods** and **info object methods**. In particular, they are never returned when the **-all** option is given, are found when **-private** option is given (unless the **-all** option is _also_ given), and can be found when the new option **-scope** is given, depending on what scope is chosen from the options below: |
︙ | ︙ | |||
235 236 237 238 239 240 241 | returns the creation ID of any existing object. It also applies to classes. Again, note that creation IDs are _always_ system-allocated and are _never_ guaranteed to be unique between interpreters, either in multiple processes or in the same thread or process; they are only ever locally unique. # Implementation | | | 274 275 276 277 278 279 280 281 282 283 284 285 | returns the creation ID of any existing object. It also applies to classes. Again, note that creation IDs are _always_ system-allocated and are _never_ guaranteed to be unique between interpreters, either in multiple processes or in the same thread or process; they are only ever locally unique. # Implementation See the [`tip-500` branch](https://core.tcl.tk/tcl/timeline?r=tip-500). # Copyright This document has been placed in the public domain. |