JavaScriptCore:
authorggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Mar 2008 19:46:33 +0000 (19:46 +0000)
committerggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 7 Mar 2008 19:46:33 +0000 (19:46 +0000)
        Reviewed by Darin Adler.

        Fixed <rdar://problem/5689093> Stricter (ES4) eval semantics

        The basic rule is:

        - "eval(s)" is treated as an operator that gives the ES3 eval behavior.
            ... but only if there is no overriding declaration of "eval" in scope.
        - All other invocations treat eval as a function that evaluates a
        script in the context of its "this" object.
            ... but if its "this" object is not the global object it was
            originally associated with, eval throws an exception.

        Because only expressions of the form "eval(s)" have access to local
        scope, the compiler can now statically determine whether a function
        needs local scope to be dynamic.

        * kjs/nodes.h: Added FunctionCallEvalNode. It works just like
        FuncationCallResolveNode, except it statically indicates that the node
        may execute eval in the ES3 way.
        * kjs/nodes.cpp:
        * kjs/nodes2string.cpp:

        * tests/mozilla/expected.html: This patch happens to fix a Mozilla JS
        test, but it's a bit of a pyrrhic victory. The test intends to test
        Mozilla's generic API for calling eval on any object, but, in reality,
        we only support calling eval on the global object.

LayoutTests:

        Reviewed by Darin Adler.

        Tests for <rdar://problem/5689093> Stricter (ES4) eval semantics

        * fast/js/eval-cross-window-expected.txt: Added.
        * fast/js/eval-cross-window.html: Added.
        * fast/js/eval-keyword-vs-function-expected.txt: Added.
        * fast/js/eval-keyword-vs-function.html: Added.
        * fast/js/eval-overriding-expected.txt: Added.
        * fast/js/eval-overriding.html: Added.

        Tests to make sure not to regress security:

        * http/tests/security/resources/xss-eval2.html: Added.
        * http/tests/security/resources/xss-eval3.html: Added.
        * http/tests/security/xss-eval-expected.txt: Added.
        * http/tests/security/xss-eval.html: Added.

        I removed these tests because we no longer match the behavior they
        expected, and the new tests are more comprehensive:

        * fast/js/window-eval-context-expected.txt: Removed.
        * fast/js/window-eval-context.html: Removed.
        * fast/js/window-eval-tearoff-expected.txt: Removed.
        * fast/js/window-eval-tearoff.html: Removed.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@30871 268f45cc-cd09-0410-ab3c-d52691b4dbfc

30 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/kjs/CommonIdentifiers.h
JavaScriptCore/kjs/ExecState.cpp
JavaScriptCore/kjs/ExecState.h
JavaScriptCore/kjs/JSGlobalObject.cpp
JavaScriptCore/kjs/JSGlobalObject.h
JavaScriptCore/kjs/SavedBuiltins.h
JavaScriptCore/kjs/function.cpp
JavaScriptCore/kjs/function.h
JavaScriptCore/kjs/grammar.y
JavaScriptCore/kjs/nodes.cpp
JavaScriptCore/kjs/nodes.h
JavaScriptCore/kjs/nodes2string.cpp
JavaScriptCore/kjs/scope_chain.h
JavaScriptCore/tests/mozilla/expected.html
LayoutTests/ChangeLog
LayoutTests/fast/js/eval-cross-window-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/eval-cross-window.html [new file with mode: 0644]
LayoutTests/fast/js/eval-keyword-vs-function-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/eval-keyword-vs-function.html [new file with mode: 0644]
LayoutTests/fast/js/eval-overriding-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/eval-overriding.html [new file with mode: 0644]
LayoutTests/fast/js/window-eval-context-expected.txt [deleted file]
LayoutTests/fast/js/window-eval-context.html [deleted file]
LayoutTests/fast/js/window-eval-tearoff-expected.txt [deleted file]
LayoutTests/fast/js/window-eval-tearoff.html [deleted file]
LayoutTests/http/tests/security/resources/xss-eval2.html [new file with mode: 0644]
LayoutTests/http/tests/security/resources/xss-eval3.html [new file with mode: 0644]
LayoutTests/http/tests/security/xss-eval-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/xss-eval.html [new file with mode: 0644]

index 557a03bd1d880981a774b9f5fa92312bd9045961..9deaf88c94c3abfd57fa81fbc657f8c612b99c04 100644 (file)
@@ -1,3 +1,33 @@
+2008-03-07  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by Darin Adler.
+        
+        Fixed <rdar://problem/5689093> Stricter (ES4) eval semantics
+        
+        The basic rule is:
+        
+        - "eval(s)" is treated as an operator that gives the ES3 eval behavior.
+            ... but only if there is no overriding declaration of "eval" in scope.
+        - All other invocations treat eval as a function that evaluates a
+        script in the context of its "this" object.
+            ... but if its "this" object is not the global object it was
+            originally associated with, eval throws an exception.
+        
+        Because only expressions of the form "eval(s)" have access to local
+        scope, the compiler can now statically determine whether a function
+        needs local scope to be dynamic.
+
+        * kjs/nodes.h: Added FunctionCallEvalNode. It works just like
+        FuncationCallResolveNode, except it statically indicates that the node
+        may execute eval in the ES3 way.
+        * kjs/nodes.cpp:
+        * kjs/nodes2string.cpp:
+
+        * tests/mozilla/expected.html: This patch happens to fix a Mozilla JS
+        test, but it's a bit of a pyrrhic victory. The test intends to test
+        Mozilla's generic API for calling eval on any object, but, in reality,
+        we only support calling eval on the global object.
+
 2008-03-06  Steve Falkenburg  <sfalken@apple.com>
 
         Build fix.
index ae5c778ffdeac4a64cd672d83099b766b503e46e..210cd614fe537b96aa10a2f60959e9d762f13a62 100644 (file)
@@ -47,7 +47,8 @@
     macro(toLocaleString) \
     macro(toPrecision) \
     macro(toString) \
