mirror of https://github.com/lukechilds/node.git
Ryan
16 years ago
53 changed files with 2256 additions and 231 deletions
@ -0,0 +1,124 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions are
|
||||
|
// met:
|
||||
|
//
|
||||
|
// * Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
// * 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.
|
||||
|
// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
|
// OWNER 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 "v8.h" |
||||
|
|
||||
|
#include "cfg.h" |
||||
|
#include "codegen-inl.h" |
||||
|
#include "macro-assembler-arm.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
#define __ ACCESS_MASM(masm) |
||||
|
|
||||
|
void InstructionBlock::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
{ |
||||
|
Comment cmt(masm, "[ InstructionBlock"); |
||||
|
for (int i = 0, len = instructions_.length(); i < len; i++) { |
||||
|
instructions_[i]->Compile(masm); |
||||
|
} |
||||
|
} |
||||
|
successor_->Compile(masm); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void EntryNode::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
{ |
||||
|
Comment cmnt(masm, "[ EntryNode"); |
||||
|
__ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); |
||||
|
__ add(fp, sp, Operand(2 * kPointerSize)); |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots(); |
||||
|
if (count > 0) { |
||||
|
__ mov(ip, Operand(Factory::undefined_value())); |
||||
|
for (int i = 0; i < count; i++) { |
||||
|
__ push(ip); |
||||
|
} |
||||
|
} |
||||
|
if (FLAG_trace) { |
||||
|
__ CallRuntime(Runtime::kTraceEnter, 0); |
||||
|
} |
||||
|
if (FLAG_check_stack) { |
||||
|
StackCheckStub stub; |
||||
|
__ CallStub(&stub); |
||||
|
} |
||||
|
} |
||||
|
successor_->Compile(masm); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExitNode::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
Comment cmnt(masm, "[ ExitNode"); |
||||
|
if (FLAG_trace) { |
||||
|
__ push(r0); |
||||
|
__ CallRuntime(Runtime::kTraceExit, 1); |
||||
|
} |
||||
|
__ mov(sp, fp); |
||||
|
__ ldm(ia_w, sp, fp.bit() | lr.bit()); |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
||||
|
__ add(sp, sp, Operand((count + 1) * kPointerSize)); |
||||
|
__ Jump(lr); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ReturnInstr::Compile(MacroAssembler* masm) { |
||||
|
Comment cmnt(masm, "[ ReturnInstr"); |
||||
|
value_->ToRegister(masm, r0); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Constant::ToRegister(MacroAssembler* masm, Register reg) { |
||||
|
__ mov(reg, Operand(handle_)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) { |
||||
|
switch (type_) { |
||||
|
case Slot::PARAMETER: { |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
||||
|
__ ldr(reg, MemOperand(fp, (1 + count - index_) * kPointerSize)); |
||||
|
break; |
||||
|
} |
||||
|
case Slot::LOCAL: { |
||||
|
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; |
||||
|
__ ldr(reg, MemOperand(fp, kOffset - index_ * kPointerSize)); |
||||
|
break; |
||||
|
} |
||||
|
default: |
||||
|
UNREACHABLE(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#undef __ |
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,485 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions are
|
||||
|
// met:
|
||||
|
//
|
||||
|
// * Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
// * 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.
|
||||
|
// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
|
// OWNER 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 "v8.h" |
||||
|
|
||||
|
#include "bootstrapper.h" |
||||
|
#include "cfg.h" |
||||
|
#include "scopeinfo.h" |
||||
|
#include "scopes.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
|
||||
|
CfgGlobals* CfgGlobals::top_ = NULL; |
||||
|
|
||||
|
|
||||
|
CfgGlobals::CfgGlobals(FunctionLiteral* fun) |
||||
|
: global_fun_(fun), |
||||
|
global_exit_(new ExitNode()), |
||||
|
#ifdef DEBUG |
||||
|
node_counter_(0), |
||||
|
#endif |
||||
|
previous_(top_) { |
||||
|
top_ = this; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#define BAILOUT(reason) \ |
||||
|
do { return NULL; } while (false) |
||||
|
|
||||
|
Cfg* Cfg::Build() { |
||||
|
FunctionLiteral* fun = CfgGlobals::current()->fun(); |
||||
|
if (fun->scope()->num_heap_slots() > 0) { |
||||
|
BAILOUT("function has context slots"); |
||||
|
} |
||||
|
if (fun->scope()->arguments() != NULL) { |
||||
|
BAILOUT("function uses .arguments"); |
||||
|
} |
||||
|
|
||||
|
ZoneList<Statement*>* body = fun->body(); |
||||
|
if (body->is_empty()) { |
||||
|
BAILOUT("empty function body"); |
||||
|
} |
||||
|
|
||||
|
StatementBuilder builder; |
||||
|
builder.VisitStatements(body); |
||||
|
Cfg* cfg = builder.cfg(); |
||||
|
if (cfg == NULL) { |
||||
|
BAILOUT("unsupported statement type"); |
||||
|
} |
||||
|
if (cfg->has_exit()) { |
||||
|
BAILOUT("control path without explicit return"); |
||||
|
} |
||||
|
cfg->PrependEntryNode(); |
||||
|
return cfg; |
||||
|
} |
||||
|
|
||||
|
#undef BAILOUT |
||||
|
|
||||
|
|
||||
|
void Cfg::PrependEntryNode() { |
||||
|
ASSERT(!is_empty()); |
||||
|
entry_ = new EntryNode(InstructionBlock::cast(entry())); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Cfg::Append(Instruction* instr) { |
||||
|
ASSERT(has_exit()); |
||||
|
ASSERT(!is_empty()); |
||||
|
InstructionBlock::cast(exit_)->Append(instr); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Cfg::AppendReturnInstruction(Value* value) { |
||||
|
Append(new ReturnInstr(value)); |
||||
|
ExitNode* global_exit = CfgGlobals::current()->exit(); |
||||
|
InstructionBlock::cast(exit_)->set_successor(global_exit); |
||||
|
exit_ = NULL; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void InstructionBlock::Unmark() { |
||||
|
if (is_marked_) { |
||||
|
is_marked_ = false; |
||||
|
successor_->Unmark(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void EntryNode::Unmark() { |
||||
|
if (is_marked_) { |
||||
|
is_marked_ = false; |
||||
|
successor_->Unmark(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExitNode::Unmark() { |
||||
|
is_marked_ = false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Handle<Code> Cfg::Compile(Handle<Script> script) { |
||||
|
const int kInitialBufferSize = 4 * KB; |
||||
|
MacroAssembler* masm = new MacroAssembler(NULL, kInitialBufferSize); |
||||
|
entry()->Compile(masm); |
||||
|
entry()->Unmark(); |
||||
|
CodeDesc desc; |
||||
|
masm->GetCode(&desc); |
||||
|
FunctionLiteral* fun = CfgGlobals::current()->fun(); |
||||
|
ZoneScopeInfo info(fun->scope()); |
||||
|
InLoopFlag in_loop = fun->loop_nesting() ? IN_LOOP : NOT_IN_LOOP; |
||||
|
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop); |
||||
|
Handle<Code> code = Factory::NewCode(desc, &info, flags, masm->CodeObject()); |
||||
|
|
||||
|
// Add unresolved entries in the code to the fixup list.
|
||||
|
Bootstrapper::AddFixup(*code, masm); |
||||
|
|
||||
|
#ifdef ENABLE_DISASSEMBLER |
||||
|
if (FLAG_print_code) { |
||||
|
// Print the source code if available.
|
||||
|
if (!script->IsUndefined() && !script->source()->IsUndefined()) { |
||||
|
PrintF("--- Raw source ---\n"); |
||||
|
StringInputBuffer stream(String::cast(script->source())); |
||||
|
stream.Seek(fun->start_position()); |
||||
|
// fun->end_position() points to the last character in the
|
||||
|
// stream. We need to compensate by adding one to calculate the
|
||||
|
// length.
|
||||
|
int source_len = fun->end_position() - fun->start_position() + 1; |
||||
|
for (int i = 0; i < source_len; i++) { |
||||
|
if (stream.has_more()) PrintF("%c", stream.GetNext()); |
||||
|
} |
||||
|
PrintF("\n\n"); |
||||
|
} |
||||
|
PrintF("--- Code ---\n"); |
||||
|
code->Disassemble(*fun->name()->ToCString()); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
return code; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// The expression builder should not be used for declarations or statements.
|
||||
|
void ExpressionBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } |
||||
|
|
||||
|
#define DEFINE_VISIT(type) \ |
||||
|
void ExpressionBuilder::Visit##type(type* stmt) { UNREACHABLE(); } |
||||
|
STATEMENT_NODE_LIST(DEFINE_VISIT) |
||||
|
#undef DEFINE_VISIT |
||||
|
|
||||
|
|
||||
|
// Macros (temporarily) handling unsupported expression types.
|
||||
|
#define BAILOUT(reason) \ |
||||
|
do { \ |
||||
|
value_ = NULL; \ |
||||
|
return; \ |
||||
|
} while (false) |
||||
|
|
||||
|
#define CHECK_BAILOUT() \ |
||||
|
if (value_ == NULL) { return; } else {} |
||||
|
|
||||
|
void ExpressionBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
||||
|
BAILOUT("FunctionLiteral"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitFunctionBoilerplateLiteral( |
||||
|
FunctionBoilerplateLiteral* expr) { |
||||
|
BAILOUT("FunctionBoilerplateLiteral"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitConditional(Conditional* expr) { |
||||
|
BAILOUT("Conditional"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitSlot(Slot* expr) { |
||||
|
BAILOUT("Slot"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitVariableProxy(VariableProxy* expr) { |
||||
|
Expression* rewrite = expr->var()->rewrite(); |
||||
|
if (rewrite == NULL || rewrite->AsSlot() == NULL) { |
||||
|
BAILOUT("unsupported variable (not a slot)"); |
||||
|
} |
||||
|
Slot* slot = rewrite->AsSlot(); |
||||
|
if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) { |
||||
|
BAILOUT("unsupported slot type (not a parameter or local)"); |
||||
|
} |
||||
|
value_ = new SlotLocation(slot->type(), slot->index()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitLiteral(Literal* expr) { |
||||
|
value_ = new Constant(expr->handle()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { |
||||
|
BAILOUT("RegExpLiteral"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
||||
|
BAILOUT("ObjectLiteral"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
||||
|
BAILOUT("ArrayLiteral"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
||||
|
BAILOUT("CatchExtensionObject"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitAssignment(Assignment* expr) { |
||||
|
BAILOUT("Assignment"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitThrow(Throw* expr) { |
||||
|
BAILOUT("Throw"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitProperty(Property* expr) { |
||||
|
BAILOUT("Property"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCall(Call* expr) { |
||||
|
BAILOUT("Call"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCallEval(CallEval* expr) { |
||||
|
BAILOUT("CallEval"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCallNew(CallNew* expr) { |
||||
|
BAILOUT("CallNew"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCallRuntime(CallRuntime* expr) { |
||||
|
BAILOUT("CallRuntime"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitUnaryOperation(UnaryOperation* expr) { |
||||
|
BAILOUT("UnaryOperation"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCountOperation(CountOperation* expr) { |
||||
|
BAILOUT("CountOperation"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitBinaryOperation(BinaryOperation* expr) { |
||||
|
BAILOUT("BinaryOperation"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitCompareOperation(CompareOperation* expr) { |
||||
|
BAILOUT("CompareOperation"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExpressionBuilder::VisitThisFunction(ThisFunction* expr) { |
||||
|
BAILOUT("ThisFunction"); |
||||
|
} |
||||
|
|
||||
|
#undef BAILOUT |
||||
|
#undef CHECK_BAILOUT |
||||
|
|
||||
|
|
||||
|
// Macros (temporarily) handling unsupported statement types.
|
||||
|
#define BAILOUT(reason) \ |
||||
|
do { \ |
||||
|
cfg_ = NULL; \ |
||||
|
return; \ |
||||
|
} while (false) |
||||
|
|
||||
|
#define CHECK_BAILOUT() \ |
||||
|
if (cfg_ == NULL) { return; } else {} |
||||
|
|
||||
|
void StatementBuilder::VisitStatements(ZoneList<Statement*>* stmts) { |
||||
|
for (int i = 0, len = stmts->length(); i < len; i++) { |
||||
|
Visit(stmts->at(i)); |
||||
|
CHECK_BAILOUT(); |
||||
|
if (!cfg_->has_exit()) return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// The statement builder should not be used for declarations or expressions.
|
||||
|
void StatementBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } |
||||
|
|
||||
|
#define DEFINE_VISIT(type) \ |
||||
|
void StatementBuilder::Visit##type(type* expr) { UNREACHABLE(); } |
||||
|
EXPRESSION_NODE_LIST(DEFINE_VISIT) |
||||
|
#undef DEFINE_VISIT |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitBlock(Block* stmt) { |
||||
|
VisitStatements(stmt->statements()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { |
||||
|
BAILOUT("ExpressionStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitEmptyStatement(EmptyStatement* stmt) { |
||||
|
// Nothing to do.
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitIfStatement(IfStatement* stmt) { |
||||
|
BAILOUT("IfStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitContinueStatement(ContinueStatement* stmt) { |
||||
|
BAILOUT("ContinueStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitBreakStatement(BreakStatement* stmt) { |
||||
|
BAILOUT("BreakStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
||||
|
ExpressionBuilder builder; |
||||
|
builder.Visit(stmt->expression()); |
||||
|
Value* value = builder.value(); |
||||
|
if (value == NULL) BAILOUT("unsupported expression type"); |
||||
|
cfg_->AppendReturnInstruction(value); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { |
||||
|
BAILOUT("WithEnterStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitWithExitStatement(WithExitStatement* stmt) { |
||||
|
BAILOUT("WithExitStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
||||
|
BAILOUT("SwitchStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitLoopStatement(LoopStatement* stmt) { |
||||
|
BAILOUT("LoopStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitForInStatement(ForInStatement* stmt) { |
||||
|
BAILOUT("ForInStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitTryCatch(TryCatch* stmt) { |
||||
|
BAILOUT("TryCatch"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitTryFinally(TryFinally* stmt) { |
||||
|
BAILOUT("TryFinally"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void StatementBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { |
||||
|
BAILOUT("DebuggerStatement"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
// CFG printing support (via depth-first, preorder block traversal).
|
||||
|
|
||||
|
void Cfg::Print() { |
||||
|
entry_->Print(); |
||||
|
entry_->Unmark(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Constant::Print() { |
||||
|
PrintF("Constant("); |
||||
|
handle_->Print(); |
||||
|
PrintF(")"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void SlotLocation::Print() { |
||||
|
PrintF("Slot("); |
||||
|
switch (type_) { |
||||
|
case Slot::PARAMETER: |
||||
|
PrintF("PARAMETER, %d)", index_); |
||||
|
break; |
||||
|
case Slot::LOCAL: |
||||
|
PrintF("LOCAL, %d)", index_); |
||||
|
break; |
||||
|
default: |
||||
|
UNREACHABLE(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ReturnInstr::Print() { |
||||
|
PrintF("Return "); |
||||
|
value_->Print(); |
||||
|
PrintF("\n"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void InstructionBlock::Print() { |
||||
|
if (!is_marked_) { |
||||
|
is_marked_ = true; |
||||
|
PrintF("L%d:\n", number()); |
||||
|
for (int i = 0, len = instructions_.length(); i < len; i++) { |
||||
|
instructions_[i]->Print(); |
||||
|
} |
||||
|
PrintF("Goto L%d\n\n", successor_->number()); |
||||
|
successor_->Print(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void EntryNode::Print() { |
||||
|
if (!is_marked_) { |
||||
|
is_marked_ = true; |
||||
|
successor_->Print(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExitNode::Print() { |
||||
|
if (!is_marked_) { |
||||
|
is_marked_ = true; |
||||
|
PrintF("L%d:\nExit\n\n", number()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif // DEBUG
|
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,385 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions are
|
||||
|
// met:
|
||||
|
//
|
||||
|
// * Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
// * 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.
|
||||
|
// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
|
// OWNER 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.
|
||||
|
|
||||
|
#ifndef V8_CFG_H_ |
||||
|
#define V8_CFG_H_ |
||||
|
|
||||
|
#include "ast.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
class ExitNode; |
||||
|
|
||||
|
// A convenient class to keep 'global' values when building a CFG. Since
|
||||
|
// CFG construction can be invoked recursively, CFG globals are stacked.
|
||||
|
class CfgGlobals BASE_EMBEDDED { |
||||
|
public: |
||||
|
explicit CfgGlobals(FunctionLiteral* fun); |
||||
|
|
||||
|
~CfgGlobals() { top_ = previous_; } |
||||
|
|
||||
|
static CfgGlobals* current() { |
||||
|
ASSERT(top_ != NULL); |
||||
|
return top_; |
||||
|
} |
||||
|
|
||||
|
FunctionLiteral* fun() { return global_fun_; } |
||||
|
|
||||
|
ExitNode* exit() { return global_exit_; } |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
int next_number() { return node_counter_++; } |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
static CfgGlobals* top_; |
||||
|
|
||||
|
// Function literal currently compiling.
|
||||
|
FunctionLiteral* global_fun_; |
||||
|
|
||||
|
// Shared global exit node for all returns from the same function.
|
||||
|
ExitNode* global_exit_; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
// Used to number nodes when printing.
|
||||
|
int node_counter_; |
||||
|
#endif |
||||
|
|
||||
|
CfgGlobals* previous_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// Values appear in instructions. They represent trivial source
|
||||
|
// expressions: ones with no side effects and that do not require code to be
|
||||
|
// generated.
|
||||
|
class Value : public ZoneObject { |
||||
|
public: |
||||
|
virtual ~Value() {} |
||||
|
|
||||
|
virtual void ToRegister(MacroAssembler* masm, Register reg) = 0; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
virtual void Print() = 0; |
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// A compile-time constant that appeared as a literal in the source AST.
|
||||
|
class Constant : public Value { |
||||
|
public: |
||||
|
explicit Constant(Handle<Object> handle) : handle_(handle) {} |
||||
|
|
||||
|
virtual ~Constant() {} |
||||
|
|
||||
|
void ToRegister(MacroAssembler* masm, Register reg); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
void Print(); |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
Handle<Object> handle_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// Locations are values that can be stored into ('lvalues').
|
||||
|
class Location : public Value { |
||||
|
public: |
||||
|
virtual ~Location() {} |
||||
|
|
||||
|
virtual void ToRegister(MacroAssembler* masm, Register reg) = 0; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
virtual void Print() = 0; |
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// SlotLocations represent parameters and stack-allocated (i.e.,
|
||||
|
// non-context) local variables.
|
||||
|
class SlotLocation : public Location { |
||||
|
public: |
||||
|
SlotLocation(Slot::Type type, int index) : type_(type), index_(index) {} |
||||
|
|
||||
|
void ToRegister(MacroAssembler* masm, Register reg); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
void Print(); |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
Slot::Type type_; |
||||
|
int index_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// Instructions are computations. The represent non-trivial source
|
||||
|
// expressions: typically ones that have side effects and require code to
|
||||
|
// be generated.
|
||||
|
class Instruction : public ZoneObject { |
||||
|
public: |
||||
|
virtual ~Instruction() {} |
||||
|
|
||||
|
virtual void Compile(MacroAssembler* masm) = 0; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
virtual void Print() = 0; |
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// Return a value.
|
||||
|
class ReturnInstr : public Instruction { |
||||
|
public: |
||||
|
explicit ReturnInstr(Value* value) : value_(value) {} |
||||
|
|
||||
|
virtual ~ReturnInstr() {} |
||||
|
|
||||
|
void Compile(MacroAssembler* masm); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
void Print(); |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
Value* value_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// Nodes make up control-flow graphs. They contain single-entry,
|
||||
|
// single-exit blocks of instructions and administrative nodes making up the
|
||||
|
// graph structure.
|
||||
|
class CfgNode : public ZoneObject { |
||||
|
public: |
||||
|
CfgNode() : is_marked_(false) { |
||||
|
#ifdef DEBUG |
||||
|
number_ = -1; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
virtual ~CfgNode() {} |
||||
|
|
||||
|
bool is_marked() { return is_marked_; } |
||||
|
|
||||
|
virtual bool is_block() { return false; } |
||||
|
|
||||
|
virtual void Unmark() = 0; |
||||
|
|
||||
|
virtual void Compile(MacroAssembler* masm) = 0; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
int number() { |
||||
|
if (number_ == -1) number_ = CfgGlobals::current()->next_number(); |
||||
|
return number_; |
||||
|
} |
||||
|
|
||||
|
virtual void Print() = 0; |
||||
|
#endif |
||||
|
|
||||
|
protected: |
||||
|
bool is_marked_; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
int number_; |
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// A block is a single-entry, single-exit block of instructions.
|
||||
|
class InstructionBlock : public CfgNode { |
||||
|
public: |
||||
|
InstructionBlock() : successor_(NULL), instructions_(4) {} |
||||
|
|
||||
|
virtual ~InstructionBlock() {} |
||||
|
|
||||
|
static InstructionBlock* cast(CfgNode* node) { |
||||
|
ASSERT(node->is_block()); |
||||
|
return reinterpret_cast<InstructionBlock*>(node); |
||||
|
} |
||||
|
|
||||
|
void set_successor(CfgNode* succ) { |
||||
|
ASSERT(successor_ == NULL); |
||||
|
successor_ = succ; |
||||
|
} |
||||
|
|
||||
|
bool is_block() { return true; } |
||||
|
|
||||
|
void Unmark(); |
||||
|
|
||||
|
void Compile(MacroAssembler* masm); |
||||
|
|
||||
|
void Append(Instruction* instr) { instructions_.Add(instr); } |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
void Print(); |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
CfgNode* successor_; |
||||
|
ZoneList<Instruction*> instructions_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// The CFG for a function has a distinguished entry node. It has no
|
||||
|
// predecessors and a single successor. The successor is the block
|
||||
|
// containing the function's first instruction.
|
||||
|
class EntryNode : public CfgNode { |
||||
|
public: |
||||
|
explicit EntryNode(InstructionBlock* succ) : successor_(succ) {} |
||||
|
|
||||
|
virtual ~EntryNode() {} |
||||
|
|
||||
|
void Unmark(); |
||||
|
|
||||
|
void Compile(MacroAssembler* masm); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
void Print(); |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
InstructionBlock* successor_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// The CFG for a function has a distinguished exit node. It has no
|
||||
|
// successor and arbitrarily many predecessors. The predecessors are all
|
||||
|
// the blocks returning from the function.
|
||||
|
class ExitNode : public CfgNode { |
||||
|
public: |
||||
|
ExitNode() {} |
||||
|
|
||||
|
virtual ~ExitNode() {} |
||||
|
|
||||
|
void Unmark(); |
||||
|
|
||||
|
void Compile(MacroAssembler* masm); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
void Print(); |
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// A CFG consists of a linked structure of nodes. It has a single entry
|
||||
|
// node and optionally an exit node. There is a distinguished global exit
|
||||
|
// node that is used as the successor of all blocks that return from the
|
||||
|
// function.
|
||||
|
//
|
||||
|
// Fragments of control-flow graphs, produced when traversing the statements
|
||||
|
// and expressions in the source AST, are represented by the same class.
|
||||
|
// They have instruction blocks as both their entry and exit (if there is
|
||||
|
// one). Instructions can always be prepended or appended to fragments, and
|
||||
|
// fragments can always be concatenated.
|
||||
|
//
|
||||
|
// A singleton CFG fragment (i.e., with only one node) has the same node as
|
||||
|
// both entry and exit (if the exit is available).
|
||||
|
class Cfg : public ZoneObject { |
||||
|
public: |
||||
|
// Create a singleton CFG fragment.
|
||||
|
explicit Cfg(InstructionBlock* block) : entry_(block), exit_(block) {} |
||||
|
|
||||
|
// Build the CFG for a function.
|
||||
|
static Cfg* Build(); |
||||
|
|
||||
|
// The entry and exit nodes.
|
||||
|
CfgNode* entry() { return entry_; } |
||||
|
CfgNode* exit() { return exit_; } |
||||
|
|
||||
|
// True if the CFG has no nodes.
|
||||
|
bool is_empty() { return entry_ == NULL; } |
||||
|
|
||||
|
// True if the CFG has an available exit node (i.e., it can be appended or
|
||||
|
// concatenated to).
|
||||
|
bool has_exit() { return exit_ != NULL; } |
||||
|
|
||||
|
// Add an entry node to a CFG fragment. It is no longer a fragment
|
||||
|
// (instructions cannot be prepended).
|
||||
|
void PrependEntryNode(); |
||||
|
|
||||
|
// Append an instruction to the end of a CFG fragment. Assumes it has an
|
||||
|
// available exit.
|
||||
|
void Append(Instruction* instr); |
||||
|
|
||||
|
// Appends a return instruction to the end of a CFG fragment. It no
|
||||
|
// longer has an available exit node.
|
||||
|
void AppendReturnInstruction(Value* value); |
||||
|
|
||||
|
Handle<Code> Compile(Handle<Script> script); |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
// Support for printing.
|
||||
|
void Print(); |
||||
|
#endif |
||||
|
|
||||
|
private: |
||||
|
// Entry and exit nodes.
|
||||
|
CfgNode* entry_; |
||||
|
CfgNode* exit_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// An Expression Builder traverses a trivial expression and returns a value.
|
||||
|
class ExpressionBuilder : public AstVisitor { |
||||
|
public: |
||||
|
ExpressionBuilder() : value_(new Constant(Handle<Object>::null())) {} |
||||
|
|
||||
|
Value* value() { return value_; } |
||||
|
|
||||
|
// AST node visitors.
|
||||
|
#define DECLARE_VISIT(type) void Visit##type(type* node); |
||||
|
AST_NODE_LIST(DECLARE_VISIT) |
||||
|
#undef DECLARE_VISIT |
||||
|
|
||||
|
private: |
||||
|
Value* value_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// A StatementBuilder traverses a statement and returns a CFG.
|
||||
|
class StatementBuilder : public AstVisitor { |
||||
|
public: |
||||
|
StatementBuilder() : cfg_(new Cfg(new InstructionBlock())) {} |
||||
|
|
||||
|
Cfg* cfg() { return cfg_; } |
||||
|
|
||||
|
void VisitStatements(ZoneList<Statement*>* stmts); |
||||
|
|
||||
|
// AST node visitors.
|
||||
|
#define DECLARE_VISIT(type) void Visit##type(type* node); |
||||
|
AST_NODE_LIST(DECLARE_VISIT) |
||||
|
#undef DECLARE_VISIT |
||||
|
|
||||
|
private: |
||||
|
Cfg* cfg_; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
#endif // V8_CFG_H_
|
@ -0,0 +1,137 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions are
|
||||
|
// met:
|
||||
|
//
|
||||
|
// * Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
// * 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.
|
||||
|
// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
|
// OWNER 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 "v8.h" |
||||
|
|
||||
|
#include "cfg.h" |
||||
|
#include "codegen-inl.h" |
||||
|
#include "macro-assembler-ia32.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
#define __ ACCESS_MASM(masm) |
||||
|
|
||||
|
void InstructionBlock::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
{ |
||||
|
Comment cmt(masm, "[ InstructionBlock"); |
||||
|
for (int i = 0, len = instructions_.length(); i < len; i++) { |
||||
|
instructions_[i]->Compile(masm); |
||||
|
} |
||||
|
} |
||||
|
successor_->Compile(masm); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void EntryNode::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
Label deferred_enter, deferred_exit; |
||||
|
{ |
||||
|
Comment cmnt(masm, "[ EntryNode"); |
||||
|
__ push(ebp); |
||||
|
__ mov(ebp, esp); |
||||
|
__ push(esi); |
||||
|
__ push(edi); |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots(); |
||||
|
if (count > 0) { |
||||
|
__ Set(eax, Immediate(Factory::undefined_value())); |
||||
|
for (int i = 0; i < count; i++) { |
||||
|
__ push(eax); |
||||
|
} |
||||
|
} |
||||
|
if (FLAG_trace) { |
||||
|
__ CallRuntime(Runtime::kTraceEnter, 0); |
||||
|
} |
||||
|
if (FLAG_check_stack) { |
||||
|
ExternalReference stack_limit = |
||||
|
ExternalReference::address_of_stack_guard_limit(); |
||||
|
__ cmp(esp, Operand::StaticVariable(stack_limit)); |
||||
|
__ j(below, &deferred_enter); |
||||
|
__ bind(&deferred_exit); |
||||
|
} |
||||
|
} |
||||
|
successor_->Compile(masm); |
||||
|
if (FLAG_check_stack) { |
||||
|
__ bind(&deferred_enter); |
||||
|
StackCheckStub stub; |
||||
|
__ CallStub(&stub); |
||||
|
__ jmp(&deferred_exit); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExitNode::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
Comment cmnt(masm, "[ ExitNode"); |
||||
|
if (FLAG_trace) { |
||||
|
__ push(eax); |
||||
|
__ CallRuntime(Runtime::kTraceExit, 1); |
||||
|
} |
||||
|
__ RecordJSReturn(); |
||||
|
__ mov(esp, ebp); |
||||
|
__ pop(ebp); |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
||||
|
__ ret((count + 1) * kPointerSize); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ReturnInstr::Compile(MacroAssembler* masm) { |
||||
|
Comment cmnt(masm, "[ ReturnInstr"); |
||||
|
value_->ToRegister(masm, eax); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Constant::ToRegister(MacroAssembler* masm, Register reg) { |
||||
|
__ mov(reg, Immediate(handle_)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) { |
||||
|
switch (type_) { |
||||
|
case Slot::PARAMETER: { |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
||||
|
__ mov(reg, Operand(ebp, (1 + count - index_) * kPointerSize)); |
||||
|
break; |
||||
|
} |
||||
|
case Slot::LOCAL: { |
||||
|
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; |
||||
|
__ mov(reg, Operand(ebp, kOffset - index_ * kPointerSize)); |
||||
|
break; |
||||
|
} |
||||
|
default: |
||||
|
UNREACHABLE(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#undef __ |
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,146 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions are
|
||||
|
// met:
|
||||
|
//
|
||||
|
// * Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
// * 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.
|
||||
|
// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
|
// OWNER 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 "v8.h" |
||||
|
|
||||
|
#include "cfg.h" |
||||
|
#include "codegen-inl.h" |
||||
|
#include "debug.h" |
||||
|
#include "macro-assembler-x64.h" |
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
#define __ ACCESS_MASM(masm) |
||||
|
|
||||
|
void InstructionBlock::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
{ |
||||
|
Comment cmt(masm, "[ InstructionBlock"); |
||||
|
for (int i = 0, len = instructions_.length(); i < len; i++) { |
||||
|
instructions_[i]->Compile(masm); |
||||
|
} |
||||
|
} |
||||
|
successor_->Compile(masm); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void EntryNode::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
Label deferred_enter, deferred_exit; |
||||
|
{ |
||||
|
Comment cmnt(masm, "[ EntryNode"); |
||||
|
__ push(rbp); |
||||
|
__ movq(rbp, rsp); |
||||
|
__ push(rsi); |
||||
|
__ push(rdi); |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_stack_slots(); |
||||
|
if (count > 0) { |
||||
|
__ movq(kScratchRegister, Factory::undefined_value(), |
||||
|
RelocInfo::EMBEDDED_OBJECT); |
||||
|
for (int i = 0; i < count; i++) { |
||||
|
__ push(kScratchRegister); |
||||
|
} |
||||
|
} |
||||
|
if (FLAG_trace) { |
||||
|
__ CallRuntime(Runtime::kTraceEnter, 0); |
||||
|
} |
||||
|
if (FLAG_check_stack) { |
||||
|
ExternalReference stack_limit = |
||||
|
ExternalReference::address_of_stack_guard_limit(); |
||||
|
__ movq(kScratchRegister, stack_limit); |
||||
|
__ cmpq(rsp, Operand(kScratchRegister, 0)); |
||||
|
__ j(below, &deferred_enter); |
||||
|
__ bind(&deferred_exit); |
||||
|
} |
||||
|
} |
||||
|
successor_->Compile(masm); |
||||
|
if (FLAG_check_stack) { |
||||
|
__ bind(&deferred_enter); |
||||
|
StackCheckStub stub; |
||||
|
__ CallStub(&stub); |
||||
|
__ jmp(&deferred_exit); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ExitNode::Compile(MacroAssembler* masm) { |
||||
|
ASSERT(!is_marked()); |
||||
|
is_marked_ = true; |
||||
|
|
||||
|
Comment cmnt(masm, "[ ExitNode"); |
||||
|
if (FLAG_trace) { |
||||
|
__ push(rax); |
||||
|
__ CallRuntime(Runtime::kTraceExit, 1); |
||||
|
} |
||||
|
__ RecordJSReturn(); |
||||
|
__ movq(rsp, rbp); |
||||
|
__ pop(rbp); |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
||||
|
__ ret((count + 1) * kPointerSize); |
||||
|
// Add padding that will be overwritten by a debugger breakpoint.
|
||||
|
// "movq rsp, rbp; pop rbp" has length 5. "ret k" has length 2.
|
||||
|
const int kPadding = Debug::kX64JSReturnSequenceLength - 5 - 2; |
||||
|
for (int i = 0; i < kPadding; ++i) { |
||||
|
__ int3(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ReturnInstr::Compile(MacroAssembler* masm) { |
||||
|
Comment cmnt(masm, "[ ReturnInstr"); |
||||
|
value_->ToRegister(masm, rax); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Constant::ToRegister(MacroAssembler* masm, Register reg) { |
||||
|
__ Move(reg, handle_); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void SlotLocation::ToRegister(MacroAssembler* masm, Register reg) { |
||||
|
switch (type_) { |
||||
|
case Slot::PARAMETER: { |
||||
|
int count = CfgGlobals::current()->fun()->scope()->num_parameters(); |
||||
|
__ movq(reg, Operand(rbp, (1 + count - index_) * kPointerSize)); |
||||
|
break; |
||||
|
} |
||||
|
case Slot::LOCAL: { |
||||
|
const int kOffset = JavaScriptFrameConstants::kLocal0Offset; |
||||
|
__ movq(reg, Operand(rbp, kOffset - index_ * kPointerSize)); |
||||
|
break; |
||||
|
} |
||||
|
default: |
||||
|
UNREACHABLE(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#undef __ |
||||
|
|
||||
|
} } // namespace v8::internal
|
@ -0,0 +1,376 @@ |
|||||
|
// Copyright 2006-2009 the V8 project authors. All rights reserved.
|
||||
|
//
|
||||
|
// Tests of profiler-related functions from log.h
|
||||
|
|
||||
|
#ifdef ENABLE_LOGGING_AND_PROFILING |
||||
|
|
||||
|
#include <stdlib.h> |
||||
|
|
||||
|
#include "v8.h" |
||||
|
|
||||
|
#include "codegen.h" |
||||
|
#include "log.h" |
||||
|
#include "top.h" |
||||
|
#include "cctest.h" |
||||
|
#include "disassembler.h" |
||||
|
#include "register-allocator-inl.h" |
||||
|
|
||||
|
using v8::Function; |
||||
|
using v8::Local; |
||||
|
using v8::Object; |
||||
|
using v8::Script; |
||||
|
using v8::String; |
||||
|
using v8::Value; |
||||
|
|
||||
|
using v8::internal::byte; |
||||
|
using v8::internal::Address; |
||||
|
using v8::internal::Handle; |
||||
|
using v8::internal::JSFunction; |
||||
|
using v8::internal::StackTracer; |
||||
|
using v8::internal::TickSample; |
||||
|
using v8::internal::Top; |
||||
|
|
||||
|
namespace i = v8::internal; |
||||
|
|
||||
|
|
||||
|
static v8::Persistent<v8::Context> env; |
||||
|
|
||||
|
|
||||
|
static struct { |
||||
|
TickSample* sample; |
||||
|
} trace_env = { NULL }; |
||||
|
|
||||
|
|
||||
|
static void InitTraceEnv(TickSample* sample) { |
||||
|
trace_env.sample = sample; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void DoTrace(Address fp) { |
||||
|
trace_env.sample->fp = reinterpret_cast<uintptr_t>(fp); |
||||
|
// sp is only used to define stack high bound
|
||||
|
trace_env.sample->sp = |
||||
|
reinterpret_cast<uintptr_t>(trace_env.sample) - 10240; |
||||
|
StackTracer::Trace(trace_env.sample); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// Hide c_entry_fp to emulate situation when sampling is done while
|
||||
|
// pure JS code is being executed
|
||||
|
static void DoTraceHideCEntryFPAddress(Address fp) { |
||||
|
v8::internal::Address saved_c_frame_fp = *(Top::c_entry_fp_address()); |
||||
|
CHECK(saved_c_frame_fp); |
||||
|
*(Top::c_entry_fp_address()) = 0; |
||||
|
DoTrace(fp); |
||||
|
*(Top::c_entry_fp_address()) = saved_c_frame_fp; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void CheckRetAddrIsInFunction(const char* func_name, |
||||
|
Address ret_addr, |
||||
|
Address func_start_addr, |
||||
|
unsigned int func_len) { |
||||
|
printf("CheckRetAddrIsInFunction \"%s\": %p %p %p\n", |
||||
|
func_name, func_start_addr, ret_addr, func_start_addr + func_len); |
||||
|
CHECK_GE(ret_addr, func_start_addr); |
||||
|
CHECK_GE(func_start_addr + func_len, ret_addr); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void CheckRetAddrIsInJSFunction(const char* func_name, |
||||
|
Address ret_addr, |
||||
|
Handle<JSFunction> func) { |
||||
|
v8::internal::Code* func_code = func->code(); |
||||
|
CheckRetAddrIsInFunction( |
||||
|
func_name, ret_addr, |
||||
|
func_code->instruction_start(), |
||||
|
func_code->ExecutableSize()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// --- T r a c e E x t e n s i o n ---
|
||||
|
|
||||
|
class TraceExtension : public v8::Extension { |
||||
|
public: |
||||
|
TraceExtension() : v8::Extension("v8/trace", kSource) { } |
||||
|
virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( |
||||
|
v8::Handle<String> name); |
||||
|
static v8::Handle<v8::Value> Trace(const v8::Arguments& args); |
||||
|
static v8::Handle<v8::Value> JSTrace(const v8::Arguments& args); |
||||
|
static v8::Handle<v8::Value> JSEntrySP(const v8::Arguments& args); |
||||
|
static v8::Handle<v8::Value> JSEntrySPLevel2(const v8::Arguments& args); |
||||
|
private: |
||||
|
static Address GetFP(const v8::Arguments& args); |
||||
|
static const char* kSource; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
const char* TraceExtension::kSource = |
||||
|
"native function trace();" |
||||
|
"native function js_trace();" |
||||
|
"native function js_entry_sp();" |
||||
|
"native function js_entry_sp_level2();"; |
||||
|
|
||||
|
v8::Handle<v8::FunctionTemplate> TraceExtension::GetNativeFunction( |
||||
|
v8::Handle<String> name) { |
||||
|
if (name->Equals(String::New("trace"))) { |
||||
|
return v8::FunctionTemplate::New(TraceExtension::Trace); |
||||
|
} else if (name->Equals(String::New("js_trace"))) { |
||||
|
return v8::FunctionTemplate::New(TraceExtension::JSTrace); |
||||
|
} else if (name->Equals(String::New("js_entry_sp"))) { |
||||
|
return v8::FunctionTemplate::New(TraceExtension::JSEntrySP); |
||||
|
} else if (name->Equals(String::New("js_entry_sp_level2"))) { |
||||
|
return v8::FunctionTemplate::New(TraceExtension::JSEntrySPLevel2); |
||||
|
} else { |
||||
|
CHECK(false); |
||||
|
return v8::Handle<v8::FunctionTemplate>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
Address TraceExtension::GetFP(const v8::Arguments& args) { |
||||
|
CHECK_EQ(1, args.Length()); |
||||
|
// CodeGenerator::GenerateGetFramePointer pushes EBP / RBP value
|
||||
|
// on stack. In 64-bit mode we can't use Smi operations code because
|
||||
|
// they check that value is within Smi bounds.
|
||||
|
Address fp = *reinterpret_cast<Address*>(*args[0]); |
||||
|
printf("Trace: %p\n", fp); |
||||
|
return fp; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
v8::Handle<v8::Value> TraceExtension::Trace(const v8::Arguments& args) { |
||||
|
DoTrace(GetFP(args)); |
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
v8::Handle<v8::Value> TraceExtension::JSTrace(const v8::Arguments& args) { |
||||
|
DoTraceHideCEntryFPAddress(GetFP(args)); |
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static Address GetJsEntrySp() { |
||||
|
CHECK_NE(NULL, Top::GetCurrentThread()); |
||||
|
return Top::js_entry_sp(Top::GetCurrentThread()); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
v8::Handle<v8::Value> TraceExtension::JSEntrySP(const v8::Arguments& args) { |
||||
|
CHECK_NE(0, GetJsEntrySp()); |
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void CompileRun(const char* source) { |
||||
|
Script::Compile(String::New(source))->Run(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
v8::Handle<v8::Value> TraceExtension::JSEntrySPLevel2( |
||||
|
const v8::Arguments& args) { |
||||
|
v8::HandleScope scope; |
||||
|
const Address js_entry_sp = GetJsEntrySp(); |
||||
|
CHECK_NE(0, js_entry_sp); |
||||
|
CompileRun("js_entry_sp();"); |
||||
|
CHECK_EQ(js_entry_sp, GetJsEntrySp()); |
||||
|
return v8::Undefined(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static TraceExtension kTraceExtension; |
||||
|
v8::DeclareExtension kTraceExtensionDeclaration(&kTraceExtension); |
||||
|
|
||||
|
|
||||
|
static void InitializeVM() { |
||||
|
if (env.IsEmpty()) { |
||||
|
v8::HandleScope scope; |
||||
|
const char* extensions[] = { "v8/trace" }; |
||||
|
v8::ExtensionConfiguration config(1, extensions); |
||||
|
env = v8::Context::New(&config); |
||||
|
} |
||||
|
v8::HandleScope scope; |
||||
|
env->Enter(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static Handle<JSFunction> CompileFunction(const char* source) { |
||||
|
return v8::Utils::OpenHandle(*Script::Compile(String::New(source))); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static Local<Value> GetGlobalProperty(const char* name) { |
||||
|
return env->Global()->Get(String::New(name)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static Handle<JSFunction> GetGlobalJSFunction(const char* name) { |
||||
|
Handle<JSFunction> js_func(JSFunction::cast( |
||||
|
*(v8::Utils::OpenHandle( |
||||
|
*GetGlobalProperty(name))))); |
||||
|
return js_func; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void CheckRetAddrIsInJSFunction(const char* func_name, |
||||
|
Address ret_addr) { |
||||
|
CheckRetAddrIsInJSFunction(func_name, ret_addr, |
||||
|
GetGlobalJSFunction(func_name)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void SetGlobalProperty(const char* name, Local<Value> value) { |
||||
|
env->Global()->Set(String::New(name), value); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static Handle<v8::internal::String> NewString(const char* s) { |
||||
|
return i::Factory::NewStringFromAscii(i::CStrVector(s)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
namespace v8 { |
||||
|
namespace internal { |
||||
|
|
||||
|
class CodeGeneratorPatcher { |
||||
|
public: |
||||
|
CodeGeneratorPatcher() { |
||||
|
CodeGenerator::InlineRuntimeLUT genGetFramePointer = |
||||
|
{&CodeGenerator::GenerateGetFramePointer, "_GetFramePointer"}; |
||||
|
// _FastCharCodeAt is not used in our tests.
|
||||
|
bool result = CodeGenerator::PatchInlineRuntimeEntry( |
||||
|
NewString("_FastCharCodeAt"), |
||||
|
genGetFramePointer, &oldInlineEntry); |
||||
|
CHECK(result); |
||||
|
} |
||||
|
|
||||
|
~CodeGeneratorPatcher() { |
||||
|
CHECK(CodeGenerator::PatchInlineRuntimeEntry( |
||||
|
NewString("_GetFramePointer"), |
||||
|
oldInlineEntry, NULL)); |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
CodeGenerator::InlineRuntimeLUT oldInlineEntry; |
||||
|
}; |
||||
|
|
||||
|
} } // namespace v8::internal
|
||||
|
|
||||
|
|
||||
|
// Creates a global function named 'func_name' that calls the tracing
|
||||
|
// function 'trace_func_name' with an actual EBP register value,
|
||||
|
// shifted right to be presented as Smi.
|
||||
|
static void CreateTraceCallerFunction(const char* func_name, |
||||
|
const char* trace_func_name) { |
||||
|
i::EmbeddedVector<char, 256> trace_call_buf; |
||||
|
i::OS::SNPrintF(trace_call_buf, "%s(%%_GetFramePointer());", trace_func_name); |
||||
|
|
||||
|
// Compile the script.
|
||||
|
i::CodeGeneratorPatcher patcher; |
||||
|
bool allow_natives_syntax = i::FLAG_allow_natives_syntax; |
||||
|
i::FLAG_allow_natives_syntax = true; |
||||
|
Handle<JSFunction> func = CompileFunction(trace_call_buf.start()); |
||||
|
CHECK(!func.is_null()); |
||||
|
i::FLAG_allow_natives_syntax = allow_natives_syntax; |
||||
|
|
||||
|
#ifdef DEBUG |
||||
|
v8::internal::Code* func_code = func->code(); |
||||
|
CHECK(func_code->IsCode()); |
||||
|
func_code->Print(); |
||||
|
#endif |
||||
|
|
||||
|
SetGlobalProperty(func_name, v8::ToApi<Value>(func)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(CFromJSStackTrace) { |
||||
|
TickSample sample; |
||||
|
InitTraceEnv(&sample); |
||||
|
|
||||
|
InitializeVM(); |
||||
|
v8::HandleScope scope; |
||||
|
CreateTraceCallerFunction("JSFuncDoTrace", "trace"); |
||||
|
CompileRun( |
||||
|
"function JSTrace() {" |
||||
|
" JSFuncDoTrace();" |
||||
|
"};\n" |
||||
|
"JSTrace();"); |
||||
|
CHECK_GT(sample.frames_count, 1); |
||||
|
// Stack sampling will start from the first JS function, i.e. "JSFuncDoTrace"
|
||||
|
CheckRetAddrIsInJSFunction("JSFuncDoTrace", |
||||
|
sample.stack[0]); |
||||
|
CheckRetAddrIsInJSFunction("JSTrace", |
||||
|
sample.stack[1]); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(PureJSStackTrace) { |
||||
|
TickSample sample; |
||||
|
InitTraceEnv(&sample); |
||||
|
|
||||
|
InitializeVM(); |
||||
|
v8::HandleScope scope; |
||||
|
CreateTraceCallerFunction("JSFuncDoTrace", "js_trace"); |
||||
|
CompileRun( |
||||
|
"function JSTrace() {" |
||||
|
" JSFuncDoTrace();" |
||||
|
"};\n" |
||||
|
"function OuterJSTrace() {" |
||||
|
" JSTrace();" |
||||
|
"};\n" |
||||
|
"OuterJSTrace();"); |
||||
|
CHECK_GT(sample.frames_count, 1); |
||||
|
// Stack sampling will start from the caller of JSFuncDoTrace, i.e. "JSTrace"
|
||||
|
CheckRetAddrIsInJSFunction("JSTrace", |
||||
|
sample.stack[0]); |
||||
|
CheckRetAddrIsInJSFunction("OuterJSTrace", |
||||
|
sample.stack[1]); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void CFuncDoTrace() { |
||||
|
Address fp; |
||||
|
#ifdef __GNUC__ |
||||
|
fp = reinterpret_cast<Address>(__builtin_frame_address(0)); |
||||
|
#elif defined _MSC_VER && defined V8_TARGET_ARCH_IA32 |
||||
|
__asm mov [fp], ebp // NOLINT
|
||||
|
#elif defined _MSC_VER && defined V8_TARGET_ARCH_X64 |
||||
|
// FIXME: I haven't really tried to compile it.
|
||||
|
__asm movq [fp], rbp // NOLINT
|
||||
|
#endif |
||||
|
DoTrace(fp); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static int CFunc(int depth) { |
||||
|
if (depth <= 0) { |
||||
|
CFuncDoTrace(); |
||||
|
return 0; |
||||
|
} else { |
||||
|
return CFunc(depth - 1) + 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(PureCStackTrace) { |
||||
|
TickSample sample; |
||||
|
InitTraceEnv(&sample); |
||||
|
// Check that sampler doesn't crash
|
||||
|
CHECK_EQ(10, CFunc(10)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST(JsEntrySp) { |
||||
|
InitializeVM(); |
||||
|
v8::HandleScope scope; |
||||
|
CHECK_EQ(0, GetJsEntrySp()); |
||||
|
CompileRun("a = 1; b = a + 1;"); |
||||
|
CHECK_EQ(0, GetJsEntrySp()); |
||||
|
CompileRun("js_entry_sp();"); |
||||
|
CHECK_EQ(0, GetJsEntrySp()); |
||||
|
CompileRun("js_entry_sp_level2();"); |
||||
|
CHECK_EQ(0, GetJsEntrySp()); |
||||
|
} |
||||
|
|
||||
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
@ -0,0 +1,38 @@ |
|||||
|
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
|
// Redistribution and use in source and binary forms, with or without
|
||||
|
// modification, are permitted provided that the following conditions are
|
||||
|
// met:
|
||||
|
//
|
||||
|
// * Redistributions of source code must retain the above copyright
|
||||
|
// notice, this list of conditions and the following disclaimer.
|
||||
|
// * 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.
|
||||
|
// * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
|
||||
|
// OWNER 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.
|
||||
|
|
||||
|
// Test of invalid Date construction, and TimeClip function.
|
||||
|
|
||||
|
// See http://code.google.com/p/v8/issues/detail?id=416
|
||||
|
|
||||
|
assertTrue(isNaN(new Date(1e81).getTime()), "new Date(1e81)"); |
||||
|
assertTrue(isNaN(new Date(-1e81).getTime()), "new Date(-1e81)"); |
||||
|
assertTrue(isNaN(new Date(1e81, "").getTime()), "new Date(1e81, \"\")"); |
||||
|
assertTrue(isNaN(new Date(-1e81, "").getTime()), "new Date(-1e81, \"\")"); |
||||
|
assertTrue(isNaN(new Date(Number.NaN).getTime()), "new Date(Number.NaN)"); |
||||
|
assertTrue(isNaN(new Date(Number.NaN, "").getTime()), |
||||
|
"new Date(Number.NaN, \"\")"); |
Loading…
Reference in new issue