/*
 * Helma License Notice
 *
 * The contents of this file are subject to the Helma License
 * Version 2.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://adele.helma.org/download/helma/license.txt
 *
 * Copyright 2017 Daniel Ruthardt. All rights reserved.
 */

package helma.scripting.rhino;

import java.net.URI;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.commonjs.module.ModuleScript;
import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProvider;
import org.mozilla.javascript.commonjs.module.provider.StrongCachingModuleScriptProvider;

/**
 * Provides module scripts without compiling, should compiling not be possible for whatever reason.
 * The main reason being targeted though, is the "generated bytecode for method exceeds 64K limit" issue.
 */
public class CompiledOrInterpretedModuleScriptProvider extends StrongCachingModuleScriptProvider {

    /**
     * Define the serialization UID.
     */
    private static final long serialVersionUID = 1170789670529274963L;

    /**
     * Delegates to the super constructor.
     */
    public CompiledOrInterpretedModuleScriptProvider(ModuleSourceProvider moduleSourceProvider) {
        // do what would have been done anyways
        super(moduleSourceProvider);
    }

    @Override
    public ModuleScript getModuleScript(Context cx, String moduleId, URI moduleUri, URI baseUri, Scriptable paths) throws Exception {
        try {
            // try to load the module script with whatever optimization level is set for the application
            return super.getModuleScript(cx, moduleId, moduleUri, baseUri, paths);
        } catch (EvaluatorException ignore) {
            // unlikely, but possible exception during loading the module script without compilation
            Exception exception;
            // get the application's optimization level
            int optimizationLevel = cx.getOptimizationLevel();
            
            try {
                // set the optimization level to not compile, but interpret
                cx.setOptimizationLevel(-1);
                // load the module script with the newly set optimization level
                ModuleScript moduleScript = super.getModuleScript(cx, moduleId, moduleUri, baseUri, paths);
                // return the module script
                return moduleScript;
            } catch (Exception e) {
                // remember the exception
                exception = e;
            } finally {
                // re-set the optimization
                cx.setOptimizationLevel(optimizationLevel);
            }
            
            // re-throw the exception catched when trying to load the module script without compilation 
            throw exception;
        }
    }

}