2009-01-07  Sam Weinig  <sam@webkit.org>

        Reviewed by Geoffrey Garen.

        <rdar://problem/6469060> Don't store exception information for a CodeBlock until first exception is thrown

        Don't initially store exception information (lineNumber/expressionRange/getByIdExcecptionInfo)
        in CodeBlocks blocks.  Instead, re-parse for the data on demand and cache it then.

        One important change that was needed to make this work was to pad op_get_global_var with nops to
        be the same length as op_resolve_global, since one could be replaced for the other on re-parsing,
        and we want to keep the offsets bytecode offsets the same.

        1.3MB improvement on Membuster head.

        * bytecode/CodeBlock.cpp:
        (JSC::CodeBlock::dump): Update op_get_global_var to account for the padding.
        (JSC::CodeBlock::dumpStatistics): Add more statistic dumping.
        (JSC::CodeBlock::CodeBlock): Initialize m_exceptionInfo.
        (JSC::CodeBlock::reparseForExceptionInfoIfNecessary): Re-parses the CodeBlocks
        associated SourceCode and steals the ExceptionInfo from it.
        (JSC::CodeBlock::lineNumberForBytecodeOffset): Creates the exception info on demand.
        (JSC::CodeBlock::expressionRangeForBytecodeOffset): Ditto.
        (JSC::CodeBlock::getByIdExceptionInfoForBytecodeOffset): Ditto.
        * bytecode/CodeBlock.h:
        (JSC::CodeBlock::numberOfExceptionHandlers): Updated to account for m_exceptionInfo indirection.
        (JSC::CodeBlock::addExceptionHandler): Ditto.
        (JSC::CodeBlock::exceptionHandler): Ditto.
        (JSC::CodeBlock::clearExceptionInfo): Ditto.
        (JSC::CodeBlock::addExpressionInfo): Ditto.
        (JSC::CodeBlock::addGetByIdExceptionInfo): Ditto.
        (JSC::CodeBlock::numberOfLineInfos): Ditto.
        (JSC::CodeBlock::addLineInfo): Ditto.
        (JSC::CodeBlock::lastLineInfo): Ditto.

        * bytecode/Opcode.h: Change length of op_get_global_var to match op_resolve_global.

        * bytecode/SamplingTool.cpp:
        (JSC::SamplingTool::dump): Add comment indicating why it is okay not to pass a CallFrame.

        * bytecompiler/BytecodeGenerator.cpp:
        (JSC::BytecodeGenerator::generate): Clear the exception info after generation for Function and Eval
        Code when not in regenerate for exception info mode.
        (JSC::BytecodeGenerator::BytecodeGenerator): Initialize m_regeneratingForExceptionInfo to false.
        (JSC::BytecodeGenerator::emitGetScopedVar): Pad op_get_global_var with 2 nops.
        * bytecompiler/BytecodeGenerator.h:
        (JSC::BytecodeGenerator::setRegeneratingForExcpeptionInfo): Added.

        * interpreter/Interpreter.cpp:
        (JSC::Interpreter::throwException): Pass the CallFrame to exception info accessors.
        (JSC::Interpreter::privateExecute): Ditto.
        (JSC::Interpreter::retrieveLastCaller): Ditto.
        (JSC::Interpreter::cti_op_new_error): Ditto.

        * jit/JIT.cpp:
        (JSC::JIT::privateCompileMainPass): Pass the current bytecode offset instead of hard coding the
        line number, the stub will do the accessing if it gets called.

        * parser/Nodes.cpp:
        (JSC::ProgramNode::emitBytecode): Moved.
        (JSC::ProgramNode::generateBytecode): Moved.
        (JSC::EvalNode::create): Moved.
        (JSC::EvalNode::bytecodeForExceptionInfoReparse): Added.
        (JSC::FunctionBodyNode::generateBytecode): Rename reparse to reparseInPlace.
        (JSC::FunctionBodyNode::bytecodeForExceptionInfoReparse): Addded.

        * parser/Nodes.h:
        (JSC::ScopeNode::features): Added getter.
        * parser/Parser.cpp:
        (JSC::Parser::reparseInPlace): Renamed from reparse.
        * parser/Parser.h:
        (JSC::Parser::reparse): Added. Re-parses the passed in Node into
        a new Node.
        * runtime/ExceptionHelpers.cpp:
        (JSC::createUndefinedVariableError): Pass along CallFrame.
        (JSC::createInvalidParamError): Ditto.
        (JSC::createNotAConstructorError): Ditto.
        (JSC::createNotAFunctionError): Ditto.
        (JSC::createNotAnObjectError): Ditto.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@39697 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp
