Search code examples
javarhinocontinuations

Is it possible to resume a continuation in Rhino multiple times?


I want to be able to capture a continuation and resume it several times, such that each such invocation would be independent of the others.

For example, in the following code, I'd want the 2 calls to context.resumeContinuation in the run method to result in the output: 1 1, rather than the current output of 1 2.

As far as I understand, the reason for the resulting output is that I always use the same scope object, which is being modified by the first continuation before being passed to the second one. So it seems that I should resume each continuation with a copy of the original scope, but type Scriptable has no clone method (or anything equivalent), and copying it using serialization/deserialization doesn't help either.

P.S. I am using Rhino version 1.7R5.

Example.java:

import org.mozilla.javascript.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Example {
    public void run() throws IOException {
        Context context = Context.enter();
        context.setOptimizationLevel(-2);   // Use interpreter mode.
        Scriptable scope = context.initStandardObjects();
        scope.put("javaProxy", scope, Context.javaToJS(this, scope));

        Object capturedContinuation = null;
        try {
            String scriptSource =
                new String(Files.readAllBytes(Paths.get("example.js")));

            String scriptName = "example";
            int startLine = 1;
            Object securityDomain = null;
            Script script =
                context.compileString(scriptSource, scriptName, startLine, securityDomain);
            context.executeScriptWithContinuations(script, scope);
        } catch (ContinuationPending continuationPending) {
            capturedContinuation = continuationPending.getContinuation();
        }

        Object result = "";

        context.resumeContinuation(capturedContinuation, scope, result);
        context.resumeContinuation(capturedContinuation, scope, result);

        Context.exit();
    }

    public void captureContinuation() {
        Context context = Context.enter();
        ContinuationPending continuationPending =
            context.captureContinuation();
        Context.exit();
        throw continuationPending;
    }

    public void print(int i) {
        System.out.print(i + " ");
    }

    public static void main(String[] args) throws IOException {
        new Example().run();
    }
}

example.js:

var i = 1;
javaProxy.captureContinuation();
javaProxy.print(i);
i = i + 1;

Solution

  • So I came up with a working solution:

    Instead of copying the scope object, I should've copied the capturedContinuation object, so the 2 calls to resumeContinuation would be:

    context.resumeContinuation(deepCopy(capturedContinuation), scope, result);
    context.resumeContinuation(deepCopy(capturedContinuation), scope, result);
    

    This question offers possible imeplementations of the deepCopy method.

    A word of caution, though: instances of Rhino's NativeContinuation type (which is the dynamic type of capturedContinuation in the code above) seem to be quite large (~15KB and up when serialized to a byte array), so time/memory consumption implications should be considered when deep copying them in an application.