-    macro(valueOf)
+    macro(valueOf) \
+    macro(eval)
 
 namespace KJS {
 
index b37523e2f8cd13487d6d36529f5b6edcd6e7899b..9ae6ecc2c9c32aa0c7a7b9f8a33143053c863c3d 100644 (file)
@@ -85,7 +85,7 @@ inline ExecState::ExecState(JSGlobalObject* globalObject, JSObject* /*thisObject
     m_scopeChain.push(globalObject);
 }
 
-inline ExecState::ExecState(JSGlobalObject* globalObject, EvalNode* evalNode, ExecState* callingExec)
+inline ExecState::ExecState(JSGlobalObject* globalObject, JSObject* thisObject, EvalNode* evalNode, ExecState* callingExec, const ScopeChain& scopeChain, JSVariableObject* variableObject)
     : m_globalObject(globalObject)
     , m_exception(0)
     , m_propertyNames(callingExec->m_propertyNames)
@@ -96,9 +96,9 @@ inline ExecState::ExecState(JSGlobalObject* globalObject, EvalNode* evalNode, Ex
     , m_arguments(0)
     , m_activation(0)
     , m_localStorage(callingExec->m_localStorage)
-    , m_scopeChain(callingExec->m_scopeChain)
-    , m_variableObject(callingExec->m_variableObject)
-    , m_thisValue(callingExec->m_thisValue)
+    , m_scopeChain(scopeChain)
+    , m_variableObject(variableObject)
+    , m_thisValue(thisObject)
     , m_iterationDepth(0)
     , m_switchDepth(0) 
     , m_codeType(EvalCode)
@@ -183,8 +183,8 @@ InterpreterExecState::~InterpreterExecState()
     inlineActiveExecStates().removeLast();
 }
 
-EvalExecState::EvalExecState(JSGlobalObject* globalObject, EvalNode* evalNode, ExecState* callingExec)
-    : ExecState(globalObject, evalNode, callingExec)
+EvalExecState::EvalExecState(JSGlobalObject* globalObject, JSObject* thisObj, EvalNode* evalNode, ExecState* callingExec, const ScopeChain& scopeChain, JSVariableObject* variableObject)
+    : ExecState(globalObject, thisObj, evalNode, callingExec, scopeChain, variableObject)
 {
     inlineActiveExecStates().append(this);
 }
index ffa067d967bfbac50800551af1037ebed2645cdf..144aebfc018d3079930ab627b2c050593345fcd1 100644 (file)
@@ -170,9 +170,8 @@ namespace KJS  {
     protected:
         ExecState(JSGlobalObject*);
         ExecState(JSGlobalObject*, JSObject* thisObject, ProgramNode*);
-        ExecState(JSGlobalObject*, EvalNode*, ExecState* callingExecState);
-        ExecState(JSGlobalObject*, JSObject* thisObject, FunctionBodyNode*,
-            ExecState* callingExecState, FunctionImp*, const List& args);
+        ExecState(JSGlobalObject*, JSObject* thisObject, EvalNode*, ExecState* callingExecState, const ScopeChain&, JSVariableObject*);
+        ExecState(JSGlobalObject*, JSObject* thisObject, FunctionBodyNode*, ExecState* callingExecState, FunctionImp*, const List& args);
         ~ExecState();
 
         // ExecStates are always stack-allocated, and the garbage collector
@@ -219,7 +218,7 @@ namespace KJS  {
 
     class EvalExecState : public ExecState {
     public:
-        EvalExecState(JSGlobalObject*, EvalNode*, ExecState* callingExecState);
+        EvalExecState(JSGlobalObject*, JSObject* thisObj, EvalNode*, ExecState* callingExec, const ScopeChain&, JSVariableObject*);
         ~EvalExecState();
     };
 
index aaf11e3288e720b40be2b1c947d88587ee862c92..07d13ef617528a2bbd020425bd8171b60e1565da 100644 (file)
@@ -227,6 +227,8 @@ void JSGlobalObject::reset(JSValue* prototype)
     d()->typeErrorConstructor = 0;
     d()->URIErrorConstructor = 0;
 
+    d()->evalFunction = 0;
+
     ExecState* exec = &d()->globalExec;
 
     // Prototypes
@@ -315,7 +317,8 @@ void JSGlobalObject::reset(JSValue* prototype)
 
     // Set global functions.
 
-    putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "eval", globalFuncEval), DontEnum);
+    d()->evalFunction = new PrototypeReflexiveFunction(exec, d()->functionPrototype, 1, exec->propertyNames().eval, globalFuncEval);
+    putDirectFunction(d()->evalFunction, DontEnum);
     putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 2, "parseInt", globalFuncParseInt), DontEnum);
     putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "parseFloat", globalFuncParseFloat), DontEnum);
     putDirectFunction(new PrototypeFunction(exec, d()->functionPrototype, 1, "isNaN", globalFuncIsNaN), DontEnum);
@@ -417,6 +420,8 @@ void JSGlobalObject::saveBuiltins(SavedBuiltins& builtins) const
     builtins._internal->typeErrorConstructor = d()->typeErrorConstructor;
     builtins._internal->URIErrorConstructor = d()->URIErrorConstructor;
     
+    builtins._internal->evalFunction = d()->evalFunction;
+    
     builtins._internal->objectPrototype = d()->objectPrototype;
     builtins._internal->functionPrototype = d()->functionPrototype;
     builtins._internal->arrayPrototype = d()->arrayPrototype;
@@ -454,6 +459,8 @@ void JSGlobalObject::restoreBuiltins(const SavedBuiltins& builtins)
     d()->syntaxErrorConstructor = builtins._internal->syntaxErrorConstructor;
     d()->typeErrorConstructor = builtins._internal->typeErrorConstructor;
     d()->URIErrorConstructor = builtins._internal->URIErrorConstructor;
+    
+    d()->evalFunction = builtins._internal->evalFunction;
 
     d()->objectPrototype = builtins._internal->objectPrototype;
     d()->functionPrototype = builtins._internal->functionPrototype;
@@ -494,6 +501,8 @@ void JSGlobalObject::mark()
     markIfNeeded(d()->typeErrorConstructor);
     markIfNeeded(d()->URIErrorConstructor);
     
+    markIfNeeded(d()->evalFunction);
+    
     markIfNeeded(d()->objectPrototype);
     markIfNeeded(d()->functionPrototype);
     markIfNeeded(d()->arrayPrototype);
index d6c055e2200db18c838d3a5e9107e8d611dea8ea..06fc2ecf4caa55b4ec01ac45e223f689b842606b 100644 (file)
@@ -48,6 +48,7 @@ namespace KJS {
     class NumberPrototype;
     class ObjectObjectImp;
     class ObjectPrototype;
+    class PrototypeReflexiveFunction;
     class RangeError;
     class RangeErrorPrototype;
     class ReferenceError;
@@ -109,6 +110,8 @@ namespace KJS {
             NativeErrorImp* typeErrorConstructor;
             NativeErrorImp* URIErrorConstructor;
 
+            PrototypeReflexiveFunction* evalFunction;
+
             ObjectPrototype* objectPrototype;
             FunctionPrototype* functionPrototype;
             ArrayPrototype* arrayPrototype;
@@ -124,7 +127,7 @@ namespace KJS {
             NativeErrorPrototype* syntaxErrorPrototype;
             NativeErrorPrototype* typeErrorPrototype;
             NativeErrorPrototype* URIErrorPrototype;
-
+            
             SymbolTable inlineSymbolTable;
 
             ActivationStackNode* activations;
@@ -181,6 +184,8 @@ namespace KJS {
         NativeErrorImp* typeErrorConstructor() const { return d()->typeErrorConstructor; }
         NativeErrorImp* URIErrorConstructor() const { return d()->URIErrorConstructor; }
 
+        PrototypeReflexiveFunction* evalFunction() const { return d()->evalFunction; }
+
         ObjectPrototype* objectPrototype() const { return d()->objectPrototype; }
         FunctionPrototype* functionPrototype() const { return d()->functionPrototype; }
         ArrayPrototype* arrayPrototype() const { return d()->arrayPrototype; }
index 9901e41b97200fd04cd307117c166e3a677a5bb2..eabefcefcda918bc1ddecacdeb2a1f2966a31ea3 100644 (file)
@@ -55,6 +55,8 @@ struct SavedBuiltinsInternal {
     ProtectedPtr<NativeErrorImp> typeErrorConstructor;
     ProtectedPtr<NativeErrorImp> URIErrorConstructor;
     
+    ProtectedPtr<PrototypeReflexiveFunction> evalFunction;
+
     ProtectedPtr<ObjectPrototype> objectPrototype;
     ProtectedPtr<FunctionPrototype> functionPrototype;
     ProtectedPtr<ArrayPrototype> arrayPrototype;
index b996122bb681e290455815be31e6b59de9d937bb..e47d3af8cf6eb2462fe2ef88a8e053969f63ee6c 100644 (file)
@@ -703,7 +703,7 @@ static double parseFloat(const UString& s)
     return s.toDouble( true /*tolerant*/, false /* NaN for empty string */ );
 }
 
-JSValue* globalFuncEval(ExecState* exec, JSObject* thisObj, const List& args)
+JSValue* eval(ExecState* exec, const ScopeChain& scopeChain, JSVariableObject* variableObject, JSGlobalObject* globalObject, JSObject* thisObj, const List& args)
 {
     JSValue* x = args[0];
     if (!x->isString())
@@ -723,24 +723,12 @@ JSValue* globalFuncEval(ExecState* exec, JSObject* thisObj, const List& args)
             return jsUndefined();
     }
 
-    // No program node means a syntax occurred
     if (!evalNode)
         return throwError(exec, SyntaxError, errMsg, errLine, sourceId, NULL);
 
-    bool switchGlobal = thisObj && thisObj != exec->dynamicGlobalObject() && thisObj->isGlobalObject();
+    EvalExecState newExec(globalObject, thisObj, evalNode.get(), exec, scopeChain, variableObject);
 
-    // enter a new execution context
-    exec->dynamicGlobalObject()->tearOffActivation(exec);
-    JSGlobalObject* globalObject = switchGlobal ? static_cast<JSGlobalObject*>(thisObj) : exec->dynamicGlobalObject();
-    EvalExecState newExec(globalObject, evalNode.get(), exec);
-
-    if (switchGlobal) {
-        newExec.pushScope(thisObj);
-        newExec.setVariableObject(static_cast<JSGlobalObject*>(thisObj));
-    }
     JSValue* value = evalNode->execute(&newExec);
-    if (switchGlobal)
-        newExec.popScope();
 
     if (newExec.completionType() == Throw) {
         exec->setException(value);
@@ -750,6 +738,17 @@ JSValue* globalFuncEval(ExecState* exec, JSObject* thisObj, const List& args)
     return value ? value : jsUndefined();
 }
 
+JSValue* globalFuncEval(ExecState* exec, PrototypeReflexiveFunction* function, JSObject* thisObj, const List& args)
+{
+    JSGlobalObject* globalObject = thisObj->isGlobalObject() ? static_cast<JSGlobalObject*>(thisObj) : 0;
+
+    if (!globalObject || globalObject->evalFunction() != function)
+        return throwError(exec, EvalError, "The \"this\" value passed to eval must be the global object from which eval originated");
+
+    ScopeChain scopeChain(globalObject);
+    return eval(exec, scopeChain, globalObject, globalObject, globalObject, args);
+}
+
 JSValue* globalFuncParseInt(ExecState* exec, JSObject*, const List& args)
 {
     return jsNumber(parseInt(args[0]->toString(exec), args[1]->toInt32(exec)));
@@ -891,4 +890,19 @@ JSValue* PrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, c
     return m_function(exec, thisObj, args);
 }
 
+// ------------------------------ PrototypeReflexiveFunction -------------------------------
+
+PrototypeReflexiveFunction::PrototypeReflexiveFunction(ExecState* exec, FunctionPrototype* functionPrototype, int len, const Identifier& name, JSMemberFunction function)
+    : InternalFunctionImp(functionPrototype, name)
+    , m_function(function)
+{
+    ASSERT_ARG(function, function);
+    putDirect(exec->propertyNames().length, jsNumber(len), DontDelete | ReadOnly | DontEnum);
+}
+
+JSValue* PrototypeReflexiveFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
+{
+    return m_function(exec, this, thisObj, args);
+}
+
 } // namespace KJS
