blob: 62a2676032e5096f63921605205d403795a85666 [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) {
Ben Murdoch097c5b22016-05-18 11:27:45 +010040 // TODO(titzer): allow typed array views.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000041 if (args.Length() < 1 || !args[0]->IsArrayBuffer()) {
42 thrower.Error("Argument 0 must be an array buffer");
43 return {nullptr, nullptr};
44 }
45 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
46 ArrayBuffer::Contents contents = buffer->GetContents();
47
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000048 const byte* start = reinterpret_cast<const byte*>(contents.Data());
49 const byte* end = start + contents.ByteLength();
50
51 if (start == nullptr) {
52 thrower.Error("ArrayBuffer argument is empty");
53 }
54 return {start, end};
55}
56
57
58void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
59 HandleScope scope(args.GetIsolate());
60 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
61 ErrorThrower thrower(isolate, "WASM.verifyModule()");
62
63 RawBuffer buffer = GetRawBufferArgument(thrower, args);
64 if (thrower.error()) return;
65
66 i::Zone zone;
67 internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
68 isolate, &zone, buffer.start, buffer.end, true, false);
69
70 if (result.failed()) {
71 thrower.Failed("", result);
72 }
73
74 if (result.val) delete result.val;
75}
76
77
78void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
79 HandleScope scope(args.GetIsolate());
80 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
81 ErrorThrower thrower(isolate, "WASM.verifyFunction()");
82
83 RawBuffer buffer = GetRawBufferArgument(thrower, args);
84 if (thrower.error()) return;
85
86 internal::wasm::FunctionResult result;
87 {
88 // Verification of a single function shouldn't allocate.
89 i::DisallowHeapAllocation no_allocation;
90 i::Zone zone;
91 result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr,
92 buffer.start, buffer.end);
93 }
94
95 if (result.failed()) {
96 thrower.Failed("", result);
97 }
98
99 if (result.val) delete result.val;
100}
101
Ben Murdoch097c5b22016-05-18 11:27:45 +0100102v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
103 i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000104 info->set_global();
105 info->set_lazy(false);
106 info->set_allow_lazy_parsing(false);
107 info->set_toplevel(true);
108
109 if (!i::Compiler::ParseAndAnalyze(info)) {
110 return nullptr;
111 }
112
113 info->set_literal(
114 info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun());
115
116 v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
117 info->literal());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100118 if (i::FLAG_enable_simd_asmjs) {
119 typer.set_allow_simd(true);
120 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000121 if (!typer.Validate()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100122 thrower->Error("Asm.js validation failed: %s", typer.error_message());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000123 return nullptr;
124 }
125
126 auto module = v8::internal::wasm::AsmWasmBuilder(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100127 info->isolate(), info->zone(), info->literal(), foreign)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000128 .Run();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100129
130 if (i::FLAG_dump_asmjs_wasm) {
131 FILE* wasm_file = fopen(i::FLAG_asmjs_wasm_dumpfile, "wb");
132 if (wasm_file) {
133 fwrite(module->Begin(), module->End() - module->Begin(), 1, wasm_file);
134 fclose(wasm_file);
135 }
136 }
137
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000138 return module;
139}
140
141
Ben Murdoch097c5b22016-05-18 11:27:45 +0100142void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args,
143 const byte* start, const byte* end,
144 ErrorThrower* thrower, bool must_decode) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000145 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000146
147 i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
148 if (args.Length() > 2 && args[2]->IsArrayBuffer()) {
149 Local<Object> obj = Local<Object>::Cast(args[2]);
150 i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
151 memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
152 }
153
154 // Decode but avoid a redundant pass over function bodies for verification.
155 // Verification will happen during compilation.
156 i::Zone zone;
157 internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
Ben Murdoch097c5b22016-05-18 11:27:45 +0100158 isolate, &zone, start, end, false, false);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000159
Ben Murdoch097c5b22016-05-18 11:27:45 +0100160 if (result.failed() && must_decode) {
161 thrower->Error("Asm.js converted module failed to decode");
162 } else if (result.failed()) {
163 thrower->Failed("", result);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000164 } else {
165 // Success. Instantiate the module and return the object.
166 i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null();
167 if (args.Length() > 1 && args[1]->IsObject()) {
168 Local<Object> obj = Local<Object>::Cast(args[1]);
169 ffi = i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj));
170 }
171
172 i::MaybeHandle<i::JSObject> object =
173 result.val->Instantiate(isolate, ffi, memory);
174
175 if (!object.is_null()) {
176 args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
177 }
178 }
179
180 if (result.val) delete result.val;
181}
Ben Murdoch097c5b22016-05-18 11:27:45 +0100182
183
184void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
185 HandleScope scope(args.GetIsolate());
186 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
187 ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()");
188
189 if (!args[0]->IsString()) {
190 thrower.Error("Asm module text should be a string");
191 return;
192 }
193
194 i::Factory* factory = isolate->factory();
195 i::Zone zone;
196 Local<String> source = Local<String>::Cast(args[0]);
197 i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
198 i::ParseInfo info(&zone, script);
199
200 i::Handle<i::Object> foreign;
201 if (args.Length() > 1 && args[1]->IsObject()) {
202 Local<Object> local_foreign = Local<Object>::Cast(args[1]);
203 foreign = v8::Utils::OpenHandle(*local_foreign);
204 }
205
206 auto module = TranslateAsmModule(&info, foreign, &thrower);
207 if (module == nullptr) {
208 return;
209 }
210
211 InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, true);
212}
213
214
215void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
216 HandleScope scope(args.GetIsolate());
217 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
218 ErrorThrower thrower(isolate, "WASM.instantiateModule()");
219
220 RawBuffer buffer = GetRawBufferArgument(thrower, args);
221 if (buffer.start == nullptr) return;
222
223 InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower, false);
224}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000225} // namespace
226
227
228// TODO(titzer): we use the API to create the function template because the
229// internal guts are too ugly to replicate here.
230static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
231 FunctionCallback func) {
232 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
233 Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func);
234 return v8::Utils::OpenHandle(*local);
235}
236
237
238namespace internal {
239static Handle<String> v8_str(Isolate* isolate, const char* str) {
240 return isolate->factory()->NewStringFromAsciiChecked(str);
241}
242
243
244static void InstallFunc(Isolate* isolate, Handle<JSObject> object,
245 const char* str, FunctionCallback func) {
246 Handle<String> name = v8_str(isolate, str);
247 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
248 Handle<JSFunction> function =
249 ApiNatives::InstantiateFunction(temp).ToHandleChecked();
250 PropertyAttributes attributes =
251 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
252 JSObject::AddProperty(object, name, function, attributes);
253}
254
255
256void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
257 // Setup wasm function map.
258 Handle<Context> context(global->native_context(), isolate);
259 InstallWasmFunctionMap(isolate, context);
260
261 // Bind the WASM object.
262 Factory* factory = isolate->factory();
263 Handle<String> name = v8_str(isolate, "_WASMEXP_");
264 Handle<JSFunction> cons = factory->NewFunction(name);
265 JSFunction::SetInstancePrototype(
266 cons, Handle<Object>(context->initial_object_prototype(), isolate));
267 cons->shared()->set_instance_class_name(*name);
268 Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
269 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
270 JSObject::AddProperty(global, name, wasm_object, attributes);
271
272 // Install functions on the WASM object.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000273 InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule);
274 InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100275 InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000276 InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm",
277 InstantiateModuleFromAsm);
278}
279
280
281void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
282 if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
283 Handle<Map> wasm_function_map = isolate->factory()->NewMap(
284 JS_FUNCTION_TYPE, JSFunction::kSize + kPointerSize);
285 wasm_function_map->set_is_callable();
286 context->set_wasm_function_map(*wasm_function_map);
287 }
288}
289
290} // namespace internal
291} // namespace v8