OK, good :) &nbsp;So Igor, what are the three functions interpreterProxy still has?<div><br></div><div>TIA<br><br></div><div>(P.S. &nbsp;this can be backward-compatible, right? &nbsp;We can still provide the old nterface for a while to allow external plugins to still load)</div>
<div><br><div class="gmail_quote">On Sat, Nov 22, 2008 at 11:06 AM, Igor Stasenko <span dir="ltr">&lt;<a href="mailto:siguctua@gmail.com">siguctua@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
2008/11/22 Eliot Miranda &lt;<a href="mailto:eliot.miranda@gmail.com">eliot.miranda@gmail.com</a>&gt;:<br>
<div><div></div><div class="Wj3C7c">&gt;<br>
&gt;<br>
&gt; On Sat, Nov 22, 2008 at 9:34 AM, Igor Stasenko &lt;<a href="mailto:siguctua@gmail.com">siguctua@gmail.com</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; 2008/11/22 Andreas Raab &lt;<a href="mailto:andreas.raab@gmx.de">andreas.raab@gmx.de</a>&gt;:<br>
&gt;&gt; &gt; Igor Stasenko wrote:<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; By more security, i meant a little feature, that since getting atom<br>
&gt;&gt; &gt;&gt; value is inexpensive operation, you can<br>
&gt;&gt; &gt;&gt; load value identified by atom and check for non-null value each time<br>
&gt;&gt; &gt;&gt; before calling function.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Oh, I think you mean it can be safer to use (with which I would agree)<br>
&gt;&gt; &gt; not<br>
&gt;&gt; &gt; necessarily more secure.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;&gt; Old scheme, needs to check for non-null as well, but in addition you<br>
&gt;&gt; &gt;&gt; need to be careful to clean out this pointer when you get notification<br>
&gt;&gt; &gt;&gt; that module, from where you taken this pointer is unloaded.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Yes. Although, you won&#39;t get around doing something about unloading<br>
&gt;&gt; &gt; either -<br>
&gt;&gt; &gt; we spent today learning about the intricacies of unloading OpenAL32.dll<br>
&gt;&gt; &gt; and<br>
&gt;&gt; &gt; it turned out to be absolutely crucial to be able to unload our own<br>
&gt;&gt; &gt; plugins.<br>
&gt;&gt; &gt; A flat shared namespace might have caused some serious issues here.<br>
&gt;&gt; &gt;<br>
&gt;&gt; Almost anything &quot;might have cause some serious issues&quot;, if you don&#39;t<br>
&gt;&gt; use it wisely (Class become: nil).<br>
&gt;&gt; Its not an argument not to use it.<br>
&gt;&gt;<br>
&gt;&gt; &gt;&gt; I&#39;m not sure what you mean by per-plugin namespace.<br>
&gt;&gt; &gt;&gt; And how much difference in namespaces between this:<br>
&gt;&gt; &gt;&gt; &nbsp;bitblt = ioLoadFunctionFrom(&quot;ioBitBlt&quot;, &quot;BitBltPlugin&quot;)<br>
&gt;&gt; &gt;&gt; and this:<br>
&gt;&gt; &gt;&gt; &nbsp; atom = makeAtom(&quot;BitBltPlugin.ioBitBlt&quot;);<br>
&gt;&gt; &gt;&gt; &nbsp; bitBlt = getAtomValue(atom);<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; The difference that first binds symbol at compile/link time, while<br>
&gt;&gt; &gt;&gt; second - at run time.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; It would be quite possible to bind this at runtime, too. But what I mean<br>
&gt;&gt; &gt; by<br>
&gt;&gt; &gt; per-plugin namespace is that the *export* isn&#39;t done into a flat<br>
&gt;&gt; &gt; namespace<br>
&gt;&gt; &gt; but rather into a structured one.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; And yes, one could conceivably use a naming scheme that is equivalent in<br>
&gt;&gt; &gt; practice but unless that&#39;s automated (the current scheme is fully<br>
&gt;&gt; &gt; automated)<br>
&gt;&gt; &gt; it seems error-prone and one of these things were people are simply too<br>
&gt;&gt; &gt; lazy<br>
&gt;&gt; &gt; in practice to utilize it correctly (if that were different I would<br>
&gt;&gt; &gt; expect<br>
&gt;&gt; &gt; people to use class and selector prefixes consistently which they<br>
&gt;&gt; &gt; don&#39;t).<br>
&gt;&gt; &gt;<br>
&gt;&gt; It could be automated or used manually. Its open for any intrusion :)<br>
&gt;&gt;<br>
&gt;&gt; &gt;&gt; As for modularity, let me illustrate what i meant.<br>
&gt;&gt; &gt;&gt; There are many primitives in VM, which look like:<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; primitiveFoo: x with: y<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; ^ self cCode: &#39;ioFoo(x,y)&#39;.<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; Obviously, when you generate this code using VMMaker, it wont compile<br>
&gt;&gt; &gt;&gt; unless you having ioFoo() function implemented somewhere.<br>
&gt;&gt; &gt;&gt; And you have a little choice where to put this implementation - in one<br>
&gt;&gt; &gt;&gt; of internal plugins or in platform-specific part of VM.<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; Now, lets consider, if i refactor this code to use atoms (i skipping<br>
&gt;&gt; &gt;&gt; details like declarations etc) :<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; primitiveFoo: x with: y<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; ioFoo := getAtomValue: AtomIoFoo.<br>
&gt;&gt; &gt;&gt; ioFoo notNil ifTrue: [ ^ self cCode: &#39;ioFoo(x,y)&#39; ].<br>
&gt;&gt; &gt;&gt; ^ self primitiveFail.<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; 1. the code will compile regardless i declared a function or not.<br>
&gt;&gt; &gt;&gt; 2. i&#39;m free in choice where to put the implementation of this<br>
&gt;&gt; &gt;&gt; function, or even build VM initially w/o this function.<br>
&gt;&gt; &gt;&gt; 3. across different platforms, one can use same VMMaker to generate<br>
&gt;&gt; &gt;&gt; sources , and it will always compile &amp; link.<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; doesn&#39;t it look like more modular?<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; No. It looks utterly pointless to me. You introduce a plugin that does<br>
&gt;&gt; &gt; nothing but looking up and call an atom; what good is that plugin? If<br>
&gt;&gt; &gt; you<br>
&gt;&gt; &gt; generalize that just a little you have the FFI where you might declare<br>
&gt;&gt; &gt; ioFoo() directly and call it. Which of course could be done via atom<br>
&gt;&gt; &gt; table<br>
&gt;&gt; &gt; too, but I still fail to see how that would be more modular.<br>
&gt;&gt; &gt;<br>
&gt;&gt;<br>
&gt;&gt; Not a plugin. I mentioned VM code in interp.c. I see little need in<br>
&gt;&gt; having such constructs in plugin.<br>
&gt;&gt; In VM core, however, there is always a bit of uncertainty - VM forced<br>
&gt;&gt; to provide primitive by default (because its a standart one), but on<br>
&gt;&gt; different platforms, depending on their capabilities or your intent,<br>
&gt;&gt; you may omit putting functionality of some primitives into VM.<br>
&gt;&gt; This is where such scheme is can be quite useful - instead of stubbing<br>
&gt;&gt; function in sources, or exploring what function has to return to make<br>
&gt;&gt; primitive fail , just remove it from build.<br>
&gt;&gt;<br>
&gt;&gt; &gt;&gt; Now, take a look at a sqWin32Stubs.c - how it would look if we would<br>
&gt;&gt; &gt;&gt; use shared namespace? It would look as zero length file.<br>
&gt;&gt; &gt;&gt; Because all we have to do in platform code is just initialize pointers:<br>
&gt;&gt; &gt;&gt;<br>
&gt;&gt; &gt;&gt; pointers = {<br>
&gt;&gt; &gt;&gt; &nbsp;&quot;ioFoo&quot; , ioFoo<br>
&gt;&gt; &gt;&gt; #ifdef NO_SOME_STUFF<br>
&gt;&gt; &gt;&gt; &nbsp;&quot;ioBar&quot; , ioBar<br>
&gt;&gt; &gt;&gt; #endif<br>
&gt;&gt; &gt;&gt; &nbsp;...<br>
&gt;&gt; &gt;&gt; };<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; And if you stick this in sqWin32Stubs.c (or its equivalent) you end up<br>
&gt;&gt; &gt; with<br>
&gt;&gt; &gt; a non-empty stubs file. In other words, you are replacing one set of<br>
&gt;&gt; &gt; stubs<br>
&gt;&gt; &gt; with another one. Not much of an improvement.<br>
&gt;&gt; &gt;<br>
&gt;&gt;<br>
&gt;&gt; Its not the same thing. Currently you have to define a function - with<br>
&gt;&gt; implementation or with empty body to make compiler content.<br>
&gt;&gt; In example i showed - you can do simpler - just omit binding a<br>
&gt;&gt; function to a symbol.<br>
&gt;&gt; Suppose we having a well structured organization of VM platform<br>
&gt;&gt; sources, &nbsp;then it might look like:<br>
&gt;&gt;<br>
&gt;&gt; #ifdef SUPPORT_FILES<br>
&gt;&gt; #include &quot;file_functions.inc&quot;<br>
&gt;&gt; /// or put all functions here w/o using #include<br>
&gt;&gt; #endif<br>
&gt;&gt;<br>
&gt;&gt; instead of something like:<br>
&gt;&gt;<br>
&gt;&gt; #ifdef SUPPORT_FILES<br>
&gt;&gt; #include &quot;file_functions.inc&quot;<br>
&gt;&gt; /// or put all functions here w/o using #include<br>
&gt;&gt; #else<br>
&gt;&gt; #include &quot;file_functions_stubs.inc&quot;<br>
&gt;&gt; #endif<br>
&gt;&gt;<br>
&gt;&gt; &gt;&gt; I&#39;m currently trying to make a HostWindowsPlugin and want to put all<br>
&gt;&gt; &gt;&gt; windowing and i/o stuff in it from VM platform sources.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Why do another one? Is there something that the current host window<br>
&gt;&gt; &gt; plugin<br>
&gt;&gt; &gt; doesn&#39;t address?<br>
&gt;&gt;<br>
&gt;&gt; Well, there&#39;s many things.<br>
&gt;&gt; Don&#39;t want to OT here, just simple example: when image fires up, i<br>
&gt;&gt; want to show a splash screen, and then, when user clicks on it, or<br>
&gt;&gt; some time passed , hide it and show the &quot;main&quot; window. ALL is<br>
&gt;&gt; controlled from image, of course , e.g. one might want to show splash<br>
&gt;&gt; screen and when user clicks on it - just quit the squeak :)<br>
&gt;&gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;&gt; It would be much a cut&#39;n&#39;paste experience to me, if all callouts in VM<br>
&gt;&gt; &gt;&gt; would use atoms - because then i don&#39;t need to touch headers &amp; sources<br>
&gt;&gt; &gt;&gt; and many different places to make compiler content. Because there<br>
&gt;&gt; &gt;&gt; would be no need in exporting function in C-like manner.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Ah. But that&#39;s a fallacy. That you don&#39;t need to &quot;touch&quot; these places<br>
&gt;&gt; &gt; doesn&#39;t mean you don&#39;t need to know about them. In fact, having to touch<br>
&gt;&gt; &gt; them, having the compiler complain about them is a great way to learn<br>
&gt;&gt; &gt; and<br>
&gt;&gt; &gt; know and understand all the areas you need to touch - if the VM would<br>
&gt;&gt; &gt; randomly crash on you because you have replaced one but not another<br>
&gt;&gt; &gt; function<br>
&gt;&gt; &gt; you&#39;d be in for a hellish experience. Yes, the C compiler can be<br>
&gt;&gt; &gt; notorious<br>
&gt;&gt; &gt; but it can also be a pretty good teacher.<br>
&gt;&gt; &gt;<br>
&gt;&gt;<br>
&gt;&gt; The same self-fallacy is to be confident, that if your code compiles<br>
&gt;&gt; ok it doesn&#39;t crash in some random place :)<br>
&gt;&gt;<br>
&gt;&gt; &gt;&gt; And lastly, remember InterpreterProxy thing? Each time you want to<br>
&gt;&gt; &gt;&gt; introduce new functionality , you have to extend the structure and<br>
&gt;&gt; &gt;&gt; increase version number. But what is it? Its just a list of pointers!<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; No, it&#39;s not. It&#39;s an interface (a contract) between the VM and the<br>
&gt;&gt; &gt; plugin.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;&gt; Isn&#39;t it would be simpler for plugin to just lookup a function by its<br>
&gt;&gt; &gt;&gt; name - and use it if such function exists. Its pretty much same as<br>
&gt;&gt; &gt;&gt; dynamic linking, just s/dlsym/getAtomValue/ and don&#39;t let your code be<br>
&gt;&gt; &gt;&gt; constrained by compiler/linker anymore :)<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; I *very much* doubt that it would be simpler for each plugin to look up<br>
&gt;&gt; &gt; the<br>
&gt;&gt; &gt; function every time they use it and test for its existence every time it<br>
&gt;&gt; &gt; is<br>
&gt;&gt; &gt; used. Consider this primitive:<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; primitiveStringClone<br>
&gt;&gt; &gt; &nbsp;interpreterProxy methodArgument = 1<br>
&gt;&gt; &gt; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;arg := interpreterProxy stackValue: 0.<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy isBytes: arg)<br>
&gt;&gt; &gt; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;clone := interpreterProxy clone: arg.<br>
&gt;&gt; &gt; &nbsp;interpreterProxy pop: 2.<br>
&gt;&gt; &gt; &nbsp;interpreterProxy push: clone.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Now let&#39;s rewrite this in pseudo-code to see what it would look like<br>
&gt;&gt; &gt; without<br>
&gt;&gt; &gt; an interface:<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; primitiveStringClone<br>
&gt;&gt; &gt; &nbsp;((interpreterProxy has: #methodArgumentCount)<br>
&gt;&gt; &gt; &nbsp; &nbsp;and:[interpreterProxy methodArgument = 1])<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy has: #stackValue)<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;arg := interpreterProxy stackValue: 0.<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy has: #isBytes)<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy isBytes: arg)<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy has: #clone)<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;clone := interpreterProxy clone: arg.<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy has: #pop)<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;interpreterProxy pop: 2.<br>
&gt;&gt; &gt; &nbsp;(interpreterProxy has: #push)<br>
&gt;&gt; &gt; &nbsp; &nbsp; &nbsp;ifFalse:[^interpreterProxy primitiveFail].<br>
&gt;&gt; &gt; &nbsp;interpreterProxy push: clone.<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; Simpler? You&#39;ve got to be kidding me ;-)<br>
&gt;&gt; &gt;<br>
&gt;&gt;<br>
&gt;&gt; This counter-example missing a point :)<br>
&gt;&gt; Take a look at Hydra code, where InterpreterProxy has left with only 3<br>
&gt;&gt; functions, and will stay to have 3 functions forever without need in<br>
&gt;&gt; extending protocol , because rest functions is obtained dynamicaly<br>
&gt;&gt; using name lookup.<br>
&gt;&gt; A plugin simply refuse to start without having all requested VM<br>
&gt;&gt; functions be non-null. But the trick is, that VM not forced anymore to<br>
&gt;&gt; support obsolete stuff of ever-growing InterpreterProxy protocol.<br>
&gt;<br>
&gt; I like this very much. &nbsp;Not totally, just very much :)<br>
&gt; What&#39;s good here is that the plugin can access functions directly in the VM<br>
&gt; and not go through the slow interpreterProxy. &nbsp;Because C supports syntax for<br>
&gt; calls through function pointers that looks just like normal functions calls<br>
&gt; wouldn&#39;t look very different to normal calls. &nbsp;The function pointers simply<br>
&gt; need to be decared.<br>
<br>
</div></div>right!<br>
<div class="Ih2E3d">&gt; But what I don&#39;t like is having interpreterProxy persist at all. Why not get<br>
&gt; rid of it except for initialization and pass it in through setInterpreter?<br>
&gt; &nbsp;What do you need interpreterProxy to persist for?<br>
</div>right!<br>
<div class="Ih2E3d">&gt; So Slang generates a standard prolog for all the functions used in a plugin<br>
&gt; at the beginning of the file (nice documentation; lists exactly the<br>
&gt; functions the plugin uses). &nbsp;setInterpreter (better called<br>
&gt; initializePlugin?) takes an argument that provides a function to lookup<br>
&gt; function pointers by name. &nbsp;Slang generates the code to initialize these<br>
&gt; fnction pointers. &nbsp;Uses of the function pointers look just like normal<br>
&gt; calls.<br>
</div>Right!<br>
<div class="Ih2E3d">&gt; Then internal plugins can be linked directly against the VM. &nbsp;e.g.<br>
&gt;<br>
<br>
</div>Things works exactly in that way in Hydra. :)<br>
<div class="Ih2E3d"><br>
&gt; #if EXTERNAL_PLUGIN<br>
&gt; static void (*popthenPush(sqInt,sqInt);<br>
&gt; static void (*primitiveFailed)(void);<br>
&gt; #endif<br>
&gt; ...<br>
&gt; void somePrimitive()<br>
&gt; {<br>
&gt; &nbsp; &nbsp; ....<br>
&gt; &nbsp; &nbsp; if (!ok) {<br>
&gt; &nbsp; &nbsp; &nbsp; &nbsp; primitiveFailed();<br>
&gt; &nbsp; &nbsp; &nbsp; &nbsp; return;<br>
&gt; &nbsp; &nbsp; }<br>
&gt; &nbsp; &nbsp; popthenPush(result);<br>
&gt; }<br>
&gt; ...<br>
&gt; #if EXTERNAL_PLUGIN<br>
&gt; static sqInt<br>
&gt; setInterpreter(InterpreterProxy *interpreterProxy)<br>
&gt; {<br>
&gt; &nbsp; &nbsp; if (interpreterProxy.oopSize() != 4)<br>
&gt; &nbsp; &nbsp; &nbsp; &nbsp; return InitErrorWrongOopSize;<br>
&gt; &nbsp; &nbsp; if (!interpreterProxy.getFunction(&quot;popthenPush&quot;, &amp;popthenPush)<br>
&gt; &nbsp; &nbsp; &nbsp;|| !interpreterProxy.getFunction(&quot;primitiveFailed&quot;, &amp; primitiveFailed))<br>
&gt; &nbsp; &nbsp; &nbsp; &nbsp; return InitErrorMissingFunction;<br>
&gt; &nbsp; &nbsp; return 0;<br>
&gt; }<br>
&gt; #endif /* EXTERNAL_PLUGIN */<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; &gt; Cheers,<br>
&gt;&gt; &gt; &nbsp;- Andreas<br>
&gt;&gt; &gt;<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; --<br>
&gt;&gt; Best regards,<br>
&gt;&gt; Igor Stasenko AKA sig.<br>
&gt;&gt;<br>
&gt;<br>
&gt;<br>
&gt;<br>
&gt;<br>
&gt;<br>
<br>
<br>
<br>
</div>--<br>
<div><div></div><div class="Wj3C7c">Best regards,<br>
Igor Stasenko AKA sig.<br>
<br>
</div></div></blockquote></div><br></div>