blob: 8a4b2ff80cc66e72cae373ef5734ece0417ab128 [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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000037RawBuffer GetRawBufferArgument(
38 ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) {
Ben Murdochda12d292016-06-02 14:46:10 +010039 if (args.Length() < 1) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000040 thrower.Error("Argument 0 must be an array buffer");
41 return {nullptr, nullptr};
42 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000043
Ben Murdochda12d292016-06-02 14:46:10 +010044 const byte* start = nullptr;
45 const byte* end = nullptr;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000046
Ben Murdochda12d292016-06-02 14:46:10 +010047 if (args[0]->IsArrayBuffer()) {
48 // A raw array buffer was passed.
49 Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]);
50 ArrayBuffer::Contents contents = buffer->GetContents();
51
52 start = reinterpret_cast<const byte*>(contents.Data());
53 end = start + contents.ByteLength();
54
55 if (start == nullptr || end == start) {
56 thrower.Error("ArrayBuffer argument is empty");
57 }
58 } else if (args[0]->IsTypedArray()) {
59 // A TypedArray was passed.
60 Local<TypedArray> array = Local<TypedArray>::Cast(args[0]);
61 Local<ArrayBuffer> buffer = array->Buffer();
62
63 ArrayBuffer::Contents contents = buffer->GetContents();
64
65 start =
66 reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset();
67 end = start + array->ByteLength();
68
69 if (start == nullptr || end == start) {
70 thrower.Error("ArrayBuffer argument is empty");
71 }
72 } else {
73 thrower.Error("Argument 0 must be an ArrayBuffer or Uint8Array");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000074 }
Ben Murdochda12d292016-06-02 14:46:10 +010075
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000076 return {start, end};
77}
78
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000079void VerifyModule(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.verifyModule()");
83
84 RawBuffer buffer = GetRawBufferArgument(thrower, args);
85 if (thrower.error()) return;
86
Ben Murdochda12d292016-06-02 14:46:10 +010087 i::Zone zone(isolate->allocator());
88 internal::wasm::ModuleResult result =
89 internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end,
90 true, internal::wasm::kWasmOrigin);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000091
92 if (result.failed()) {
93 thrower.Failed("", result);
94 }
95
96 if (result.val) delete result.val;
97}
98
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000099void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
100 HandleScope scope(args.GetIsolate());
101 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
102 ErrorThrower thrower(isolate, "WASM.verifyFunction()");
103
104 RawBuffer buffer = GetRawBufferArgument(thrower, args);
105 if (thrower.error()) return;
106
107 internal::wasm::FunctionResult result;
108 {
109 // Verification of a single function shouldn't allocate.
110 i::DisallowHeapAllocation no_allocation;
Ben Murdochda12d292016-06-02 14:46:10 +0100111 i::Zone zone(isolate->allocator());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000112 result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr,
113 buffer.start, buffer.end);
114 }
115
116 if (result.failed()) {
117 thrower.Failed("", result);
118 }
119
120 if (result.val) delete result.val;
121}
122
Ben Murdoch097c5b22016-05-18 11:27:45 +0100123v8::internal::wasm::WasmModuleIndex* TranslateAsmModule(
124 i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000125 info->set_global();
126 info->set_lazy(false);
127 info->set_allow_lazy_parsing(false);
128 info->set_toplevel(true);
129
130 if (!i::Compiler::ParseAndAnalyze(info)) {
131 return nullptr;
132 }
133
Ben Murdochc5610432016-08-08 18:44:38 +0100134 if (info->scope()->declarations()->length() == 0) {
135 thrower->Error("Asm.js validation failed: no declarations in scope");
136 return nullptr;
137 }
138
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000139 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());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100144 if (i::FLAG_enable_simd_asmjs) {
145 typer.set_allow_simd(true);
146 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000147 if (!typer.Validate()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100148 thrower->Error("Asm.js validation failed: %s", typer.error_message());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000149 return nullptr;
150 }
151
Ben Murdochda12d292016-06-02 14:46:10 +0100152 auto module =
153 v8::internal::wasm::AsmWasmBuilder(info->isolate(), info->zone(),
154 info->literal(), foreign, &typer)
155 .Run();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100156
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000157 return module;
158}
159
Ben Murdoch097c5b22016-05-18 11:27:45 +0100160void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args,
161 const byte* start, const byte* end,
Ben Murdochda12d292016-06-02 14:46:10 +0100162 ErrorThrower* thrower,
163 internal::wasm::ModuleOrigin origin) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000164 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000165
166 i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null();
167 if (args.Length() > 2 && args[2]->IsArrayBuffer()) {
168 Local<Object> obj = Local<Object>::Cast(args[2]);
169 i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj);
170 memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj));
171 }
172
173 // Decode but avoid a redundant pass over function bodies for verification.
174 // Verification will happen during compilation.
Ben Murdochda12d292016-06-02 14:46:10 +0100175 i::Zone zone(isolate->allocator());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000176 internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule(
Ben Murdochda12d292016-06-02 14:46:10 +0100177 isolate, &zone, start, end, false, origin);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000178
Ben Murdochda12d292016-06-02 14:46:10 +0100179 if (result.failed() && origin == internal::wasm::kAsmJsOrigin) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100180 thrower->Error("Asm.js converted module failed to decode");
181 } else if (result.failed()) {
182 thrower->Failed("", result);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000183 } else {
184 // Success. Instantiate the module and return the object.
Ben Murdochc5610432016-08-08 18:44:38 +0100185 i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000186 if (args.Length() > 1 && args[1]->IsObject()) {
187 Local<Object> obj = Local<Object>::Cast(args[1]);
Ben Murdochc5610432016-08-08 18:44:38 +0100188 ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000189 }
190
191 i::MaybeHandle<i::JSObject> object =
192 result.val->Instantiate(isolate, ffi, memory);
193
194 if (!object.is_null()) {
195 args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked()));
196 }
197 }
198
199 if (result.val) delete result.val;
200}
Ben Murdoch097c5b22016-05-18 11:27:45 +0100201
Ben Murdoch097c5b22016-05-18 11:27:45 +0100202void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) {
203 HandleScope scope(args.GetIsolate());
204 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
205 ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()");
206
207 if (!args[0]->IsString()) {
208 thrower.Error("Asm module text should be a string");
209 return;
210 }
211
212 i::Factory* factory = isolate->factory();
Ben Murdochda12d292016-06-02 14:46:10 +0100213 i::Zone zone(isolate->allocator());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100214 Local<String> source = Local<String>::Cast(args[0]);
215 i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source));
216 i::ParseInfo info(&zone, script);
217
218 i::Handle<i::Object> foreign;
219 if (args.Length() > 1 && args[1]->IsObject()) {
220 Local<Object> local_foreign = Local<Object>::Cast(args[1]);
221 foreign = v8::Utils::OpenHandle(*local_foreign);
222 }
223
224 auto module = TranslateAsmModule(&info, foreign, &thrower);
225 if (module == nullptr) {
226 return;
227 }
228
Ben Murdochda12d292016-06-02 14:46:10 +0100229 InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower,
230 internal::wasm::kAsmJsOrigin);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100231}
232
Ben Murdoch097c5b22016-05-18 11:27:45 +0100233void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) {
234 HandleScope scope(args.GetIsolate());
235 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
236 ErrorThrower thrower(isolate, "WASM.instantiateModule()");
237
238 RawBuffer buffer = GetRawBufferArgument(thrower, args);
239 if (buffer.start == nullptr) return;
240
Ben Murdochda12d292016-06-02 14:46:10 +0100241 InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower,
242 internal::wasm::kWasmOrigin);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100243}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000244} // namespace
245
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000246// TODO(titzer): we use the API to create the function template because the
247// internal guts are too ugly to replicate here.
248static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate,
249 FunctionCallback func) {
250 Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate);
251 Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func);
252 return v8::Utils::OpenHandle(*local);
253}
254
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000255namespace internal {
256static Handle<String> v8_str(Isolate* isolate, const char* str) {
257 return isolate->factory()->NewStringFromAsciiChecked(str);
258}
259
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000260static void InstallFunc(Isolate* isolate, Handle<JSObject> object,
261 const char* str, FunctionCallback func) {
262 Handle<String> name = v8_str(isolate, str);
263 Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func);
264 Handle<JSFunction> function =
265 ApiNatives::InstantiateFunction(temp).ToHandleChecked();
266 PropertyAttributes attributes =
267 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
268 JSObject::AddProperty(object, name, function, attributes);
269}
270
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000271void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) {
272 // Setup wasm function map.
273 Handle<Context> context(global->native_context(), isolate);
274 InstallWasmFunctionMap(isolate, context);
275
276 // Bind the WASM object.
277 Factory* factory = isolate->factory();
Ben Murdochda12d292016-06-02 14:46:10 +0100278 Handle<String> name = v8_str(isolate, "Wasm");
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000279 Handle<JSFunction> cons = factory->NewFunction(name);
280 JSFunction::SetInstancePrototype(
281 cons, Handle<Object>(context->initial_object_prototype(), isolate));
282 cons->shared()->set_instance_class_name(*name);
283 Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED);
284 PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM);
285 JSObject::AddProperty(global, name, wasm_object, attributes);
286
287 // Install functions on the WASM object.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000288 InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule);
289 InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100290 InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000291 InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm",
292 InstantiateModuleFromAsm);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000293
Ben Murdochc5610432016-08-08 18:44:38 +0100294 {
295 // Add the Wasm.experimentalVersion property.
296 Handle<String> name = v8_str(isolate, "experimentalVersion");
297 PropertyAttributes attributes =
298 static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
299 Handle<Smi> value = Handle<Smi>(Smi::FromInt(wasm::kWasmVersion), isolate);
300 JSObject::AddProperty(wasm_object, name, value, attributes);
301 }
302}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000303
304void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) {
305 if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) {
Ben Murdochda12d292016-06-02 14:46:10 +0100306 // TODO(titzer): Move this to bootstrapper.cc??
307 // TODO(titzer): Also make one for strict mode functions?
308 Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate);
309
310 InstanceType instance_type = prev_map->instance_type();
311 int internal_fields = JSObject::GetInternalFieldCount(*prev_map);
312 CHECK_EQ(0, internal_fields);
313 int pre_allocated =
314 prev_map->GetInObjectProperties() - prev_map->unused_property_fields();
315 int instance_size;
316 int in_object_properties;
317 JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields + 1,
318 0, &instance_size,
319 &in_object_properties);
320
321 int unused_property_fields = in_object_properties - pre_allocated;
322 Handle<Map> map = Map::CopyInitialMap(
323 prev_map, instance_size, in_object_properties, unused_property_fields);
324
325 context->set_wasm_function_map(*map);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000326 }
327}
328
329} // namespace internal
330} // namespace v8