Top call scope tracking changes:

Since E4X implementation needs to know the activation scope for tracking of default namespaces, previously an elaborated schema was added to set/restore the activation scope which relied on the fact that scrip and function with activation record should always call special entry/exit functions.

But that does not work for functions without activation records since they never call any special entry/exit pairs. So if application call such function directly, the function would not store its top scope anywhere and the E4X subsystem would not be able to get E4X library object.

The patch fixes with introduction of 2 functions, hasTopCall and doTopCall to ScriptRuntime and adding the following code prefix to each implementation of Callable.call that can start execution of script code:

    public Object call(Context cx, Scriptable scope,
                       Scriptable thisObj, Object[] args)
        throws JavaScriptException
    {
// Prefix start
        if (!ScriptRuntime.hasTopCall(cx)) {
            return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
        }
// Prefix end
        ...

In this way there is always registered top scope during script execution and the previous elaborated schema became unnecessary so I reverted that part to almost pre-E4x state.
This commit is contained in:
igor%mir2.org 2004-08-09 11:00:47 +00:00
Родитель 033645c483
Коммит caf00e67ee
9 изменённых файлов: 156 добавлений и 230 удалений

Просмотреть файл

@ -226,10 +226,10 @@ final class Arguments extends IdScriptableObject
if (value == UniqueTag.NULL_VALUE) { value = null; }
else if (value == null) {
NativeCall caller = activation.parentActivationCall;
if (caller == null) {
value = null;
} else {
if (caller != null) {
value = caller.get("arguments", caller);
} else {
value = null;
}
}
return value;

Просмотреть файл

@ -2547,11 +2547,8 @@ public class Context
private boolean sealed;
private Object sealKey;
Scriptable topActivationScope;
Scriptable topCallScope;
NativeCall currentActivationCall;
Scriptable currentActivationScope;
int currentActivationDepth;
XMLLib cachedXMLLib;
// for Objects, Arrays to tag themselves as being printed out,

Просмотреть файл

@ -54,6 +54,9 @@ final class InterpretedFunction extends NativeFunction
Object[] args)
throws JavaScriptException
{
if (!ScriptRuntime.hasTopCall(cx)) {
return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
}
return Interpreter.interpret(cx, scope, thisObj,
args, null, 0, args.length,
this, itsData);

Просмотреть файл

@ -1,72 +0,0 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Roger Lawrence
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript;
import java.util.*;
final class InterpretedScript extends NativeFunction implements Script
{
InterpretedScript(InterpreterData theData)
{
itsData = theData;
initScriptFunction(itsData.languageVersion, "",
itsData.argNames, itsData.argCount);
}
public Object exec(Context cx, Scriptable scope)
throws JavaScriptException
{
return call(cx, scope, scope, ScriptRuntime.emptyArgs);
}
public Object call(Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
throws JavaScriptException
{
return Interpreter.interpret(cx, scope, thisObj,
args, null, 0, args.length,
this, itsData);
}
public String getEncodedSource()
{
return Interpreter.getEncodedSource(itsData);
}
InterpreterData itsData;
}

Просмотреть файл

@ -2041,8 +2041,18 @@ public class Interpreter
}
DebugFrame debuggerFrame = null;
boolean useActivationVars = false;
if (cx.debugger != null) {
debuggerFrame = cx.debugger.getFrame(cx, idata);
useActivationVars = (debuggerFrame != null);
}
if (idata.itsNeedsActivation || useActivationVars) {
if (argsDbl != null) {
args = getArgsArray(args, argsDbl, argShift, argCount);
argShift = 0;
argsDbl = null;
}
}
if (idata.itsFunctionType != 0) {
@ -2055,19 +2065,14 @@ public class Interpreter
thisObj = ScriptRuntime.getThis(thisObj);
}
if (idata.itsNeedsActivation) {
if (argsDbl != null) {
args = getArgsArray(args, argsDbl, argShift, argCount);
argShift = 0;
argsDbl = null;
}
if (idata.itsNeedsActivation || useActivationVars) {
scope = ScriptRuntime.enterActivationFunction(cx, scope,
fnOrScript,
thisObj, args);
}
} else {
scope = ScriptRuntime.enterScript(cx, scope, fnOrScript, thisObj);
ScriptRuntime.initScript(fnOrScript, thisObj, cx, scope,
idata.itsFromEvalCode);
}
if (idata.itsNestedFunctions != null) {
@ -2086,19 +2091,7 @@ public class Interpreter
// the regexps re-wrapped during each script execution
Scriptable[] scriptRegExps = null;
boolean useActivationVars = false;
if (debuggerFrame != null) {
if (argsDbl != null) {
args = getArgsArray(args, argsDbl, argShift, argCount);
argShift = 0;
argsDbl = null;
}
if (idata.itsFunctionType != 0 && !idata.itsNeedsActivation) {
useActivationVars = true;
scope = ScriptRuntime.enterActivationFunction(cx, scope,
fnOrScript,
thisObj, args);
}
debuggerFrame.onEnter(cx, scope, thisObj, args);
}
@ -3119,11 +3112,9 @@ switch (op) {
}
if (idata.itsFunctionType != 0) {
if (idata.itsNeedsActivation || debuggerFrame != null) {
if (idata.itsNeedsActivation || useActivationVars) {
ScriptRuntime.exitActivationFunction(cx);
}
} else {
ScriptRuntime.exitScript(cx);
}
if (javaException != null) {

Просмотреть файл

@ -157,6 +157,5 @@ public final class NativeCall extends IdScriptableObject
private Object[] originalArgs;
NativeCall parentActivationCall;
Scriptable parentActivationScope;
}

Просмотреть файл

@ -1085,9 +1085,12 @@ public class ScriptRuntime {
public static Object setDefaultNamespace(Object namespace, Context cx)
{
Scriptable scope = cx.currentActivationScope;
if (scope == null)
throw new IllegalStateException();
Scriptable scope = cx.currentActivationCall;
if (scope == null) {
scope = cx.topCallScope;
if (scope == null)
throw new IllegalStateException();
}
XMLLib xmlLib = currentXMLLib(cx);
Object ns = xmlLib.toDefaultXmlNamespace(cx, namespace);
@ -1107,9 +1110,11 @@ public class ScriptRuntime {
public static Object searchDefaultNamespace(Context cx)
{
Scriptable scope = cx.currentActivationScope;
Scriptable scope = cx.currentActivationCall;
if (scope == null) {
return null;
scope = cx.topCallScope;
if (scope == null)
throw new IllegalStateException();
}
Object nsObject;
for (;;) {
@ -1751,8 +1756,7 @@ public class ScriptRuntime {
// been defined, creates a new property in the
// global object. Find the global object by
// walking up the scope chain.
Scriptable scope = cx.currentActivationScope;
bound = ScriptableObject.getTopLevelScope(scope);
bound = cx.topCallScope;
bound.put(id, bound, value);
/*
This code is causing immense performance problems in
@ -2749,27 +2753,64 @@ public class ScriptRuntime {
return new ImporterTopLevel(cx);
}
public static Scriptable enterScript(Context cx, Scriptable scope,
NativeFunction funObj,
Scriptable thisObj)
public static boolean hasTopCall(Context cx)
{
boolean topLevel = (cx.currentActivationDepth == 0);
return (cx.topCallScope != null);
}
Scriptable varScope = scope;
// Never define any variables from var statements inside with object.
// See bug 38590.
while (varScope instanceof NativeWith) {
varScope = varScope.getParentScope();
public static Object doTopCall(Callable callable,
Context cx, Scriptable scope,
Scriptable thisObj, Object[] args)
{
if (cx.topCallScope != null)
throw new IllegalStateException();
Object result;
cx.topCallScope = ScriptableObject.getTopLevelScope(scope);
try {
result = callable.call(cx, scope, thisObj, args);
} finally {
releaseTopCall(cx);
}
return result;
}
private static void releaseTopCall(Context cx)
{
cx.topCallScope = null;
// Cleanup cached references
cx.cachedXMLLib = null;
if (cx.currentActivationCall != null) {
// Function should always call exitActivationFunction
// if it creates activation record
throw new IllegalStateException();
}
}
public static void initScript(NativeFunction funObj, Scriptable thisObj,
Context cx, Scriptable scope,
boolean evalScript)
{
if (cx.topCallScope == null)
throw new IllegalStateException();
String[] argNames = funObj.argNames;
if (argNames != null) {
Scriptable varScope = scope;
// Never define any variables from var statements inside with
// object. See bug 38590.
while (varScope instanceof NativeWith) {
varScope = varScope.getParentScope();
}
for (int i = argNames.length; i-- != 0;) {
String name = argNames[i];
// Don't overwrite existing def if already defined in object
// or prototypes of object.
if (!hasProp(scope, name)) {
if (topLevel) {
if (!evalScript) {
// Global var definitions are supposed to be DONTDELETE
ScriptableObject.defineProperty(
varScope, name, Undefined.instance,
@ -2780,29 +2821,6 @@ public class ScriptRuntime {
}
}
}
if (topLevel) {
if (cx.currentActivationScope != null)
throw new IllegalStateException();
cx.currentActivationScope = varScope;
}
increaseActivationDepth(cx);
return scope;
}
public static void exitScript(Context cx)
{
if (cx.currentActivationDepth == 0)
throw new IllegalStateException();
if (cx.currentActivationScope == null)
throw new IllegalStateException();
if (cx.currentActivationDepth == 1) {
cx.currentActivationScope = null;
}
decreaseActivationDepth(cx);
}
public static Scriptable enterActivationFunction(Context cx,
@ -2811,64 +2829,21 @@ public class ScriptRuntime {
Scriptable thisObj,
Object[] args)
{
if (cx.topCallScope == null)
throw new IllegalStateException();
NativeCall call = new NativeCall(scope, funObj, thisObj, args);
call.parentActivationCall = cx.currentActivationCall;
call.parentActivationScope = cx.currentActivationScope;
cx.currentActivationCall = call;
cx.currentActivationScope = call;
increaseActivationDepth(cx);
return call;
}
public static void exitActivationFunction(Context cx)
{
if (cx.currentActivationDepth == 0)
throw new IllegalStateException();
if (cx.currentActivationScope == null)
throw new IllegalStateException();
if (cx.currentActivationCall != cx.currentActivationScope)
throw new IllegalStateException();
NativeCall call = cx.currentActivationCall;
cx.currentActivationCall = call.parentActivationCall;
cx.currentActivationScope = call.parentActivationScope;
call.parentActivationCall = null;
call.parentActivationScope = null;
decreaseActivationDepth(cx);
}
private static void increaseActivationDepth(Context cx)
{
// The caller should initialize this already
if (cx.currentActivationScope == null)
throw new IllegalStateException();
if (cx.currentActivationDepth == 0) {
cx.topActivationScope = cx.currentActivationScope;
}
++cx.currentActivationDepth;
}
private static void decreaseActivationDepth(Context cx)
{
--cx.currentActivationDepth;
if (cx.currentActivationDepth == 0) {
// The caller should do proper cleanup
if (cx.currentActivationScope != null)
throw new IllegalStateException();
if (cx.currentActivationCall != null)
throw new IllegalStateException();
cx.topActivationScope = null;
// Cleanup references
cx.cachedXMLLib = null;
}
}
static NativeCall findFunctionActivation(Context cx, Function f)
@ -3185,12 +3160,12 @@ public class ScriptRuntime {
private static XMLLib currentXMLLib(Context cx)
{
// Scripts should be running to access this
if (cx.topActivationScope == null)
if (cx.topCallScope == null)
throw new IllegalStateException();
XMLLib xmlLib = cx.cachedXMLLib;
if (xmlLib == null) {
xmlLib = XMLLib.extractFromScope(cx.topActivationScope);
xmlLib = XMLLib.extractFromScope(cx.topCallScope);
if (xmlLib == null)
throw new IllegalStateException();
cx.cachedXMLLib = xmlLib;

Просмотреть файл

@ -374,6 +374,38 @@ public class Codegen extends Interpreter
(short)(ClassFileWriter.ACC_PUBLIC
| ClassFileWriter.ACC_FINAL));
// Generate code for:
// if (!ScriptRuntime.hasTopCall(cx)) {
// return ScriptRuntime.doTopCall(this, cx, scope, thisObj, args);
// }
int nonTopCallLabel = cfw.acquireLabel();
cfw.addALoad(1); //cx
cfw.addInvoke(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/ScriptRuntime",
"hasTopCall",
"(Lorg/mozilla/javascript/Context;"
+")Z");
cfw.add(ByteCode.IFNE, nonTopCallLabel);
cfw.addALoad(0);
cfw.addALoad(1);
cfw.addALoad(2);
cfw.addALoad(3);
cfw.addALoad(4);
cfw.addInvoke(ByteCode.INVOKESTATIC,
"org/mozilla/javascript/ScriptRuntime",
"doTopCall",
"(Lorg/mozilla/javascript/Callable;"
+"Lorg/mozilla/javascript/Context;"
+"Lorg/mozilla/javascript/Scriptable;"
+"Lorg/mozilla/javascript/Scriptable;"
+"[Ljava/lang/Object;"
+")Ljava/lang/Object;");
cfw.add(ByteCode.ARETURN);
cfw.markLabel(nonTopCallLabel);
// No generate switch to call the real methods
cfw.addALoad(0);
cfw.addALoad(1);
cfw.addALoad(2);
@ -1261,20 +1293,22 @@ class BodyCodegen
+"Lorg/mozilla/javascript/Scriptable;"
+"[Ljava/lang/Object;"
+")Lorg/mozilla/javascript/Scriptable;");
cfw.addAStore(variableObjectLocal);
} else {
debugVariableName = "global";
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addALoad(funObjLocal);
cfw.addALoad(thisObjLocal);
addScriptRuntimeInvoke("enterScript",
"(Lorg/mozilla/javascript/Context;"
cfw.addALoad(contextLocal);
cfw.addALoad(variableObjectLocal);
cfw.addPush(0); // false to indicate it is not eval script
addScriptRuntimeInvoke("initScript",
"(Lorg/mozilla/javascript/NativeFunction;"
+"Lorg/mozilla/javascript/Scriptable;"
+"Lorg/mozilla/javascript/NativeFunction;"
+"Lorg/mozilla/javascript/Context;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Lorg/mozilla/javascript/Scriptable;");
+"Z"
+")V");
}
cfw.addAStore(variableObjectLocal);
enterAreaStartLabel = cfw.acquireLabel();
epilogueLabel = cfw.acquireLabel();
@ -1334,44 +1368,42 @@ class BodyCodegen
}
cfw.markLabel(epilogueLabel);
generateExitCode();
if (fnCurrent == null) {
cfw.addALoad(popvLocal);
cfw.add(ByteCode.ARETURN);
} else {
generateActivationExit();
cfw.add(ByteCode.ARETURN);
// Generate catch block to catch all and rethrow to call exit code
// under exception propagation as well.
int finallyHandler = cfw.acquireLabel();
cfw.markHandler(finallyHandler);
short exceptionObject = getNewWordLocal();
cfw.addAStore(exceptionObject);
// Duplicate generateActivationExit() in the catch block since it
// takes less space then full-fetured ByteCode.JSR/ByteCode.RET
generateActivationExit();
cfw.addALoad(exceptionObject);
releaseWordLocal(exceptionObject);
// rethrow
cfw.add(ByteCode.ATHROW);
// mark the handler
cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel,
finallyHandler, null); // catch any
}
cfw.add(ByteCode.ARETURN);
// Generate catch block to catch all and rethrow to call exit code
// under exception propagation as well.
int finallyHandler = cfw.acquireLabel();
cfw.markHandler(finallyHandler);
short exceptionObject = getNewWordLocal();
cfw.addAStore(exceptionObject);
// Duplicate generateExitCode() in the catch block since it takes
// less space then full-fetured ByteCode.JSR/ByteCode.RET
generateExitCode();
cfw.addALoad(exceptionObject);
releaseWordLocal(exceptionObject);
// rethrow
cfw.add(ByteCode.ATHROW);
// mark the handler
cfw.addExceptionHandler(enterAreaStartLabel, epilogueLabel,
finallyHandler, null); // catch any
}
private void generateExitCode()
private void generateActivationExit()
{
if (fnCurrent == null || hasVarsInRegs) throw Kit.codeBug();
cfw.addALoad(contextLocal);
if (fnCurrent != null) {
addScriptRuntimeInvoke("exitActivationFunction",
"(Lorg/mozilla/javascript/Context;)V");
} else {
addScriptRuntimeInvoke("exitScript",
"(Lorg/mozilla/javascript/Context;)V");
}
addScriptRuntimeInvoke("exitActivationFunction",
"(Lorg/mozilla/javascript/Context;)V");
}
private void generateStatement(Node node, Node parent)

Просмотреть файл

@ -532,7 +532,8 @@ msg.invalid.escape =\
invalid Unicode escape sequence
msg.bad.namespace =\
not a valid namespace. Syntax is: namespace variableName as "URI";
not a valid default namespace statement. \
Syntax is: default xml namespace = EXPRESSION;
# TokensStream warnings
msg.bad.octal.literal =\