}
+void StackGuard::RequestInterrupt(InterruptCallback callback, void* data) {
+ ExecutionAccess access(isolate_);
+ thread_local_.interrupt_flags_ |= API_INTERRUPT;
+ thread_local_.interrupt_callback_ = callback;
+ thread_local_.interrupt_callback_data_ = data;
+ set_interrupt_limits(access);
+}
+
+
+void StackGuard::ClearInterrupt() {
+ thread_local_.interrupt_callback_ = 0;
+ thread_local_.interrupt_callback_data_ = 0;
+ Continue(API_INTERRUPT);
+}
+
+
+bool StackGuard::IsAPIInterrupt() {
+ ExecutionAccess access(isolate_);
+ return thread_local_.interrupt_flags_ & API_INTERRUPT;
+}
+
+
+void StackGuard::InvokeInterruptCallback() {
+ InterruptCallback callback = 0;
+ void* data = 0;
+
+ {
+ ExecutionAccess access(isolate_);
+ callback = thread_local_.interrupt_callback_;
+ data = thread_local_.interrupt_callback_data_;
+ thread_local_.interrupt_callback_ = NULL;
+ thread_local_.interrupt_callback_data_ = NULL;
+ }
+
+ if (callback != NULL) {
+ VMState<EXTERNAL> state(isolate_);
+ HandleScope handle_scope(isolate_);
+ callback(reinterpret_cast<v8::Isolate*>(isolate_), data);
+ }
+}
+
+
char* StackGuard::ArchiveStackGuard(char* to) {
ExecutionAccess access(isolate_);
OS::MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal));
nesting_ = 0;
postpone_interrupts_nesting_ = 0;
interrupt_flags_ = 0;
+ interrupt_callback_ = 0;
}
nesting_ = 0;
postpone_interrupts_nesting_ = 0;
interrupt_flags_ = 0;
+ interrupt_callback_ = 0;
return should_set_stack_limits;
}
return isolate->heap()->undefined_value();
}
+ if (stack_guard->IsAPIInterrupt()) {
+ stack_guard->InvokeInterruptCallback();
+ stack_guard->Continue(API_INTERRUPT);
+ }
+
if (stack_guard->IsGCRequest()) {
isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags,
"StackGuard GC request");
virtual void Verify() V8_OVERRIDE;
#endif
- virtual bool IsCall() { return false; }
+ virtual bool HasStackCheck() { return false; }
DECLARE_ABSTRACT_INSTRUCTION(Instruction)
return -argument_count();
}
- virtual bool IsCall() V8_FINAL V8_OVERRIDE { return true; }
-
private:
int argument_count_;
};
known_function_(known_function) {
formal_parameter_count_ = known_function.is_null()
? 0 : known_function->shared()->formal_parameter_count();
+ has_stack_check_ = !known_function.is_null() &&
+ (known_function->code()->kind() == Code::FUNCTION ||
+ known_function->code()->kind() == Code::OPTIMIZED_FUNCTION);
}
static HInvokeFunction* New(Zone* zone,
Handle<JSFunction> known_function() { return known_function_; }
int formal_parameter_count() const { return formal_parameter_count_; }
+ virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
+ return has_stack_check_;
+ }
+
DECLARE_CONCRETE_INSTRUCTION(InvokeFunction)
private:
HInvokeFunction(HValue* context, HValue* function, int argument_count)
- : HBinaryCall(context, function, argument_count) {
+ : HBinaryCall(context, function, argument_count),
+ has_stack_check_(false) {
}
Handle<JSFunction> known_function_;
int formal_parameter_count_;
+ bool has_stack_check_;
};
return Representation::None();
}
+ virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
+ return has_stack_check_;
+ }
+
DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction)
private:
HCallConstantFunction(Handle<JSFunction> function, int argument_count)
: HCall<0>(argument_count),
function_(function),
- formal_parameter_count_(function->shared()->formal_parameter_count()) {}
+ formal_parameter_count_(function->shared()->formal_parameter_count()),
+ has_stack_check_(
+ function->code()->kind() == Code::FUNCTION ||
+ function->code()->kind() == Code::OPTIMIZED_FUNCTION) {}
Handle<JSFunction> function_;
int formal_parameter_count_;
+ bool has_stack_check_;
};
return Representation::None();
}
+ virtual bool HasStackCheck() V8_FINAL V8_OVERRIDE {
+ return has_stack_check_;
+ }
+
DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal)
private:
HCallKnownGlobal(Handle<JSFunction> target, int argument_count)
: HCall<0>(argument_count),
target_(target),
- formal_parameter_count_(target->shared()->formal_parameter_count()) { }
+ formal_parameter_count_(target->shared()->formal_parameter_count()),
+ has_stack_check_(
+ target->code()->kind() == Code::FUNCTION ||
+ target->code()->kind() == Code::OPTIMIZED_FUNCTION) {}
Handle<JSFunction> target_;
int formal_parameter_count_;
+ bool has_stack_check_;
};
}
+#endif // V8_OS_POSIX
+
+
static bool NamedAccessAlwaysBlocked(Local<v8::Object> global,
Local<Value> name,
v8::AccessType type,
}
-#endif // V8_OS_POSIX
+class RequestInterruptTestBase {
+ public:
+ RequestInterruptTestBase()
+ : env_(),
+ isolate_(env_->GetIsolate()),
+ sem_(0),
+ warmup_(20000),
+ should_continue_(true) {
+ }
+
+ virtual ~RequestInterruptTestBase() { }
+
+ virtual void TestBody() = 0;
+
+ void RunTest() {
+ InterruptThread i_thread(this);
+ i_thread.Start();
+
+ v8::HandleScope handle_scope(isolate_);
+
+ TestBody();
+
+ isolate_->ClearInterrupt();
+ }
+
+ void WakeUpInterruptor() {
+ sem_.Signal();
+ }
+
+ bool should_continue() const { return should_continue_; }
+
+ bool ShouldContinue() {
+ if (warmup_ > 0) {
+ if (--warmup_ == 0) {
+ WakeUpInterruptor();
+ }
+ }
+
+ return should_continue_;
+ }
+
+ protected:
+ static void ShouldContinueCallback(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ info.GetReturnValue().Set(test->ShouldContinue());
+ }
+
+ class InterruptThread : public i::Thread {
+ public:
+ explicit InterruptThread(RequestInterruptTestBase* test)
+ : Thread("RequestInterruptTest"), test_(test) {}
+
+ virtual void Run() {
+ test_->sem_.Wait();
+ test_->isolate_->RequestInterrupt(&OnInterrupt, test_);
+ }
+
+ static void OnInterrupt(v8::Isolate* isolate, void* data) {
+ reinterpret_cast<RequestInterruptTestBase*>(data)->
+ should_continue_ = false;
+ }
+
+ private:
+ RequestInterruptTestBase* test_;
+ };
+
+ LocalContext env_;
+ v8::Isolate* isolate_;
+ i::Semaphore sem_;
+ int warmup_;
+ bool should_continue_;
+};
+
+
+class RequestInterruptTestWithFunctionCall : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ Local<Function> func = Function::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_, this));
+ env_->Global()->Set(v8_str("ShouldContinue"), func);
+
+ CompileRun("while (ShouldContinue()) { }");
+ }
+};
+
+
+class RequestInterruptTestWithMethodCall : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->Set(v8_str("shouldContinue"), Function::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_, this)));
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue()) { }");
+ }
+};
+
+
+class RequestInterruptTestWithAccessor : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->SetAccessorProperty(v8_str("shouldContinue"), FunctionTemplate::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_, this)));
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue) { }");
+ }
+};
+
+
+class RequestInterruptTestWithNativeAccessor : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->SetNativeDataProperty(v8_str("shouldContinue"),
+ &ShouldContinueNativeGetter,
+ NULL,
+ v8::External::New(isolate_, this));
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue) { }");
+ }
+
+ private:
+ static void ShouldContinueNativeGetter(
+ Local<String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ info.GetReturnValue().Set(test->ShouldContinue());
+ }
+};
+
+
+class RequestInterruptTestWithMethodCallAndInterceptor
+ : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate_);
+ v8::Local<v8::Template> proto = t->PrototypeTemplate();
+ proto->Set(v8_str("shouldContinue"), Function::New(
+ isolate_, ShouldContinueCallback, v8::External::New(isolate_, this)));
+ v8::Local<v8::ObjectTemplate> instance_template = t->InstanceTemplate();
+ instance_template->SetNamedPropertyHandler(EmptyInterceptor);
+
+ env_->Global()->Set(v8_str("Klass"), t->GetFunction());
+
+ CompileRun("var obj = new Klass; while (obj.shouldContinue()) { }");
+ }
+
+ private:
+ static void EmptyInterceptor(
+ Local<String> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ }
+};
+
+
+class RequestInterruptTestWithMathAbs : public RequestInterruptTestBase {
+ public:
+ virtual void TestBody() {
+ env_->Global()->Set(v8_str("WakeUpInterruptor"), Function::New(
+ isolate_,
+ WakeUpInterruptorCallback,
+ v8::External::New(isolate_, this)));
+
+ env_->Global()->Set(v8_str("ShouldContinue"), Function::New(
+ isolate_,
+ ShouldContinueCallback,
+ v8::External::New(isolate_, this)));
+
+ i::FLAG_allow_natives_syntax = true;
+ CompileRun("function loopish(o) {"
+ " var pre = 10;"
+ " while (o.abs(1) > 0) {"
+ " if (o.abs(1) >= 0 && !ShouldContinue()) break;"
+ " if (pre > 0) {"
+ " if (--pre === 0) WakeUpInterruptor(o === Math);"
+ " }"
+ " }"
+ "}"
+ "var i = 50;"
+ "var obj = {abs: function () { return i-- }, x: null};"
+ "delete obj.x;"
+ "loopish(obj);"
+ "%OptimizeFunctionOnNextCall(loopish);"
+ "loopish(Math);");
+
+ i::FLAG_allow_natives_syntax = false;
+ }
+
+ private:
+ static void WakeUpInterruptorCallback(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ if (!info[0]->BooleanValue()) return;
+
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ test->WakeUpInterruptor();
+ }
+
+ static void ShouldContinueCallback(
+ const v8::FunctionCallbackInfo<Value>& info) {
+ RequestInterruptTestBase* test =
+ reinterpret_cast<RequestInterruptTestBase*>(
+ info.Data().As<v8::External>()->Value());
+ info.GetReturnValue().Set(test->should_continue());
+ }
+};
+
+
+TEST(RequestInterruptTestWithFunctionCall) {
+ RequestInterruptTestWithFunctionCall().RunTest();
+}
+
+
+TEST(RequestInterruptTestWithMethodCall) {
+ RequestInterruptTestWithMethodCall().RunTest();
+}
+
+
+TEST(RequestInterruptTestWithAccessor) {
+ RequestInterruptTestWithAccessor().RunTest();
+}
+
+
+TEST(RequestInterruptTestWithNativeAccessor) {
+ RequestInterruptTestWithNativeAccessor().RunTest();
+}
+
+
+TEST(RequestInterruptTestWithMethodCallAndInterceptor) {
+ RequestInterruptTestWithMethodCallAndInterceptor().RunTest();
+}
+
+
+TEST(RequestInterruptTestWithMathAbs) {
+ RequestInterruptTestWithMathAbs().RunTest();
+}
static Local<Value> function_new_expected_env;