]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - bytecode/UnlinkedCodeBlock.cpp
JavaScriptCore-1218.tar.gz
[apple/javascriptcore.git] / bytecode / UnlinkedCodeBlock.cpp
diff --git a/bytecode/UnlinkedCodeBlock.cpp b/bytecode/UnlinkedCodeBlock.cpp
new file mode 100644 (file)
index 0000000..8aba1ff
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "UnlinkedCodeBlock.h"
+
+#include "BytecodeGenerator.h"
+#include "ClassInfo.h"
+#include "CodeCache.h"
+#include "Executable.h"
+#include "JSString.h"
+#include "Operations.h"
+#include "Parser.h"
+#include "SourceProvider.h"
+#include "Structure.h"
+#include "SymbolTable.h"
+
+namespace JSC {
+
+const ClassInfo UnlinkedFunctionExecutable::s_info = { "UnlinkedFunctionExecutable", 0, 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionExecutable) };
+const ClassInfo UnlinkedCodeBlock::s_info = { "UnlinkedCodeBlock", 0, 0, 0, CREATE_METHOD_TABLE(UnlinkedCodeBlock) };
+const ClassInfo UnlinkedGlobalCodeBlock::s_info = { "UnlinkedGlobalCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedGlobalCodeBlock) };
+const ClassInfo UnlinkedProgramCodeBlock::s_info = { "UnlinkedProgramCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedProgramCodeBlock) };
+const ClassInfo UnlinkedEvalCodeBlock::s_info = { "UnlinkedEvalCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedEvalCodeBlock) };
+const ClassInfo UnlinkedFunctionCodeBlock::s_info = { "UnlinkedFunctionCodeBlock", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(UnlinkedFunctionCodeBlock) };
+
+static UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(VM& vm, JSScope* scope, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
+{
+    RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(&vm, source, executable->parameters(), executable->name(), executable->isInStrictContext() ? JSParseStrict : JSParseNormal, JSParseFunctionCode, error);
+
+    if (!body) {
+        ASSERT(error.m_type != ParserError::ErrorNone);
+        return 0;
+    }
+
+    if (executable->forceUsesArguments())
+        body->setUsesArguments();
+    body->finishParsing(executable->parameters(), executable->name(), executable->functionNameIsInScopeToggle());
+    executable->recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine());
+    
+    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(body->needsActivation(), body->usesEval(), body->isStrictMode(), kind == CodeForConstruct));
+    OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(vm, scope, body.get(), result, debuggerMode, profilerMode)));
+    error = generator->generate();
+    body->destroyData();
+    if (error.m_type != ParserError::ErrorNone)
+        return 0;
+    return result;
+}
+
+unsigned UnlinkedCodeBlock::addOrFindConstant(JSValue v)
+{
+    unsigned numberOfConstants = numberOfConstantRegisters();
+    for (unsigned i = 0; i < numberOfConstants; ++i) {
+        if (getConstant(FirstConstantRegisterIndex + i) == v)
+            return i;
+    }
+    return addConstant(v);
+}
+
+UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* structure, const SourceCode& source, FunctionBodyNode* node)
+    : Base(*vm, structure)
+    , m_numCapturedVariables(node->capturedVariableCount())
+    , m_forceUsesArguments(node->usesArguments())
+    , m_isInStrictContext(node->isStrictMode())
+    , m_hasCapturedVariables(node->hasCapturedVariables())
+    , m_name(node->ident())
+    , m_inferredName(node->inferredName())
+    , m_parameters(node->parameters())
+    , m_firstLineOffset(node->firstLine() - source.firstLine())
+    , m_lineCount(node->lastLine() - node->firstLine())
+    , m_functionStartOffset(node->functionStart() - source.startOffset())
+    , m_functionStartColumn(node->startColumn())
+    , m_startOffset(node->source().startOffset() - source.startOffset())
+    , m_sourceLength(node->source().length())
+    , m_features(node->features())
+    , m_functionNameIsInScopeToggle(node->functionNameIsInScopeToggle())
+{
+}
+
+size_t UnlinkedFunctionExecutable::parameterCount() const
+{
+    return m_parameters->size();
+}
+
+void UnlinkedFunctionExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    UnlinkedFunctionExecutable* thisObject = jsCast<UnlinkedFunctionExecutable*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
+    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
+    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_codeBlockForCall);
+    visitor.append(&thisObject->m_codeBlockForConstruct);
+    visitor.append(&thisObject->m_nameValue);
+    visitor.append(&thisObject->m_symbolTableForCall);
+    visitor.append(&thisObject->m_symbolTableForConstruct);
+}
+
+FunctionExecutable* UnlinkedFunctionExecutable::link(VM& vm, const SourceCode& source, size_t lineOffset, size_t sourceOffset)
+{
+    unsigned firstLine = lineOffset + m_firstLineOffset;
+    unsigned startOffset = sourceOffset + m_startOffset;
+    unsigned startColumn = m_functionStartColumn + 1; // startColumn should start from 1, not 0.
+    SourceCode code(source.provider(), startOffset, startOffset + m_sourceLength, firstLine, startColumn);
+    return FunctionExecutable::create(vm, code, this, firstLine, firstLine + m_lineCount, startColumn);
+}
+
+UnlinkedFunctionExecutable* UnlinkedFunctionExecutable::fromGlobalCode(const Identifier& name, ExecState* exec, Debugger*, const SourceCode& source, JSObject** exception)
+{
+    ParserError error;
+    CodeCache* codeCache = exec->vm().codeCache();
+    UnlinkedFunctionExecutable* executable = codeCache->getFunctionExecutableFromGlobalCode(exec->vm(), name, source, error);
+
+    if (exec->lexicalGlobalObject()->hasDebugger())
+        exec->lexicalGlobalObject()->debugger()->sourceParsed(exec, source.provider(), error.m_line, error.m_message);
+
+    if (error.m_type != ParserError::ErrorNone) {
+        *exception = error.toErrorObject(exec->lexicalGlobalObject(), source);
+        return 0;
+    }
+
+    return executable;
+}
+
+UnlinkedFunctionCodeBlock* UnlinkedFunctionExecutable::codeBlockFor(VM& vm, JSScope* scope, const SourceCode& source, CodeSpecializationKind specializationKind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
+{
+    switch (specializationKind) {
+    case CodeForCall:
+        if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForCall.get())
+            return codeBlock;
+        break;
+    case CodeForConstruct:
+        if (UnlinkedFunctionCodeBlock* codeBlock = m_codeBlockForConstruct.get())
+            return codeBlock;
+        break;
+    }
+
+    UnlinkedFunctionCodeBlock* result = generateFunctionCodeBlock(vm, scope, this, source, specializationKind, debuggerMode, profilerMode, error);
+    
+    if (error.m_type != ParserError::ErrorNone)
+        return 0;
+
+    switch (specializationKind) {
+    case CodeForCall:
+        m_codeBlockForCall.set(vm, this, result);
+        m_symbolTableForCall.set(vm, this, result->symbolTable());
+        break;
+    case CodeForConstruct:
+        m_codeBlockForConstruct.set(vm, this, result);
+        m_symbolTableForConstruct.set(vm, this, result->symbolTable());
+        break;
+    }
+    return result;
+}
+
+String UnlinkedFunctionExecutable::paramString() const
+{
+    FunctionParameters& parameters = *m_parameters;
+    StringBuilder builder;
+    for (size_t pos = 0; pos < parameters.size(); ++pos) {
+        if (!builder.isEmpty())
+            builder.appendLiteral(", ");
+        builder.append(parameters.at(pos).string());
+    }
+    return builder.toString();
+}
+
+UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info)
+    : Base(*vm, structure)
+    , m_numVars(0)
+    , m_numCalleeRegisters(0)
+    , m_numParameters(0)
+    , m_vm(vm)
+    , m_argumentsRegister(-1)
+    , m_globalObjectRegister(-1)
+    , m_needsFullScopeChain(info.m_needsActivation)
+    , m_usesEval(info.m_usesEval)
+    , m_isNumericCompareFunction(false)
+    , m_isStrictMode(info.m_isStrictMode)
+    , m_isConstructor(info.m_isConstructor)
+    , m_hasCapturedVariables(false)
+    , m_firstLine(0)
+    , m_lineCount(0)
+    , m_features(0)
+    , m_codeType(codeType)
+    , m_resolveOperationCount(0)
+    , m_putToBaseOperationCount(1)
+    , m_arrayProfileCount(0)
+    , m_arrayAllocationProfileCount(0)
+    , m_objectAllocationProfileCount(0)
+    , m_valueProfileCount(0)
+    , m_llintCallLinkInfoCount(0)
+#if ENABLE(BYTECODE_COMMENTS)
+    , m_bytecodeCommentIterator(0)
+#endif
+{
+
+}
+
+void UnlinkedCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    UnlinkedCodeBlock* thisObject = jsCast<UnlinkedCodeBlock*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
+    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
+    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_symbolTable);
+    for (FunctionExpressionVector::iterator ptr = thisObject->m_functionDecls.begin(), end = thisObject->m_functionDecls.end(); ptr != end; ++ptr)
+        visitor.append(ptr);
+    for (FunctionExpressionVector::iterator ptr = thisObject->m_functionExprs.begin(), end = thisObject->m_functionExprs.end(); ptr != end; ++ptr)
+        visitor.append(ptr);
+    visitor.appendValues(thisObject->m_constantRegisters.data(), thisObject->m_constantRegisters.size());
+    if (thisObject->m_rareData) {
+        for (size_t i = 0, end = thisObject->m_rareData->m_regexps.size(); i != end; i++)
+            visitor.append(&thisObject->m_rareData->m_regexps[i]);
+    }
+}
+
+int UnlinkedCodeBlock::lineNumberForBytecodeOffset(unsigned bytecodeOffset)
+{
+    ASSERT(bytecodeOffset < instructions().size());
+    int divot;
+    int startOffset;
+    int endOffset;
+    unsigned line;
+    unsigned column;
+    expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column);
+    return line;
+}
+
+void UnlinkedCodeBlock::expressionRangeForBytecodeOffset(unsigned bytecodeOffset,
+    int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
+{
+    ASSERT(bytecodeOffset < instructions().size());
+
+    if (!m_expressionInfo.size()) {
+        startOffset = 0;
+        endOffset = 0;
+        divot = 0;
+        line = 0;
+        column = 0;
+        return;
+    }
+
+    Vector<ExpressionRangeInfo>& expressionInfo = m_expressionInfo;
+
+    int low = 0;
+    int high = expressionInfo.size();
+    while (low < high) {
+        int mid = low + (high - low) / 2;
+        if (expressionInfo[mid].instructionOffset <= bytecodeOffset)
+            low = mid + 1;
+        else
+            high = mid;
+    }
+
+    if (!low)
+        low = 1;
+
+    ExpressionRangeInfo& info = expressionInfo[low - 1];
+    startOffset = info.startOffset;
+    endOffset = info.endOffset;
+    divot = info.divotPoint;
+
+    switch (info.mode) {
+    case ExpressionRangeInfo::FatLineMode:
+        info.decodeFatLineMode(line, column);
+        break;
+    case ExpressionRangeInfo::FatColumnMode:
+        info.decodeFatColumnMode(line, column);
+        break;
+    case ExpressionRangeInfo::FatLineAndColumnMode: {
+        unsigned fatIndex = info.position;
+        ExpressionRangeInfo::FatPosition& fatPos = m_rareData->m_expressionInfoFatPositions[fatIndex];
+        line = fatPos.line;
+        column = fatPos.column;
+        break;
+    }
+    } // switch
+}
+
+void UnlinkedCodeBlock::addExpressionInfo(unsigned instructionOffset,
+    int divot, int startOffset, int endOffset, unsigned line, unsigned column)
+{
+    if (divot > ExpressionRangeInfo::MaxDivot) {
+        // Overflow has occurred, we can only give line number info for errors for this region
+        divot = 0;
+        startOffset = 0;
+        endOffset = 0;
+    } else if (startOffset > ExpressionRangeInfo::MaxOffset) {
+        // If the start offset is out of bounds we clear both offsets
+        // so we only get the divot marker. Error message will have to be reduced
+        // to line and charPosition number.
+        startOffset = 0;
+        endOffset = 0;
+    } else if (endOffset > ExpressionRangeInfo::MaxOffset) {
+        // The end offset is only used for additional context, and is much more likely
+        // to overflow (eg. function call arguments) so we are willing to drop it without
+        // dropping the rest of the range.
+        endOffset = 0;
+    }
+
+    unsigned positionMode =
+        (line <= ExpressionRangeInfo::MaxFatLineModeLine && column <= ExpressionRangeInfo::MaxFatLineModeColumn) 
+        ? ExpressionRangeInfo::FatLineMode
+        : (line <= ExpressionRangeInfo::MaxFatColumnModeLine && column <= ExpressionRangeInfo::MaxFatColumnModeColumn)
+        ? ExpressionRangeInfo::FatColumnMode
+        : ExpressionRangeInfo::FatLineAndColumnMode;
+
+    ExpressionRangeInfo info;
+    info.instructionOffset = instructionOffset;
+    info.divotPoint = divot;
+    info.startOffset = startOffset;
+    info.endOffset = endOffset;
+
+    info.mode = positionMode;
+    switch (positionMode) {
+    case ExpressionRangeInfo::FatLineMode:
+        info.encodeFatLineMode(line, column);
+        break;
+    case ExpressionRangeInfo::FatColumnMode:
+        info.encodeFatColumnMode(line, column);
+        break;
+    case ExpressionRangeInfo::FatLineAndColumnMode: {
+        createRareDataIfNecessary();
+        unsigned fatIndex = m_rareData->m_expressionInfoFatPositions.size();
+        ExpressionRangeInfo::FatPosition fatPos = { line, column };
+        m_rareData->m_expressionInfoFatPositions.append(fatPos);
+        info.position = fatIndex;
+    }
+    } // switch
+
+    m_expressionInfo.append(info);
+}
+
+void UnlinkedProgramCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    UnlinkedProgramCodeBlock* thisObject = jsCast<UnlinkedProgramCodeBlock*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
+    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
+    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
+    Base::visitChildren(thisObject, visitor);
+    for (size_t i = 0, end = thisObject->m_functionDeclarations.size(); i != end; i++)
+        visitor.append(&thisObject->m_functionDeclarations[i].second);
+}
+
+UnlinkedCodeBlock::~UnlinkedCodeBlock()
+{
+}
+
+void UnlinkedProgramCodeBlock::destroy(JSCell* cell)
+{
+    jsCast<UnlinkedProgramCodeBlock*>(cell)->~UnlinkedProgramCodeBlock();
+}
+
+void UnlinkedEvalCodeBlock::destroy(JSCell* cell)
+{
+    jsCast<UnlinkedEvalCodeBlock*>(cell)->~UnlinkedEvalCodeBlock();
+}
+
+void UnlinkedFunctionCodeBlock::destroy(JSCell* cell)
+{
+    jsCast<UnlinkedFunctionCodeBlock*>(cell)->~UnlinkedFunctionCodeBlock();
+}
+
+void UnlinkedFunctionExecutable::destroy(JSCell* cell)
+{
+    jsCast<UnlinkedFunctionExecutable*>(cell)->~UnlinkedFunctionExecutable();
+}
+
+}
+