Commit 5930185c authored by oliver@apple.com's avatar oliver@apple.com

2010-10-01 Oliver Hunt <oliver@apple.com>

        Reviewed by Gavin Barraclough.

        [ES5] Implement strict mode
        https://bugs.webkit.org/show_bug.cgi?id=10701

        Initial strict mode implementation.  This is the simplest
        implementation that could possibly work and adds (hopefully)
        all of the restrictions required by strict mode.  There are
        a number of inefficiencies, especially in the handling of
        arguments and eval as smart implementations would make this
        patch more complicated.

        The SyntaxChecker AST builder has become somewhat more complex
        as strict mode does require more parse tree information to
        validate the syntax.

        Summary of major changes to the parser:
            * We track when we enter strict mode (this may come as a surprise)
            * Strict mode actually requires a degree of AST knowledge to validate
              so the SyntaxChecker now produces values that can be used to distinguish
              "node" types.
            * We now track variables that are written to.  We do this to
              statically identify writes to global properties that don't exist
              and abort at that point.  This should actually make it possible
              to optimise some other cases in the future but for now it's
              purely for validity checking.  Currently writes are only tracked
              in strict mode code.
            * Labels are now tracked as it is now a syntax error to jump to a label
              that does not exist (or to use break, continue, or return in a context
              where they would be invalid).

        Runtime changes:
            * In order to get correct hanlding of the Arguments object all
              strict mode functions that reference arguments create and tearoff
              the arguments object on entry.  This is not strictly necessary
              but was the least work necessary to get the correct behaviour.
            * PutPropertySlot now tracks whether it is being used for a strict
              mode write, and if so Object::put will throw when a write can't be
              completed.
            * StrictEvalActivation was added as an "activation" object for strict
              mode eval (so that strict eval does not introduce new variables into
              the containing scope).

        * CMakeLists.txt:
        * GNUmakefile.am:
        * JavaScriptCore.exp:
        * JavaScriptCore.pro:
        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
        * JavaScriptCore.xcodeproj/project.pbxproj:
        * bytecode/CodeBlock.cpp:
        (JSC::CodeBlock::dump):
        (JSC::CodeBlock::CodeBlock):
        (JSC::CodeBlock::reparseForExceptionInfoIfNecessary):
        * bytecode/CodeBlock.h:
        (JSC::CodeBlock::isStrictMode):
        * bytecode/EvalCodeCache.h:
        (JSC::EvalCodeCache::get):
        * bytecode/Opcode.h:
        * bytecompiler/BytecodeGenerator.cpp:
        (JSC::BytecodeGenerator::BytecodeGenerator):
        (JSC::BytecodeGenerator::createArgumentsIfNecessary):
        (JSC::BytecodeGenerator::emitReturn):
        * bytecompiler/BytecodeGenerator.h:
        (JSC::BytecodeGenerator::isStrictMode):
        (JSC::BytecodeGenerator::makeFunction):
        * debugger/Debugger.cpp:
        (JSC::evaluateInGlobalCallFrame):
        * debugger/DebuggerCallFrame.cpp:
        (JSC::DebuggerCallFrame::evaluate):
        * interpreter/Interpreter.cpp:
        (JSC::Interpreter::callEval):
        (JSC::Interpreter::unwindCallFrame):
        (JSC::Interpreter::execute):
        (JSC::Interpreter::privateExecute):
        * jit/JIT.cpp:
        (JSC::JIT::privateCompileMainPass):
        (JSC::JIT::privateCompileSlowCases):
        * jit/JIT.h:
        * jit/JITOpcodes.cpp:
        (JSC::JIT::emit_op_get_pnames):
        (JSC::JIT::emit_op_convert_this_strict):
        (JSC::JIT::emitSlow_op_convert_this_strict):
        * jit/JITOpcodes32_64.cpp:
        (JSC::JIT::emit_op_get_pnames):
        * jit/JITStubs.cpp:
        (JSC::DEFINE_STUB_FUNCTION):
        * jit/JITStubs.h:
        * parser/ASTBuilder.h:
        (JSC::ASTBuilder::createFunctionBody):
        (JSC::ASTBuilder::isResolve):
        * parser/JSParser.cpp:
        (JSC::JSParser::next):
        (JSC::JSParser::startLoop):
        (JSC::JSParser::endLoop):
        (JSC::JSParser::startSwitch):
        (JSC::JSParser::endSwitch):
        (JSC::JSParser::setStrictMode):
        (JSC::JSParser::strictMode):
        (JSC::JSParser::isValidStrictMode):
        (JSC::JSParser::declareParameter):
        (JSC::JSParser::breakIsValid):
        (JSC::JSParser::pushLabel):
        (JSC::JSParser::popLabel):
        (JSC::JSParser::hasLabel):
        (JSC::JSParser::DepthManager::DepthManager):
        (JSC::JSParser::DepthManager::~DepthManager):
        (JSC::JSParser::Scope::Scope):
        (JSC::JSParser::Scope::startSwitch):
        (JSC::JSParser::Scope::endSwitch):
        (JSC::JSParser::Scope::startLoop):
        (JSC::JSParser::Scope::endLoop):
        (JSC::JSParser::Scope::inLoop):
        (JSC::JSParser::Scope::breakIsValid):
        (JSC::JSParser::Scope::pushLabel):
        (JSC::JSParser::Scope::popLabel):
        (JSC::JSParser::Scope::hasLabel):
        (JSC::JSParser::Scope::isFunction):
        (JSC::JSParser::Scope::declareVariable):
        (JSC::JSParser::Scope::declareWrite):
        (JSC::JSParser::Scope::deleteProperty):
        (JSC::JSParser::Scope::declareParameter):
        (JSC::JSParser::Scope::setNeedsFullActivation):
        (JSC::JSParser::Scope::collectFreeVariables):
        (JSC::JSParser::Scope::getUncapturedWrittenVariables):
        (JSC::JSParser::Scope::getDeletedVariables):
        (JSC::JSParser::Scope::setStrictMode):
        (JSC::JSParser::Scope::strictMode):
        (JSC::JSParser::Scope::isValidStrictMode):
        (JSC::JSParser::pushScope):
        (JSC::JSParser::popScope):
        (JSC::JSParser::declareVariable):
        (JSC::JSParser::declareWrite):
        (JSC::JSParser::deleteProperty):
        (JSC::jsParse):
        (JSC::JSParser::JSParser):
        (JSC::JSParser::parseProgram):
        (JSC::JSParser::parseSourceElements):
        (JSC::JSParser::parseDoWhileStatement):
        (JSC::JSParser::parseWhileStatement):
        (JSC::JSParser::parseVarDeclarationList):
        (JSC::JSParser::parseConstDeclarationList):
        (JSC::JSParser::parseForStatement):
        (JSC::JSParser::parseBreakStatement):
        (JSC::JSParser::parseContinueStatement):
        (JSC::JSParser::parseReturnStatement):
        (JSC::JSParser::parseWithStatement):
        (JSC::JSParser::parseSwitchStatement):
        (JSC::JSParser::parseSwitchClauses):
        (JSC::JSParser::parseSwitchDefaultClause):
        (JSC::JSParser::parseTryStatement):
        (JSC::JSParser::parseBlockStatement):
        (JSC::JSParser::parseStatement):
        (JSC::JSParser::parseFormalParameters):
        (JSC::JSParser::parseFunctionBody):
        (JSC::JSParser::parseFunctionInfo):
        (JSC::JSParser::parseFunctionDeclaration):
        (JSC::JSParser::parseExpressionOrLabelStatement):
        (JSC::JSParser::parseIfStatement):
        (JSC::JSParser::parseExpression):
        (JSC::JSParser::parseAssignmentExpression):
        (JSC::JSParser::parseConditionalExpression):
        (JSC::JSParser::parseBinaryExpression):
        (JSC::JSParser::parseStrictObjectLiteral):
        (JSC::JSParser::parsePrimaryExpression):
        (JSC::JSParser::parseMemberExpression):
        (JSC::JSParser::parseUnaryExpression):
        * parser/JSParser.h:
        * parser/Lexer.cpp:
        (JSC::Lexer::parseString):
        (JSC::Lexer::lex):
        * parser/Lexer.h:
        (JSC::Lexer::isReparsing):
        * parser/Nodes.cpp:
        (JSC::ScopeNode::ScopeNode):
        (JSC::FunctionBodyNode::FunctionBodyNode):
        (JSC::FunctionBodyNode::create):
        * parser/Nodes.h:
        (JSC::ScopeNode::isStrictMode):
        * parser/Parser.cpp:
        (JSC::Parser::parse):
        * parser/Parser.h:
        (JSC::Parser::parse):
        * parser/SyntaxChecker.h:
        (JSC::SyntaxChecker::SyntaxChecker):
        (JSC::SyntaxChecker::makeFunctionCallNode):
        (JSC::SyntaxChecker::appendToComma):
        (JSC::SyntaxChecker::createCommaExpr):
        (JSC::SyntaxChecker::makeAssignNode):
        (JSC::SyntaxChecker::makePrefixNode):
        (JSC::SyntaxChecker::makePostfixNode):
        (JSC::SyntaxChecker::makeTypeOfNode):
        (JSC::SyntaxChecker::makeDeleteNode):
        (JSC::SyntaxChecker::makeNegateNode):
        (JSC::SyntaxChecker::makeBitwiseNotNode):
        (JSC::SyntaxChecker::createLogicalNot):
        (JSC::SyntaxChecker::createUnaryPlus):
        (JSC::SyntaxChecker::createVoid):
        (JSC::SyntaxChecker::thisExpr):
        (JSC::SyntaxChecker::createResolve):
        (JSC::SyntaxChecker::createObjectLiteral):
        (JSC::SyntaxChecker::createArray):
        (JSC::SyntaxChecker::createNumberExpr):
        (JSC::SyntaxChecker::createString):
        (JSC::SyntaxChecker::createBoolean):
        (JSC::SyntaxChecker::createNull):
        (JSC::SyntaxChecker::createBracketAccess):
        (JSC::SyntaxChecker::createDotAccess):
        (JSC::SyntaxChecker::createRegex):
        (JSC::SyntaxChecker::createNewExpr):
        (JSC::SyntaxChecker::createConditionalExpr):
        (JSC::SyntaxChecker::createAssignResolve):
        (JSC::SyntaxChecker::createFunctionExpr):
        (JSC::SyntaxChecker::createFunctionBody):
        (JSC::SyntaxChecker::appendBinaryExpressionInfo):
        (JSC::SyntaxChecker::operatorStackPop):
        * runtime/Arguments.cpp:
        (JSC::Arguments::createStrictModeCallerIfNecessary):
        (JSC::Arguments::createStrictModeCalleeIfNecessary):
        (JSC::Arguments::getOwnPropertySlot):
        (JSC::Arguments::getOwnPropertyDescriptor):
        (JSC::Arguments::put):
        (JSC::Arguments::deleteProperty):
        * runtime/Arguments.h:
        (JSC::Arguments::Arguments):
        * runtime/CommonIdentifiers.cpp:
        (JSC::CommonIdentifiers::CommonIdentifiers):
        * runtime/CommonIdentifiers.h:
        * runtime/Error.cpp:
        (JSC::StrictModeTypeErrorFunction::StrictModeTypeErrorFunction):
        (JSC::StrictModeTypeErrorFunction::constructThrowTypeError):
        (JSC::StrictModeTypeErrorFunction::getConstructData):
        (JSC::StrictModeTypeErrorFunction::callThrowTypeError):
        (JSC::StrictModeTypeErrorFunction::getCallData):
        (JSC::createTypeErrorFunction):
        * runtime/Error.h:
        * runtime/Executable.cpp:
        (JSC::EvalExecutable::EvalExecutable):
        (JSC::ProgramExecutable::ProgramExecutable):
        (JSC::FunctionExecutable::FunctionExecutable):
        (JSC::EvalExecutable::compileInternal):
        (JSC::ProgramExecutable::checkSyntax):
        (JSC::ProgramExecutable::compileInternal):
        (JSC::FunctionExecutable::compileForCallInternal):
        (JSC::FunctionExecutable::compileForConstructInternal):
        (JSC::FunctionExecutable::reparseExceptionInfo):
        (JSC::EvalExecutable::reparseExceptionInfo):
        (JSC::FunctionExecutable::fromGlobalCode):
        (JSC::ProgramExecutable::reparseExceptionInfo):
        * runtime/Executable.h:
        (JSC::ScriptExecutable::ScriptExecutable):
        (JSC::ScriptExecutable::isStrictMode):
        (JSC::EvalExecutable::create):
        (JSC::FunctionExecutable::create):
        * runtime/JSActivation.cpp:
        (JSC::JSActivation::toStrictThisObject):
        * runtime/JSActivation.h:
        * runtime/JSFunction.cpp:
        (JSC::createDescriptorForThrowingProperty):
        (JSC::JSFunction::getOwnPropertySlot):
        (JSC::JSFunction::getOwnPropertyDescriptor):
        (JSC::JSFunction::put):
        * runtime/JSGlobalData.cpp:
        (JSC::JSGlobalData::JSGlobalData):
        * runtime/JSGlobalData.h:
        * runtime/JSGlobalObject.cpp:
        (JSC::JSGlobalObject::reset):
        * runtime/JSGlobalObject.h:
        (JSC::JSGlobalObject::internalFunctionStructure):
        * runtime/JSGlobalObjectFunctions.cpp:
        (JSC::globalFuncEval):
        * runtime/JSObject.cpp:
        (JSC::JSObject::put):
        (JSC::JSObject::toStrictThisObject):
        (JSC::throwTypeError):
        * runtime/JSObject.h:
        (JSC::JSObject::isStrictModeFunction):
        (JSC::JSObject::putDirectInternal):
        (JSC::JSObject::putDirect):
        (JSC::JSValue::putDirect):
        (JSC::JSValue::toStrictThisObject):
        * runtime/JSStaticScopeObject.cpp:
        (JSC::JSStaticScopeObject::toStrictThisObject):
        * runtime/JSStaticScopeObject.h:
        * runtime/JSValue.h:
        * runtime/JSZombie.h:
        (JSC::JSZombie::toStrictThisObject):
        * runtime/PutPropertySlot.h:
        (JSC::PutPropertySlot::PutPropertySlot):
        (JSC::PutPropertySlot::isStrictMode):
        * runtime/StrictEvalActivation.cpp: Added.
        (JSC::StrictEvalActivation::StrictEvalActivation):
        (JSC::StrictEvalActivation::deleteProperty):
        (JSC::StrictEvalActivation::toThisObject):
        (JSC::StrictEvalActivation::toStrictThisObject):
        * runtime/StrictEvalActivation.h: Added.