index 154e741..91fb1c0 100644
--- a/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
  * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -724,10 +724,11 @@
             break;
         }
         case op_get_global_var: {
-            int r0 = (++it)->u.operand;
-            JSValuePtr scope = JSValuePtr((++it)->u.jsCell);
-            int index = (++it)->u.operand;
+            int r0 = it[1].u.operand;
+            JSValuePtr scope = JSValuePtr(it[2].u.jsCell);
+            int index = it[3].u.operand;
             printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), index);
+            it += OPCODE_LENGTH(op_get_global_var);
             break;
         }
         case op_put_global_var: {
@@ -1091,9 +1092,6 @@
     macro(identifiers) \
     macro(functionExpressions) \
     macro(constantRegisters) \
-    macro(expressionInfo) \
-    macro(lineInfo) \
-    macro(getByIdExceptionInfo) \
     macro(pcVector)
 
 #define FOR_EACH_MEMBER_VECTOR_RARE_DATA(macro) \
@@ -1106,6 +1104,11 @@
     macro(stringSwitchJumpTables) \
     macro(functionRegisterInfos)
 
+#define FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(macro) \
+    macro(expressionInfo) \
+    macro(lineInfo) \
+    macro(getByIdExceptionInfo)
+
 template<typename T>
 static size_t sizeInBytes(const Vector<T>& vector)
 {
@@ -1118,6 +1121,7 @@
     #define DEFINE_VARS(name) size_t name##IsNotEmpty = 0; size_t name##TotalSize = 0;
         FOR_EACH_MEMBER_VECTOR(DEFINE_VARS)
         FOR_EACH_MEMBER_VECTOR_RARE_DATA(DEFINE_VARS)
+        FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(DEFINE_VARS)
     #undef DEFINE_VARS
 
     // Non-vector data members
@@ -1126,8 +1130,13 @@
     size_t symbolTableIsNotEmpty = 0;
     size_t symbolTableTotalSize = 0;
 
+    size_t hasExceptionInfo = 0;
     size_t hasRareData = 0;
 
+    size_t isFunctionCode = 0;
+    size_t isGlobalCode = 0;
+    size_t isEvalCode = 0;
+
     HashSet<CodeBlock*>::const_iterator end = liveCodeBlockSet.end();
     for (HashSet<CodeBlock*>::const_iterator it = liveCodeBlockSet.begin(); it != end; ++it) {
         CodeBlock* codeBlock = *it;
@@ -1141,6 +1150,13 @@
             symbolTableTotalSize += (codeBlock->m_symbolTable.capacity() * (sizeof(SymbolTable::KeyType) + sizeof(SymbolTable::MappedType)));
         }
 
+        if (codeBlock->m_exceptionInfo) {
+            hasExceptionInfo++;
+            #define GET_STATS(name) if (!codeBlock->m_exceptionInfo->m_##name.isEmpty()) { name##IsNotEmpty++; name##TotalSize += sizeInBytes(codeBlock->m_exceptionInfo->m_##name); }
+                FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(GET_STATS)
+            #undef GET_STATS
+        }
+
         if (codeBlock->m_rareData) {
             hasRareData++;
             #define GET_STATS(name) if (!codeBlock->m_rareData->m_##name.isEmpty()) { name##IsNotEmpty++; name##TotalSize += sizeInBytes(codeBlock->m_rareData->m_##name); }
@@ -1150,13 +1166,26 @@
             if (!codeBlock->m_rareData->m_evalCodeCache.isEmpty())
                 evalCodeCacheIsNotEmpty++;
         }
+
+        switch (codeBlock->codeType()) {
+            case FunctionCode:
+                ++isFunctionCode;
+                break;
+            case GlobalCode:
+                ++isGlobalCode;
+                break;
+            case EvalCode:
+                ++isEvalCode;
+                break;
+        }
     }
 
     size_t totalSize = 0;
 
     #define GET_TOTAL_SIZE(name) totalSize += name##TotalSize;
-            FOR_EACH_MEMBER_VECTOR(GET_TOTAL_SIZE)
-            FOR_EACH_MEMBER_VECTOR_RARE_DATA(GET_TOTAL_SIZE)
+        FOR_EACH_MEMBER_VECTOR(GET_TOTAL_SIZE)
+        FOR_EACH_MEMBER_VECTOR_RARE_DATA(GET_TOTAL_SIZE)
+        FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(GET_TOTAL_SIZE)
     #undef GET_TOTAL_SIZE
 
     totalSize += symbolTableTotalSize;
@@ -1167,11 +1196,17 @@
     printf("Size of all CodeBlocks: %zu\n", totalSize);
     printf("Average size of a CodeBlock: %zu\n", totalSize / liveCodeBlockSet.size());
 
-    printf("Number of CodeBlocks with rare data: %zu\n", hasRareData);
+    printf("Number of FunctionCode CodeBlocks: %zu (%.3f%%)\n", isFunctionCode, static_cast<double>(isFunctionCode) * 100.0 / liveCodeBlockSet.size());
+    printf("Number of GlobalCode CodeBlocks: %zu (%.3f%%)\n", isGlobalCode, static_cast<double>(isGlobalCode) * 100.0 / liveCodeBlockSet.size());
+    printf("Number of EvalCode CodeBlocks: %zu (%.3f%%)\n", isEvalCode, static_cast<double>(isEvalCode) * 100.0 / liveCodeBlockSet.size());
+
+    printf("Number of CodeBlocks with exception info: %zu (%.3f%%)\n", hasExceptionInfo, static_cast<double>(hasExceptionInfo) * 100.0 / liveCodeBlockSet.size());
+    printf("Number of CodeBlocks with rare data: %zu (%.3f%%)\n", hasRareData, static_cast<double>(hasRareData) * 100.0 / liveCodeBlockSet.size());
 
     #define PRINT_STATS(name) printf("Number of CodeBlocks with " #name ": %zu\n", name##IsNotEmpty); printf("Size of all " #name ": %zu\n", name##TotalSize); 
         FOR_EACH_MEMBER_VECTOR(PRINT_STATS)
         FOR_EACH_MEMBER_VECTOR_RARE_DATA(PRINT_STATS)
+        FOR_EACH_MEMBER_VECTOR_EXCEPTION_INFO(PRINT_STATS)
     #undef PRINT_STATS
 
     printf("Number of CodeBlocks with evalCodeCache: %zu\n", evalCodeCacheIsNotEmpty);
@@ -1200,6 +1235,7 @@
     , m_codeType(codeType)
     , m_source(sourceProvider)
     , m_sourceOffset(sourceOffset)
+    , m_exceptionInfo(new ExceptionInfo)
 {
     ASSERT(m_source);
 
@@ -1350,6 +1386,40 @@
     }
 }
 
