/* * Main VM dispatch loop and related routines for KJS/Frostbyte * This file is part of the KDE libraries * Copyright (C) 2008 Maksim Orlovich (maksim@kde.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "completion.h" #include "ExecState.h" #include "value.h" #include "nodes.h" #include "opcodes.h" #include "LocalStorage.h" #include "bytecode/opargs.h" #include "bytecode/machine.h" #include "scriptfunction.h" #include "internal.h" // for printInfo #include "ustring.h" #include "debugger.h" #include // maximum global call stack size. Protects against accidental or // malicious infinite recursions. Define to -1 if you want no limit. #if PLATFORM(DARWIN) // Given OS X stack sizes we run out of stack at about 350 levels. // If we improve our stack usage, we can bump this number. #define KJS_MAX_STACK 100 #else #define KJS_MAX_STACK 1000 // ### set system specific #endif namespace KJS { #if defined(__GNUC__) #define USE_LABEL_VALS #endif // We can either do jumps via switch or a GCC extension #ifdef USE_LABEL_VALS #define handler(X) l##X #else #define handler(X) case OpByteCode_##X #endif // #define COUNT_INSTRS // A little profiling aid -- counts the # of dynamic instances of each instruction. #ifdef COUNT_INSTRS static unsigned instrCounts[OpByteCode_NumValues]; struct InstrCountPrinter { ~InstrCountPrinter() { for (int c = 0; c < OpByteCode_NumValues; ++c) { printf("%9d %s\n", instrCounts[c], OpByteCodeVals[c]); } } }; static InstrCountPrinter dumpCounts; #endif // ### Need to consolidate this w/things remaining in nodes.cpp somehow. static void substitute(UString &string, const UString &substring) { int position = string.find("%s"); assert(position != -1); UString newString = string.substr(0, position); newString.append(substring); newString.append(string.substr(position + 2)); string = newString; } NEVER_INLINE void throwUndefinedVariableError(ExecState* exec, const Identifier& ident) { UString msg = "Can't find variable: %s"; substitute(msg, ident.ustring()); throwError(exec, ReferenceError, msg); } static JSValue* typeStringForValue(JSValue* v) { switch (v->type()) { case UndefinedType: return jsString("undefined"); case NullType: return jsString("object"); case BooleanType: return jsString("boolean"); case NumberType: return jsString("number"); case StringType: return jsString("string"); default: if (v->isObject()) { // Return "undefined" for objects that should be treated // as null when doing comparisons. if (static_cast(v)->masqueradeAsUndefined()) return jsString("undefined"); else if (static_cast(v)->isFunctionType()) return jsString("function"); } return jsString("object"); } } template ALWAYS_INLINE void lookupScopeAndFetch(ExecState* exec, Identifier* varName, JSValue*& scopeOut, JSValue*& valOut) { 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); JSObject *scopeObj = *iter; if (trySkipOne && !scopeObj->isLocalInjected()) { // Unless eval introduced new variables dynamically, // we know this isn't in the top scope ++iter; } PropertySlot slot; do { scopeObj = *iter; ++iter; if (scopeObj->getPropertySlot(exec, *varName, slot)) { if (!replaceActivations) { scopeOut = scopeObj; } else { // Common case: we found it in global object -- no need to check // if it's an activation if (iter == end || !scopeObj->isActivation()) scopeOut = scopeObj; else scopeOut = exec->dynamicInterpreter()->globalObject(); } valOut = slot.getValue(exec, scopeObj, *varName); return; } } while (iter != end); scopeOut = scopeObj; valOut = jsUndefined(); if (errorOut) { throwUndefinedVariableError(exec, *varName); } } static int depth; NEVER_INLINE JSValue* handleStackOverflow(ExecState* exec) { depth -= 11; //Give the debugger some room.. JSValue *ret = throwError(exec, RangeError, "Maximum call stack size exceeded."); depth += 10; //Put it back.. return ret; } enum Dir { Enter, Exit }; NEVER_INLINE void changeDebugContext(Dir d, ExecState* exec, Node* n) { FunctionBodyNode* body = static_cast(n); Debugger* dbg = exec->dynamicInterpreter()->debugger(); List args; FunctionImp* fn = 0; // Find the activation that contains arguments, fn const ScopeChain& chain = exec->scopeChain(); for (ScopeChainIterator iter = chain.begin(); iter != chain.end(); ++iter) { JSObject* scopeObj = *iter; if (scopeObj->isActivation()) { ActivationImp* act = static_cast(scopeObj); args = act->passedInArguments(); fn = static_cast(act->function()); break; } } if (d == Enter) dbg->enterContext(exec, body->sourceId(), body->firstLine(), fn, args); else dbg->exitContext(exec, body->sourceId(), body->lastLine(), fn); } class ForInState: public JSObject { public: PropertyNameArray* array; int pos; ForInState() { array = new PropertyNameArray; pos = 0; } ~ForInState() { delete array; } }; struct DepthCleanup { ~DepthCleanup() { --depth; } }; ALWAYS_INLINE_INTO JSValue* Machine::runBlock(ExecState* exec, const CodeBlock& codeBlock, ExecState* parentExec) { #ifdef USE_LABEL_VALS // Jump table, if needed @generate #endif ++depth; if (depth > KJS_MAX_STACK) return handleStackOverflow(exec); DepthCleanup dc; const unsigned char* base = codeBlock.data(); const unsigned char* pc = base; List workList; LocalStorageEntry* localStore = exec->localStorage(); exec->setMachineRegisters(base, &pc, &localStore); JSObject* globalObject = exec->dynamicInterpreter()->globalObject(); while (true) { begin: #ifdef COUNT_INSTRS OpByteCode opCodeLog = *reinterpret_cast(pc); ++instrCounts[opCodeLog]; #endif @generate } } } // kate: indent-width 4; replace-tabs on; tab-width 4; space-indent on; hl c++;