Closed Bug 181834 Opened 22 years ago Closed 22 years ago

wrong scope used for inner functions when compiling functions with dynamic scopes (interpreted only)

Categories

(Rhino Graveyard :: Core, defect)

defect
Not set
normal

Tracking

(Not tracked)

VERIFIED FIXED

People

(Reporter: felix.meschberger, Assigned: norrisboyd)

References

Details

Attachments

(1 file, 3 obsolete files)

User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.2b) Gecko/20021016
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.2b) Gecko/20021016

i fear, i found a scoping problem with rhino related to the 'compile functions
with dynamic scope' feature. given the code :

	function outer( /* String */ arg ) {
        var outer_d = 0;
	  function inner( /* int */ level ) {
          outer_d++;
          if (level > 0) {
            inner( level - 1 );
          } else {
            return outer_d;
          }
        }
        return inner(5);
      }
      var result = outer("arg");

should set the result variable to 6, but this is what happens (where o is the
optimization level and d is the dynamic function scope flag) :

	o >= 0 && d     : result == 6
	o >= 0 && !d    : result == 6
	o == -1 && d    : ReferenceError: "outer_d" is not defined.
	o == -1 && !d   : result == 6

so it seems, as long as the code is compiled to a class file, execution works
all right. but if the code is interpreted, something goes wrong with the scopes.

by inspecting the code (Interpreter.java), i found out, that inner functions are
compiled with the context's compileFunctionsWithDynamicScope flag. in the
dynamic case, this of course is the scope within which the outer function is
running instead of the variable object of the outer function, in which the inner
function is defined and obviously should be running.

i assume, inner functions should always be compiled with static scope regardless
of whether the context is set for dynamic scope compilations of functions or not.

if i am correct, then the Interpreter.generateNestedFunctions method should
probably be added the line

	            jsi.itsData.itsUseDynamicScope = false;

after creating the InterpreterData instance.

i tried it with the above sample code and it seems to fix the problem. though i
am not sure, whether it runs fine with more thorough regression tests.

what do you think of it ?

regards, felix

Reproducible: Always

Steps to Reproduce:
1. make Context interpreting (optimizationLevel = -1)
2. make Context "compile" functions with dynamic scope
3. run the above script and see the error noted using the shell tool.

Actual Results:  
error message : ReferenceError: "outer_d" is not defined.

Expected Results:  
the variable should be set to 6.
cc-ing igor.
A possible fix can be rather nontrivial as it has to address the issue of
functions defined by eval and new Function.

For example, int the above example should not give different results if 
function inner would be replaced by

inner = new Function("level", "outer_d++; ...");

so setting itsUseDynamicScope should depend on if the scope during compilation
time is suitable for dynamic behaviour... 
I have a couple questions about the example:

1. The parameter |arg| to the outer function plays no role
   and can be omitted, right?

2. Shouldn't there be a |return| statement here?

         if (level > 0) {
            inner( level - 1 );   <<<---- should be |return inner(level -1);|  (?)
(1) yes, the argument arg to the outer function plays no role and the argument
level to the inner function is only used to limit recursion depth.

(2) hmpf, yes, of course the line should read
        return inner(level-1);
Felix: thanks. I've added your test to the JS testsuite:

      mozilla/js/tests/js1_5/Scope/regress-181834.js
This is related to bug 154693 where the problem is exposed without activation of
dynamic scope. 
Depends on: 154693
The patch changes Interpreter to ignore dynamic scope flag for nested functions
and functions defined inside with statements as in the first case a dynamic
scope object should already present on the scope chain of a nested function and
in the second case there is no good way to use a dynamic scope. For example,
consider a script compiled with dynamic scope on:

var f;

with (some_object) {
  f = function() { ... }
}

here f contains a function with some_object as its scope object. But if a
dynamic scope object should be taken into account when resolving a particular
reference during execution of the function, how should it interfere with
some_object? The patch takes a simple approach of ignoring dynamic scope in
this case as anything else would add a lot of code which nobody may use...
Attachment #108605 - Attachment is obsolete: true
Question: is there any way for me to test this in the Rhino shell?

Would the testcase above, mozilla/js/tests/js1_5/Scope/regress-181834.js,
have to include code to turn on the 'compile functions with dynamic scope'
feature? Is that possible?
I committed the patch part that check for dynamic scope flag at function
initialization, not during compilation phase. It removes difference between
ordinary functions and functions defined via Function constructor.
Attachment #108765 - Attachment is obsolete: true
In Rhino it is possible to check/switch to dynamic scope mode from JavaScript via:

function setDynamicScope(flag) {
	if (this.Packages) {
		var cx = this.Packages.org.mozilla.javascript.Context.getCurrentContext();
		cx.setCompileFunctionsWithDynamicScope(flag); 
	}
}


function getDynamicScope() {
	if (this.Packages) {
		var cx = this.Packages.org.mozilla.javascript.Context.getCurrentContext();
		return cx.hasCompileFunctionsWithDynamicScope(); 
	}
}

but it would not affect function statements in the current script and prior that
partial commit of the patch function expressions after switch to the dynamic
scope mode are not affected either. Thus one has to either use Function, eval or
call load on another script to see the difference.
With more commits patch became ratjer small and readable but still it does not
reduce its consiquences.
Attachment #109102 - Attachment is obsolete: true
Igor: thanks. I have added functionality from your Comment #11 
to the testcase above, to set and unset the 'compile functions
with dynamic scope' feature of Rhino. 

I use eval() to recompile the |outer| function each time
I set or unset this feature.

With the latest build of Rhino, the testcase fails in both interpreted
and compiled modes with the error that Felix reported above:

         ReferenceError: "outer_d" is not defined
Note for the test cases: in Rhino eval and new Function always use the
interpreted mode, so the compilation mode under dynamic scope setup can not be
tested that way.

I commited the fix so the bug can be marked as fixed.
Marking FIXED -
Status: NEW → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Marking Verified Fixed.

The above testcase now passes in Rhino. As Igor indicated in Comment #14, 
however, this only exercises Rhino interpreted mode. To check Rhino
compiled mode, I tested interactively as follows:

 java org.mozilla.javascript.tools.shell.Main  -f file1.js -f file2.js

where "file1.js" sets the dynamic mode and "file2.js" contains the test.


We found that Rhino compiled mode passed this test both before and after
the patch was committed. As reported above, it was only interpreted mode
that was having a problem on this.

But that's fixed now -
Status: RESOLVED → VERIFIED
thanks very much too all.
Targeting as resolved against 1.5R4
Target Milestone: --- → 1.5R4
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: