Fix following bug:

Subject:
             Re: Rhino: [[DefaultValue]] missing for Call object
 Resent-Date:
             Mon, 2 Jul 2001 08:52:07 -0700 (PDT)
 Resent-From:
             mozilla-jseng@mozilla.org
        Date:
             Mon, 02 Jul 2001 11:49:59 -0400
        From:
             Norris Boyd <nboyd@atg.com>
 Organization:
             Art Technology Group
          To:
             Christopher Oliver <coliver@mminternet.com>
         CC:
             mozilla-jseng@mozilla.org
  References:
             1




I believe the correct result of the script should be

[object global]
[object Object]
[object global]

The activation object (which goes by the name of "Call" for historical
reasons) should never be the 'this' value in a function call. See "10.1.6
Activation Object" in the ECMA spec.

I'll look at fixing the problem for Rhino. If there's agreement on my
analysis, someone should fix this for Spidermonkey too.

--N

Christopher Oliver wrote:

> Hi,
>
> function a() {
>     function b() {
>          print(this);
>     }
>     this.f = function() {
>          print(this);
>          b();
>     }
>     b();
> }
>
> var a = new a();
> a.f();
>
> Running the above script with SpiderMonkey produces:
>
> [object global]
> [object Object]
> [object Call]
>
> Running with Rhino produces the following exception:
>
> uncaught JavaScript exception: undefined: Cannot find default value for
> object. (line 3)
>
> This is due to a bug in org.mozilla.javascript.NativeCall which doesn't
> implement toString or valueOf or override getDefaultValue.
> However, even after I hacked in an implementation of getDefaultValue in
> NativeCall, Rhino still produces a different result then spidermonkey:
>
> [object Call]
> [object Object]
> [object Call]
This commit is contained in:
nboyd%atg.com 2001-07-03 02:19:51 +00:00
parent b9b3076a0d
commit 61f8164eba
6 changed files with 39 additions and 7 deletions

View File

@ -61,6 +61,14 @@ public class FunctionNode extends Node {
return itsNeedsActivation = b;
}
public boolean getCheckThis() {
return itsCheckThis;
}
public void setCheckThis(boolean b) {
itsCheckThis = b;
}
/**
* There are three types of functions that can be defined. The first
* is a function statement. This is a function appearing as a top-level
@ -90,5 +98,6 @@ public class FunctionNode extends Node {
protected VariableTable itsVariableTable;
protected boolean itsNeedsActivation;
protected boolean itsCheckThis;
protected byte itsFunctionType;
}

View File

@ -81,6 +81,9 @@ class InterpretedFunction extends NativeFunction implements DebuggableScript {
scope = itsClosure;
else if (!itsData.itsUseDynamicScope)
scope = getParentScope();
if (itsData.itsCheckThis)
thisObj = ScriptRuntime.getThis(thisObj);
if (itsData.itsNeedsActivation) {
scope = ScriptRuntime.initVarObj(cx, scope, this, thisObj, args);

View File

@ -66,7 +66,7 @@ public class Interpreter extends LabelTable {
{
version = cx.getLanguageVersion();
itsData = new InterpreterData(0, 0, 0, securityDomain,
cx.hasCompileFunctionsWithDynamicScope());
cx.hasCompileFunctionsWithDynamicScope(), false);
if (tree instanceof FunctionNode) {
FunctionNode f = (FunctionNode) tree;
InterpretedFunction result =
@ -153,7 +153,8 @@ public class Interpreter extends LabelTable {
Interpreter jsi = new Interpreter();
jsi.itsSourceFile = itsSourceFile;
jsi.itsData = new InterpreterData(0, 0, 0, securityDomain,
cx.hasCompileFunctionsWithDynamicScope());
cx.hasCompileFunctionsWithDynamicScope(),
def.getCheckThis());
jsi.itsData.itsFunctionType = def.getFunctionType();
jsi.itsInFunctionFlag = true;
jsi.debugSource = debugSource;

View File

@ -46,7 +46,7 @@ class InterpreterData {
InterpreterData(int lastICodeTop, int lastStringTableIndex,
int lastNumberTableIndex, Object securityDomain,
boolean useDynamicScope)
boolean useDynamicScope, boolean checkThis)
{
itsICodeTop = lastICodeTop == 0
? INITIAL_MAX_ICODE_LENGTH
@ -62,6 +62,7 @@ class InterpreterData {
: lastNumberTableIndex * 2];
itsUseDynamicScope = useDynamicScope;
itsCheckThis = checkThis;
if (securityDomain == null)
Context.checkSecurityDomainRequired();
this.securityDomain = securityDomain;
@ -108,6 +109,7 @@ class InterpreterData {
boolean itsNeedsActivation;
boolean itsFromEvalCode;
boolean itsUseDynamicScope;
boolean itsCheckThis;
byte itsFunctionType;
String[] itsStringTable;

View File

@ -101,12 +101,18 @@ public class NodeTransformer {
}
} else {
if (inFunction) {
// Nested functions require activation objects.
((FunctionNode) tree).setRequiresActivation(true);
}
FunctionNode fnNode = (FunctionNode)
node.getProp(Node.FUNCTION_PROP);
if (inFunction) {
// Functions containing other functions require
// activation objects
((FunctionNode) tree).setRequiresActivation(true);
// Nested functions must check their 'this' value to
// insure it is not an activation object:
// see 10.1.6 Activation Object
fnNode.setCheckThis(true);
}
addParameters(fnNode);
NodeTransformer inner = newInstance();
fnNode = (FunctionNode)

View File

@ -1430,6 +1430,17 @@ public class Codegen extends Interpreter {
}
}
if (inFunction && ((OptFunctionNode)tree).getCheckThis()) {
// Nested functions must check their 'this' value to
// insure it is not an activation object:
// see 10.1.6 Activation Object
aload(thisObjLocal);
addScriptRuntimeInvoke("getThis",
"(Lorg/mozilla/javascript/Scriptable;)",
"Lorg/mozilla/javascript/Scriptable;");
astore(thisObjLocal);
}
hasVarsInRegs = inFunction &&
!((OptFunctionNode)tree).requiresActivation();
if (hasVarsInRegs) {