+void CodeBlock::reparseForExceptionInfoIfNecessary(CallFrame* callFrame)
+{
+    if (m_exceptionInfo)
+        return;
+
+    ScopeChainNode* scopeChain = callFrame->scopeChain();
+
+    switch (m_codeType) {
+        case FunctionCode: {
+            FunctionBodyNode* ownerFunctionBodyNode = static_cast<FunctionBodyNode*>(m_ownerNode);
+            RefPtr<FunctionBodyNode> newFunctionBody = m_globalData->parser->reparse<FunctionBodyNode>(m_globalData, ownerFunctionBodyNode);
+            newFunctionBody->finishParsing(ownerFunctionBodyNode->copyParameters(), ownerFunctionBodyNode->parameterCount());
+            CodeBlock& newCodeBlock = newFunctionBody->bytecodeForExceptionInfoReparse(scopeChain);
+            ASSERT(newCodeBlock.m_exceptionInfo);
+            ASSERT(newCodeBlock.m_instructionCount == m_instructionCount);
+            m_exceptionInfo.set(newCodeBlock.m_exceptionInfo.release());
+            break;
+        }
+        case EvalCode: {
+            EvalNode* ownerEvalNode = static_cast<EvalNode*>(m_ownerNode);
+            RefPtr<EvalNode> newEvalBody = m_globalData->parser->reparse<EvalNode>(m_globalData, ownerEvalNode);
+            EvalCodeBlock& newCodeBlock = newEvalBody->bytecodeForExceptionInfoReparse(scopeChain);
+            ASSERT(newCodeBlock.m_exceptionInfo);
+            ASSERT(newCodeBlock.m_instructionCount == m_instructionCount);
+            m_exceptionInfo.set(newCodeBlock.m_exceptionInfo.release());
+            break;
+        }
+        default:
+            // CodeBlocks for Global code blocks are transient and therefore to not gain from 
+            // from throwing out there exception information.
+            ASSERT_NOT_REACHED();
+    }
+}
+
 HandlerInfo* CodeBlock::handlerForBytecodeOffset(unsigned bytecodeOffset)
 {
     ASSERT(bytecodeOffset < m_instructionCount);
@@ -1368,18 +1438,21 @@
     return 0;
 }
 
