OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/renderer/module_system.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSup
pression.h" | |
9 | |
10 namespace { | |
11 | |
12 const char* kModuleSystem = "module_system"; | |
13 const char* kModuleName = "module_name"; | |
14 const char* kModuleField = "module_field"; | |
15 const char* kModulesField = "modules"; | |
16 | |
17 } // namespace | |
18 | |
19 ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context, | |
20 SourceMap* source_map) | |
21 : context_(v8::Persistent<v8::Context>::New(context)), | |
22 source_map_(source_map), | |
23 natives_enabled_(0) { | |
24 RouteFunction("require", | |
25 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); | |
26 RouteFunction("requireNative", | |
27 base::Bind(&ModuleSystem::GetNative, base::Unretained(this))); | |
28 | |
29 v8::Handle<v8::Object> global(context_->Global()); | |
30 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New()); | |
31 global->SetHiddenValue(v8::String::New(kModuleSystem), | |
32 v8::External::New(this)); | |
33 } | |
34 | |
35 ModuleSystem::~ModuleSystem() { | |
36 v8::HandleScope handle_scope; | |
37 // Deleting this value here prevents future lazy field accesses from | |
38 // referencing ModuleSystem after it has been freed. | |
39 context_->Global()->DeleteHiddenValue(v8::String::New(kModuleSystem)); | |
40 context_.Dispose(); | |
41 } | |
42 | |
43 ModuleSystem::NativesEnabledScope::NativesEnabledScope( | |
44 ModuleSystem* module_system) | |
45 : module_system_(module_system) { | |
46 module_system_->natives_enabled_++; | |
47 } | |
48 | |
49 ModuleSystem::NativesEnabledScope::~NativesEnabledScope() { | |
50 module_system_->natives_enabled_--; | |
51 CHECK_GE(module_system_->natives_enabled_, 0); | |
52 } | |
53 | |
54 // static | |
55 bool ModuleSystem::IsPresentInCurrentContext() { | |
56 v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); | |
57 v8::Handle<v8::Value> module_system = | |
58 global->GetHiddenValue(v8::String::New(kModuleSystem)); | |
59 return !module_system.IsEmpty() && !module_system->IsUndefined(); | |
60 } | |
61 | |
62 // static | |
63 void ModuleSystem::DumpException(const v8::TryCatch& try_catch) { | |
64 v8::HandleScope handle_scope; | |
65 | |
66 v8::Handle<v8::Message> message(try_catch.Message()); | |
67 if (message.IsEmpty()) { | |
68 LOG(ERROR) << "try_catch has no message"; | |
69 return; | |
70 } | |
71 | |
72 std::string resource_name = "<unknown resource>"; | |
73 if (!message->GetScriptResourceName().IsEmpty()) { | |
74 resource_name = | |
75 *v8::String::Utf8Value(message->GetScriptResourceName()->ToString()); | |
76 } | |
77 | |
78 std::string error_message = "<no error message>"; | |
79 if (!message->Get().IsEmpty()) | |
80 error_message = *v8::String::Utf8Value(message->Get()); | |
81 | |
82 std::string stack_trace = "<stack trace unavailable>"; | |
83 if (!try_catch.StackTrace().IsEmpty()) { | |
84 v8::String::Utf8Value stack_value(try_catch.StackTrace()); | |
85 if (*stack_value) | |
86 stack_trace.assign(*stack_value, stack_value.length()); | |
87 else | |
88 stack_trace = "<could not convert stack trace to string>"; | |
89 } | |
90 | |
91 LOG(ERROR) << "[" << resource_name << "(" << message->GetLineNumber() << ")] " | |
92 << error_message | |
93 << "{" << stack_trace << "}"; | |
94 } | |
95 | |
96 void ModuleSystem::Require(const std::string& module_name) { | |
97 v8::HandleScope handle_scope; | |
98 RequireForJsInner(v8::String::New(module_name.c_str())); | |
99 } | |
100 | |
101 v8::Handle<v8::Value> ModuleSystem::RequireForJs(const v8::Arguments& args) { | |
102 v8::HandleScope handle_scope; | |
103 v8::Handle<v8::String> module_name = args[0]->ToString(); | |
104 return handle_scope.Close(RequireForJsInner(module_name)); | |
105 } | |
106 | |
107 v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( | |
108 v8::Handle<v8::String> module_name) { | |
109 v8::HandleScope handle_scope; | |
110 v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); | |
111 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast( | |
112 global->GetHiddenValue(v8::String::New(kModulesField)))); | |
113 v8::Handle<v8::Value> exports(modules->Get(module_name)); | |
114 if (!exports->IsUndefined()) | |
115 return handle_scope.Close(exports); | |
116 | |
117 v8::Handle<v8::Value> source(GetSource(module_name)); | |
118 if (source->IsUndefined()) | |
119 return handle_scope.Close(v8::Undefined()); | |
120 v8::Handle<v8::String> wrapped_source(WrapSource( | |
121 v8::Handle<v8::String>::Cast(source))); | |
122 v8::Handle<v8::Function> func = | |
123 v8::Handle<v8::Function>::Cast(RunString(wrapped_source, module_name)); | |
124 if (func.IsEmpty()) { | |
125 return ThrowException(std::string(*v8::String::AsciiValue(module_name)) + | |
126 ": Bad source"); | |
127 } | |
128 | |
129 exports = v8::Object::New(); | |
130 v8::Handle<v8::Object> natives(NewInstance()); | |
131 v8::Handle<v8::Value> args[] = { | |
132 natives->Get(v8::String::NewSymbol("require")), | |
133 natives->Get(v8::String::NewSymbol("requireNative")), | |
134 exports, | |
135 }; | |
136 { | |
137 WebKit::WebScopedMicrotaskSuppression suppression; | |
138 v8::TryCatch try_catch; | |
139 try_catch.SetCaptureMessage(true); | |
140 func->Call(global, 3, args); | |
141 if (try_catch.HasCaught()) { | |
142 DumpException(try_catch); | |
143 return v8::Undefined(); | |
144 } | |
145 } | |
146 modules->Set(module_name, exports); | |
147 return handle_scope.Close(exports); | |
148 } | |
149 | |
150 void ModuleSystem::RegisterNativeHandler(const std::string& name, | |
151 scoped_ptr<NativeHandler> native_handler) { | |
152 native_handler_map_[name] = | |
153 linked_ptr<NativeHandler>(native_handler.release()); | |
154 } | |
155 | |
156 void ModuleSystem::OverrideNativeHandler(const std::string& name) { | |
157 overridden_native_handlers_.insert(name); | |
158 } | |
159 | |
160 void ModuleSystem::RunString(const std::string& code, const std::string& name) { | |
161 v8::HandleScope handle_scope; | |
162 RunString(v8::String::New(code.c_str()), v8::String::New(name.c_str())); | |
163 } | |
164 | |
165 // static | |
166 v8::Handle<v8::Value> ModuleSystem::LazyFieldGetter( | |
167 v8::Local<v8::String> property, const v8::AccessorInfo& info) { | |
168 CHECK(!info.Data().IsEmpty()); | |
169 CHECK(info.Data()->IsObject()); | |
170 v8::HandleScope handle_scope; | |
171 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data()); | |
172 v8::Handle<v8::Object> global(v8::Context::GetCurrent()->Global()); | |
173 v8::Handle<v8::Value> module_system_value = | |
174 global->GetHiddenValue(v8::String::New(kModuleSystem)); | |
175 if (module_system_value->IsUndefined()) { | |
176 // ModuleSystem has been deleted. | |
177 return v8::Undefined(); | |
178 } | |
179 ModuleSystem* module_system = static_cast<ModuleSystem*>( | |
180 v8::Handle<v8::External>::Cast(module_system_value)->Value()); | |
181 | |
182 v8::Handle<v8::Object> module; | |
183 { | |
184 NativesEnabledScope scope(module_system); | |
185 module = v8::Handle<v8::Object>::Cast(module_system->RequireForJsInner( | |
186 parameters->Get(v8::String::New(kModuleName))->ToString())); | |
187 } | |
188 if (module.IsEmpty()) | |
189 return handle_scope.Close(v8::Handle<v8::Value>()); | |
190 | |
191 v8::Handle<v8::String> field = | |
192 parameters->Get(v8::String::New(kModuleField))->ToString(); | |
193 | |
194 return handle_scope.Close(module->Get(field)); | |
195 } | |
196 | |
197 void ModuleSystem::SetLazyField(v8::Handle<v8::Object> object, | |
198 const std::string& field, | |
199 const std::string& module_name, | |
200 const std::string& module_field) { | |
201 v8::HandleScope handle_scope; | |
202 v8::Handle<v8::Object> parameters = v8::Object::New(); | |
203 parameters->Set(v8::String::New(kModuleName), | |
204 v8::String::New(module_name.c_str())); | |
205 parameters->Set(v8::String::New(kModuleField), | |
206 v8::String::New(module_field.c_str())); | |
207 | |
208 object->SetAccessor(v8::String::New(field.c_str()), | |
209 &ModuleSystem::LazyFieldGetter, | |
210 NULL, | |
211 parameters); | |
212 } | |
213 | |
214 v8::Handle<v8::Value> ModuleSystem::RunString(v8::Handle<v8::String> code, | |
215 v8::Handle<v8::String> name) { | |
216 v8::HandleScope handle_scope; | |
217 WebKit::WebScopedMicrotaskSuppression suppression; | |
218 v8::Handle<v8::Value> result; | |
219 v8::TryCatch try_catch; | |
220 try_catch.SetCaptureMessage(true); | |
221 v8::Handle<v8::Script> script(v8::Script::New(code, name)); | |
222 if (try_catch.HasCaught()) { | |
223 DumpException(try_catch); | |
224 return handle_scope.Close(result); | |
225 } | |
226 | |
227 result = script->Run(); | |
228 if (try_catch.HasCaught()) | |
229 DumpException(try_catch); | |
230 | |
231 return handle_scope.Close(result); | |
232 } | |
233 | |
234 v8::Handle<v8::Value> ModuleSystem::GetSource( | |
235 v8::Handle<v8::String> source_name) { | |
236 v8::HandleScope handle_scope; | |
237 std::string module_name = *v8::String::AsciiValue(source_name); | |
238 if (!source_map_->Contains(module_name)) | |
239 return v8::Undefined(); | |
240 return handle_scope.Close(source_map_->GetSource(module_name)); | |
241 } | |
242 | |
243 v8::Handle<v8::Value> ModuleSystem::GetNative(const v8::Arguments& args) { | |
244 CHECK_EQ(1, args.Length()); | |
245 if (natives_enabled_ == 0) | |
246 return ThrowException("Natives disabled"); | |
247 std::string native_name = *v8::String::AsciiValue(args[0]->ToString()); | |
248 if (overridden_native_handlers_.count(native_name) > 0u) | |
249 return RequireForJs(args); | |
250 NativeHandlerMap::iterator i = native_handler_map_.find(native_name); | |
251 if (i == native_handler_map_.end()) | |
252 return v8::Undefined(); | |
253 return i->second->NewInstance(); | |
254 } | |
255 | |
256 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) { | |
257 v8::HandleScope handle_scope; | |
258 v8::Handle<v8::String> left = | |
259 v8::String::New("(function(require, requireNative, exports) {"); | |
260 v8::Handle<v8::String> right = v8::String::New("\n})"); | |
261 return handle_scope.Close( | |
262 v8::String::Concat(left, v8::String::Concat(source, right))); | |
263 } | |
264 | |
265 v8::Handle<v8::Value> ModuleSystem::ThrowException(const std::string& message) { | |
266 return v8::ThrowException(v8::String::New(message.c_str())); | |
267 } | |
OLD | NEW |