• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 the V8 project 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 "src/api.h"
6 #include "src/api-natives.h"
7 #include "src/assert-scope.h"
8 #include "src/ast/ast.h"
9 #include "src/ast/scopes.h"
10 #include "src/factory.h"
11 #include "src/handles.h"
12 #include "src/isolate.h"
13 #include "src/objects.h"
14 #include "src/parsing/parser.h"
15 #include "src/typing-asm.h"
16 
17 #include "src/wasm/asm-wasm-builder.h"
18 #include "src/wasm/encoder.h"
19 #include "src/wasm/module-decoder.h"
20 #include "src/wasm/wasm-js.h"
21 #include "src/wasm/wasm-module.h"
22 #include "src/wasm/wasm-result.h"
23 
24 typedef uint8_t byte;
25 
26 using v8::internal::wasm::ErrorThrower;
27 
28 namespace v8 {
29 
30 namespace {
31 struct RawBuffer {
32   const byte* start;
33   const byte* end;
sizev8::__anonf1352b070111::RawBuffer34   size_t size() { return static_cast<size_t>(end - start); }
35 };
36 
37 
GetRawBufferArgument(ErrorThrower & thrower,const v8::FunctionCallbackInfo<v8::Value> & args)38 RawBuffer GetRawBufferArgument(
39     ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) {
40   if (args.Length() < 1 || !args[0]->IsArrayBuffer()) {
41     thrower.Error("Argument 0 must be an array buffer");
42     return {nullptr, nullptr};
43   }
44   Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
45   ArrayBuffer::Contents contents = buffer->GetContents();
46 
47   // TODO(titzer): allow offsets into buffers, views, etc.
48 
49   const byte* start = reinterpret_cast<const byte*>(contents.Data());
50   const byte* end = start + contents.ByteLength();
51 
52   if (start == nullptr) {
53     thrower.Error("ArrayBuffer argument is empty");
54   }
55   return {start, end};
56 }
57 
58 
VerifyModule(const v8::FunctionCallbackInfo<v8::Value> & args)59 void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
60   HandleScope scope(args.GetIsolate());
61   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
62   ErrorThrower thrower(isolate, "WASM.verifyModule()");
63 
64   RawBuffer buffer = GetRawBufferArgument(thrower, args);
65   if (thrower.error()) return;
66 
67   i::Zone zone;
68   internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
69       isolate, &zone, buffer.start, buffer.end, true, false);
70 
71   if (result.failed()) {
72     thrower.Failed("", result);
73   }
74 
75   if (result.val) delete result.val;
76 }
77 
78 
VerifyFunction(const v8::FunctionCallbackInfo<v8::Value> & args)79 void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
80   HandleScope scope(args.GetIsolate());
81   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
82   ErrorThrower thrower(isolate, "WASM.verifyFunction()");
83 
84   RawBuffer buffer = GetRawBufferArgument(thrower, args);
85   if (thrower.error()) return;
86 
87   internal::wasm::FunctionResult result;
88   {
89     // Verification of a single function shouldn't allocate.
90     i::DisallowHeapAllocation no_allocation;
91     i::Zone zone;
92     result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr,
93                                                 buffer.start, buffer.end);
94   }
95 
96   if (result.failed()) {
97     thrower.Failed("", result);
98   }
99 
100   if (result.val) delete result.val;
101 }
102 
103 
CompileRun(const v8::FunctionCallbackInfo<v8::Value> & args)104 void CompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) {
105   HandleScope scope(args.GetIsolate());
106   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
107   ErrorThrower thrower(isolate, "WASM.compileRun()");
108 
109   RawBuffer buffer = GetRawBufferArgument(thrower, args);
110   if (thrower.error()) return;
111 
112   // Decode and pre-verify the functions before compiling and running.
113   i::Zone zone;
114   internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
115       isolate, &zone, buffer.start, buffer.end, true, false);
116 
117   if (result.failed()) {
118     thrower.Failed("", result);
119   } else {
120     // Success. Compile and run!
121     int32_t retval = i::wasm::CompileAndRunWasmModule(isolate, result.val);
122     args.GetReturnValue().Set(retval);
123   }
124 
125   if (result.val) delete result.val;
126 }
127 
128 
TranslateAsmModule(i::ParseInfo * info)129 v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(i::ParseInfo* info) {
130   info->set_global();
131   info->set_lazy(false);
132   info->set_allow_lazy_parsing(false);
133   info->set_toplevel(true);
134 
135   if (!i::Compiler::ParseAndAnalyze(info)) {
136     return nullptr;
137   }
138 
139   info->set_literal(
140       info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun());
141 
142   v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
143                                info->literal());
144   if (!typer.Validate()) {
145     return nullptr;
146   }
147 
148   auto module = v8::internal::wasm::AsmWasmBuilder(
149                     info->isolate(), info->zone(), info->literal())
150                     .Run();
151   return module;
152 }
153 
154 
AsmCompileRun(const v8::FunctionCallbackInfo<v8::Value> & args)155 void AsmCompileRun(const v8::FunctionCallbackInfo<v8::Value>& args) {
156   HandleScope scope(args.GetIsolate());
157   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
158   ErrorThrower thrower(isolate, "WASM.asmCompileRun()");
159 
160   if (args.Length() != 1) {
161     thrower.Error("Invalid argument count");
162     return;
163   }
164   if (!args[0]->IsString()) {
165     thrower.Error("Invalid argument count");
166     return;
167   }
168 
169   i::Factory* factory = isolate->factory();
170   i::Zone zone;
171   Local<String> source = Local<String>::Cast(args[0]);
172   i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
173   i::ParseInfo info(&zone, script);
174 
175   auto module = TranslateAsmModule(&info);
176   if (module == nullptr) {
177     thrower.Error("Asm.js validation failed");
178     return;
179   }
180 
181   int32_t result = v8::internal::wasm::CompileAndRunWasmModule(
182       isolate, module->Begin(), module->End(), true);
183   args.GetReturnValue().Set(result);
184 }
185 
186 
187 // TODO(aseemgarg): deal with arraybuffer and foreign functions
InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value> & args)188 void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
189   HandleScope scope(args.GetIsolate());
190   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
191   ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()");
192 
193   if (args.Length() != 1) {
194     thrower.Error("Invalid argument count");
195     return;
196   }
197   if (!args[0]->IsString()) {
198     thrower.Error("Invalid argument count");
199     return;
200   }
201 
202   i::Factory* factory = isolate->factory();
203   i::Zone zone;
204   Local<String> source = Local<String>::Cast(args[0]);
205   i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
206   i::ParseInfo info(&zone, script);
207 
208   auto module = TranslateAsmModule(&info);
209   if (module == nullptr) {
210     thrower.Error("Asm.js validation failed");
211     return;
212   }
213 
214   i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
215   internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
216       isolate, &zone, module->Begin(), module->End(), false, false);
217 
218   if (result.failed()) {
219     thrower.Failed("", result);
220   } else {
221     // Success. Instantiate the module and return the object.
222     i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
223 
224     i::MaybeHandle<i::JSObject> object =
225         result.val->Instantiate(isolate, ffi, memory);
226 
227     if (!object.is_null()) {
228       args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
229     }
230   }
231 
232   if (result.val) delete result.val;
233 }
234 
235 
InstantiateModule(const v8::FunctionCallbackInfo<v8::Value> & args)236 void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
237   HandleScope scope(args.GetIsolate());
238   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
239   ErrorThrower thrower(isolate, "WASM.instantiateModule()");
240 
241   RawBuffer buffer = GetRawBufferArgument(thrower, args);
242   if (buffer.start == nullptr) return;
243 
244   i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
245   if (args.Length() > 2 && args[2]->IsArrayBuffer()) {
246     Local<Object> obj = Local<Object>::Cast(args[2]);
247     i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
248     memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
249   }
250 
251   // Decode but avoid a redundant pass over function bodies for verification.
252   // Verification will happen during compilation.
253   i::Zone zone;
254   internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
255       isolate, &zone, buffer.start, buffer.end, false, false);
256 
257   if (result.failed()) {
258     thrower.Failed("", result);
259   } else {
260     // Success. Instantiate the module and return the object.
261     i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
262     if (args.Length() > 1 && args[1]->IsObject()) {
263       Local<Object> obj = Local<Object>::Cast(args[1]);
264       ffi = i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj));
265     }
266 
267     i::MaybeHandle<i::JSObject> object =
268         result.val->Instantiate(isolate, ffi, memory);
269 
270     if (!object.is_null()) {
271       args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
272     }
273   }
274 
275   if (result.val) delete result.val;
276 }
277 }  // namespace
278 
279 
280 // TODO(titzer): we use the API to create the function template because the
281 // internal guts are too ugly to replicate here.
NewTemplate(i::Isolate * i_isolate,FunctionCallback func)282 static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
283                                                       FunctionCallback func) {
284   Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
285   Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func);
286   return v8::Utils::OpenHandle(*local);
287 }
288 
289 
290 namespace internal {
v8_str(Isolate * isolate,const char * str)291 static Handle<String> v8_str(Isolate* isolate, const char* str) {
292   return isolate->factory()->NewStringFromAsciiChecked(str);
293 }
294 
295 
InstallFunc(Isolate * isolate,Handle<JSObject> object,const char * str,FunctionCallback func)296 static void InstallFunc(Isolate* isolate, Handle<JSObject> object,
297                         const char* str, FunctionCallback func) {
298   Handle<String> name = v8_str(isolate, str);
299   Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
300   Handle<JSFunction> function =
301       ApiNatives::InstantiateFunction(temp).ToHandleChecked();
302   PropertyAttributes attributes =
303       static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
304   JSObject::AddProperty(object, name, function, attributes);
305 }
306 
307 
Install(Isolate * isolate,Handle<JSGlobalObject> global)308 void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
309   // Setup wasm function map.
310   Handle<Context> context(global->native_context(), isolate);
311   InstallWasmFunctionMap(isolate, context);
312 
313   // Bind the WASM object.
314   Factory* factory = isolate->factory();
315   Handle<String> name = v8_str(isolate, "_WASMEXP_");
316   Handle<JSFunction> cons = factory->NewFunction(name);
317   JSFunction::SetInstancePrototype(
318       cons, Handle<Object>(context->initial_object_prototype(), isolate));
319   cons->shared()->set_instance_class_name(*name);
320   Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
321   PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
322   JSObject::AddProperty(global, name, wasm_object, attributes);
323 
324   // Install functions on the WASM object.
325   InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule);
326   InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule);
327   InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction);
328   InstallFunc(isolate, wasm_object, "compileRun", CompileRun);
329   InstallFunc(isolate, wasm_object, "asmCompileRun", AsmCompileRun);
330   InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm",
331               InstantiateModuleFromAsm);
332 }
333 
334 
InstallWasmFunctionMap(Isolate * isolate,Handle<Context> context)335 void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
336   if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
337     Handle<Map> wasm_function_map = isolate->factory()->NewMap(
338         JS_FUNCTION_TYPE, JSFunction::kSize + kPointerSize);
339     wasm_function_map->set_is_callable();
340     context->set_wasm_function_map(*wasm_function_map);
341   }
342 }
343 
344 }  // namespace internal
345 }  // namespace v8
346