Why Is There No #Include?

common and entirely sensible programming practice is to put commonly used utility
functions in one file, and then somehow link that file in to many different programs. 
In traditional compiled languages you can compile a bunch of utilities into a statically
linked .LIB file or a dynamically linked .DLL file.  But what do you do in script,
where a program is just text?


often ask me why there is no
#include statement
in VBScript or JScript.  Wouldn't it be nice to be able to write up a page full
of helpful utility functions and then in half a dozen different scripts say 


if you actually think for a bit about how such a statement would be implemented, you
see why we didn't do it.  The reason is pretty simple: the script engine does
not know where the current file came from, so it has no way of finding the referenced
.  Consider IE, for example:




' ...


does the script engine know that this page was downloaded from http://www.rezrov.com/gnusto/ ?
The script engine knows nothing about where the script came from -- IE just hands
the text to the engine.  Even assuming that it could figure it out, the script
engine would then have to call IE's code to download files off the internet. 
Worse, what if the path was fully qualified to a path in a different domain? 
Now the script engine needs to implement IE's security rules.


do any of those factors apply when running in ASP?  No!  ASP would have
to have a completely different
#include mechanism
where the relative path was based on facts about the current virtual root and the
security semantics checked for cross-root violations.  What about WSH? 
There we're worried about the current directory but have no security considerations.


about third party hosts?  Every host could have a different rule for where
the script comes from and what is a legal path
.  We cannot possibly put all
those semantics into the script engine.  To make
work, we'd have to define a callback function into the host so that the host could
fetch the included script for us.  It's just too much work for too little gain,
so we simply declared a rule: the host is responsible for implementing any mechanism
whereby script files can be included into the script namespace.
  This is
why IE and WSH have the

Comments (7)

  1. Jeff says:

    A great explanation, thanks. My problem was just finding the work-around of using <script src= or for ASP something like <!–#include file="utilites.asp"–>. Took a lot of fumbling around to just find the work-around.

  2. Dan Shappir says:

    Rather than blithely stating: "this problem can’t be solved, so we aren’t going to think about it anymore", a more positive approach would have been to think a bit outside the box (though not to far outside, as I will show).

    First, an #include for script would have been immensely useful. The current situation breaks encapsulation and results in brittle code. For example, since I can’t include from within a .js file, I need to rely on proper use by whomever is using that file. So, if a.js relies on b.js, there is no way for me to guarantee that both will be included, and in proper order. All I can do is document and hope for the best.

    Also, as you have shown, most every script host provides some sort of #include: the browser, ASP, WSF, Rhino. Unfortunately, each uses a very different syntax.

    True, the script engine can’t know how to fetch a file, but as shown above, most script hosts do. So, why couldn’t IActiveScriptSite define an Include method? Such a method could accept a string resource reference and return an IStream to it, or something like that. Since the host is invoked to construct the stream, it has complete control over the process.

  3. Eric Lippert says:

    That’s a _technically_ good idea, and one that we considered. But the problem is that this solution FORCES implementation of a new feature onto the HOST.

    As a solution provider, we must always ensure that the burden upon the host is as small as possible. And in particular, it is very unpopular to make forward-compatibility-impactful changes on binary interfaces — particularly changes which our largest customers (IE and IIS) do not even want or need!

    In software, anything is possible, but not everything is pragmatic. This turned out to be a feature that didn’t meet the bar.

  4. Dan Shappir says:

    Eric, I definitely agree that IActiveScriptSite cannot be changed to implement this functionality. You could define an IActiveScriptSite2 interface that the host optionally implements. Query for this interface and use it if it’s there. After all, COM/OLE provides a ton of mechanisms for host/component negotiation (that don’t require changing binary interfaces).

    With regard to that method, it could also be made optional with the host allowed to return something like E_NOTIMPL. This is something already allowed for some methods on IActiveScriptSite as I recall. The host should also be allowed to return some error value indicating security problems or issues like that.

    IE and IIS do provide their own mechanisms, but as I have shown, these mechanisms are somewhat flawed as a result of this deficiency. It would be very straightforward IMO to wire their existing download and activation services to such a facility.

    Anyway, all this is not to say that I don’t appreciate what Microsoft has done for scripting.

  5. Eric Lippert says:

    I think I’m not being clear with why its hard to do this. Sure, obviously the way to do it would be to add another site interface. If you look at the IDL files, we’ve got like a half dozen in there and I think the SE team just added another. We can add site interfaces all day, that’s easy enough.

    The problems are twofold. First, if the host has a mechanism for resolving include files, the host can surface that mechanism to the user without involving a callback, so why would the host do the work of implementing an unneeded feature? The whole point of having an #include mechanism is to have a standardized mechanism, but unless you force hosts to implement their end of the bargain, it isn’t standardized — and no host we talked to wanted to take that end of the bargain. So the feature would have failed before we even implemented it.

    Second, there are numerous technical difficulties which you have conveniently handwaved away. Correctly implementing such a beast is incredibly nontrivial in a language as complex and dynamic as JScript or VBScript. This isn’t the trivial little C preprocessor we’re talking about here.

    When does the include functionality run? When the script is run? Then we have to dynamically change the global namespace of an already compiled script. Doable, but there are issues. OK, then when the script is parsed? Then we need to rewrite the parser into a multi-pass parser. Again, doable. But how long would it take?

    Other problems surface. How do we detect circular includes? Remember, the host is responsible for implementing the include moniker semantics, so we would have to define ANOTHER callback, a highly complex one, that could determine whether a given set of nested includes constituted a circle or not. Now the hosts are even less likely to want this feature, but if they don’t do it, then someone writes a web page that includes itself and the process goes into an expensive recursive death spiral.

    And let’s not forget: how exactly does this feature interact with the script debugger? The script debugger allows hosts to specify the context in which a script was supplied. So now we need ANOTHER callback whereby the debugger can identify to the host the partial moniker used to include the file, in the event that the host wishes to show a context around that file.

    Etc, etc, etc. I could give you deep technical problems all night. Believe me, it gets to be a huge awful mess and we’d end up rewriting most of the parsing code and a lot of the debugger support.

    This is a complex, difficult, expensive feature that no host wanted or needed, and we only have a finite amount of people and time.

    Imagine you were faced with a choice: on the one hand, implement a complex, difficult, expensive feature that IE, ASP and WSH do not want or need so that developers have the incredibly trivial benefit of being able to type

    #include "foo.js"

    instead of

    <script src="foo.js">

    On the other hand, take the time that it would have taken to implement that feature and instead do a security review, review the documentation and sample code, fix memory leaks and other bugs, start planning the next version, etc.

    It’s a no-brainer. Making these tough choices is the only way that quality software ever gets shipped. There are no free features, and EVERY feature you implement takes time and brain cycles away from other features like security, performance, robustness, maintainability, documentation, etc. And those are all MUCH more important than a trivial syntactic sugar.

  6. Dan Shappir says:

    I definitely agree that any sensible software development shop needs to make sane, reasonable business choices about which features get included and which do not. A development team I am in charge of is in the process of finalizing a product release and I’m chopping features left and right in order to meet the release schedule. And this is not the first time I’ve had to do this.

    But lets not confuse business issue with technical ones. While it may not make business sense not to implement this feature, it does make technical sense to implement it.

    First, by your own admission in the original post, this is a feature that has been requested often: "People often ask me why there is no #include statement in VBScript or JScript". Thus you can’t say that nobody wants it. Even if host *implementers* do not want it, because it may add a bit to their workload and they feel they have provided an appropriate, though proprietary, alternative, it’s a question you should ask *people who use the host*. That is, not IE’s developers but people writing scripts for IE.

    Also, as I have pointed out. It could easily enough be made and optional feature that a host could implement or not.

    As for resolving resource location, you are correct that different hosts may implement different methods. But:
    1. I assume most Windows-based hosts would rely on IMoniker services so they would all pretty much work the same.
    2. I also assume that in most cases relative paths would be used, which make this pretty much a non-issue.
    3. Standardizing the mechanism is only one potential benefit. As I’ve shown there are others.

    I have not "hand waved away" the technical difficulties, I’ve ignored them 😉 After all, this is not my project and I don’t need to do the work or foot the bill. But I will point out the Rhino does provide such a mechanism (the load command), so apparently it is doable. And Rhino being open source, there was no one to pay the bill anyway.

    Must you really detect circular includes? A script writer does not need to do that in order to "jam" the script engine. She can simply write an infinite loop. And you have sensibly enough provided a mechanism to detect this a allow the user to suspend the script. Since the include command is a script command, the same mechanism should work in this case as well.

    With regard to the IE script snippet:
    1. Interestingly enough CSS does provide such a facility in the form of the @import property, despite providing an alternative mechanism through the <link> tag. Exactly the same scenario. Yes, I know JScript is more complex than CSS, but it’s the principal I’m talking about.
    2. An include statement would be redundant in an *inline* script, but would be immensely useful in an *external script file*. I’ve developed a JavaScript library that, because of its complexity, is broken up into several interdependent files. But because there is no include command, I can’t express this interdependency. Instead I must rely on the end user to include all the needed files in proper order (a Rhino user simply includes one file and gets everything).

    Bottom line, it is a useful feature IMO. It is also very doable (it has been done after all). It may be hard to do, and may not make business sense for you. Given your reply, I can see that it will probably never be done, unless it’s added to the ECMAScript standard. That’s OK, I didn’t expect it to.

    Eric, I didn’t mean to get on your case. As I’ve stated, I very much appreciate what you guys have done for scripting. I’ve used your tools in quit a number of cool applications. Isn’t it a typical end-user to always ask for more 😉

  7. estee says:

    Well, there is no ‘#include’.. That’s right, as for me–module inclusion it is too dependent on a host.. (At least when it comes to decide whether to create an instance of the IActiveScript interface for every such inclusion.. And as a result of positive decision must think how to combine 2 distinct execution contexts.. Ahhr, it is not me–a big specialist in all that matters, i’m just a user of one of such hosts that supports inclusion by custom ‘\USEUNIT myfile.js’ construct.. very suspicious design–it would be much better to exclude this functionality from the source completelly..)

    But after aLL, what is the purpose of @set, @cc_on, @if, etc., statements? Are we dealing with complete different meta-language over the core JS syntax? Or is it just another context for the same JS syntax?

Skip to main content