User Tools

Site Tools


dynamic-loading

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision Both sides next revision
dynamic-loading [2017/09/27 05:16]
barryfp
dynamic-loading [2018/02/09 04:45]
barryfp Adding search tutorial
Line 121: Line 121:
  
 The RecursiveLoader is an open-source component in Dana's standard library so you can see for yourself how it works. The RecursiveLoader is an open-source component in Dana's standard library so you can see for yourself how it works.
 +
 +====== Searching for possible implementations ======
 +
 +Dana is designed to support seamless adaptation between multiple different implementations of the same interface. The standard library has a set of APIs to support easy search for implementations as follows.
 +
 +Let's imagine that you want to load a component in the file ''​MyComponent.o'';​ we might do this using:
 +
 +<code d>
 +component provides App requires io.Output out, Loader loader {
 +
 + int App:​main(AppParam params[])
 + {
 + IDC com = loader.load("​MyComponent.o"​)
 +
 + return 0
 + }
 + }
 +</​code>​
 +
 +We'd then like to look at the required interfaces of this component (i.e., it dependencies) and search for any components that we can use to fulfill those dependencies. With the aid of a helper function to extract required interfaces from a compiled component, we can use a combination of ''​composition.ObjectWriter'',​ ''​data.JSON.JSONParser'',​ and ''​composition.Search''​ to quickly find all alternatives:​
 +
 +<code d>
 +data ReqIntf {
 + char package[]
 + char alias[]
 + }
 +
 +component provides App requires io.Output out, Loader loader,
 +                       ​composition.ObjectWriter,​ composition.Search search, data.JSON.JSONParser parser {
 +
 + ReqIntf[] getRequiredInterfaces(char com[])
 + {
 + ReqIntf result[]
 + ObjectWriter reader = new ObjectWriter(com)
 + InfoSection section = reader.getInfoSection("​DNIL",​ "​json"​)
 + JSONElement document = parser.parseDocument(section.content)
 + JSONElement requiredInterfaces = parser.getValue(document,​ "​requiredInterfaces"​)
 +
 + if (requiredInterfaces != null)
 + {
 + for (int i = 0; i < requiredInterfaces.children.arrayLength;​ i++)
 + {
 + JSONElement ri = requiredInterfaces.children[i]
 + char package[] = parser.getValue(ri,​ "​package"​).value
 + char alias[] = parser.getValue(ri,​ "​alias"​).value
 + result = new ReqIntf[](result,​ new ReqIntf(package,​ alias))
 + }
 + }
 +
 + return result
 + }
 +
 + int App:​main(AppParam params[])
 + {
 + IDC com = loader.load("​MyComponent.o"​)
 +                ReqIntf dependencies[] = getRequiredInterfaces("​MyComponent.o"​)
 +                ​
 +                for (int i = 0; i < dependencies.arrayLength;​ i++)
 +                   {
 +                   ​String results[] = search.getComponents(dependencies[i].package)
 +                   }
 +
 + return 0
 + }
 + }
 +</​code>​
 +
 +Our helper function uses a feature of Dana's compiled component file format, in which we store meta data in "​information sections"​ of the file. Dana uses a standard information section named "​DNIL"​ which lists the interfaces that are both provided and required by the component, represented in a JSON structure. Interfaces in this structure have a "​package"​ and an "​alias"​ along with structured type information;​ the package is the full path to the interface, while the alias is the name by which we can dynamically query the interface on the component. Internally, Dana does not care whether the package or alias of a required and provided interface match up, instead only checking that the two interface types are structurally compatible.
 +
 +In the main method, the array ''​results''​ now contains a list of relative file paths to components that implement each required interface, searching both locally and in the standard library. You can also use the ''​composition.Search''​ API to search in specific directories.
 +
 +Now that we've found some options, we can wire each dependency up to the first option on this list, by loading the component that we'd like to connect to and then using ''​dana.rewire'':​
 +
 +<code d>
 +data ReqIntf {
 + char package[]
 + char alias[]
 + }
 +
 +component provides App requires io.Output out, Loader loader,
 +                       ​composition.ObjectWriter,​ composition.Search search, data.JSON.JSONParser parser {
 +
 + ReqIntf[] getRequiredInterfaces(char com[]) {...}
 +
 + int App:​main(AppParam params[])
 + {
 + IDC com = loader.load("​MyComponent.o"​)
 +                ReqIntf dependencies[] = getRequiredInterfaces("​MyComponent.o"​)
 +                ​
 +                for (int i = 0; i < dependencies.arrayLength;​ i++)
 +                   {
 +                   ​String results[] = search.getComponents(dependencies[i].package)
 +                   
 +                   IDC ncom = loader.load(results[i].string)
 +                   ​dana.rewire(com :> dependencies[i].alias,​ ncom :< dependencies[i].alias)
 +                   }
 +
 + return 0
 + }
 + }
 +</​code>​
 +
 +We can later use runtime adaptation to adapt these wirings to point to different components while the program is running. To build a complete system we could also need to recursively apply the above procedure to the components chosen to satisfy dependencies as well, wiring up //their// dependencies and so on.
dynamic-loading.txt ยท Last modified: 2019/07/24 13:17 by barryfp