blob: 80d8bdb2369e1b87e4fbf3427c19660f4a3e3bb6 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
24typedef uint8_t byte;
25
26using v8::internal::wasm::ErrorThrower;
27
28namespace v8 {
29
30namespace {
31struct RawBuffer {
32 const byte* start;
33 const byte* end;
34 size_t size() { return static_cast<size_t>(end - start); }
35};
36
37
38RawBuffer 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
59void 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
79void 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
104void 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
129v8::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
155void 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
188void 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
236void 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.
282static 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
290namespace internal {
291static Handle<String> v8_str(Isolate* isolate, const char* str) {
292 return isolate->factory()->NewStringFromAsciiChecked(str);
293}
294
295
296static 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
308void 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
335void 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