index be3e136bf80c249c3823f4129e3e6d95b48b90f5..7b21d0d3f60dbab96c9379ae1b9a14c73d19dd07 100644 (file)
@@ -125,7 +125,7 @@ namespace KJS {
 
   class PrototypeFunction : public InternalFunctionImp {
   public:
-    typedef KJS::JSValue* (*JSMemberFunction)(ExecState*, JSObject*, const List&);
+    typedef JSValue* (*JSMemberFunction)(ExecState*, JSObject* thisObj, const List&);
 
     PrototypeFunction(ExecState*, int len, const Identifier&, JSMemberFunction);
     PrototypeFunction(ExecState*, FunctionPrototype*, int len, const Identifier&, JSMemberFunction);
@@ -137,8 +137,21 @@ namespace KJS {
   };
 
 
+  // Just like PrototypeFunction, but callbacks also get passed the JS function object.
+  class PrototypeReflexiveFunction : public InternalFunctionImp {
+  public:
+    typedef JSValue* (*JSMemberFunction)(ExecState*, PrototypeReflexiveFunction*, JSObject* thisObj, const List&);
+
+    PrototypeReflexiveFunction(ExecState*, FunctionPrototype*, int len, const Identifier&, JSMemberFunction);
+
+    virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List&);
+
+  private:
+    const JSMemberFunction m_function;
+  };
+
     // Global Functions
-    JSValue* globalFuncEval(ExecState*, JSObject*, const List&);
+    JSValue* globalFuncEval(ExecState*, PrototypeReflexiveFunction*, JSObject*, const List&);
     JSValue* globalFuncParseInt(ExecState*, JSObject*, const List&);
     JSValue* globalFuncParseFloat(ExecState*, JSObject*, const List&);
     JSValue* globalFuncIsNaN(ExecState*, JSObject*, const List&);
@@ -153,6 +166,8 @@ namespace KJS {
     JSValue* globalFuncKJSPrint(ExecState*, JSObject*, const List&);
 #endif
 
+    JSValue* eval(ExecState*, const ScopeChain&, JSVariableObject*, JSGlobalObject*, JSObject* thisObj, const List& args);
+
     static const double mantissaOverflowLowerBound = 9007199254740992.0;
     double parseIntOverflow(const char*, int length, int radix);
 
index 658ae8948aaf0de612b0d0587c2feac7e4603e21..094d231a6e2123c95beb44b00e30c05e194ae3a8 100644 (file)
@@ -1159,7 +1159,10 @@ static ExpressionNode* makeFunctionCallNode(ExpressionNode* func, ArgumentsNode*
         return new FunctionCallValueNode(func, args);
     if (func->isResolveNode()) {
         ResolveNode* resolve = static_cast<ResolveNode*>(func);
-        return new FunctionCallResolveNode(resolve->identifier(), args);
+        const Identifier& identifier = resolve->identifier();
+        if (identifier == CommonIdentifiers::shared()->eval)
+            return new EvalFunctionCallNode(args);
+        return new FunctionCallResolveNode(identifier, args);
     }
     if (func->isBracketAccessorNode()) {
         BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(func);
index 4607edc1233dd2ce06a29942c0d980fd1c70096d..3daa1cd739b84813895e47475ff0749167293e68 100644 (file)
@@ -948,6 +948,70 @@ uint32_t NewExprNode::evaluateToUInt32(ExecState* exec)
     return v->toUInt32(exec);
 }
 
+template <ExpressionNode::CallerType callerType> 
+inline JSValue* ExpressionNode::resolveAndCall(ExecState* exec, const Identifier& ident, ArgumentsNode* args)
+{
+    const ScopeChain& chain = exec->scopeChain();
+    ScopeChainIterator iter = chain.begin();
+    ScopeChainIterator end = chain.end();
+
+    // we must always have something in the scope chain
+    ASSERT(iter != end);
+
+    PropertySlot slot;
+    JSObject* base;
+    do {
+        base = *iter;
+        if (base->getPropertySlot(exec, ident, slot)) {
+            JSValue* v = slot.getValue(exec, base, ident);
+            KJS_CHECKEXCEPTIONVALUE
+
+            if (!v->isObject())
+                return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, ident);
+
+            JSObject* func = static_cast<JSObject*>(v);
+
+            if (!func->implementsCall())
+                return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, ident);
+
+            List argList;
+            args->evaluateList(exec, argList);
+            KJS_CHECKEXCEPTIONVALUE
+
+            JSObject* thisObj = base;
+            // ECMA 11.2.3 says that in this situation the this value should be null.
+            // However, section 10.2.3 says that in the case where the value provided
+            // by the caller is null, the global object should be used. It also says
+            // that the section does not apply to internal functions, but for simplicity
+            // of implementation we use the global object anyway here. This guarantees
+            // that in host objects you always get a valid object for this.
+            if (thisObj->isActivationObject())
+                thisObj = exec->dynamicGlobalObject();
+
+            if (callerType == EvalOperator) {
+                if (base == exec->lexicalGlobalObject() && func == exec->lexicalGlobalObject()->evalFunction()) {
+                    exec->dynamicGlobalObject()->tearOffActivation(exec);
+                    return eval(exec, exec->scopeChain(), exec->variableObject(), exec->dynamicGlobalObject(), exec->thisValue(), argList);
+                }
+            }
+            return func->call(exec, thisObj, argList);
+        }
+        ++iter;
+    } while (iter != end);
+
+    return throwUndefinedVariableError(exec, ident);
+}
+
+void EvalFunctionCallNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
+{
+    nodeStack.append(m_args.get());
+}
+
+JSValue* EvalFunctionCallNode::evaluate(ExecState* exec)
+{
+    return resolveAndCall<EvalOperator>(exec, exec->propertyNames().eval, m_args.get());
+}
+
 void FunctionCallValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
 {
     nodeStack.append(m_args.get());
@@ -994,49 +1058,7 @@ JSValue* FunctionCallResolveNode::inlineEvaluate(ExecState* exec)
     // Check for missed optimization opportunity.
     ASSERT(!canSkipLookup(exec, m_ident));
 
-    const ScopeChain& chain = exec->scopeChain();
-    ScopeChainIterator iter = chain.begin();
-    ScopeChainIterator end = chain.end();
-
-    // we must always have something in the scope chain
-    ASSERT(iter != end);
-
-    PropertySlot slot;
-    JSObject* base;
-    do {
-        base = *iter;
-        if (base->getPropertySlot(exec, m_ident, slot)) {
-            JSValue* v = slot.getValue(exec, base, m_ident);
-            KJS_CHECKEXCEPTIONVALUE
-
-            if (!v->isObject())
-                return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident);
-
-            JSObject* func = static_cast<JSObject*>(v);
-
-            if (!func->implementsCall())
-                return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident);
-
-            List argList;
-            m_args->evaluateList(exec, argList);
-            KJS_CHECKEXCEPTIONVALUE
-
-            JSObject* thisObj = base;
-            // ECMA 11.2.3 says that in this situation the this value should be null.
-            // However, section 10.2.3 says that in the case where the value provided
-            // by the caller is null, the global object should be used. It also says
-            // that the section does not apply to internal functions, but for simplicity
-            // of implementation we use the global object anyway here. This guarantees
-            // that in host objects you always get a valid object for this.
-            if (thisObj->isActivationObject())
-                thisObj = exec->dynamicGlobalObject();
-
-            return func->call(exec, thisObj, argList);
-        }
-        ++iter;
-    } while (iter != end);
-
-    return throwUndefinedVariableError(exec, m_ident);
+    return resolveAndCall<FunctionCall>(exec, m_ident, m_args.get());
 }
 
 JSValue* FunctionCallResolveNode::evaluate(ExecState* exec)
