/* * Copyright (C) 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich * * 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "CodeBlock.h" #include "CTI.h" #include "JSValue.h" #include "Machine.h" #include "Debugger.h" #include #include namespace JSC { #if !defined(NDEBUG) || ENABLE(OPCODE_SAMPLING) static UString escapeQuotes(const UString& str) { UString result = str; int pos = 0; while ((pos = result.find('\"', pos)) >= 0) { result = result.substr(0, pos) + "\"\\\"\"" + result.substr(pos + 1); pos += 4; } return result; } static UString valueToSourceString(ExecState* exec, JSValue* val) { if (val->isString()) { UString result("\""); result += escapeQuotes(val->toString(exec)) + "\""; return result; } return val->toString(exec); } static CString registerName(int r) { if (r == missingThisObjectMarker()) return ""; return (UString("r") + UString::from(r)).UTF8String(); } static CString constantName(ExecState* exec, int k, JSValue* value) { return (valueToSourceString(exec, value) + "(@k" + UString::from(k) + ")").UTF8String(); } static CString idName(int id0, const Identifier& ident) { return (ident.ustring() + "(@id" + UString::from(id0) +")").UTF8String(); } static UString regexpToSourceString(RegExp* regExp) { UString pattern = UString("/") + regExp->pattern() + "/"; if (regExp->global()) pattern += "g"; if (regExp->ignoreCase()) pattern += "i"; if (regExp->multiline()) pattern += "m"; return pattern; } static CString regexpName(int re, RegExp* regexp) { return (regexpToSourceString(regexp) + "(@re" + UString::from(re) + ")").UTF8String(); } static UString pointerToSourceString(void* p) { char buffer[2 + 2 * sizeof(void*) + 1]; // 0x [two characters per byte] \0 snprintf(buffer, sizeof(buffer), "%p", p); return buffer; } NEVER_INLINE static const char* debugHookName(int debugHookID) { switch (static_cast(debugHookID)) { case DidEnterCallFrame: return "didEnterCallFrame"; case WillLeaveCallFrame: return "willLeaveCallFrame"; case WillExecuteStatement: return "willExecuteStatement"; case WillExecuteProgram: return "willExecuteProgram"; case DidExecuteProgram: return "didExecuteProgram"; case DidReachBreakpoint: return "didReachBreakpoint"; } ASSERT_NOT_REACHED(); return ""; } static int jumpTarget(const Vector::const_iterator& begin, Vector::const_iterator& it, int offset) { return it - begin + offset; } static void printUnaryOp(int location, Vector::const_iterator& it, const char* op) { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] %s\t\t %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str()); } static void printBinaryOp(int location, Vector::const_iterator& it, const char* op) { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; printf("[%4d] %s\t\t %s, %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); } static void printConditionalJump(const Vector::const_iterator& begin, Vector::const_iterator& it, int location, const char* op) { int r0 = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] %s\t\t %s, %d(->%d)\n", location, op, registerName(r0).c_str(), offset, jumpTarget(begin, it, offset)); } static void printGetByIdOp(int location, Vector::const_iterator& it, const Vector& identifiers, const char* op) { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str()); it += 4; } static void printPutByIdOp(int location, Vector::const_iterator& it, const Vector& identifiers, const char* op) { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str()); it += 4; } void CodeBlock::printStructureID(const char* name, const Instruction* vPC, int operand) const { unsigned instructionOffset = vPC - instructions.begin(); printf(" [%4d] %s: %s\n", instructionOffset, name, pointerToSourceString(vPC[operand].u.structureID).UTF8String().c_str()); } void CodeBlock::printStructureIDs(const Instruction* vPC) const { Machine* machine = globalData->machine; unsigned instructionOffset = vPC - instructions.begin(); if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id)) { printStructureID("get_by_id", vPC, 4); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_self)) { printStructureID("get_by_id_self", vPC, 4); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_proto)) { printf(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_proto", pointerToSourceString(vPC[4].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[5].u.structureID).UTF8String().c_str()); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_transition)) { printf(" [%4d] %s: %s, %s, %s\n", instructionOffset, "put_by_id_new", pointerToSourceString(vPC[4].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[5].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[6].u.structureIDChain).UTF8String().c_str()); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_chain)) { printf(" [%4d] %s: %s, %s\n", instructionOffset, "get_by_id_chain", pointerToSourceString(vPC[4].u.structureID).UTF8String().c_str(), pointerToSourceString(vPC[5].u.structureIDChain).UTF8String().c_str()); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id)) { printStructureID("put_by_id", vPC, 4); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_replace)) { printStructureID("put_by_id_replace", vPC, 4); return; } if (vPC[0].u.opcode == machine->getOpcode(op_resolve_global)) { printStructureID("resolve_global", vPC, 4); return; } // These instructions doesn't ref StructureIDs. ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_call) || vPC[0].u.opcode == machine->getOpcode(op_call_eval) || vPC[0].u.opcode == machine->getOpcode(op_construct)); } void CodeBlock::dump(ExecState* exec) const { Vector::const_iterator begin = instructions.begin(); Vector::const_iterator end = instructions.end(); size_t instructionCount = 0; for (Vector::const_iterator it = begin; it != end; ++it) if (exec->machine()->isOpcode(it->u.opcode)) ++instructionCount; printf("%lu instructions; %lu bytes at %p; %d parameter(s); %d callee register(s)\n\n", static_cast(instructionCount), static_cast(instructions.size() * sizeof(Instruction)), this, numParameters, numCalleeRegisters); for (Vector::const_iterator it = begin; it != end; ++it) dump(exec, begin, it); if (identifiers.size()) { printf("\nIdentifiers:\n"); size_t i = 0; do { printf(" id%u = %s\n", static_cast(i), identifiers[i].ascii()); ++i; } while (i != identifiers.size()); } if (constantRegisters.size()) { printf("\nConstants:\n"); unsigned registerIndex = numVars; size_t i = 0; do { printf(" r%u = %s\n", registerIndex, valueToSourceString(exec, constantRegisters[i].jsValue(exec)).ascii()); ++i; ++registerIndex; } while (i < constantRegisters.size()); } if (unexpectedConstants.size()) { printf("\nUnexpected Constants:\n"); size_t i = 0; do { printf(" k%u = %s\n", static_cast(i), valueToSourceString(exec, unexpectedConstants[i]).ascii()); ++i; } while (i < unexpectedConstants.size()); } if (regexps.size()) { printf("\nRegExps:\n"); size_t i = 0; do { printf(" re%u = %s\n", static_cast(i), regexpToSourceString(regexps[i].get()).ascii()); ++i; } while (i < regexps.size()); } if (globalResolveInstructions.size() || propertyAccessInstructions.size()) printf("\nStructureIDs:\n"); if (globalResolveInstructions.size()) { size_t i = 0; do { printStructureIDs(&instructions[globalResolveInstructions[i]]); ++i; } while (i < globalResolveInstructions.size()); } if (propertyAccessInstructions.size()) { size_t i = 0; do { printStructureIDs(&instructions[propertyAccessInstructions[i].opcodeIndex]); ++i; } while (i < propertyAccessInstructions.size()); } if (exceptionHandlers.size()) { printf("\nException Handlers:\n"); unsigned i = 0; do { printf("\t %d: { start: [%4d] end: [%4d] target: [%4d] }\n", i + 1, exceptionHandlers[i].start, exceptionHandlers[i].end, exceptionHandlers[i].target); ++i; } while (i < exceptionHandlers.size()); } if (immediateSwitchJumpTables.size()) { printf("Immediate Switch Jump Tables:\n"); unsigned i = 0; do { printf(" %1d = {\n", i); int entry = 0; Vector::const_iterator end = immediateSwitchJumpTables[i].branchOffsets.end(); for (Vector::const_iterator iter = immediateSwitchJumpTables[i].branchOffsets.begin(); iter != end; ++iter, ++entry) { if (!*iter) continue; printf("\t\t%4d => %04d\n", entry + immediateSwitchJumpTables[i].min, *iter); } printf(" }\n"); ++i; } while (i < immediateSwitchJumpTables.size()); } if (characterSwitchJumpTables.size()) { printf("\nCharacter Switch Jump Tables:\n"); unsigned i = 0; do { printf(" %1d = {\n", i); int entry = 0; Vector::const_iterator end = characterSwitchJumpTables[i].branchOffsets.end(); for (Vector::const_iterator iter = characterSwitchJumpTables[i].branchOffsets.begin(); iter != end; ++iter, ++entry) { if (!*iter) continue; ASSERT(!((i + characterSwitchJumpTables[i].min) & ~0xFFFF)); UChar ch = static_cast(entry + characterSwitchJumpTables[i].min); printf("\t\t\"%s\" => %04d\n", UString(&ch, 1).ascii(), *iter); } printf(" }\n"); ++i; } while (i < characterSwitchJumpTables.size()); } if (stringSwitchJumpTables.size()) { printf("\nString Switch Jump Tables:\n"); unsigned i = 0; do { printf(" %1d = {\n", i); StringJumpTable::StringOffsetTable::const_iterator end = stringSwitchJumpTables[i].offsetTable.end(); for (StringJumpTable::StringOffsetTable::const_iterator iter = stringSwitchJumpTables[i].offsetTable.begin(); iter != end; ++iter) printf("\t\t\"%s\" => %04d\n", UString(iter->first).ascii(), iter->second.branchOffset); printf(" }\n"); ++i; } while (i < stringSwitchJumpTables.size()); } printf("\n"); } void CodeBlock::dump(ExecState* exec, const Vector::const_iterator& begin, Vector::const_iterator& it) const { int location = it - begin; switch (exec->machine()->getOpcodeID(it->u.opcode)) { case op_enter: { printf("[%4d] enter\n", location); break; } case op_enter_with_activation: { int r0 = (++it)->u.operand; printf("[%4d] enter_with_activation %s\n", location, registerName(r0).c_str()); break; } case op_create_arguments: { printf("[%4d] create_arguments\n", location); break; } case op_convert_this: { int r0 = (++it)->u.operand; printf("[%4d] convert_this %s\n", location, registerName(r0).c_str()); break; } case op_unexpected_load: { int r0 = (++it)->u.operand; int k0 = (++it)->u.operand; printf("[%4d] unexpected_load\t %s, %s\n", location, registerName(r0).c_str(), constantName(exec, k0, unexpectedConstants[k0]).c_str()); break; } case op_new_object: { int r0 = (++it)->u.operand; printf("[%4d] new_object\t %s\n", location, registerName(r0).c_str()); break; } case op_new_array: { int dst = (++it)->u.operand; int argv = (++it)->u.operand; int argc = (++it)->u.operand; printf("[%4d] new_array\t %s, %s, %d\n", location, registerName(dst).c_str(), registerName(argv).c_str(), argc); break; } case op_new_regexp: { int r0 = (++it)->u.operand; int re0 = (++it)->u.operand; printf("[%4d] new_regexp\t %s, %s\n", location, registerName(r0).c_str(), regexpName(re0, regexps[re0].get()).c_str()); break; } case op_mov: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] mov\t\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); break; } case op_not: { printUnaryOp(location, it, "not"); break; } case op_eq: { printBinaryOp(location, it, "eq"); break; } case op_eq_null: { printUnaryOp(location, it, "eq_null"); break; } case op_neq: { printBinaryOp(location, it, "neq"); break; } case op_neq_null: { printUnaryOp(location, it, "neq_null"); break; } case op_stricteq: { printBinaryOp(location, it, "stricteq"); break; } case op_nstricteq: { printBinaryOp(location, it, "nstricteq"); break; } case op_less: { printBinaryOp(location, it, "less"); break; } case op_lesseq: { printBinaryOp(location, it, "lesseq"); break; } case op_pre_inc: { int r0 = (++it)->u.operand; printf("[%4d] pre_inc\t\t %s\n", location, registerName(r0).c_str()); break; } case op_pre_dec: { int r0 = (++it)->u.operand; printf("[%4d] pre_dec\t\t %s\n", location, registerName(r0).c_str()); break; } case op_post_inc: { printUnaryOp(location, it, "post_inc"); break; } case op_post_dec: { printUnaryOp(location, it, "post_dec"); break; } case op_to_jsnumber: { printUnaryOp(location, it, "to_jsnumber"); break; } case op_negate: { printUnaryOp(location, it, "negate"); ++it; break; } case op_add: { printBinaryOp(location, it, "add"); ++it; break; } case op_mul: { printBinaryOp(location, it, "mul"); ++it; break; } case op_div: { printBinaryOp(location, it, "div"); break; } case op_mod: { printBinaryOp(location, it, "mod"); break; } case op_sub: { printBinaryOp(location, it, "sub"); ++it; break; } case op_lshift: { printBinaryOp(location, it, "lshift"); break; } case op_rshift: { printBinaryOp(location, it, "rshift"); break; } case op_urshift: { printBinaryOp(location, it, "urshift"); break; } case op_bitand: { printBinaryOp(location, it, "bitand"); ++it; break; } case op_bitxor: { printBinaryOp(location, it, "bitxor"); ++it; break; } case op_bitor: { printBinaryOp(location, it, "bitor"); ++it; break; } case op_bitnot: { printUnaryOp(location, it, "bitnot"); break; } case op_instanceof: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; int r3 = (++it)->u.operand; printf("[%4d] instanceof\t\t %s, %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), registerName(r3).c_str()); break; } case op_typeof: { printUnaryOp(location, it, "typeof"); break; } case op_is_undefined: { printUnaryOp(location, it, "is_undefined"); break; } case op_is_boolean: { printUnaryOp(location, it, "is_boolean"); break; } case op_is_number: { printUnaryOp(location, it, "is_number"); break; } case op_is_string: { printUnaryOp(location, it, "is_string"); break; } case op_is_object: { printUnaryOp(location, it, "is_object"); break; } case op_is_function: { printUnaryOp(location, it, "is_function"); break; } case op_in: { printBinaryOp(location, it, "in"); break; } case op_resolve: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; printf("[%4d] resolve\t\t %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str()); break; } case op_resolve_skip: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int skipLevels = (++it)->u.operand; printf("[%4d] resolve_skip\t %s, %s, %d\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), skipLevels); break; } case op_resolve_global: { int r0 = (++it)->u.operand; JSValue* scope = static_cast((++it)->u.jsCell); int id0 = (++it)->u.operand; printf("[%4d] resolve_global\t %s, %s, %s\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), idName(id0, identifiers[id0]).c_str()); it += 2; break; } case op_get_scoped_var: { int r0 = (++it)->u.operand; int index = (++it)->u.operand; int skipLevels = (++it)->u.operand; printf("[%4d] get_scoped_var\t %s, %d, %d\n", location, registerName(r0).c_str(), index, skipLevels); break; } case op_put_scoped_var: { int index = (++it)->u.operand; int skipLevels = (++it)->u.operand; int r0 = (++it)->u.operand; printf("[%4d] put_scoped_var\t %d, %d, %s\n", location, index, skipLevels, registerName(r0).c_str()); break; } case op_get_global_var: { int r0 = (++it)->u.operand; JSValue* scope = static_cast((++it)->u.jsCell); int index = (++it)->u.operand; printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), index); break; } case op_put_global_var: { JSValue* scope = static_cast((++it)->u.jsCell); int index = (++it)->u.operand; int r0 = (++it)->u.operand; printf("[%4d] put_global_var\t %s, %d, %s\n", location, valueToSourceString(exec, scope).ascii(), index, registerName(r0).c_str()); break; } case op_resolve_base: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; printf("[%4d] resolve_base\t %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str()); break; } case op_resolve_with_base: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; printf("[%4d] resolve_with_base %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str()); break; } case op_resolve_func: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; printf("[%4d] resolve_func\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str()); break; } case op_get_by_id: { printGetByIdOp(location, it, identifiers, "get_by_id"); break; } case op_get_by_id_self: { printGetByIdOp(location, it, identifiers, "get_by_id_self"); break; } case op_get_by_id_proto: { printGetByIdOp(location, it, identifiers, "get_by_id_proto"); break; } case op_get_by_id_chain: { printGetByIdOp(location, it, identifiers, "get_by_id_chain"); break; } case op_get_by_id_generic: { printGetByIdOp(location, it, identifiers, "get_by_id_generic"); break; } case op_get_array_length: { printGetByIdOp(location, it, identifiers, "get_array_length"); break; } case op_get_string_length: { printGetByIdOp(location, it, identifiers, "get_string_length"); break; } case op_put_by_id: { printPutByIdOp(location, it, identifiers, "put_by_id"); break; } case op_put_by_id_replace: { printPutByIdOp(location, it, identifiers, "put_by_id_replace"); break; } case op_put_by_id_transition: { printPutByIdOp(location, it, identifiers, "put_by_id_transition"); break; } case op_put_by_id_generic: { printPutByIdOp(location, it, identifiers, "put_by_id_generic"); break; } case op_put_getter: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] put_getter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str()); break; } case op_put_setter: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] put_setter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str()); break; } case op_del_by_id: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; printf("[%4d] del_by_id\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, identifiers[id0]).c_str()); break; } case op_get_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; printf("[%4d] get_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); break; } case op_put_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; printf("[%4d] put_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); break; } case op_del_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; printf("[%4d] del_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); break; } case op_put_by_index: { int r0 = (++it)->u.operand; unsigned n0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] put_by_index\t %s, %u, %s\n", location, registerName(r0).c_str(), n0, registerName(r1).c_str()); break; } case op_jmp: { int offset = (++it)->u.operand; printf("[%4d] jmp\t\t %d(->%d)\n", location, offset, jumpTarget(begin, it, offset)); break; } case op_loop: { int offset = (++it)->u.operand; printf("[%4d] loop\t\t %d(->%d)\n", location, offset, jumpTarget(begin, it, offset)); break; } case op_jtrue: { printConditionalJump(begin, it, location, "jtrue"); break; } case op_loop_if_true: { printConditionalJump(begin, it, location, "loop_if_true"); break; } case op_jfalse: { printConditionalJump(begin, it, location, "jfalse"); break; } case op_jeq_null: { printConditionalJump(begin, it, location, "jeq_null"); break; } case op_jneq_null: { printConditionalJump(begin, it, location, "jneq_null"); break; } case op_jnless: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] jnless\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, jumpTarget(begin, it, offset)); break; } case op_loop_if_less: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] loop_if_less\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, jumpTarget(begin, it, offset)); break; } case op_loop_if_lesseq: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] loop_if_lesseq\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, jumpTarget(begin, it, offset)); break; } case op_switch_imm: { int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; printf("[%4d] switch_imm\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, jumpTarget(begin, it, defaultTarget), registerName(scrutineeRegister).c_str()); break; } case op_switch_char: { int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; printf("[%4d] switch_char\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, jumpTarget(begin, it, defaultTarget), registerName(scrutineeRegister).c_str()); break; } case op_switch_string: { int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; printf("[%4d] switch_string\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, jumpTarget(begin, it, defaultTarget), registerName(scrutineeRegister).c_str()); break; } case op_new_func: { int r0 = (++it)->u.operand; int f0 = (++it)->u.operand; printf("[%4d] new_func\t\t %s, f%d\n", location, registerName(r0).c_str(), f0); break; } case op_new_func_exp: { int r0 = (++it)->u.operand; int f0 = (++it)->u.operand; printf("[%4d] new_func_exp\t %s, f%d\n", location, registerName(r0).c_str(), f0); break; } case op_call: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; int tempCount = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; printf("[%4d] call\t\t %s, %s, %s, %d, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount, registerOffset); break; } case op_call_eval: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; int tempCount = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; printf("[%4d] call_eval\t\t %s, %s, %s, %d, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount, registerOffset); break; } case op_tear_off_activation: { int r0 = (++it)->u.operand; printf("[%4d] tear_off_activation\t %s\n", location, registerName(r0).c_str()); break; } case op_tear_off_arguments: { printf("[%4d] tear_off_arguments\n", location); break; } case op_ret: { int r0 = (++it)->u.operand; printf("[%4d] ret\t\t %s\n", location, registerName(r0).c_str()); break; } case op_construct: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; int tempCount = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; printf("[%4d] construct\t %s, %s, %s, %d, %d, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), tempCount, argCount, registerOffset); break; } case op_construct_verify: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] construct_verify\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); break; } case op_get_pnames: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] get_pnames\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); break; } case op_next_pname: { int dest = (++it)->u.operand; int iter = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] next_pname\t %s, %s, %d(->%d)\n", location, registerName(dest).c_str(), registerName(iter).c_str(), offset, jumpTarget(begin, it, offset)); break; } case op_push_scope: { int r0 = (++it)->u.operand; printf("[%4d] push_scope\t %s\n", location, registerName(r0).c_str()); break; } case op_pop_scope: { printf("[%4d] pop_scope\n", location); break; } case op_push_new_scope: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; printf("[%4d] push_new_scope \t%s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, identifiers[id0]).c_str(), registerName(r1).c_str()); break; } case op_jmp_scopes: { int scopeDelta = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] jmp_scopes\t^%d, %d(->%d)\n", location, scopeDelta, offset, jumpTarget(begin, it, offset)); break; } case op_catch: { int r0 = (++it)->u.operand; printf("[%4d] catch\t\t %s\n", location, registerName(r0).c_str()); break; } case op_throw: { int r0 = (++it)->u.operand; printf("[%4d] throw\t\t %s\n", location, registerName(r0).c_str()); break; } case op_new_error: { int r0 = (++it)->u.operand; int errorType = (++it)->u.operand; int k0 = (++it)->u.operand; printf("[%4d] new_error\t %s, %d, %s\n", location, registerName(r0).c_str(), errorType, constantName(exec, k0, unexpectedConstants[k0]).c_str()); break; } case op_jsr: { int retAddrDst = (++it)->u.operand; int offset = (++it)->u.operand; printf("[%4d] jsr\t\t %s, %d(->%d)\n", location, registerName(retAddrDst).c_str(), offset, jumpTarget(begin, it, offset)); break; } case op_sret: { int retAddrSrc = (++it)->u.operand; printf("[%4d] sret\t\t %s\n", location, registerName(retAddrSrc).c_str()); break; } case op_debug: { int debugHookID = (++it)->u.operand; int firstLine = (++it)->u.operand; int lastLine = (++it)->u.operand; printf("[%4d] debug\t\t %s, %d, %d\n", location, debugHookName(debugHookID), firstLine, lastLine); break; } case op_profile_will_call: { int function = (++it)->u.operand; printf("[%4d] profile_will_call %s\n", location, registerName(function).c_str()); break; } case op_profile_did_call: { int function = (++it)->u.operand; printf("[%4d] profile_did_call\t %s\n", location, registerName(function).c_str()); break; } case op_end: { int r0 = (++it)->u.operand; printf("[%4d] end\t\t %s\n", location, registerName(r0).c_str()); break; } } } #endif // !defined(NDEBUG) || ENABLE(OPCODE_SAMPLING) CodeBlock::~CodeBlock() { for (size_t size = globalResolveInstructions.size(), i = 0; i < size; ++i) { derefStructureIDs(&instructions[globalResolveInstructions[i]]); } for (size_t size = propertyAccessInstructions.size(), i = 0; i < size; ++i) { derefStructureIDs(&instructions[propertyAccessInstructions[i].opcodeIndex]); if (propertyAccessInstructions[i].stubRoutine) WTF::fastFreeExecutable(propertyAccessInstructions[i].stubRoutine); } for (size_t size = callLinkInfos.size(), i = 0; i < size; ++i) { CallLinkInfo* callLinkInfo = &callLinkInfos[i]; if (callLinkInfo->isLinked()) callLinkInfo->callee->removeCaller(callLinkInfo); } #if ENABLE(CTI) unlinkCallers(); if (ctiCode) WTF::fastFreeExecutable(ctiCode); #endif } #if ENABLE(CTI) void CodeBlock::unlinkCallers() { size_t size = linkedCallerList.size(); for (size_t i = 0; i < size; ++i) { CallLinkInfo* currentCaller = linkedCallerList[i]; CTI::unlinkCall(currentCaller); currentCaller->setUnlinked(); } linkedCallerList.clear(); } #endif void CodeBlock::derefStructureIDs(Instruction* vPC) const { Machine* machine = globalData->machine; if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_self)) { vPC[4].u.structureID->deref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_proto)) { vPC[4].u.structureID->deref(); vPC[5].u.structureID->deref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_chain)) { vPC[4].u.structureID->deref(); vPC[5].u.structureIDChain->deref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_transition)) { vPC[4].u.structureID->deref(); vPC[5].u.structureID->deref(); vPC[6].u.structureIDChain->deref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_replace)) { vPC[4].u.structureID->deref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_resolve_global)) { if(vPC[4].u.structureID) vPC[4].u.structureID->deref(); return; } // These instructions don't ref their StructureIDs. ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id) || vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_get_array_length) || vPC[0].u.opcode == machine->getOpcode(op_get_string_length)); } void CodeBlock::refStructureIDs(Instruction* vPC) const { Machine* machine = globalData->machine; if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_self)) { vPC[4].u.structureID->ref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_proto)) { vPC[4].u.structureID->ref(); vPC[5].u.structureID->ref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_get_by_id_chain)) { vPC[4].u.structureID->ref(); vPC[5].u.structureIDChain->ref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_transition)) { vPC[4].u.structureID->ref(); vPC[5].u.structureID->ref(); vPC[6].u.structureIDChain->ref(); return; } if (vPC[0].u.opcode == machine->getOpcode(op_put_by_id_replace)) { vPC[4].u.structureID->ref(); return; } // These instructions don't ref their StructureIDs. ASSERT(vPC[0].u.opcode == machine->getOpcode(op_get_by_id) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id) || vPC[0].u.opcode == machine->getOpcode(op_get_by_id_generic) || vPC[0].u.opcode == machine->getOpcode(op_put_by_id_generic)); } void CodeBlock::mark() { for (size_t i = 0; i < constantRegisters.size(); ++i) if (!constantRegisters[i].marked()) constantRegisters[i].mark(); for (size_t i = 0; i < unexpectedConstants.size(); ++i) if (!unexpectedConstants[i]->marked()) unexpectedConstants[i]->mark(); for (size_t i = 0; i < functions.size(); ++i) functions[i]->body()->mark(); for (size_t i = 0; i < functionExpressions.size(); ++i) functionExpressions[i]->body()->mark(); } bool CodeBlock::getHandlerForVPC(const Instruction* vPC, Instruction*& target, int& scopeDepth) { Vector::iterator ptr = exceptionHandlers.begin(); Vector::iterator end = exceptionHandlers.end(); unsigned addressOffset = vPC - instructions.begin(); ASSERT(addressOffset < instructions.size()); for (; ptr != end; ++ptr) { // Handlers are ordered innermost first, so the first handler we encounter // that contains the source address is the correct handler to use. if (ptr->start <= addressOffset && ptr->end >= addressOffset) { scopeDepth = ptr->scopeDepth; target = instructions.begin() + ptr->target; return true; } } return false; } void* CodeBlock::nativeExceptionCodeForHandlerVPC(const Instruction* handlerVPC) { Vector::iterator ptr = exceptionHandlers.begin(); Vector::iterator end = exceptionHandlers.end(); for (; ptr != end; ++ptr) { Instruction*target = instructions.begin() + ptr->target; if (handlerVPC == target) return ptr->nativeCode; } return 0; } int CodeBlock::lineNumberForVPC(const Instruction* vPC) { unsigned instructionOffset = vPC - instructions.begin(); ASSERT(instructionOffset < instructions.size()); if (!lineInfo.size()) return ownerNode->source().firstLine(); // Empty function int low = 0; int high = lineInfo.size(); while (low < high) { int mid = low + (high - low) / 2; if (lineInfo[mid].instructionOffset <= instructionOffset) low = mid + 1; else high = mid; } if (!low) return ownerNode->source().firstLine(); return lineInfo[low - 1].lineNumber; } int CodeBlock::expressionRangeForVPC(const Instruction* vPC, int& divot, int& startOffset, int& endOffset) { unsigned instructionOffset = vPC - instructions.begin(); ASSERT(instructionOffset < instructions.size()); if (!expressionInfo.size()) { // We didn't think anything could throw. Apparently we were wrong. startOffset = 0; endOffset = 0; divot = 0; return lineNumberForVPC(vPC); } int low = 0; int high = expressionInfo.size(); while (low < high) { int mid = low + (high - low) / 2; if (expressionInfo[mid].instructionOffset <= instructionOffset) low = mid + 1; else high = mid; } ASSERT(low); if (!low) { startOffset = 0; endOffset = 0; divot = 0; return lineNumberForVPC(vPC); } startOffset = expressionInfo[low - 1].startOffset; endOffset = expressionInfo[low - 1].endOffset; divot = expressionInfo[low - 1].divotPoint + sourceOffset; return lineNumberForVPC(vPC); } int32_t SimpleJumpTable::offsetForValue(int32_t value, int32_t defaultOffset) { if (value >= min && static_cast(value - min) < branchOffsets.size()) { int32_t offset = branchOffsets[value - min]; if (offset) return offset; } return defaultOffset; } } // namespace JSC