-int CodeBlock::lineNumberForBytecodeOffset(unsigned bytecodeOffset)
+int CodeBlock::lineNumberForBytecodeOffset(CallFrame* callFrame, unsigned bytecodeOffset)
 {
     ASSERT(bytecodeOffset < m_instructionCount);
 
-    if (!m_lineInfo.size())
+    reparseForExceptionInfoIfNecessary(callFrame);
+    ASSERT(m_exceptionInfo);
+
+    if (!m_exceptionInfo->m_lineInfo.size())
         return m_ownerNode->source().firstLine(); // Empty function
 
     int low = 0;
-    int high = m_lineInfo.size();
+    int high = m_exceptionInfo->m_lineInfo.size();
     while (low < high) {
         int mid = low + (high - low) / 2;
-        if (m_lineInfo[mid].instructionOffset <= bytecodeOffset)
+        if (m_exceptionInfo->m_lineInfo[mid].instructionOffset <= bytecodeOffset)
             low = mid + 1;
         else
             high = mid;
@@ -1387,26 +1460,29 @@
     
     if (!low)
         return m_ownerNode->source().firstLine();
-    return m_lineInfo[low - 1].lineNumber;
+    return m_exceptionInfo->m_lineInfo[low - 1].lineNumber;
 }
 
-int CodeBlock::expressionRangeForBytecodeOffset(unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset)
+int CodeBlock::expressionRangeForBytecodeOffset(CallFrame* callFrame, unsigned bytecodeOffset, int& divot, int& startOffset, int& endOffset)
 {
     ASSERT(bytecodeOffset < m_instructionCount);
 
-    if (!m_expressionInfo.size()) {
+    reparseForExceptionInfoIfNecessary(callFrame);
+    ASSERT(m_exceptionInfo);
+
+    if (!m_exceptionInfo->m_expressionInfo.size()) {
         // We didn't think anything could throw.  Apparently we were wrong.
         startOffset = 0;
         endOffset = 0;
         divot = 0;
-        return lineNumberForBytecodeOffset(bytecodeOffset);
+        return lineNumberForBytecodeOffset(callFrame, bytecodeOffset);
     }
 
     int low = 0;
-    int high = m_expressionInfo.size();
+    int high = m_exceptionInfo->m_expressionInfo.size();
     while (low < high) {
         int mid = low + (high - low) / 2;
-        if (m_expressionInfo[mid].instructionOffset <= bytecodeOffset)
+        if (m_exceptionInfo->m_expressionInfo[mid].instructionOffset <= bytecodeOffset)
             low = mid + 1;
         else
             high = mid;
@@ -1417,36 +1493,39 @@
         startOffset = 0;
         endOffset = 0;
         divot = 0;
-        return lineNumberForBytecodeOffset(bytecodeOffset);
+        return lineNumberForBytecodeOffset(callFrame, bytecodeOffset);
     }
 
-    startOffset = m_expressionInfo[low - 1].startOffset;
-    endOffset = m_expressionInfo[low - 1].endOffset;
-    divot = m_expressionInfo[low - 1].divotPoint + m_sourceOffset;
-    return lineNumberForBytecodeOffset(bytecodeOffset);
+    startOffset = m_exceptionInfo->m_expressionInfo[low - 1].startOffset;
+    endOffset = m_exceptionInfo->m_expressionInfo[low - 1].endOffset;
+    divot = m_exceptionInfo->m_expressionInfo[low - 1].divotPoint + m_sourceOffset;
+    return lineNumberForBytecodeOffset(callFrame, bytecodeOffset);
 }
 
-bool CodeBlock::getByIdExceptionInfoForBytecodeOffset(unsigned bytecodeOffset, OpcodeID& opcodeID)
+bool CodeBlock::getByIdExceptionInfoForBytecodeOffset(CallFrame* callFrame, unsigned bytecodeOffset, OpcodeID& opcodeID)
 {
     ASSERT(bytecodeOffset < m_instructionCount);
 
-    if (!m_getByIdExceptionInfo.size())
+    reparseForExceptionInfoIfNecessary(callFrame);
+    ASSERT(m_exceptionInfo);        
+
+    if (!m_exceptionInfo->m_getByIdExceptionInfo.size())
         return false;
 
     int low = 0;
-    int high = m_getByIdExceptionInfo.size();
+    int high = m_exceptionInfo->m_getByIdExceptionInfo.size();
     while (low < high) {
         int mid = low + (high - low) / 2;
-        if (m_getByIdExceptionInfo[mid].bytecodeOffset <= bytecodeOffset)
+        if (m_exceptionInfo->m_getByIdExceptionInfo[mid].bytecodeOffset <= bytecodeOffset)
             low = mid + 1;
         else
             high = mid;
     }
 
-    if (!low || m_getByIdExceptionInfo[low - 1].bytecodeOffset != bytecodeOffset)
+    if (!low || m_exceptionInfo->m_getByIdExceptionInfo[low - 1].bytecodeOffset != bytecodeOffset)
         return false;
 
-    opcodeID = m_getByIdExceptionInfo[low - 1].isOpConstruct ? op_construct : op_instanceof;
+    opcodeID = m_exceptionInfo->m_getByIdExceptionInfo[low - 1].isOpConstruct ? op_construct : op_instanceof;
     return true;
 }
 
@@ -1499,14 +1578,16 @@
     m_linkedCallerList.shrinkToFit();
 #endif
 
-    m_expressionInfo.shrinkToFit();
-    m_lineInfo.shrinkToFit();
-    m_getByIdExceptionInfo.shrinkToFit();
-
     m_identifiers.shrinkToFit();
     m_functionExpressions.shrinkToFit();
     m_constantRegisters.shrinkToFit();
 
+    if (m_exceptionInfo) {
+        m_exceptionInfo->m_expressionInfo.shrinkToFit();
+        m_exceptionInfo->m_lineInfo.shrinkToFit();
+        m_exceptionInfo->m_getByIdExceptionInfo.shrinkToFit();
+    }
+
     if (m_rareData) {
         m_rareData->m_exceptionHandlers.shrinkToFit();
         m_rareData->m_functions.shrinkToFit();