index f366557f053b6dd20de161e4adbaa35e1d60a81f..d85584ea27b39eff27210b7d4b8ef94edefd4d09 100644 (file)
@@ -42,6 +42,7 @@
 
 namespace KJS {
 
+    class ArgumentsNode;
     class ConstDeclNode;
     class FuncDeclNode;
     class Node;
@@ -205,6 +206,10 @@ namespace KJS {
 
         // Used to optimize those nodes that do extra work when returning a result, even if the result has no semantic relevance
         virtual void optimizeForUnnecessaryResult() { }
+
+    protected:
+        typedef enum { EvalOperator, FunctionCall } CallerType;
+        template <CallerType> inline JSValue* resolveAndCall(ExecState*, const Identifier&, ArgumentsNode*);
     };
 
     class StatementNode : public Node {
@@ -680,6 +685,22 @@ namespace KJS {
         RefPtr<ArgumentsNode> m_args;
     };
 
+    class EvalFunctionCallNode : public ExpressionNode {
+    public:
+        EvalFunctionCallNode(ArgumentsNode* args) KJS_FAST_CALL
+            : m_args(args)
+        {
+        }
+
+        virtual void optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack&) KJS_FAST_CALL;
+        virtual JSValue* evaluate(ExecState*) KJS_FAST_CALL;
+        virtual void streamTo(SourceStream&) const KJS_FAST_CALL;
+        virtual Precedence precedence() const { return PrecCall; }
+
+    private:
+        RefPtr<ArgumentsNode> m_args;
+    };
+
     class FunctionCallValueNode : public ExpressionNode {
     public:
         FunctionCallValueNode(ExpressionNode* expr, ArgumentsNode* args) KJS_FAST_CALL
index b10e180131160a36dcd794c2287d9999df20e298..b98b5d842ae77583ea4ec6304e1932ef00e70b7a 100644 (file)
@@ -426,6 +426,11 @@ void NewExprNode::streamTo(SourceStream& s) const
     s << "new " << PrecMember << m_expr << m_args;
 }
 
+void EvalFunctionCallNode::streamTo(SourceStream& s) const
+{
+    s << "eval" << m_args;
+}
+
 void FunctionCallValueNode::streamTo(SourceStream& s) const
 {
     s << PrecCall << m_expr << m_args;
index 7441cb82cd2fd58b229aa06dca4298f32afda636..43050a49224b818062928d91a464c2b9e07a031a 100644 (file)
@@ -65,8 +65,18 @@ namespace KJS {
         ScopeChain() : _node(0) { }
         ~ScopeChain() { deref(); }
 
-        ScopeChain(const ScopeChain &c) : _node(c._node)
-            { if (_node) ++_node->refCount; }
+        ScopeChain(const ScopeChain& c)
+            : _node(c._node)
+        {
+            if (_node)
+                ++_node->refCount;
+        }
+
+        ScopeChain(JSObject* o)
+            : _node(new ScopeChainNode(0, o))
+        {
+        }
+
         ScopeChain &operator=(const ScopeChain &);
 
         bool isEmpty() const { return !_node; }
index 51be647dd21cffb8f0a2a353701a3c5010e837ec..ba9d72a0e1bc3953db5a780f3c1eaf0c4082d1e4 100644 (file)
@@ -7,11 +7,11 @@
 <p class='results_summary'>
 Test List: All tests<br>
 Skip List: (none)<br>
-1135 test(s) selected, 1127 test(s) completed, 52 failures reported (4.61% failed)<br>
-Engine command line: /Users/darin/Build/Debug/testkjs <br>
-OS type: Darwin Darin-Adlers-Mac-Pro.local 9.1.0 Darwin Kernel Version 9.1.0: Wed Oct 31 17:46:22 PDT 2007; root:xnu-1228.0.2~1/RELEASE_I386 i386<br>
-Testcase execution time: 1 minutes, 13 seconds.<br>
-Tests completed on Thu Jan  3 00:46:35 2008.<br><br>
+1135 test(s) selected, 1127 test(s) completed, 51 failures reported (4.52% failed)<br>
+Engine command line: /Volumes/Big/ggaren/build/Debug/testkjs <br>
+OS type: Darwin il0301e-dhcp191.apple.com 9.2.0 Darwin Kernel Version 9.2.0: Tue Feb  5 16:13:22 PST 2008; root:xnu-1228.3.13~1/RELEASE_I386 i386<br>
+Testcase execution time: 1 minutes, 50 seconds.<br>
+Tests completed on Thu Mar  6 13:53:55 2008.<br><br>
 [ <a href='#fail_detail'>Failure Details</a> | <a href='#retest_list'>Retest List</a> | <a href='menu.html'>Test Selection Page</a> ]<br>
 <hr>
 <a name='fail_detail'></a>
@@ -43,8 +43,8 @@ Failure messages were:<br>
 (Mon Feb 28 2000 16:00:00 GMT-0800 (PST)).toLocaleTimeString() = 4:00:00 PM PST FAILED! expected: 16:00:00<br>
 (Mon Feb 28 2000 15:59:59 GMT-0800 (PST)).toLocaleTimeString() = 3:59:59 PM PST FAILED! expected: 15:59:59<br>
 (Tue Feb 29 2000 00:00:00 GMT-0800 (PST)).toLocaleTimeString() = 12:00:00 AM PST FAILED! expected: 00:00:00<br>
-(Thu Jan 03 2008 00:46:22 GMT-0800 (PST)).toLocaleTimeString() = 12:46:22 AM PST FAILED! expected: 00:46:22<br>
-(Thu Jan 03 2008 08:46:22 GMT-0800 (PST)).toLocaleTimeString() = 8:46:22 AM PST FAILED! expected: 08:46:22<br>
+(Thu Mar 06 2008 13:53:32 GMT-0800 (PST)).toLocaleTimeString() = 1:53:32 PM PST FAILED! expected: 13:53:32<br>
+(Thu Mar 06 2008 21:53:32 GMT-0800 (PST)).toLocaleTimeString() = 9:53:32 PM PST FAILED! expected: 21:53:32<br>
 (Fri Dec 31 2004 16:00:00 GMT-0800 (PST)).toLocaleTimeString() = 4:00:00 PM PST FAILED! expected: 16:00:00<br>
 (Fri Dec 31 2004 15:59:59 GMT-0800 (PST)).toLocaleTimeString() = 3:59:59 PM PST FAILED! expected: 15:59:59<br>
 (Sat Jan 01 2005 00:00:00 GMT-0800 (PST)).toLocaleTimeString() = 12:00:00 AM PST FAILED! expected: 00:00:00<br>
@@ -166,7 +166,7 @@ FAILED!: [reported from test()] <br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82270] ./ecma_3/Statements/regress-194364.js line 1: SyntaxError: Parse error<br>
+[93325] ./ecma_3/Statements/regress-194364.js line 1: SyntaxError: Parse error<br>
 </tt><br>
 <a name='failure9'></a><dd><b>Testcase <a target='other_window' href='./ecma_3/Unicode/uc-001.js'>ecma_3/Unicode/uc-001.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=23610' target='other_window'>Bug Number 23610</a><br>
  [ <a href='#failure8'>Previous Failure</a> | <a href='#failure10'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
@@ -317,7 +317,7 @@ eval("function f(){}function g(){}") = undefined FAILED! expected: error<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 script-001 NativeScript<br>
-[82375] ./js1_3/Script/script-001.js line 133: ReferenceError: Can't find variable: Script<br>
+[93430] ./js1_3/Script/script-001.js line 133: ReferenceError: Can't find variable: Script<br>
 </tt><br>
 <a name='failure27'></a><dd><b>Testcase <a target='other_window' href='./js1_3/regress/function-001-n.js'>js1_3/regress/function-001-n.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=10278' target='other_window'>Bug Number 10278</a><br>
  [ <a href='#failure26'>Previous Failure</a> | <a href='#failure28'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
@@ -351,7 +351,7 @@ Testcase produced no output!</tt><br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82420] ./js1_5/Exceptions/errstack-001.js line 247: TypeError: Undefined value<br>
+[93477] ./js1_5/Exceptions/errstack-001.js line 247: TypeError: Undefined value<br>
 </tt><br>
 <a name='failure32'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Exceptions/regress-50447.js'>js1_5/Exceptions/regress-50447.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=50447' target='other_window'>Bug Number 50447</a><br>
  [ <a href='#failure31'>Previous Failure</a> | <a href='#failure33'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
@@ -360,7 +360,7 @@ Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 BUGNUMBER: 50447<br>
 STATUS: Test (non-ECMA) Error object properties fileName, lineNumber<br>
-[82421] ./js1_5/Exceptions/regress-50447.js line 65: TypeError: Undefined value<br>
+[93478] ./js1_5/Exceptions/regress-50447.js line 65: TypeError: Undefined value<br>
 </tt><br>
 <a name='failure33'></a><dd><b>Testcase <a target='other_window' href='./js1_5/GetSet/getset-001.js'>js1_5/GetSet/getset-001.js</a> failed</b> <br>
  [ <a href='#failure32'>Previous Failure</a> | <a href='#failure34'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
@@ -385,28 +385,28 @@ Testcase produced no output!</tt><br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82436] ./js1_5/Object/regress-90596-001.js line 48: TypeError: Value undefined (result of expression obj.toSource) is not object.<br>
+[93493] ./js1_5/Object/regress-90596-001.js line 48: TypeError: Value undefined (result of expression obj.toSource) is not object.<br>
 </tt><br>
 <a name='failure37'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Object/regress-90596-002.js'>js1_5/Object/regress-90596-002.js</a> failed</b> <br>
  [ <a href='#failure36'>Previous Failure</a> | <a href='#failure38'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82437] ./js1_5/Object/regress-90596-002.js line 48: ReferenceError: Can't find variable: uneval<br>
+[93494] ./js1_5/Object/regress-90596-002.js line 48: ReferenceError: Can't find variable: uneval<br>
 </tt><br>
 <a name='failure38'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Object/regress-96284-001.js'>js1_5/Object/regress-96284-001.js</a> failed</b> <br>
  [ <a href='#failure37'>Previous Failure</a> | <a href='#failure39'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82439] ./js1_5/Object/regress-96284-001.js line 49: TypeError: Value undefined (result of expression obj1.toSource) is not object.<br>
+[93496] ./js1_5/Object/regress-96284-001.js line 49: TypeError: Value undefined (result of expression obj1.toSource) is not object.<br>
 </tt><br>
 <a name='failure39'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Object/regress-96284-002.js'>js1_5/Object/regress-96284-002.js</a> failed</b> <br>
  [ <a href='#failure38'>Previous Failure</a> | <a href='#failure40'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82440] ./js1_5/Object/regress-96284-002.js line 49: ReferenceError: Can't find variable: uneval<br>
+[93497] ./js1_5/Object/regress-96284-002.js line 49: ReferenceError: Can't find variable: uneval<br>
 </tt><br>
 <a name='failure40'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-44009.js'>js1_5/Regress/regress-44009.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=44009' target='other_window'>Bug Number 44009</a><br>
  [ <a href='#failure39'>Previous Failure</a> | <a href='#failure41'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
@@ -415,19 +415,10 @@ Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 BUGNUMBER: 44009<br>
 STATUS: Testing that we don't crash on obj.toSource()<br>
-[82445] ./js1_5/Regress/regress-44009.js line 60: TypeError: Value undefined (result of expression obj.toSource) is not object.<br>
+[93502] ./js1_5/Regress/regress-44009.js line 60: TypeError: Value undefined (result of expression obj.toSource) is not object.<br>
 </tt><br>
-<a name='failure41'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-68498-003.js'>js1_5/Regress/regress-68498-003.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=68498' target='other_window'>Bug Number 68498</a><br>
+<a name='failure41'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-103602.js'>js1_5/Regress/regress-103602.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=103602' target='other_window'>Bug Number 103602</a><br>
  [ <a href='#failure40'>Previous Failure</a> | <a href='#failure42'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
-<tt>STATUS: Testing calling obj.eval(str)<br>
-Failure messages were:<br>
-FAILED!: [reported from test()] Testing calling obj.eval(str); currently at expect[1] within test -<br>
-FAILED!: [reported from test()] Type mismatch, expected type number, actual type boolean<br>
-FAILED!: [reported from test()] Expected value '43', Actual value 'false'<br>
-FAILED!: [reported from test()] <br>
-</tt><br>
-<a name='failure42'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-103602.js'>js1_5/Regress/regress-103602.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=103602' target='other_window'>Bug Number 103602</a><br>
- [ <a href='#failure41'>Previous Failure</a> | <a href='#failure43'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>STATUS: Reassignment to a const is NOT an error per ECMA<br>
 Failure messages were:<br>
 FAILED!: [reported from test()] Section 1 of test -<br>
@@ -437,28 +428,28 @@ FAILED!: [reported from test()] Section 3 of test -<br>
 FAILED!: [reported from test()] Expected value '1', Actual value '2'<br>
 FAILED!: [reported from test()] <br>
 </tt><br>
-<a name='failure43'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-104077.js'>js1_5/Regress/regress-104077.js</a> failed</b> <br>
- [ <a href='#failure42'>Previous Failure</a> | <a href='#failure44'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure42'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-104077.js'>js1_5/Regress/regress-104077.js</a> failed</b> <br>
+ [ <a href='#failure41'>Previous Failure</a> | <a href='#failure43'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 Testcase produced no output!</tt><br>
-<a name='failure44'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-127557.js'>js1_5/Regress/regress-127557.js</a> failed</b> <br>
- [ <a href='#failure43'>Previous Failure</a> | <a href='#failure45'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure43'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-127557.js'>js1_5/Regress/regress-127557.js</a> failed</b> <br>
+ [ <a href='#failure42'>Previous Failure</a> | <a href='#failure44'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82471] ./js1_5/Regress/regress-127557.js line 75: ReferenceError: Can't find variable: clone<br>
+[93528] ./js1_5/Regress/regress-127557.js line 75: ReferenceError: Can't find variable: clone<br>
 </tt><br>
-<a name='failure45'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-172699.js'>js1_5/Regress/regress-172699.js</a> failed</b> <br>
- [ <a href='#failure44'>Previous Failure</a> | <a href='#failure46'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure44'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-172699.js'>js1_5/Regress/regress-172699.js</a> failed</b> <br>
+ [ <a href='#failure43'>Previous Failure</a> | <a href='#failure45'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82481] ./js1_5/Regress/regress-172699.js line 61: URIError: URI error<br>
+[93537] ./js1_5/Regress/regress-172699.js line 61: URIError: URI error<br>
 </tt><br>
-<a name='failure46'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-179524.js'>js1_5/Regress/regress-179524.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=179524' target='other_window'>Bug Number 179524</a><br>
- [ <a href='#failure45'>Previous Failure</a> | <a href='#failure47'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure45'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Regress/regress-179524.js'>js1_5/Regress/regress-179524.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=179524' target='other_window'>Bug Number 179524</a><br>
+ [ <a href='#failure44'>Previous Failure</a> | <a href='#failure46'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>STATUS: Don't crash on extraneous arguments to str.match(), etc.<br>
 Failure messages were:<br>
 FAILED!: [reported from test()] Section 14 of test -<br>
@@ -508,15 +499,15 @@ FAILED!: [reported from test()] Section 36 of test -<br>
 FAILED!: [reported from test()] Expected value 'SHOULD HAVE FALLEN INTO CATCH-BLOCK!', Actual value 'ABC Zbc'<br>
 FAILED!: [reported from test()] <br>
 </tt><br>
-<a name='failure47'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Scope/regress-220584.js'>js1_5/Scope/regress-220584.js</a> failed</b> <br>
- [ <a href='#failure46'>Previous Failure</a> | <a href='#failure48'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure46'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Scope/regress-220584.js'>js1_5/Scope/regress-220584.js</a> failed</b> <br>
+ [ <a href='#failure45'>Previous Failure</a> | <a href='#failure47'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
-[82506] ./js1_5/Scope/regress-220584.js line 56: ReferenceError: Can't find variable: Script<br>
+[93562] ./js1_5/Scope/regress-220584.js line 56: ReferenceError: Can't find variable: Script<br>
 </tt><br>
-<a name='failure48'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Scope/scope-001.js'>js1_5/Scope/scope-001.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=53268' target='other_window'>Bug Number 53268</a><br>
- [ <a href='#failure47'>Previous Failure</a> | <a href='#failure49'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure47'></a><dd><b>Testcase <a target='other_window' href='./js1_5/Scope/scope-001.js'>js1_5/Scope/scope-001.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=53268' target='other_window'>Bug Number 53268</a><br>
+ [ <a href='#failure46'>Previous Failure</a> | <a href='#failure48'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>STATUS: Testing scope after changing obj.__proto__<br>
 Failure messages were:<br>
 FAILED!: [reported from test()] Step 1:  setting obj.__proto__ = global object<br>
@@ -527,8 +518,8 @@ FAILED!: [reported from test()] Type mismatch, expected type undefined, actual t
 FAILED!: [reported from test()] Expected value 'undefined', Actual value '1'<br>
 FAILED!: [reported from test()] <br>
 </tt><br>
-<a name='failure49'></a><dd><b>Testcase <a target='other_window' href='./js1_6/Regress/regress-301574.js'>js1_6/Regress/regress-301574.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=301574' target='other_window'>Bug Number 301574</a><br>
- [ <a href='#failure48'>Previous Failure</a> | <a href='#failure50'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure48'></a><dd><b>Testcase <a target='other_window' href='./js1_6/Regress/regress-301574.js'>js1_6/Regress/regress-301574.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=301574' target='other_window'>Bug Number 301574</a><br>
+ [ <a href='#failure47'>Previous Failure</a> | <a href='#failure49'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>STATUS: E4X should be enabled even when e4x=1 not specified<br>
 Failure messages were:<br>
 FAILED!: E4X should be enabled even when e4x=1 not specified: XML()<br>
@@ -538,27 +529,27 @@ FAILED!: E4X should be enabled even when e4x=1 not specified: XMLList()<br>
 FAILED!: Expected value 'No error', Actual value 'error: ReferenceError: Can't find variable: XML'<br>
 FAILED!: <br>
 </tt><br>
-<a name='failure50'></a><dd><b>Testcase <a target='other_window' href='./js1_6/Regress/regress-309242.js'>js1_6/Regress/regress-309242.js</a> failed</b> <br>
- [ <a href='#failure49'>Previous Failure</a> | <a href='#failure51'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure49'></a><dd><b>Testcase <a target='other_window' href='./js1_6/Regress/regress-309242.js'>js1_6/Regress/regress-309242.js</a> failed</b> <br>
+ [ <a href='#failure48'>Previous Failure</a> | <a href='#failure50'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 Testcase produced no output!</tt><br>
-<a name='failure51'></a><dd><b>Testcase <a target='other_window' href='./js1_6/Regress/regress-314887.js'>js1_6/Regress/regress-314887.js</a> failed</b> <br>
- [ <a href='#failure50'>Previous Failure</a> | <a href='#failure52'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure50'></a><dd><b>Testcase <a target='other_window' href='./js1_6/Regress/regress-314887.js'>js1_6/Regress/regress-314887.js</a> failed</b> <br>
+ [ <a href='#failure49'>Previous Failure</a> | <a href='#failure51'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 Testcase produced no output!</tt><br>
-<a name='failure52'></a><dd><b>Testcase <a target='other_window' href='./js1_6/String/regress-306591.js'>js1_6/String/regress-306591.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=306591' target='other_window'>Bug Number 306591</a><br>
- [ <a href='#failure51'>Previous Failure</a> | <a href='#failure53'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
+<a name='failure51'></a><dd><b>Testcase <a target='other_window' href='./js1_6/String/regress-306591.js'>js1_6/String/regress-306591.js</a> failed</b> <a href='http://bugzilla.mozilla.org/show_bug.cgi?id=306591' target='other_window'>Bug Number 306591</a><br>
+ [ <a href='#failure50'>Previous Failure</a> | <a href='#failure52'>Next Failure</a> | <a href='#tippy_top'>Top of Page</a> ]<br>
 <tt>Expected exit code 0, got 3<br>
 Testcase terminated with signal 0<br>
 Complete testcase output was:<br>
 BUGNUMBER: 306591<br>
 STATUS: String static methods<br>
 STATUS: See https://bugzilla.mozilla.org/show_bug.cgi?id=304828<br>
-[82528] ./js1_6/String/regress-306591.js line 48: TypeError: Value undefined (result of expression String.split) is not object.<br>
+[93584] ./js1_6/String/regress-306591.js line 48: TypeError: Value undefined (result of expression String.split) is not object.<br>
 </tt><br>
 </dl>
 [ <a href='#tippy_top'>Top of Page</a> | <a href='#fail_detail'>Top of Failures</a> ]<br>
@@ -566,9 +557,9 @@ STATUS: See https://bugzilla.mozilla.org/show_bug.cgi?id=304828<br>
 <pre>
 <a name='retest_list'></a>
 <h2>Retest List</h2><br>
-# Retest List, kjs, generated Thu Jan  3 00:46:35 2008.
+# Retest List, kjs, generated Thu Mar  6 13:53:55 2008.
 # Original test base was: All tests.
-# 1127 of 1135 test(s) were completed, 52 failures reported.
+# 1127 of 1135 test(s) were completed, 51 failures reported.
 ecma/TypeConversion/9.3.1-3.js
 ecma_2/Exceptions/function-001.js
 ecma_3/Date/15.9.5.7.js
@@ -609,7 +600,6 @@ js1_5/Object/regress-90596-002.js
 js1_5/Object/regress-96284-001.js
 js1_5/Object/regress-96284-002.js
 js1_5/Regress/regress-44009.js
-js1_5/Regress/regress-68498-003.js
 js1_5/Regress/regress-103602.js
 js1_5/Regress/regress-104077.js
 js1_5/Regress/regress-127557.js
index 0f44c9d4f4720c2584f3f4ba977548d174a5276d..b856dc19ac4d8ac729784feb8ea927d79b1600b4 100644 (file)
@@ -1,3 +1,31 @@
+2008-03-06  Geoffrey Garen  <ggaren@apple.com>
+
+        Reviewed by Darin Adler.
+
+        Tests for <rdar://problem/5689093> Stricter (ES4) eval semantics
+        
+        * fast/js/eval-cross-window-expected.txt: Added.
+        * fast/js/eval-cross-window.html: Added.
+        * fast/js/eval-keyword-vs-function-expected.txt: Added.
+        * fast/js/eval-keyword-vs-function.html: Added.
+        * fast/js/eval-overriding-expected.txt: Added.
+        * fast/js/eval-overriding.html: Added.
+        
+        Tests to make sure not to regress security:
+
+        * http/tests/security/resources/xss-eval2.html: Added.
+        * http/tests/security/resources/xss-eval3.html: Added.
+        * http/tests/security/xss-eval-expected.txt: Added.
+        * http/tests/security/xss-eval.html: Added.
+
+        I removed these tests because we no longer match the behavior they
+        expected, and the new tests are more comprehensive:
+        
+        * fast/js/window-eval-context-expected.txt: Removed.
+        * fast/js/window-eval-context.html: Removed.
+        * fast/js/window-eval-tearoff-expected.txt: Removed.
+        * fast/js/window-eval-tearoff.html: Removed.
+
 2008-03-06  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by Mitz.
diff --git a/LayoutTests/fast/js/eval-cross-window-expected.txt b/LayoutTests/fast/js/eval-cross-window-expected.txt
new file mode 100644 (file)
index 0000000..9ab8d69
--- /dev/null
@@ -0,0 +1,40 @@
+This page verifies that eval, when called as a function, uses the "this" object provided by the call as its variable object, scope chain, and "this" object. However, if the "this" object is not the global object eval was originally associated with, eval throws an exception.
+
+If the test passes, you'll see a series of pass messages below.
+
+
+----- Scope Chain Head for Getters: -----
+
+PASS: window.eval("x") should be 0 and is.
+PASS: frames[0].eval("x") should be 1 and is.
+PASS: window.eval("x") should be EvalError and is.
+PASS: frames[0].eval("x") should be EvalError and is.
+
+----- Scope Chain for Getters: -----
+
+PASS: window.eval("xx") should be ReferenceError and is.
+PASS: frames[0].eval("xx") should be ReferenceError and is.
+PASS: window.eval("xx") should be EvalError and is.
+PASS: frames[0].eval("xx") should be EvalError and is.
+
+----- Variable Object: -----
+
+PASS: window.eval("var y; "y" in top") should be true and is.
+PASS: frames[0].eval("var y; "y" in top.frames[0]") should be true and is.
+PASS: window.eval("var y; "y" in top.frames[0]") should be EvalError and is.
+PASS: frames[0].eval("var y; "y" in top") should be EvalError and is.
+
+----- Scope Chain for Setters: -----
+
+PASS: window.eval("z = 1; top.z") should be 1 and is.
+PASS: frames[0].eval("z = 2; top.frames[0].z") should be 2 and is.
+PASS: window.eval("z = 3; top.frames[0].z") should be EvalError and is.
+PASS: frames[0].eval("z = 4; top.z") should be EvalError and is.
+
+----- This Object: -----
+
+PASS: window.eval("this") should be [object DOMWindow] and is.
+PASS: frames[0].eval("this") should be [object DOMWindow] and is.
+PASS: window.eval("this") should be EvalError and is.
+PASS: frames[0].eval("this") should be EvalError and is.
+
diff --git a/LayoutTests/fast/js/eval-cross-window.html b/LayoutTests/fast/js/eval-cross-window.html
new file mode 100644 (file)
index 0000000..043cf63
--- /dev/null
@@ -0,0 +1,134 @@
+<p>
+This page verifies that eval, when called as a function, uses the "this" object
+provided by the call as its variable object, scope chain, and "this" object.
+However, if the "this" object is not the global object eval was originally
+associated with, eval throws an exception.
+</p>
+<p>If the test passes, you'll see a series of pass messages below.</p>
+<pre id="console"></pre>
+
+<hr>
+<pre id="console"></pre>
+<iframe style="width:0; height:0"></iframe>
+
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+    
+var topEval = eval;
+var frameEval = frames[0].eval;
+
+function log(s)
+{
+    document.getElementById("console").appendChild(document.createTextNode(s + "\n"));
+}
+
+function shouldBe(aDescription, a, b)
+{
+    if (a === b) {
+        log("PASS: " + aDescription + " should be " + b + " and is.");
+    } else {
+        log("FAIL: " + aDescription + " should be " + b + " but instead is " + a + ".");
+    }
+}
+
+function testGetX()
+{
+    window.x = 0;
+    frames[0].x = 1;
+    var x = 2;
+
+    shouldBe('window.eval("x")', window.eval("x"), 0);
+    shouldBe('frames[0].eval("x")', frames[0].eval("x"), 1);
+
+    window.eval = frameEval;
+    shouldBe('window.eval("x")', (function() { try { return window.eval("x") } catch(e) { return e.name; } })(), "EvalError");
+    window.eval = topEval;
+
+    frames[0].eval = topEval;
+    shouldBe('frames[0].eval("x")', (function() { try { frames[0].eval("x") } catch(e) { return e.name; } })(), "EvalError");
+    frames[0].eval = frameEval;
+}
+
+function testGetXX()
+{
+    var xx = 0;
+
+    shouldBe('window.eval("xx")', (function() { try { return window.eval("xx") } catch(e) { return e.name; } })(), "ReferenceError"); 
+    shouldBe('frames[0].eval("xx")', (function() { try { return frames[0].eval("xx") } catch(e) { return e.name; } })(), "ReferenceError");
+
+    window.eval = frameEval;
+    shouldBe('window.eval("xx")', (function() { try { return window.eval("xx") } catch(e) { return e.name; } })(), "EvalError");
+    window.eval = topEval;
+
+    frames[0].eval = topEval;
+    shouldBe('frames[0].eval("xx")', (function() { try { return frames[0].eval("xx") } catch(e) { return e.name; } })(), "EvalError");
+    frames[0].eval = frameEval;
+}
+
+function testVarY()
+{
+    shouldBe('window.eval("var y; \"y\" in top")', window.eval("var y; \"y\" in top"), true);
+    delete window.y;
+    delete frames[0].y;
+
+    shouldBe('frames[0].eval("var y; \"y\" in top.frames[0]")', frames[0].eval("var y; \"y\" in top.frames[0]"), true);
+    delete window.y;
+    delete frames[0].y;
+
+    window.eval = frameEval;
+    shouldBe('window.eval("var y; \"y\" in top.frames[0]")', (function() { try { window.eval("var y; \"y\" in top.frames[0]") } catch(e) { return e.name; } })(), "EvalError");
+    delete window.y;
+    delete frames[0].y;
+    window.eval = topEval;
+
+    frames[0].eval = topEval;
+    shouldBe('frames[0].eval("var y; \"y\" in top")', (function() { try { frames[0].eval("var y; \"y\" in top") } catch(e) { return e.name; } })(), "EvalError");
+    delete window.y;
+    delete frames[0].y;
+    frames[0].eval = frameEval;
+}
+
+function testSetZ()
+{
+    window.z = 0;
+    frames[0].z = 0;
+    var z = 0;
+
+    shouldBe('window.eval("z = 1; top.z")', window.eval("z = 1; top.z"), 1);
+    shouldBe('frames[0].eval("z = 2; top.frames[0].z")', frames[0].eval("z = 2; top.frames[0].z"), 2);
+
+    window.eval = frameEval;
+    shouldBe('window.eval("z = 3; top.frames[0].z")', (function() { try { window.eval("z = 3; top.frames[0].z") } catch(e) { return e.name; } })(), "EvalError");
+    window.eval = topEval;
+
+    frames[0].eval = topEval;
+    shouldBe('frames[0].eval("z = 4; top.z")', (function() { try { frames[0].eval("z = 4; top.z") } catch(e) { return e.name; } })(), "EvalError");
+    frames[0].eval = frameEval;
+}
+
+function testThis()
+{
+    shouldBe('window.eval("this")', window.eval("this"), window);
+    shouldBe('frames[0].eval("this")', frames[0].eval("this"), frames[0]);
+
+    window.eval = frameEval;
+    shouldBe('window.eval("this")', (function() { try { window.eval("this"), frames[0] } catch(e) { return e.name; } })(), "EvalError");
+    window.eval = topEval;
+
+    frames[0].eval = topEval;
+    shouldBe('frames[0].eval("this")', (function() { try { frames[0].eval("this"), window } catch(e) { return e.name; } })(), "EvalError");
+    frames[0].eval = frameEval;
+}
+
+log("\n----- Scope Chain Head for Getters: -----\n");
+testGetX();
+log("\n----- Scope Chain for Getters: -----\n");
+testGetXX();
+log("\n----- Variable Object: -----\n");
+testVarY();
+log("\n----- Scope Chain for Setters: -----\n");
+testSetZ();
+log("\n----- This Object: -----\n");
+testThis.call({});
+</script>
diff --git a/LayoutTests/fast/js/eval-keyword-vs-function-expected.txt b/LayoutTests/fast/js/eval-keyword-vs-function-expected.txt
new file mode 100644 (file)
index 0000000..602a391
--- /dev/null
@@ -0,0 +1,40 @@
+This page verifies that eval has two meanings:
+
+An operator: executes a script in local scope with the local scope's variable object and "this" object.
+A global function: executes a script in global scope with the global scope's variable object and "this" object.
+Meaning #2 should remain constant even if the global eval function is copied into a global variable ("globalEval") or a local variable ("localEval").
+If the test passes, you'll see a series of pass messages below.
+
+
+----- Scope Chain for Getters: -----
+
+PASS: eval("x") should be 1 and is.
+PASS: window.eval("x") should be 0 and is.
+PASS: globalEval("x") should be 0 and is.
+PASS: localEval("x") should be 0 and is.
+PASS: (function() { var eval = window.eval; return eval("x"); })() should be 0 and is.
+
+----- Variable Object: -----
+
+PASS: eval("var y; "y" in window") should be false and is.
+PASS: window.eval("var y; "y" in window") should be true and is.
+PASS: globalEval("var y; "y" in window") should be true and is.
+PASS: localEval("var y; "y" in window") should be true and is.
+PASS: (function() { var eval = window.eval; return eval("var y; "y" in window"); })() should be true and is.
+
+----- Scope Chain for Setters: -----
+
+PASS: eval("z = 1; window.z") should be 0 and is.
+PASS: window.eval("z = 2; window.z") should be 2 and is.
+PASS: globalEval("z = 3; window.z") should be 3 and is.
+PASS: localEval("z = 4; window.z") should be 4 and is.
+PASS: (function() { var eval = window.eval; return eval("z = 5; window.z"); })() should be 5 and is.
+
+----- This Object: -----
+
+PASS: eval("this") should be ["this" object passed to .call()] and is.
+PASS: window.eval("this") should be [object DOMWindow] and is.
+PASS: globalEval("this") should be [object DOMWindow] and is.
+PASS: localEval("this") should be [object DOMWindow] and is.
+PASS: (function() { var eval = window.eval; return eval("this"); })() should be [object DOMWindow] and is.
+
diff --git a/LayoutTests/fast/js/eval-keyword-vs-function.html b/LayoutTests/fast/js/eval-keyword-vs-function.html
new file mode 100644 (file)
index 0000000..665c5b4
--- /dev/null
@@ -0,0 +1,104 @@
+<p>
+This page verifies that eval has two meanings:
+<ol>
+    <li>An operator: executes a script in local scope with the local scope's variable object and "this" object.</li>
+    <li>A global function: executes a script in global scope with the global scope's variable object and "this" object.</li>
+</ol>
+Meaning #2 should remain constant even if the global eval function is copied
+into a global variable ("globalEval") or a local variable ("localEval").
+</p>
+<p>If the test passes, you'll see a series of pass messages below.</p>
+<hr>
+<pre id="console"></pre>
+
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+    
+var globalEval = eval;
+
+function log(s)
+{
+    document.getElementById("console").appendChild(document.createTextNode(s + "\n"));
+}
+
+function shouldBe(aDescription, a, b)
+{
+    if (a === b) {
+        log("PASS: " + aDescription + " should be " + b + " and is.");
+    } else {
+        log("FAIL: " + aDescription + " should be " + b + " but instead is " + a + ".");
+    }
+}
+
+function testGetX()
+{
+    var x = 1;
+    var localEval = window.eval;
+    
+    window.x = 0;
+
+    shouldBe('eval("x")', eval("x"), 1);
+
+    shouldBe('window.eval("x")', window.eval("x"), 0);
+    shouldBe('globalEval("x")', globalEval("x"), 0);
+    shouldBe('localEval("x")', localEval("x"), 0);
+    shouldBe('(function() { var eval = window.eval; return eval("x"); })()', (function() { var eval = window.eval; return eval("x"); })(), 0);
+}
+
+function testVarY()
+{
+    var localEval = window.eval;
+
+    shouldBe('eval("var y; \"y\" in window")', eval("var y; \"y\" in window"), false);
+    delete window.y;
+    
+    shouldBe('window.eval("var y; \"y\" in window")', window.eval("var y; \"y\" in window"), true);
+    delete window.y;
+
+    shouldBe('globalEval("var y; \"y\" in window")', globalEval("var y; \"y\" in window"), true);
+    delete window.y;
+
+    shouldBe('localEval("var y; \"y\" in window")', localEval("var y; \"y\" in window"), true);
+    delete window.y;
+    
+    shouldBe('(function() { var eval = window.eval; return eval("var y; \"y\" in window"); })()', (function() { var eval = window.eval; return eval("var y; \"y\" in window"); })(), true);
+}
+
+function testSetZ()
+{
+    var z = 0;
+    window.z = 0;
+    var localEval = window.eval;
+
+    shouldBe('eval("z = 1; window.z")', eval("z = 1; window.z"), 0);
+
+    shouldBe('window.eval("z = 2; window.z")', window.eval("z = 2; window.z"), 2);
+    
+    shouldBe('globalEval("z = 3; window.z")', globalEval("z = 3; window.z"), 3);
+
+    shouldBe('localEval("z = 4; window.z")', localEval("z = 4; window.z"), 4);
+
+    shouldBe('(function() { var eval = window.eval; return eval("z = 5; window.z"); })()', (function() { var eval = window.eval; return eval("z = 5; window.z"); })(), 5);
+}
+
+function testThis()
+{
+    var localEval = window.eval;
+
+    shouldBe('eval("this")', eval("this"), this);
+    shouldBe('window.eval("this")', window.eval("this"), window);
+    shouldBe('globalEval("this")', globalEval("this"), window);
+    shouldBe('localEval("this")', localEval("this"), window);
+    shouldBe('(function() { var eval = window.eval; return eval("this"); })()', (function() { var eval = window.eval; return eval("this"); })(), window);
+}
+
+log("\n----- Scope Chain for Getters: -----\n");
+testGetX();
+log("\n----- Variable Object: -----\n");
+testVarY();
+log("\n----- Scope Chain for Setters: -----\n");
+testSetZ();
+log("\n----- This Object: -----\n");
+testThis.call({ toString: function() { return "[\"this\" object passed to .call()]"; } });
+</script>
diff --git a/LayoutTests/fast/js/eval-overriding-expected.txt b/LayoutTests/fast/js/eval-overriding-expected.txt
new file mode 100644 (file)
index 0000000..7bcb717
--- /dev/null
@@ -0,0 +1,9 @@
+This page verifies that eval can be overridden.
+
+If the test passes, you'll see a series of pass messages below.
+
+PASS: eval("x") should be global-scope eval override and is.
+PASS: eval("x") should be with-scope eval override and is.
+PASS: eval("x") should be catch-scope eval override and is.
+PASS: eval("x") should be local-scope eval override and is.
+
diff --git a/LayoutTests/fast/js/eval-overriding.html b/LayoutTests/fast/js/eval-overriding.html
new file mode 100644 (file)
index 0000000..a0d7181
--- /dev/null
@@ -0,0 +1,48 @@
+<p>This page verifies that eval can be overridden.</p>
+<p>If the test passes, you'll see a series of pass messages below.</p>
+<pre id="console"></pre>
+<hr>
+
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+    
+var x = "built-in eval";
+
+function log(s)
+{
+    document.getElementById("console").appendChild(document.createTextNode(s + "\n"));
+}
+
+function shouldBe(aDescription, a, b)
+{
+    if (a === b) {
+        log("PASS: " + aDescription + " should be " + b + " and is.");
+    } else {
+        log("FAIL: " + aDescription + " should be " + b + " but instead is " + a + ".");
+    }
+}
+
+// Test overriding eval in global scope
+eval = function() { return "global-scope eval override"; }
+shouldBe('eval("x")', eval("x"), "global-scope eval override");
+
+// Test overriding eval using "with"
+with ({ eval: function() { return "with-scope eval override"; }}) {
+    shouldBe('eval("x")', eval("x"), "with-scope eval override");
+}
+
+// Test overriding eval using "catch"
+try {
+    throw function() { return "catch-scope eval override"; };
+} catch(eval) {
+    shouldBe('eval("x")', eval("x"), "catch-scope eval override");
+}
+
+// Test overriding eval using locally declared function
+(function()
+{
+    function eval() { return "local-scope eval override"; }
+    shouldBe('eval("x")', eval("x"), "local-scope eval override");
+})()
+</script>
diff --git a/LayoutTests/fast/js/window-eval-context-expected.txt b/LayoutTests/fast/js/window-eval-context-expected.txt
deleted file mode 100644 (file)
index d15824b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-
-Test that otherWindow.eval() keeps variables of calling context visible: PASS
-Test that otherWindow.eval() is executed with otherWindow in scope: PASS
-Test that otherWindow.eval() has otherWindow as the window object: PASS
-Test that otherWindow.eval() does not change 'this': PASS
-Test that otherWindow.eval() changes variable context: PASS
diff --git a/LayoutTests/fast/js/window-eval-context.html b/LayoutTests/fast/js/window-eval-context.html
deleted file mode 100644 (file)
index 795d74b..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<body>
-<script>
-function print(message, color) 
-{
-    var paragraph = document.createElement("div");
-    paragraph.appendChild(document.createTextNode(message));
-    paragraph.style.fontFamily = "monospace";
-    if (color)
-        paragraph.style.color = color;
-    document.getElementById("console").appendChild(paragraph);
-}
-
-if (window.layoutTestController)
-    layoutTestController.dumpAsText();
-</script>
-<iframe id=i src='about:blank' width=10 height=10>
-</iframe><br>
-<div id=console></div>
-<script>
-var otherWindow = document.getElementById('i').contentWindow;
-var res;
-var localVar = 1;
-
-res = otherWindow.eval('localVar == 1');
-print("Test that otherWindow.eval() keeps variables of calling context visible: " + (res ? "PASS" : "FAIL")) ;
-
-otherWindow.localVar = 2;
-res = otherWindow.eval('localVar == 2');
-print("Test that otherWindow.eval() is executed with otherWindow in scope: " + (res ? "PASS" : "FAIL")) ;
-
-res = otherWindow.eval('window == otherWindow');
-print("Test that otherWindow.eval() has otherWindow as the window object: " + (res ? "PASS" : "FAIL")) ;
-
-var savedThis = this;
-res = otherWindow.eval('this == savedThis');
-print("Test that otherWindow.eval() does not change 'this': " + (res ? "PASS" : "FAIL")) ;
-
-otherWindow.eval('var myObject = new Object()');
-res = (typeof myObject == "undefined");
-print("Test that otherWindow.eval() changes variable context: " + (res ? "PASS" : "FAIL")) ;
-</script>
diff --git a/LayoutTests/fast/js/window-eval-tearoff-expected.txt b/LayoutTests/fast/js/window-eval-tearoff-expected.txt
deleted file mode 100644 (file)
index c751703..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-
-Test that otherWindow.eval() performs ActivationImp tear-off: PASS
diff --git a/LayoutTests/fast/js/window-eval-tearoff.html b/LayoutTests/fast/js/window-eval-tearoff.html
deleted file mode 100644 (file)
index d3d84d8..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<body>
-<script>
-function print(message, color) 
-{
-    var paragraph = document.createElement("div");
-    paragraph.appendChild(document.createTextNode(message));
-    paragraph.style.fontFamily = "monospace";
-    if (color)
-        paragraph.style.color = color;
-    document.getElementById("console").appendChild(paragraph);
-}
-
-if (window.layoutTestController)
-    layoutTestController.dumpAsText();
-</script>
-<iframe id=i src='about:blank' width=10 height=10>
-</iframe>
-<div id=console></div>
-<script>
-var otherWindow = document.getElementById('i').contentWindow;
-var closure;
-
-function otherWindowClosure()
-{
-    var localVar = 1;
-    
-    return otherWindow.eval("(function () { return localVar; })");
-}
-
-closure = otherWindowClosure();
-
-print("Test that otherWindow.eval() performs ActivationImp tear-off: " + (closure() == 1 ? "PASS" : "FAIL")) ;
-</script>
diff --git a/LayoutTests/http/tests/security/resources/xss-eval2.html b/LayoutTests/http/tests/security/resources/xss-eval2.html
new file mode 100644 (file)
index 0000000..7e67b3a
--- /dev/null
@@ -0,0 +1,15 @@
+<script>
+parent.childEval = eval;
+
+parent.childEvalCaller = function(s) {
+    return eval(s);
+}
+
+parent.childLocalEvalCaller = (function()
+{
+    var e = eval;
+    return function(s) { return e(s); };
+})();
+
+location.href = "http://localhost:8000/security/resources/xss-eval3.html";
+</script>
diff --git a/LayoutTests/http/tests/security/resources/xss-eval3.html b/LayoutTests/http/tests/security/resources/xss-eval3.html
new file mode 100644 (file)
index 0000000..f06991b
--- /dev/null
@@ -0,0 +1,3 @@
+<script>
+parent.postMessage("done");
+</script>
diff --git a/LayoutTests/http/tests/security/xss-eval-expected.txt b/LayoutTests/http/tests/security/xss-eval-expected.txt
new file mode 100644 (file)
index 0000000..4cb623b
--- /dev/null
@@ -0,0 +1,11 @@
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/xss-eval3.html from frame with URL http://127.0.0.1:8000/security/xss-eval.html. Domains, protocols and ports must match.
+
+This page verifies that you can't use eval to subvert cross-domain checks.
+
+If the test passes, you'll see a pass message below.
+
+PASS: eval.call(frames[0], 'document') should be EvalError and is.
+PASS: childEval.call(frames[0], 'document') should be EvalError and is.
+PASS: childEvalCaller('document') should be TypeError and is.
+PASS: childLocalEvalCaller('document') should be EvalError and is.
+
diff --git a/LayoutTests/http/tests/security/xss-eval.html b/LayoutTests/http/tests/security/xss-eval.html
new file mode 100644 (file)
index 0000000..5627282
--- /dev/null
@@ -0,0 +1,41 @@
+<p>This page verifies that you can't use eval to subvert cross-domain checks.</p>
+<p>If the test passes, you'll see a pass message below.</p>
+<hr>
+<pre id="console"></pre>
+
+<iframe style="width:0; height: 0" src="resources/xss-eval2.html"></iframe>
+
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.waitUntilDone();
+}
+
+function log(s)
+{
+    document.getElementById("console").appendChild(document.createTextNode(s + "\n"));
+}
+
+function shouldBe(aDescription, a, b)
+{
+    if (a === b) {
+        log("PASS: " + aDescription + " should be " + b + " and is.");
+    } else {
+        log("FAIL: " + aDescription + " should be " + b + " but instead is " + a + ".");
+    }
+}
+
+addEventListener("message", function()
+{
+    shouldBe("eval.call(frames[0], 'document')", (function() { try { return eval.call(frames[0], 'document'); } catch(e) { return e.name; } })(), "EvalError");
+
+    shouldBe("childEval.call(frames[0], 'document')", (function() { try { return childEval.call(frames[0], 'document'); } catch(e) { return e.name; } })(), "EvalError");
+
+    shouldBe("childEvalCaller('document')", (function() { try { return childEvalCaller('document'); } catch(e) { return e.name; } })(), "TypeError");
+    
+    shouldBe("childLocalEvalCaller('document')", (function() { try { return childLocalEvalCaller('document'); } catch(e) { return e.name; } })(), "EvalError");
+    
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}, false);
+</script>