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 | |
| 37 | |
| 38 | RawBuffer GetRawBufferArgument( |
| 39 | ErrorThrower& thrower, const v8::FunctionCallbackInfo<v8::Value>& args) { |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 40 | if (args.Length() < 1) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 41 | thrower.Error("Argument 0 must be an array buffer"); |
| 42 | return {nullptr, nullptr}; |
| 43 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 44 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 45 | const byte* start = nullptr; |
| 46 | const byte* end = nullptr; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 47 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 48 | if (args[0]->IsArrayBuffer()) { |
| 49 | // A raw array buffer was passed. |
| 50 | Local<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[0]); |
| 51 | ArrayBuffer::Contents contents = buffer->GetContents(); |
| 52 | |
| 53 | start = reinterpret_cast<const byte*>(contents.Data()); |
| 54 | end = start + contents.ByteLength(); |
| 55 | |
| 56 | if (start == nullptr || end == start) { |
| 57 | thrower.Error("ArrayBuffer argument is empty"); |
| 58 | } |
| 59 | } else if (args[0]->IsTypedArray()) { |
| 60 | // A TypedArray was passed. |
| 61 | Local<TypedArray> array = Local<TypedArray>::Cast(args[0]); |
| 62 | Local<ArrayBuffer> buffer = array->Buffer(); |
| 63 | |
| 64 | ArrayBuffer::Contents contents = buffer->GetContents(); |
| 65 | |
| 66 | start = |
| 67 | reinterpret_cast<const byte*>(contents.Data()) + array->ByteOffset(); |
| 68 | end = start + array->ByteLength(); |
| 69 | |
| 70 | if (start == nullptr || end == start) { |
| 71 | thrower.Error("ArrayBuffer argument is empty"); |
| 72 | } |
| 73 | } else { |
| 74 | thrower.Error("Argument 0 must be an ArrayBuffer or Uint8Array"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 75 | } |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 76 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 77 | return {start, end}; |
| 78 | } |
| 79 | |
| 80 | |
| 81 | void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 82 | HandleScope scope(args.GetIsolate()); |
| 83 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 84 | ErrorThrower thrower(isolate, "WASM.verifyModule()"); |
| 85 | |
| 86 | RawBuffer buffer = GetRawBufferArgument(thrower, args); |
| 87 | if (thrower.error()) return; |
| 88 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 89 | i::Zone zone(isolate->allocator()); |
| 90 | internal::wasm::ModuleResult result = |
| 91 | internal::wasm::DecodeWasmModule(isolate, &zone, buffer.start, buffer.end, |
| 92 | true, internal::wasm::kWasmOrigin); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 93 | |
| 94 | if (result.failed()) { |
| 95 | thrower.Failed("", result); |
| 96 | } |
| 97 | |
| 98 | if (result.val) delete result.val; |
| 99 | } |
| 100 | |
| 101 | |
| 102 | void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 103 | HandleScope scope(args.GetIsolate()); |
| 104 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 105 | ErrorThrower thrower(isolate, "WASM.verifyFunction()"); |
| 106 | |
| 107 | RawBuffer buffer = GetRawBufferArgument(thrower, args); |
| 108 | if (thrower.error()) return; |
| 109 | |
| 110 | internal::wasm::FunctionResult result; |
| 111 | { |
| 112 | // Verification of a single function shouldn't allocate. |
| 113 | i::DisallowHeapAllocation no_allocation; |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 114 | i::Zone zone(isolate->allocator()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 115 | result = internal::wasm::DecodeWasmFunction(isolate, &zone, nullptr, |
| 116 | buffer.start, buffer.end); |
| 117 | } |
| 118 | |
| 119 | if (result.failed()) { |
| 120 | thrower.Failed("", result); |
| 121 | } |
| 122 | |
| 123 | if (result.val) delete result.val; |
| 124 | } |
| 125 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 126 | v8::internal::wasm::WasmModuleIndex* TranslateAsmModule( |
| 127 | i::ParseInfo* info, i::Handle<i::Object> foreign, ErrorThrower* thrower) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 128 | info->set_global(); |
| 129 | info->set_lazy(false); |
| 130 | info->set_allow_lazy_parsing(false); |
| 131 | info->set_toplevel(true); |
| 132 | |
| 133 | if (!i::Compiler::ParseAndAnalyze(info)) { |
| 134 | return nullptr; |
| 135 | } |
| 136 | |
| 137 | info->set_literal( |
| 138 | info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun()); |
| 139 | |
| 140 | v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()), |
| 141 | info->literal()); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 142 | if (i::FLAG_enable_simd_asmjs) { |
| 143 | typer.set_allow_simd(true); |
| 144 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 145 | if (!typer.Validate()) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 146 | thrower->Error("Asm.js validation failed: %s", typer.error_message()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 147 | return nullptr; |
| 148 | } |
| 149 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 150 | auto module = |
| 151 | v8::internal::wasm::AsmWasmBuilder(info->isolate(), info->zone(), |
| 152 | info->literal(), foreign, &typer) |
| 153 | .Run(); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 154 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 155 | return module; |
| 156 | } |
| 157 | |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 158 | void InstantiateModuleCommon(const v8::FunctionCallbackInfo<v8::Value>& args, |
| 159 | const byte* start, const byte* end, |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 160 | ErrorThrower* thrower, |
| 161 | internal::wasm::ModuleOrigin origin) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 162 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 163 | |
| 164 | i::Handle<i::JSArrayBuffer> memory = i::Handle<i::JSArrayBuffer>::null(); |
| 165 | if (args.Length() > 2 && args[2]->IsArrayBuffer()) { |
| 166 | Local<Object> obj = Local<Object>::Cast(args[2]); |
| 167 | i::Handle<i::Object> mem_obj = v8::Utils::OpenHandle(*obj); |
| 168 | memory = i::Handle<i::JSArrayBuffer>(i::JSArrayBuffer::cast(*mem_obj)); |
| 169 | } |
| 170 | |
| 171 | // Decode but avoid a redundant pass over function bodies for verification. |
| 172 | // Verification will happen during compilation. |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 173 | i::Zone zone(isolate->allocator()); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 174 | internal::wasm::ModuleResult result = internal::wasm::DecodeWasmModule( |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 175 | isolate, &zone, start, end, false, origin); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 176 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 177 | if (result.failed() && origin == internal::wasm::kAsmJsOrigin) { |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 178 | thrower->Error("Asm.js converted module failed to decode"); |
| 179 | } else if (result.failed()) { |
| 180 | thrower->Failed("", result); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 181 | } else { |
| 182 | // Success. Instantiate the module and return the object. |
| 183 | i::Handle<i::JSObject> ffi = i::Handle<i::JSObject>::null(); |
| 184 | if (args.Length() > 1 && args[1]->IsObject()) { |
| 185 | Local<Object> obj = Local<Object>::Cast(args[1]); |
| 186 | ffi = i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); |
| 187 | } |
| 188 | |
| 189 | i::MaybeHandle<i::JSObject> object = |
| 190 | result.val->Instantiate(isolate, ffi, memory); |
| 191 | |
| 192 | if (!object.is_null()) { |
| 193 | args.GetReturnValue().Set(v8::Utils::ToLocal(object.ToHandleChecked())); |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | if (result.val) delete result.val; |
| 198 | } |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 199 | |
| 200 | |
| 201 | void InstantiateModuleFromAsm(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| 202 | HandleScope scope(args.GetIsolate()); |
| 203 | i::Isolate* isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate()); |
| 204 | ErrorThrower thrower(isolate, "WASM.instantiateModuleFromAsm()"); |
| 205 | |
| 206 | if (!args[0]->IsString()) { |
| 207 | thrower.Error("Asm module text should be a string"); |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | i::Factory* factory = isolate->factory(); |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 212 | i::Zone zone(isolate->allocator()); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 213 | Local<String> source = Local<String>::Cast(args[0]); |
| 214 | i::Handle<i::Script> script = factory->NewScript(Utils::OpenHandle(*source)); |
| 215 | i::ParseInfo info(&zone, script); |
| 216 | |
| 217 | i::Handle<i::Object> foreign; |
| 218 | if (args.Length() > 1 && args[1]->IsObject()) { |
| 219 | Local<Object> local_foreign = Local<Object>::Cast(args[1]); |
| 220 | foreign = v8::Utils::OpenHandle(*local_foreign); |
| 221 | } |
| 222 | |
| 223 | auto module = TranslateAsmModule(&info, foreign, &thrower); |
| 224 | if (module == nullptr) { |
| 225 | return; |
| 226 | } |
| 227 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 228 | InstantiateModuleCommon(args, module->Begin(), module->End(), &thrower, |
| 229 | internal::wasm::kAsmJsOrigin); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | |
| 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 | |
| 246 | |
| 247 | // TODO(titzer): we use the API to create the function template because the |
| 248 | // internal guts are too ugly to replicate here. |
| 249 | static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, |
| 250 | FunctionCallback func) { |
| 251 | Isolate* isolate = reinterpret_cast<Isolate*>(i_isolate); |
| 252 | Local<FunctionTemplate> local = FunctionTemplate::New(isolate, func); |
| 253 | return v8::Utils::OpenHandle(*local); |
| 254 | } |
| 255 | |
| 256 | |
| 257 | namespace internal { |
| 258 | static Handle<String> v8_str(Isolate* isolate, const char* str) { |
| 259 | return isolate->factory()->NewStringFromAsciiChecked(str); |
| 260 | } |
| 261 | |
| 262 | |
| 263 | static void InstallFunc(Isolate* isolate, Handle<JSObject> object, |
| 264 | const char* str, FunctionCallback func) { |
| 265 | Handle<String> name = v8_str(isolate, str); |
| 266 | Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); |
| 267 | Handle<JSFunction> function = |
| 268 | ApiNatives::InstantiateFunction(temp).ToHandleChecked(); |
| 269 | PropertyAttributes attributes = |
| 270 | static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY); |
| 271 | JSObject::AddProperty(object, name, function, attributes); |
| 272 | } |
| 273 | |
| 274 | |
| 275 | void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { |
| 276 | // Setup wasm function map. |
| 277 | Handle<Context> context(global->native_context(), isolate); |
| 278 | InstallWasmFunctionMap(isolate, context); |
| 279 | |
| 280 | // Bind the WASM object. |
| 281 | Factory* factory = isolate->factory(); |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 282 | Handle<String> name = v8_str(isolate, "Wasm"); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 283 | Handle<JSFunction> cons = factory->NewFunction(name); |
| 284 | JSFunction::SetInstancePrototype( |
| 285 | cons, Handle<Object>(context->initial_object_prototype(), isolate)); |
| 286 | cons->shared()->set_instance_class_name(*name); |
| 287 | Handle<JSObject> wasm_object = factory->NewJSObject(cons, TENURED); |
| 288 | PropertyAttributes attributes = static_cast<PropertyAttributes>(DONT_ENUM); |
| 289 | JSObject::AddProperty(global, name, wasm_object, attributes); |
| 290 | |
| 291 | // Install functions on the WASM object. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 292 | InstallFunc(isolate, wasm_object, "verifyModule", VerifyModule); |
| 293 | InstallFunc(isolate, wasm_object, "verifyFunction", VerifyFunction); |
Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 294 | InstallFunc(isolate, wasm_object, "instantiateModule", InstantiateModule); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 295 | InstallFunc(isolate, wasm_object, "instantiateModuleFromAsm", |
| 296 | InstantiateModuleFromAsm); |
| 297 | } |
| 298 | |
| 299 | |
| 300 | void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) { |
| 301 | if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) { |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame^] | 302 | // TODO(titzer): Move this to bootstrapper.cc?? |
| 303 | // TODO(titzer): Also make one for strict mode functions? |
| 304 | Handle<Map> prev_map = Handle<Map>(context->sloppy_function_map(), isolate); |
| 305 | |
| 306 | InstanceType instance_type = prev_map->instance_type(); |
| 307 | int internal_fields = JSObject::GetInternalFieldCount(*prev_map); |
| 308 | CHECK_EQ(0, internal_fields); |
| 309 | int pre_allocated = |
| 310 | prev_map->GetInObjectProperties() - prev_map->unused_property_fields(); |
| 311 | int instance_size; |
| 312 | int in_object_properties; |
| 313 | JSFunction::CalculateInstanceSizeHelper(instance_type, internal_fields + 1, |
| 314 | 0, &instance_size, |
| 315 | &in_object_properties); |
| 316 | |
| 317 | int unused_property_fields = in_object_properties - pre_allocated; |
| 318 | Handle<Map> map = Map::CopyInitialMap( |
| 319 | prev_map, instance_size, in_object_properties, unused_property_fields); |
| 320 | |
| 321 | context->set_wasm_function_map(*map); |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 322 | } |
| 323 | } |
| 324 | |
| 325 | } // namespace internal |
| 326 | } // namespace v8 |