ContextFactory.observeInstructionCount() not always called consistently in interpreted mode

RESOLVED WORKSFORME

Status

RESOLVED WORKSFORME
10 years ago
10 years ago

People

(Reporter: davidparks21, Assigned: norrisboyd)

Tracking

Details

(Reporter)

Description

10 years ago
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3
Build Identifier: 1.7R2-RC1

It is possible for scripts to execute indefinitely while never calling the observeInstructionCount method when running in interpreted mode.

This script is one such case:

var i = 0; function x(){ while (true) i++; } x();

Reproducible: Always

Steps to Reproduce:
I made a simple update to ObserveInstructionCountTest to demonstrate the issue.

Replace line 74 with the following line:
var i = 0; function x(){ while (true) i++; } x();

Copy and paste source code is also included below, simply run this TestCase. The only change from the included ObserveInstructionCount class is the input script (line 74) in which I wrapped a function around the while(true) i++; statement.

_________________________________________

/**
 * 
 */
package org.mozilla.javascript.tests;

import junit.framework.TestCase;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Callable;

/**
 * @author Norris Boyd
 */
public class ObserveInstructionCountTest_Fail extends TestCase {
    // Custom Context to store execution time.
    static class MyContext extends Context {
        MyContext(ContextFactory factory) {
            super(factory);
        }
        int quota;
    }

    static class QuotaExceeded extends RuntimeException {
    }

    static {
        ContextFactory.initGlobal(new MyFactory());
    }

    static class MyFactory extends ContextFactory {

        @Override
        protected Context makeContext()
        {
            MyContext cx = new MyContext(this);
            // Make Rhino runtime call observeInstructionCount
            // each 500 bytecode instructions (if we're really enforcing
            // a quota of 2000, we could set this closer to 2000)
            cx.setInstructionObserverThreshold(500);
            return cx;
        }

        @Override
        protected void observeInstructionCount(Context cx, int instructionCount)
        {
            MyContext mcx = (MyContext)cx;
            mcx.quota -= instructionCount;
            if (mcx.quota <= 0) {
                throw new QuotaExceeded();
            }
        }

        @Override
        protected Object doTopCall(Callable callable,
                                   Context cx, Scriptable scope,
                                   Scriptable thisObj, Object[] args)
        {
            MyContext mcx = (MyContext)cx;
            mcx.quota = 2000;
            return super.doTopCall(callable, cx, scope, thisObj, args);
        }
    }

    private void baseCase(int optimizationLevel) {
        ContextFactory factory = new MyFactory();
        Context cx = factory.enterContext();
        cx.setOptimizationLevel(optimizationLevel);
        assertTrue(cx instanceof MyContext);
        try {
            Scriptable globalScope = cx.initStandardObjects();
            cx.evaluateString(globalScope,
                    "var i = 0; function x(){ while (true) i++; } x();",        //This line is the only update to the original test case
//                    "var i = 0; while (true) i++;",                           //Original test case code
                    "test source", 1, null);
            fail();
        } catch (QuotaExceeded e) {
            // expected
        } catch (RuntimeException e) {
            fail(e.toString());
        } finally {
            Context.exit();
        }
    }

    public void testInterpreted() {
        baseCase(-1); // interpreted mode
    }

    public void testCompiled() {
        baseCase(1); // compiled mode
    }
 }
Actual Results:  
Script will execute indefinitely even though the test case is set up to exit after a certain # of instructions.

Expected Results:  
The only difference in this test case is the input script. This test case should function just like ObserveInstructionCountTest and exit the script after counting ~2000 instructions.

Note: compiled mode works as expected, only when the optimization level is set to -1 does it fail.

Perhaps I don't understand the limitations of this function, however based on the javadocs example (essentially the same as the test case), I don't believe an end user would be able to know that this function has limitations in interpreted mode.

If these limitations are part of the design then they should be documented.
(Assignee)

Updated

10 years ago
Assignee: nobody → norrisboyd
(Assignee)

Comment 1

10 years ago
I can't reproduce this problem. When I change the test case as this bug describes, the test still executes successfully. This is using the latest CVS source. 

I'll check in a new version of the unit test that tests both the current and new cases.
(Assignee)

Comment 2

10 years ago
Marking WORKSFORME.
Status: UNCONFIRMED → RESOLVED
Last Resolved: 10 years ago
Resolution: --- → WORKSFORME
You need to log in before you can comment on or make changes to this bug.