Home | History | Annotate | Download | only in wasm
      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) {
     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 
     59 void 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 
     79 void 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 
    104 void 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 
    129 v8::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 
    155 void 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
    188 void 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 
    236 void 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.
    282 static 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 
    290 namespace internal {
    291 static Handle<String> v8_str(Isolate* isolate, const char* str) {
    292   return isolate->factory()->NewStringFromAsciiChecked(str);
    293 }
    294 
    295 
    296 static 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 
    308 void 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 
    335 void 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
    346