HomeForumSourceResearchGuide
Sign in to reply to forum posts.
RuntimeError while using adaptation API on wasm SDK

I'm currently using the dana wasm SDK in a project and ran into an issue with the adaptation API while following the example from the runtime adaptation guide:https://www.projectdana.com/dana/guide/runtime_adaptation

I'm using the RecursiveLoader as described here:https://www.projectdana.com/dana/guide/dynamic_loading

The relevant part of my code is:

LoadedComponents lc1 = loader.load("Main.o")

IDC loopComponent = lc1.mainComponent

LoadedComponents lc3 = loader.load("ListProxy.o")

IDC remoteList = lc3.mainComponent

MainLoop loop = new MainLoop() from loopComponent

MainLoop requires the List interface, and both List.o and ListProxy.o provide List.

The initial composition using List.o works correctly. However, when I try to replace the current List implementation with ListProxy.o:

adapt.adaptRequiredInterface(loopComponent, "List", remoteList)

I get the following runtime error:

Uncaught RuntimeError: table index is out of bounds

followed by a stack trace in dana.wasm.

I also tried leaving AdaptEvents:inactive() and AdaptEvents:active() empty, but the same error still occurs, so the issue seems to happen during the adaptation itself rather than in those callbacks.

I'm wondering whether there is any limitation or missing step when using adaptRequiredInterface() with the WASM SDK, or whether this is expected to work the same way as in the native runtime.

Thanks!

Hi Matheus,

Thanks for the query; you're correct in thinking that this should work the same way natively and in WASM. We'll take a look at this soon to see if we can replicate it from your description.

Barry

Hi Barry,

To help you replicate the issue, I'll give you a bit more context.

The project is based on the "Example game UI" from the WASM SDK guide. The main difference is that my UI is rendered in JavaScript/React instead of using the Dana UI components, so I use the virtual filesystem to communicate between JavaScript and Dana.

The App.dn file is kept the same as in the example, and RenderApp.dn is as follows:

uses MainLoop

component provides RenderApp requires io.Output out,  io.File, io.FileSystem fs,ui.FlowRender, 
ui.FlowCanvas, ui.FlowFont, time.Timer timer, data.IntUtil iu,composition.RecursiveLoader loader, composition.Adapt adapt  {




  RenderApp:RenderApp() {

  }
  
   LoadedComponents lc1 = loader.load("Main.o") 
   IDC loopComponent = lc1.mainComponent

   //LoadedComponents lc2 = loader.load("List.o") 
   //IDC localList = lc2.mainComponent
   
   LoadedComponents lc3 = loader.load("ListProxy.o")
   IDC remoteList = lc3.mainComponent 

  MainLoop loop = new MainLoop() from loopComponent

 


   bool RenderApp:loop() {
    
    
    char relocationRequest[] = "Relocation.IReq"
    

    if(fs.exists(relocationRequest)){
  
      fs.delete(relocationRequest)
      adapt.adaptRequiredInterface(loopComponent, "List", remoteList)
     
    }



    loop.loop()
		
    timer.sleep(5)

    return true
  }
}

Hope this helps with reproducing the issue, and thanks for taking a look at it!

Hi Matheus,

We've fixed a compiler bug for a potential edge case around dynamic object instantiation, which might be related to what you're seeing. Otherwise the adaptation element itself seems to work in our simple test.

The fix only applies to the desktop version of Dana (not the WASM SDK), so you'll need to update that. You can either do this by going to the components directory of your Dana install and typing:

dana source update

Or you can grab the complete fresh download package which includes this update.

Here is our complete test, with all code relative to the root of the WASM SDK:

In a sub-directory resources:

File RenderApp.dn:

interface RenderApp extends lang.ProcessLoop {
	RenderApp()
	}

File Thing.dn:

interface Thing {
	Thing()
	void do()
	}

In the root directory:

File App.dn:

component provides App requires io.Output out, System system, RenderApp {
	int App:main(AppParam params[]) {
		system.setProcessLoop(new RenderApp())
		return 0
		}
}

File RenderApp.dn:

uses Thing

component provides RenderApp requires io.Output out, ui.FlowRender, ui.FlowCanvas, ui.FlowFont, time.Timer timer, composition.RecursiveLoader rloader, Thing, composition.Adapt adaptor {
	FlowRender window
	FlowCanvas canvas

	IDC main
	IDC listA
	IDC listB
	bool cycle

	Thing mainObject
	
	RenderApp:RenderApp() {
		window = new FlowRender(60)
		window.setSize(400, 200)
		canvas = new FlowCanvas(window)

		window.setTitle("Test")
		window.setVisible(true)

		main = rloader.load("Main.o").mainComponent
		listA = rloader.load("components/data/adt/List.o").mainComponent
		listB = rloader.load("components/data/adt/List.o").mainComponent
		mainObject = new Thing() from main
		}
	
	bool processEvents(FlowEvent events[])
		{
		bool quit = false
		for (int i = 0; i < events.arrayLength; i++) {
			if (events[i].type == FlowEvent.T_QUIT)
				quit = true
			}
		
		return quit
		}
	
	bool RenderApp:loop() {
		bool quit = false

		FlowEvent events[] = window.getEvents()

		window.renderBegin()
		window.renderEnd()

		quit = processEvents(events)
		
		timer.sleep(5)

		out.println("adapting ...")
		if (!cycle)
			adaptor.adaptRequiredInterface(main, "data.adt.List", listB)
			else
			adaptor.adaptRequiredInterface(main, "data.adt.List", listA)
		
		mainObject.do()
		
		cycle = !cycle

		return !quit
		}
}

File Main.dn:

uses data.String

component provides Thing requires io.Output out, data.adt.List {

	List list = null

	Thing:Thing() {
		list = new List()
		list.add(new String("hi"))
		}

	void Thing:do() {
		for (Data d = list.getFirst(); d != null; d = list.getNext()) {
			String s = d
			out.println("item: $(s.string)")
			}
		}

	}

We build the project with:

dnc App.dn -os ubc -chip 32
dnc RenderApp.dn -os ubc -chip 32
dnc Main.dn -os ubc -chip 32
dana app.empack App.o RenderApp.o Main.o components -o file_system.js

Then run:

dana ws.core

And open the page:

http://localhost:8080/xdana.html

If the above example works for you, but your other project doesn't, I think we'll need a more specific example to try to reproduce what you're seeing.

Hope that helps!

Barry

I just noticed you posted extra detail at almost the same time as me. It looks like we're doing very similar things, so if your example still doesn't work, feel free to post all of the code somewhere so we can replicate it exactly.

Barry

Hi Barry,

After updating the project, everything is working as expected. Thanks for looking into it and for fixing it so quickly!