In computer science, bridging describes systems that map the runtime behaviour of different programming languages so they can share common resources. They are often used to allow "foreign" languages to operate a host platform's native object libraries, translating data and state across the two sides of the bridge. Bridging contrasts with "embedding" systems that allow limited interaction through a black box mechanism, where state sharing is limited or non-existent.
Functions, libraries and runtimes
Most programming languages include the concept of a subroutine or function, a mechanism that allows commonly used code to be encapsulated and re-used throughout a program. For instance, a program that makes heavy use of mathematics might need to perform the square root calculation on various numbers throughout the program, so this code might be isolated in a
sqrt(aNumber) function that is "passed in" the number to perform the square root calculation on, and "returns" the result. In many cases the code in question already exists, either implemented in hardware or as part of the underlying operating system the program runs within. In these cases the
sqrt function can be further simplified by calling the built-in code.
Functions often fall into easily identifiable groups of similar capabilities, mathematics functions for instance, or handling text files. Functions are often gathered together in collections known as libraries that are supplied with the system or, more commonly in the past, the programming language. Each language has its own method of calling functions so the libraries written for one language may not work with another; the semantics for calling functions in C is different from Pascal, so generally C programs cannot call Pascal libraries and vice versa. The commonly used solution to this problem is to pick one set of call semantics as the default system for the platform, and then have all programming languages conform to that standard.
Most computer languages and platforms have generally added functionality that cannot be expressed in the call/return model of the function. Garbage collection, for instance, runs throughout the lifetime of the application's run. This sort of functionality is effectively "outside" the program, it is present but not expressed directly in the program itself. Functions like these are generally implemented in ever-growing runtime systems, libraries that are compiled into programs but not necessarily visible within the code.
The introduction of shared library systems changed the model of conventional program construction considerably. In the past, library code was copied directly into programs by the "linker" and effectively became part of the program. With dynamic linking the library code (normally) exists in only one place, a vendor-provided file in the system that all applications share. Early systems presented many problems, often in performance terms, and shared libraries were largely isolated to particular languages or platforms, as opposed to the operating system as a whole. Many of these problems were addressed through the 1990s, and by the early 2000s most major platforms had switched to shared libraries as the primary interface to the entire system.
Although such systems addressed the problem of providing common code libraries for new applications, these systems generally added their own runtimes as well. This meant that the language, library, and now the entire system, were often tightly linked together. For instance, under OpenStep the entire operating system was, in effect, an Objective-C program. Any programs running on it that wished to use the extensive object suite provided in OpenStep would not only have to be able to call those libraries using Obj-C semantics, but also interact with the Obj-C runtime to provide basic control over the application.
In contrast, Microsoft's .NET Framework was designed from the start to be able to support multiple languages, initially C#, C++ and a new version of Visual Basic. To do this, MS isolated the object libraries and the runtime into the Common Language Infrastructure (CLI). Instead of programs compiling directly from the source code to the underlying runtime format, as is the case in most languages, under the CLI model all languages are first compiled to the Common Intermediate Language (CIL), which then calls into the Common Language Runtime (CLR). In theory, any programming language can use the CLI system and use .NET objects.
Although platforms like OSX and .NET offer the ability for most programming languages to be adapted to the platform's runtime system, it is also the case that these programming languages often have a target runtime in mind - Objective-C essentially requires the Obj-C runtime, while C# does the same for the CLR. If one wants to use C# code within Obj-C, or vice versa, one has to find a version written to use the other runtime, which often does not exist.
A more common version of this problem concerns the use of languages that are platform independent, like Java, which have their own runtimes and libraries. Although it is possible to build a Java compiler that calls the underlying system, like J#, such a system would not also be able to interact with other Java code unless it too was re-compiled. Access to code in Java libraries may be difficult or impossible.
In these cases, and many like it, the need arises for a system that allows the two runtimes to interoperate. This is known as "bridging" the runtimes.
Apple has made considerable use of bridging technologies since the earliest efforts that led to Mac OS X.
When NeXT was first purchased by Apple, the plan was to build a new version of OpenStep, then-known as Rhapsody, with an emulator known as a Blue Box that would run "classic" Mac OS programs. This led to considerable push-back from the developer community, and Rhapsody was cancelled. In its place, OS X would implement many of the older Mac OS calls on top of core functionality in OpenStep, providing a path for existing applications to be gracefully migrated forward.
To do this, Apple took useful code from the OpenStep platform and re-implemented the core functionality in a pure-C library known as Core Foundation, or CF for short. OpenStep's libraries calling CF underlying code became the Cocoa API, while the new Mac-like C libraries became the Carbon API. As the C and Obj-C sides of the system needed to share data, and the data on the Obj-C side was normally stored in objects (as opposed to base types), conversions to and from CF could be expensive. Apple was not willing to pay this performance penalty, so they implemented a scheme known as "toll-free bridging" to help reduce or eliminate this problem.
At the time, Java was becoming a major player in the programming world, and Apple also provided a Java bridging solution that was developed for the WebObjects platform. This was a more classical bridging solution, with direct conversions between Java and OpenStep/CF types being completed in code, where required. Under Carbon, a program using CFStrings was using the same code as a Cocoa application using NSString, and the two could be bridged toll-free. With the Java bridge, CFStrings were instead cast into Java's own String objects, which required more work but made porting essentially invisible. Other developers made widespread use of similar technologies to provide support for other languages, including the "peering" system used to allow Obj-C code to call .NET code under Mono.
As the need for these porting solutions waned, both Carbon and the Java Bridge were deprecated and eventually removed from later releases of the system. Java support was migrated to using the Java Native Interface (JNI), a standard from the Java world that allowed Java to interact with C-based code. On OSX, the JNI allowed Obj-C code to be used, with some difficulty.
Although there are some examples of bridging being used in the past, Microsoft's CLI system was intended to support languages on top of the .NET system rather than running under native runtimes and bridging. This led to a number of new languages being implemented in the CLI system, often including either a hash mark (#) or "Iron" in their name. See the List of CLI languages for a more comprehensive set of examples. This concept was seen as an example of MS's embrace, extend and extinguish behaviour, as it produced Java-like languages (C# and J# for instance) that did not work with other Java code or used their libraries.
Nevertheless, the "classic" Windows ecosystem included considerable code that would be needed to be used within the .NET world, and for this role MS introduced a well supported bridging system. The system included numerous utilities and language features to ease the use of Windows or Visual Basic code within the .NET system, or vice versa.
- Dave Winer, "Rhapsody Cancelled", 12 May 1998
- "Toll-Free Bridged Types", Apple, 19 September 2012
- "Using the Java Bridge", Apple
- Andrew Youll, "Apple Drops the Cocoa-Java Bridge", OSNews, 10 July 2005
- "Carbon Core Deprecations", Apple, 23 July 2013
- "Technical Note TN2147: JNI Development on Mac OS X", Apple, 14 July 2011
- Jason Clark, "Calling A .NET Managed Method from Native Code", MSDN Magazine
- "Calling A .NET Managed Method from Native Code", Microsoft
- "HTML Bridge: Interaction Between HTML and Managed Code", Microsoft
- "Using the HTML Bridge", Microsoft