2010-10-01  Oliver Hunt  <oliver@apple.com>

        Reviewed by Gavin Barraclough.

        [ES5] Implement strict mode
        https://bugs.webkit.org/show_bug.cgi?id=10701

        Tests for the many different behaviours we get in strict mode.

        * fast/js/basic-strict-mode-expected.txt: Added.
        * fast/js/basic-strict-mode.html: Added.
        * fast/js/script-tests/basic-strict-mode.js: Added.
        (testThis):
        (testGlobalAccess):
2010-10-01  Oliver Hunt  <oliver@apple.com>

        Reviewed by Gavin Barraclough.

        [ES5] Implement strict mode
        https://bugs.webkit.org/show_bug.cgi?id=10701

        Test: fast/js/basic-strict-mode.html

        Override toStrictThisObject on the domwindow so that
        it correctly provides the shell object when used as this
        in a strict mode function.

        * bindings/js/JSDOMWindowBase.cpp:
        (WebCore::JSDOMWindowBase::toStrictThisObject):
        * bindings/js/JSDOMWindowBase.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@69516 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 35f4ae58
......@@ -157,6 +157,7 @@ SET(JavaScriptCore_SOURCES
runtime/RopeImpl.cpp
runtime/ScopeChain.cpp
runtime/SmallStrings.cpp
runtime/StrictEvalActivation.cpp
runtime/StringConstructor.cpp
runtime/StringObject.cpp
runtime/StringPrototype.cpp
......
This diff is collapsed.
......@@ -388,6 +388,8 @@ javascriptcore_sources += \
JavaScriptCore/runtime/ScopeChainMark.h \
JavaScriptCore/runtime/SmallStrings.cpp \
JavaScriptCore/runtime/SmallStrings.h \
JavaScriptCore/runtime/StrictEvalActivation.cpp\
JavaScriptCore/runtime/StrictEvalActivation.h\
JavaScriptCore/runtime/StringBuilder.h \
JavaScriptCore/runtime/StringConcatenate.h \
JavaScriptCore/runtime/StringConstructor.cpp \
......
......@@ -531,6 +531,7 @@ __ZNK3JSC8JSObject11hasPropertyEPNS_9ExecStateERKNS_10IdentifierE
__ZNK3JSC8JSObject11hasPropertyEPNS_9ExecStateEj
__ZNK3JSC8JSObject12defaultValueEPNS_9ExecStateENS_22PreferredPrimitiveTypeE
__ZNK3JSC8JSObject12toThisObjectEPNS_9ExecStateE
__ZNK3JSC8JSObject18toStrictThisObjectEPNS_9ExecStateE
__ZNK3JSC8JSObject8toNumberEPNS_9ExecStateE
__ZNK3JSC8JSObject8toObjectEPNS_9ExecStateE
__ZNK3JSC8JSObject8toStringEPNS_9ExecStateE
......
......@@ -197,6 +197,7 @@ SOURCES += \
runtime/RopeImpl.cpp \
runtime/ScopeChain.cpp \
runtime/SmallStrings.cpp \
runtime/StrictEvalActivation.cpp \
runtime/StringConstructor.cpp \
runtime/StringObject.cpp \
runtime/StringPrototype.cpp \
......
......@@ -1172,6 +1172,14 @@
RelativePath="..\..\runtime\SmallStrings.h"
>
</File>
<File
RelativePath="..\..\runtime\StrictEvalActivation.cpp"
>
</File>
<File
RelativePath="..\..\runtime\StrictEvalActivation.h"
>
</File>
<File
RelativePath="..\..\runtime\StringConstructor.cpp"
>
......
......@@ -318,6 +318,8 @@
A72701B90DADE94900E548D7 /* ExceptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = A72701B30DADE94900E548D7 /* ExceptionHelpers.h */; };
A727FF6B0DA3092200E548D7 /* JSPropertyNameIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A727FF660DA3053B00E548D7 /* JSPropertyNameIterator.cpp */; };
A7280A2811557E3000D56957 /* JSObjectRefPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = A79EDB0811531CD60019E912 /* JSObjectRefPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
A730B6121250068F009D25B1 /* StrictEvalActivation.h in Headers */ = {isa = PBXBuildFile; fileRef = A730B6101250068F009D25B1 /* StrictEvalActivation.h */; };
A730B6131250068F009D25B1 /* StrictEvalActivation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A730B6111250068F009D25B1 /* StrictEvalActivation.cpp */; };
A7386554118697B400540279 /* SpecializedThunkJIT.h in Headers */ = {isa = PBXBuildFile; fileRef = A7386551118697B400540279 /* SpecializedThunkJIT.h */; };
A7386555118697B400540279 /* ThunkGenerators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7386552118697B400540279 /* ThunkGenerators.cpp */; };
A7386556118697B400540279 /* ThunkGenerators.h in Headers */ = {isa = PBXBuildFile; fileRef = A7386553118697B400540279 /* ThunkGenerators.h */; settings = {ATTRIBUTES = (Private, ); }; };
......@@ -951,6 +953,8 @@
A72701B30DADE94900E548D7 /* ExceptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExceptionHelpers.h; sourceTree = "<group>"; };
A727FF650DA3053B00E548D7 /* JSPropertyNameIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPropertyNameIterator.h; sourceTree = "<group>"; };
A727FF660DA3053B00E548D7 /* JSPropertyNameIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPropertyNameIterator.cpp; sourceTree = "<group>"; };
A730B6101250068F009D25B1 /* StrictEvalActivation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StrictEvalActivation.h; sourceTree = "<group>"; };
A730B6111250068F009D25B1 /* StrictEvalActivation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StrictEvalActivation.cpp; sourceTree = "<group>"; };
A7386551118697B400540279 /* SpecializedThunkJIT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpecializedThunkJIT.h; sourceTree = "<group>"; };
A7386552118697B400540279 /* ThunkGenerators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThunkGenerators.cpp; sourceTree = "<group>"; };
A7386553118697B400540279 /* ThunkGenerators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThunkGenerators.h; sourceTree = "<group>"; };
......@@ -1814,6 +1818,8 @@
14BFCE6810CDB1FC00364CCE /* WeakGCMap.h */,
14035DB010DBFB2A00FFFFE7 /* WeakGCPtr.h */,
1420BE7A10AA6DDB00F455D2 /* WeakRandom.h */,
A730B6101250068F009D25B1 /* StrictEvalActivation.h */,
A730B6111250068F009D25B1 /* StrictEvalActivation.cpp */,
);
path = runtime;
sourceTree = "<group>";
......@@ -2314,6 +2320,7 @@
9714AF5F122F32070092D9F5 /* ParsedURL.h in Headers */,
9714AF60122F32070092D9F5 /* URLString.h in Headers */,
90213E3E123A40C200D422F3 /* MemoryStatistics.h in Headers */,
A730B6121250068F009D25B1 /* StrictEvalActivation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -2407,7 +2414,6 @@
isa = PBXProject;
buildConfigurationList = 149C277108902AFE008A9EFC /* Build configuration list for PBXProject "JavaScriptCore" */;
compatibilityVersion = "Xcode 2.4";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
......@@ -2769,6 +2775,7 @@
9714AF46122F28850092D9F5 /* URLSegments.cpp in Sources */,
9714AF5E122F32070092D9F5 /* ParsedURL.cpp in Sources */,
90213E3D123A40C200D422F3 /* MemoryStatistics.cpp in Sources */,
A730B6131250068F009D25B1 /* StrictEvalActivation.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -517,6 +517,11 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
printf("[%4d] convert_this %s\n", location, registerName(exec, r0).data());
break;
}
case op_convert_this_strict: {
int r0 = (++it)->u.operand;
printf("[%4d] convert_this_strict %s\n", location, registerName(exec, r0).data());
break;
}
case op_new_object: {
int r0 = (++it)->u.operand;
printf("[%4d] new_object\t %s\n", location, registerName(exec, r0).data());
......@@ -1371,6 +1376,7 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, CodeType codeType, JSGlo
, m_needsFullScopeChain(ownerExecutable->needsActivation())
, m_usesEval(ownerExecutable->usesEval())
, m_isNumericCompareFunction(false)
, m_isStrictMode(ownerExecutable->isStrictMode())
, m_codeType(codeType)
, m_source(sourceProvider)
, m_sourceOffset(sourceOffset)
......@@ -1557,7 +1563,7 @@ bool CodeBlock::reparseForExceptionInfoIfNecessary(CallFrame* callFrame)
scopeChain = scopeChain->next;
}
m_exceptionInfo = m_ownerExecutable->reparseExceptionInfo(m_globalData, scopeChain, this);
m_exceptionInfo = m_ownerExecutable->reparseExceptionInfo(scopeChain, this);
return m_exceptionInfo;
}
......
......@@ -297,6 +297,8 @@ namespace JSC {
void printStructure(const char* name, const Instruction*, int operand) const;
#endif
bool isStrictMode() const { return m_isStrictMode; }
inline bool isKnownNotImmediate(int index)
{
if (index == m_thisRegister)
......@@ -564,6 +566,7 @@ namespace JSC {
bool m_needsFullScopeChain;
bool m_usesEval;
bool m_isNumericCompareFunction;
bool m_isStrictMode;
CodeType m_codeType;
......
......@@ -43,20 +43,20 @@ namespace JSC {
class EvalCodeCache {
public:
PassRefPtr<EvalExecutable> get(ExecState* exec, const UString& evalSource, ScopeChainNode* scopeChain, JSValue& exceptionValue)
PassRefPtr<EvalExecutable> get(ExecState* exec, bool inStrictContext, const UString& evalSource, ScopeChainNode* scopeChain, JSValue& exceptionValue)
{
RefPtr<EvalExecutable> evalExecutable;
if (evalSource.length() < maxCacheableSourceLength && (*scopeChain->begin())->isVariableObject())
if (!inStrictContext && evalSource.length() < maxCacheableSourceLength && (*scopeChain->begin())->isVariableObject())
evalExecutable = m_cacheMap.get(evalSource.impl());
if (!evalExecutable) {
evalExecutable = EvalExecutable::create(exec, makeSource(evalSource));
evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext);
exceptionValue = evalExecutable->compile(exec, scopeChain);
if (exceptionValue)
return 0;
if (evalSource.length() < maxCacheableSourceLength && (*scopeChain->begin())->isVariableObject() && m_cacheMap.size() < maxCacheEntries)
if (!inStrictContext && evalSource.length() < maxCacheableSourceLength && (*scopeChain->begin())->isVariableObject() && m_cacheMap.size() < maxCacheEntries)
m_cacheMap.set(evalSource.impl(), evalExecutable);
}
......
......@@ -45,6 +45,7 @@ namespace JSC {
macro(op_create_this, 3) \
macro(op_get_callee, 2) \
macro(op_convert_this, 2) \
macro(op_convert_this_strict, 2) \
\
macro(op_new_object, 2) \
macro(op_new_array, 4) \
......
......@@ -343,6 +343,11 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debug
emitInitLazyRegister(argumentsRegister);
emitInitLazyRegister(unmodifiedArgumentsRegister);
if (m_codeBlock->isStrictMode()) {
emitOpcode(op_create_arguments);
instructions().append(argumentsRegister->index());
}
// The debugger currently retrieves the arguments object from an activation rather than pulling
// it from a call frame. In the long-term it should stop doing that (<rdar://problem/6911886>),
......@@ -442,7 +447,10 @@ BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debug
instructions().append(m_thisRegister.index());
instructions().append(funcProto->index());
} else if (functionBody->usesThis() || m_shouldEmitDebugHooks) {
emitOpcode(op_convert_this);
if (codeBlock->isStrictMode())
emitOpcode(op_convert_this_strict);
else
emitOpcode(op_convert_this);
instructions().append(m_thisRegister.index());
}
}
......@@ -1557,6 +1565,12 @@ void BytecodeGenerator::createArgumentsIfNecessary()
return;
ASSERT(m_codeBlock->usesArguments());
// If we're in strict mode we tear off the arguments on function
// entry, so there's no need to check if we need to create them
// now
if (m_codeBlock->isStrictMode())
return;
emitOpcode(op_create_arguments);
instructions().append(m_codeBlock->argumentsRegister());
}
......@@ -1674,7 +1688,8 @@ RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
emitOpcode(op_tear_off_activation);
instructions().append(m_activationRegister->index());
instructions().append(m_codeBlock->argumentsRegister());
} else if (m_codeBlock->usesArguments() && m_codeBlock->m_numParameters > 1) { // If there are no named parameters, there's nothing to tear off, since extra / unnamed parameters get copied to the arguments object at construct time.
} else if (m_codeBlock->usesArguments() && m_codeBlock->m_numParameters > 1
&& !m_codeBlock->isStrictMode()) { // If there are no named parameters, there's nothing to tear off, since extra / unnamed parameters get copied to the arguments object at construct time.
emitOpcode(op_tear_off_arguments);
instructions().append(m_codeBlock->argumentsRegister());
}
......
......@@ -419,6 +419,8 @@ namespace JSC {
}
bool shouldEmitProfileHooks() { return m_shouldEmitProfileHooks; }
bool isStrictMode() const { return m_codeBlock->isStrictMode(); }
private:
void emitOpcode(OpcodeID);
......@@ -499,12 +501,12 @@ namespace JSC {
PassRefPtr<FunctionExecutable> makeFunction(ExecState* exec, FunctionBodyNode* body)
{
return FunctionExecutable::create(exec, body->ident(), body->source(), body->usesArguments(), body->parameters(), body->lineNo(), body->lastLine());
return FunctionExecutable::create(exec, body->ident(), body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine());
}
PassRefPtr<FunctionExecutable> makeFunction(JSGlobalData* globalData, FunctionBodyNode* body)
{
return FunctionExecutable::create(globalData, body->ident(), body->source(), body->usesArguments(), body->parameters(), body->lineNo(), body->lastLine());
return FunctionExecutable::create(globalData, body->ident(), body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine());
}
RegisterID* emitInitLazyRegister(RegisterID*);
......
......@@ -101,7 +101,7 @@ JSValue evaluateInGlobalCallFrame(const UString& script, JSValue& exception, JSG
{
CallFrame* globalCallFrame = globalObject->globalExec();
RefPtr<EvalExecutable> eval = EvalExecutable::create(globalCallFrame, makeSource(script));
RefPtr<EvalExecutable> eval = EvalExecutable::create(globalCallFrame, makeSource(script), false);
JSObject* error = eval->compile(globalCallFrame, globalCallFrame->scopeChain());
if (error)
return error;
......
......@@ -88,7 +88,7 @@ JSValue DebuggerCallFrame::evaluate(const UString& script, JSValue& exception) c
if (!m_callFrame->codeBlock())
return JSValue();
RefPtr<EvalExecutable> eval = EvalExecutable::create(m_callFrame, makeSource(script));
RefPtr<EvalExecutable> eval = EvalExecutable::create(m_callFrame, makeSource(script), m_callFrame->codeBlock()->isStrictMode());
JSObject* error = eval->compile(m_callFrame, m_callFrame->scopeChain());
if (error)
return error;
......
......@@ -59,6 +59,7 @@
#include "RegExpPrototype.h"
#include "Register.h"
#include "SamplingTool.h"
#include "StrictEvalActivation.h"
#include <limits.h>
#include <stdio.h>
#include <wtf/Threading.h>
......@@ -389,14 +390,18 @@ NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* r
UString programSource = asString(program)->value(callFrame);
if (callFrame->hadException())
return JSValue();
LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON);
if (JSValue parsedObject = preparser.tryLiteralParse())
return parsedObject;
CodeBlock* codeBlock = callFrame->codeBlock();
if (!codeBlock->isStrictMode()) {
// FIXME: We can use the preparser in strict mode, we just need additional logic
// to prevent duplicates.
LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON);
if (JSValue parsedObject = preparser.tryLiteralParse())
return parsedObject;
}
ScopeChainNode* scopeChain = callFrame->scopeChain();
CodeBlock* codeBlock = callFrame->codeBlock();
RefPtr<EvalExecutable> eval = codeBlock->evalCodeCache().get(callFrame, programSource, scopeChain, exceptionValue);
RefPtr<EvalExecutable> eval = codeBlock->evalCodeCache().get(callFrame, codeBlock->isStrictMode(), programSource, scopeChain, exceptionValue);
JSValue result = jsUndefined();
if (eval)
......@@ -561,9 +566,11 @@ NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue ex
scopeChain = scopeChain->pop();
JSActivation* activation = asActivation(scopeChain->object);
activation->copyRegisters();
if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue())
asArguments(arguments)->setActivation(activation);
} else if (oldCodeBlock->usesArguments()) {
if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) {
if (!oldCodeBlock->isStrictMode())
asArguments(arguments)->setActivation(activation);
}
} else if (oldCodeBlock->usesArguments() && !oldCodeBlock->isStrictMode()) {
if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue())
asArguments(arguments)->copyRegisters();
}
......@@ -1061,7 +1068,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObjec
}
EvalCodeBlock* codeBlock = &eval->generatedBytecode();
JSVariableObject* variableObject;
JSObject* variableObject;
for (ScopeChainNode* node = scopeChain; ; node = node->next) {
ASSERT(node);
if (node->object->isVariableObject()) {
......@@ -1072,7 +1079,13 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObjec
unsigned numVariables = codeBlock->numVariables();
int numFunctions = codeBlock->numberOfFunctionDecls();
bool pushedScope = false;
if (numVariables || numFunctions) {
if (codeBlock->isStrictMode()) {
variableObject = new (callFrame) StrictEvalActivation(callFrame);
scopeChain = scopeChain->push(variableObject);
pushedScope = true;
}
// Scope for BatchedTransitionOptimizer
BatchedTransitionOptimizer optimizer(variableObject);
......@@ -1095,6 +1108,8 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObjec
Register* newEnd = m_registerFile.start() + globalRegisterOffset + codeBlock->m_numCalleeRegisters;
if (!m_registerFile.grow(newEnd)) {
*exception = createStackOverflowError(callFrame);
if (pushedScope)
scopeChain->pop();
return jsNull();
}
......@@ -1137,6 +1152,8 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObjec
(*profiler)->didExecute(callFrame, eval->sourceURL(), eval->lineNo());
m_registerFile.shrink(oldEnd);
if (pushedScope)
scopeChain->pop();
return result;
}
......@@ -2964,7 +2981,7 @@ skip_id_custom_self:
JSValue baseValue = callFrame->r(base).jsValue();
Identifier& ident = codeBlock->identifier(property);
PutPropertySlot slot;
PutPropertySlot slot(codeBlock->isStrictMode());
if (direct) {
baseValue.putDirect(callFrame, ident, callFrame->r(value).jsValue(), slot);
ASSERT(slot.base() == baseValue);
......@@ -3080,7 +3097,7 @@ skip_id_custom_self:
JSValue baseValue = callFrame->r(base).jsValue();
Identifier& ident = codeBlock->identifier(property);
PutPropertySlot slot;
PutPropertySlot slot(codeBlock->isStrictMode());
if (direct) {
baseValue.putDirect(callFrame, ident, callFrame->r(value).jsValue(), slot);
ASSERT(slot.base() == baseValue);
......@@ -3105,9 +3122,13 @@ skip_id_custom_self:
JSObject* baseObj = callFrame->r(base).jsValue().toObject(callFrame);
Identifier& ident = codeBlock->identifier(property);
JSValue result = jsBoolean(baseObj->deleteProperty(callFrame, ident));
bool result = baseObj->deleteProperty(callFrame, ident);
if (!result && codeBlock->isStrictMode()) {
exceptionValue = createTypeError(callFrame, "Unable to delete property.");
goto vm_throw;
}
CHECK_FOR_EXCEPTION();
callFrame->r(dst) = result;
callFrame->r(dst) = jsBoolean(result);
vPC += OPCODE_LENGTH(op_del_by_id);
NEXT_INSTRUCTION();
}
......@@ -3260,7 +3281,7 @@ skip_id_custom_self:
} else {
Identifier property(callFrame, subscript.toString(callFrame));
if (!globalData->exception) { // Don't put to an object if toString threw an exception.
PutPropertySlot slot;
PutPropertySlot slot(codeBlock->isStrictMode());
baseValue.put(callFrame, property, callFrame->r(value).jsValue(), slot);
}
}
......@@ -3284,19 +3305,22 @@ skip_id_custom_self:
JSObject* baseObj = callFrame->r(base).jsValue().toObject(callFrame); // may throw
JSValue subscript = callFrame->r(property).jsValue();
JSValue result;
bool result;
uint32_t i;
if (subscript.getUInt32(i))
result = jsBoolean(baseObj->deleteProperty(callFrame, i));
result = baseObj->deleteProperty(callFrame, i);
else {
CHECK_FOR_EXCEPTION();
Identifier property(callFrame, subscript.toString(callFrame));
CHECK_FOR_EXCEPTION();
result = jsBoolean(baseObj->deleteProperty(callFrame, property));
result = baseObj->deleteProperty(callFrame, property);
}
if (!result && codeBlock->isStrictMode()) {
exceptionValue = createTypeError(callFrame, "Unable to delete property.");
goto vm_throw;
}
CHECK_FOR_EXCEPTION();
callFrame->r(dst) = result;
callFrame->r(dst) = jsBoolean(result);
vPC += OPCODE_LENGTH(op_del_by_val);
NEXT_INSTRUCTION();
}
......@@ -4036,6 +4060,11 @@ skip_id_custom_self:
} else if (JSValue argumentsValue = callFrame->r(unmodifiedArgumentsRegister(arguments)).jsValue())
asArguments(argumentsValue)->copyRegisters();
if (JSValue arguments = callFrame->r(unmodifiedArgumentsRegister(src2)).jsValue()) {
if (!codeBlock->isStrictMode())
asArguments(arguments)->setActivation(activation);
}
vPC += OPCODE_LENGTH(op_tear_off_activation);
NEXT_INSTRUCTION();
}
......@@ -4225,6 +4254,25 @@ skip_id_custom_self:
vPC += OPCODE_LENGTH(op_convert_this);
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(op_convert_this_strict) {
/* convert_this_strict this(r)
Takes the value in the 'this' register, and converts it to
its "this" form if (and only if) "this" is an object with a
custom this conversion
This opcode should only be used at the beginning of a code
block.
*/
int thisRegister = vPC[1].u.operand;
JSValue thisVal = callFrame->r(thisRegister).jsValue();
if (thisVal.isObject() && thisVal.needsThisConversion())
callFrame->r(thisRegister) = JSValue(thisVal.toStrictThisObject(callFrame));
vPC += OPCODE_LENGTH(op_convert_this_strict);
NEXT_INSTRUCTION();
}
DEFINE_OPCODE(op_init_lazy_reg) {
/* init_lazy_reg dst(r)
......
......@@ -225,6 +225,7 @@ void JIT::privateCompileMainPass()
DEFINE_OP(op_get_callee)
DEFINE_OP(op_create_this)
DEFINE_OP(op_convert_this)
DEFINE_OP(op_convert_this_strict)
DEFINE_OP(op_init_lazy_reg)
DEFINE_OP(op_create_arguments)
DEFINE_OP(op_debug)
......@@ -396,6 +397,7 @@ void JIT::privateCompileSlowCases()
DEFINE_SLOWCASE_OP(op_call_varargs)
DEFINE_SLOWCASE_OP(op_construct)
DEFINE_SLOWCASE_OP(op_convert_this)
DEFINE_SLOWCASE_OP(op_convert_this_strict)
#if !USE(JSVALUE32)
DEFINE_SLOWCASE_OP(op_div)
#endif
......
......@@ -738,6 +738,7 @@ namespace JSC {
void emit_op_get_callee(Instruction*);
void emit_op_create_this(Instruction*);
void emit_op_convert_this(Instruction*);
void emit_op_convert_this_strict(Instruction*);
void emit_op_create_arguments(Instruction*);
void emit_op_debug(Instruction*);
void emit_op_del_by_id(Instruction*);
......@@ -845,6 +846,7 @@ namespace JSC {
void emitSlow_op_call_varargs(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_construct(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_convert_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_convert_this_strict(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_div(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_eq(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_get_by_id(Instruction*, Vector<SlowCaseEntry>::iterator&);
......
......@@ -895,7 +895,7 @@ void JIT::emit_op_get_pnames(Instruction* currentInstruction)
emitGetVirtualRegister(base, regT0);
if (!m_codeBlock->isKnownNotImmediate(base))
isNotObject.append(emitJumpIfNotJSCell(regT0));
if (base != m_codeBlock->thisRegister()) {
if (base != m_codeBlock->thisRegister() || m_codeBlock->isStrictMode()) {
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
isNotObject.append(branch8(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo.m_type)), Imm32(ObjectType)));
}
......@@ -1255,6 +1255,23 @@ void JIT::emit_op_convert_this(Instruction* currentInstruction)
addSlowCase(branchTest8(NonZero, Address(regT1, OBJECT_OFFSETOF(Structure, m_typeInfo.m_flags)), Imm32(NeedsThisConversion)));
}
void JIT::emit_op_convert_this_strict(Instruction* currentInstruction)
{
emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
Jump notNull = branchTestPtr(NonZero, regT0);
move(ImmPtr(JSValue::encode(jsNull())), regT0);
emitPutVirtualRegister(currentInstruction[1].u.operand, regT0);
Jump setThis = jump();
notNull.link(this);
Jump isImmediate = emitJumpIfNotJSCell(regT0);
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT1);
Jump notAnObject = branch8(NotEqual, Address(regT3, OBJECT_OFFSETOF(Structure, m_typeInfo.m_type)), Imm32(ObjectType));
addSlowCase(branchTest8(NonZero, Address(regT1, OBJECT_OFFSETOF(Structure, m_typeInfo.m_flags)), Imm32(NeedsThisConversion)));
isImmediate.link(this);
notAnObject.link(this);
setThis.link(this);
}
void JIT::emit_op_get_callee(Instruction* currentInstruction)
{
unsigned result = currentInstruction[1].u.operand;
......@@ -1304,6 +1321,14 @@ void JIT::emitSlow_op_convert_this(Instruction* currentInstruction, Vector<SlowC
stubCall.call(currentInstruction[1].u.operand);
}
void JIT::emitSlow_op_convert_this_strict(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_convert_this_strict);
stubCall.addArgument(regT0);
stubCall.call(currentInstruction[1].u.operand);
}
void JIT::emitSlow_op_to_primitive(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
linkSlowCase(iter);
......
......@@ -1239,7 +1239,7 @@ void JIT::emit_op_get_pnames(Instruction* currentInstruction)
emitLoad(base, regT1, regT0);
if (!m_codeBlock->isKnownNotImmediate(base))
isNotObject.append(branch32(NotEqual, regT1, Imm32(JSValue::CellTag)));
if (base != m_codeBlock->thisRegister()) {
if (base != m_codeBlock->thisRegister() || m_codeBlock->isStrictMode()) {
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT2);
isNotObject.append(branch8(NotEqual, Address(regT2, OBJECT_OFFSETOF(Structure, m_typeInfo.m_type)), Imm32(ObjectType)));
}
......@@ -1554,6 +1554,26 @@ void JIT::emit_op_convert_this(Instruction* currentInstruction)
map(m_bytecodeOffset + OPCODE_LENGTH(op_convert_this), thisRegister, regT1, regT0);
}
void JIT::emit_op_convert_this_strict(Instruction* currentInstruction)
{
unsigned thisRegister = currentInstruction[1].u.operand;
emitLoad(thisRegister, regT1, regT0);
Jump notNull = branch32(NotEqual, regT1, Imm32(JSValue::EmptyValueTag));
emitStore(thisRegister, jsNull());
Jump setThis = jump();
notNull.link(this);
Jump isImmediate = branch32(NotEqual, regT1, Imm32(JSValue::CellTag));
loadPtr(Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), regT1);
Jump notAnObject = branch8(NotEqual, Address(regT3, OBJECT_OFFSETOF(Structure, m_typeInfo.m_type)), Imm32(ObjectType));
addSlowCase(branchTest8(NonZero, Address(regT1, OBJECT_OFFSETOF(Structure, m_typeInfo.m_flags)), Imm32(NeedsThisConversion)));
isImmediate.link(this);
notAnObject.link(this);
setThis.link(this);
map(m_bytecodeOffset + OPCODE_LENGTH(op_convert_this), thisRegister, regT1, regT0);
}
void JIT::emitSlow_op_convert_this(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
unsigned thisRegister = currentInstruction[1].u.operand;
......@@ -1566,6 +1586,17 @@ void JIT::emitSlow_op_convert_this(Instruction* currentInstruction, Vector<SlowC
stubCall.call(thisRegister);
}
void JIT::emitSlow_op_convert_this_strict(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
unsigned thisRegister = currentInstruction[1].u.operand;
linkSlowCase(iter);
JITStubCall stubCall(this, cti_op_convert_this_strict);
stubCall.addArgument(regT1, regT0);
stubCall.call(thisRegister);
}
void JIT::emit_op_profile_will_call(Instruction* currentInstruction)
{
peek(regT2, OBJECT_OFFSETOF(JITStackFrame, enabledProfilerReference) / sizeof(void*));
......
......@@ -1065,7 +1065,9 @@ static NEVER_INLINE void throwStackOverflowError(CallFrame* callFrame, JSGlobalD
return 0; \
} while (0)
#define VM_THROW_EXCEPTION_AT_END() \
returnToThrowTrampoline(stackFrame.globalData, STUB_RETURN_ADDRESS, STUB_RETURN_ADDRESS)
do {\
returnToThrowTrampoline(stackFrame.globalData, STUB_RETURN_ADDRESS, STUB_RETURN_ADDRESS);\
} while (0)
#define CHECK_FOR_EXCEPTION() \
do { \
......@@ -1301,6 +1303,18 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_convert_this)