Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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; |
| 34 | size_t size() { return static_cast<size_t>(end - start); } |
| 35 | }; |
| 36 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 37 | RawBuffer GetRawBufferArgument( |
| 38 | ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 39 | if (args.Length() < 1) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 40 | thrower.Error("Argument 0 must be an array buffer"); |
| 41 | return {nullptr, nullptr}; |
| 42 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 43 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 44 | const byte* start = nullptr; |
| 45 | const byte* end = nullptr; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 46 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 47 | 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 74 | } |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 75 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 76 | return {start, end}; |
| 77 | } |
| 78 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 79 | void 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 87 | 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 91 | |
| 92 | if (result.failed()) { |
| 93 | thrower.Failed("", result); |
| 94 | } |
| 95 | |
| 96 | if (result.val) delete result.val; |
| 97 | } |
| 98 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 99 | void 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 111 | i::Zone zone(isolate->allocator()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 112 | 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 Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 123 | v8::internal::wasm::WasmModuleIndex* TranslateAsmModule( |
| 124 | i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 125 | 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 Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame^] | 134 | if (info->scope()->declarations()->length() == 0) { |
| 135 | thrower->Error("Asm.js validation failed: no declarations in scope"); |
| 136 | return nullptr; |
| 137 | } |
| 138 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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()); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 144 | if (i::FLAG_enable_simd_asmjs) { |
| 145 | typer.set_allow_simd(true); |
| 146 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 147 | if (!typer.Validate()) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 148 | thrower->Error("Asm.js validation failed: %s", typer.error_message()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 149 | return nullptr; |
| 150 | } |
| 151 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 152 | auto module = |
| 153 | v8::internal::wasm::AsmWasmBuilder(info->isolate(), info->zone(), |
| 154 | info->literal(), foreign, &typer) |
| 155 | .Run(); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 156 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 157 | return module; |
| 158 | } |
| 159 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 160 | void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args, |
| 161 | const byte* start, const byte* end, |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 162 | ErrorThrower* thrower, |
| 163 | internal::wasm::ModuleOrigin origin) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 164 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 165 | |
| 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 175 | i::Zone zone(isolate->allocator()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 176 | internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 177 | isolate, &zone, start, end, false, origin); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 178 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 179 | if (result.failed() && origin == internal::wasm::kAsmJsOrigin) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 180 | thrower->Error("Asm.js converted module failed to decode"); |
| 181 | } else if (result.failed()) { |
| 182 | thrower->Failed("", result); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 183 | } else { |
| 184 | // Success. Instantiate the module and return the object. |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame^] | 185 | i::Handle<i::JSReceiver> ffi = i::Handle<i::JSObject>::null(); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 186 | if (args.Length() > 1 && args[1]->IsObject()) { |
| 187 | Local<Object> obj = Local<Object>::Cast(args[1]); |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame^] | 188 | ffi = i::Handle<i::JSReceiver>::cast(v8::Utils::OpenHandle(*obj)); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 189 | } |
| 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 Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 201 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 202 | void 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 213 | i::Zone zone(isolate->allocator()); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 214 | 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 229 | InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, |
| 230 | internal::wasm::kAsmJsOrigin); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 231 | } |
| 232 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 233 | void 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 241 | InstantiateModuleCommon(args, buffer.start, buffer.end, &thrower, |
| 242 | internal::wasm::kWasmOrigin); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 243 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 244 | } // namespace |
| 245 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 246 | // TODO(titzer): we use the API to create the function template because the |
| 247 | // internal guts are too ugly to replicate here. |
| 248 | static 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 255 | namespace internal { |
| 256 | static Handle<String> v8_str(Isolate* isolate, const char* str) { |
| 257 | return isolate->factory()->NewStringFromAsciiChecked(str); |
| 258 | } |
| 259 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 260 | static 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 271 | void 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 278 | Handle<String> name = v8_str(isolate, "Wasm"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 279 | 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 288 | InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule); |
| 289 | InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 290 | InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 291 | InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm", |
| 292 | InstantiateModuleFromAsm); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 293 | |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame^] | 294 | { |
| 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 303 | |
| 304 | void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) { |
| 305 | if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) { |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 306 | // 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 Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 326 | } |
| 327 | } |
| 328 | |
| 329 | } // namespace internal |
| 330 | } // namespace v8 |