Index: src/wasm/wasm-js.cc |
diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc |
index ff974cb8cd13d5666318048bee15acf84a087667..7e0e53dd755806737fa2ee137ecbb4f48adf6284 100644 |
--- a/src/wasm/wasm-js.cc |
+++ b/src/wasm/wasm-js.cc |
@@ -29,6 +29,15 @@ using v8::internal::wasm::ErrorThrower; |
namespace v8 { |
namespace { |
+i::Handle<i::String> v8_str(i::Isolate* isolate, const char* str) { |
+ return isolate->factory()->NewStringFromAsciiChecked(str); |
+} |
+ |
+Local<String> v8_str(Isolate* isolate, const char* str) { |
+ return Utils::ToLocal(v8_str(reinterpret_cast<i::Isolate*>(isolate), str)); |
+} |
+ |
+ |
struct RawBuffer { |
const byte* start; |
const byte* end; |
@@ -78,7 +87,7 @@ void VerifyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { |
ErrorThrower thrower(isolate, "Wasm.verifyModule()"); |
if (args.Length() < 1) { |
- thrower.Error("Argument 0 must be a buffer source"); |
+ thrower.TypeError("Argument 0 must be a buffer source"); |
return; |
} |
RawBuffer buffer = GetRawBufferSource(args[0], &thrower); |
@@ -102,7 +111,7 @@ void VerifyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { |
ErrorThrower thrower(isolate, "Wasm.verifyFunction()"); |
if (args.Length() < 1) { |
- thrower.Error("Argument 0 must be a buffer source"); |
+ thrower.TypeError("Argument 0 must be a buffer source"); |
return; |
} |
RawBuffer buffer = GetRawBufferSource(args[0], &thrower); |
@@ -167,7 +176,7 @@ void InstantiateModule(const v8::FunctionCallbackInfo<v8::Value>& args) { |
ErrorThrower thrower(isolate, "Wasm.instantiateModule()"); |
if (args.Length() < 1) { |
- thrower.Error("Argument 0 must be a buffer source"); |
+ thrower.TypeError("Argument 0 must be a buffer source"); |
return; |
} |
RawBuffer buffer = GetRawBufferSource(args[0], &thrower); |
@@ -191,6 +200,20 @@ static i::MaybeHandle<i::JSObject> CreateModuleObject( |
i::wasm::ModuleOrigin::kWasmOrigin); |
} |
+bool BrandCheck( |
+ Isolate* isolate, i::Handle<i::Object> value, i::Handle<i::Symbol> sym, |
+ const char* msg) { |
+ if (value->IsJSObject()) { |
+ i::Handle<i::JSObject> object = i::Handle<i::JSObject>::cast(value); |
+ Maybe<bool> has_brand = i::JSObject::HasOwnProperty(object, sym); |
+ if (has_brand.IsNothing()) return false; |
+ if (has_brand.ToChecked()) return true; |
+ } |
+ v8::Local<v8::Value> e = v8::Exception::TypeError(v8_str(isolate, msg)); |
+ isolate->ThrowException(e); |
+ return false; |
+} |
+ |
void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) { |
v8::Isolate* isolate = args.GetIsolate(); |
HandleScope scope(isolate); |
@@ -198,7 +221,7 @@ void WebAssemblyCompile(const v8::FunctionCallbackInfo<v8::Value>& args) { |
"WebAssembly.compile()"); |
if (args.Length() < 1) { |
- thrower.Error("Argument 0 must be a buffer source"); |
+ thrower.TypeError("Argument 0 must be a buffer source"); |
return; |
} |
i::MaybeHandle<i::JSObject> module_obj = |
@@ -223,7 +246,7 @@ void WebAssemblyModule(const v8::FunctionCallbackInfo<v8::Value>& args) { |
"WebAssembly.Module()"); |
if (args.Length() < 1) { |
- thrower.Error("Argument 0 must be a buffer source"); |
+ thrower.TypeError("Argument 0 must be a buffer source"); |
return; |
} |
i::MaybeHandle<i::JSObject> module_obj = |
@@ -242,28 +265,26 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) { |
ErrorThrower thrower(i_isolate, "WebAssembly.Instance()"); |
if (args.Length() < 1) { |
- thrower.Error( |
- "Argument 0 must be provided, and must be a WebAssembly.Module object"); |
+ thrower.TypeError("Argument 0 must be a WebAssembly.Module"); |
return; |
} |
Local<Context> context = isolate->GetCurrentContext(); |
i::Handle<i::Context> i_context = Utils::OpenHandle(*context); |
- i::Handle<i::Symbol> module_sym(i_context->wasm_module_sym()); |
- i::MaybeHandle<i::Object> source = |
- i::Object::GetProperty(Utils::OpenHandle(*args[0]), module_sym); |
- if (source.is_null() || source.ToHandleChecked()->IsUndefined(i_isolate)) { |
- thrower.Error("Argument 0 must be a WebAssembly.Module"); |
+ if (!BrandCheck(isolate, Utils::OpenHandle(*args[0]), |
+ i::Handle<i::Symbol>(i_context->wasm_module_sym()), |
+ "Argument 0 must be a WebAssembly.Module")) { |
return; |
} |
+ |
Local<Object> obj = Local<Object>::Cast(args[0]); |
i::Handle<i::JSObject> module_obj = |
i::Handle<i::JSObject>::cast(v8::Utils::OpenHandle(*obj)); |
if (module_obj->GetInternalFieldCount() < 1 || |
!module_obj->GetInternalField(0)->IsFixedArray()) { |
- thrower.Error("Argument 0 is an invalid WebAssembly.Module"); |
+ thrower.TypeError("Argument 0 is an invalid WebAssembly.Module"); |
return; |
} |
@@ -288,8 +309,237 @@ void WebAssemblyInstance(const v8::FunctionCallbackInfo<v8::Value>& args) { |
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); |
return_value.Set(Utils::ToLocal(instance.ToHandleChecked())); |
} |
+ |
+void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ v8::Isolate* isolate = args.GetIsolate(); |
+ HandleScope scope(isolate); |
+ ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), |
+ "WebAssembly.Module()"); |
+ |
+ if (args.Length() < 1 || !args[0]->IsObject()) { |
+ thrower.TypeError("Argument 0 must be a table descriptor"); |
+ return; |
+ } |
+ Local<Context> context = isolate->GetCurrentContext(); |
+ Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); |
+ |
+ // The descriptor's 'element'. |
+ { |
+ v8::MaybeLocal<v8::Value> maybe = |
+ descriptor->Get(context, v8_str(isolate, "element")); |
+ v8::Local<v8::Value> value; |
+ if (!maybe.ToLocal(&value)) return; |
+ v8::Local<v8::String> string; |
+ if (!value->ToString(context).ToLocal(&string)) return; |
+ bool equal; |
+ if (!string->Equals(context, v8_str(isolate, "anyfunc")).To(&equal)) return; |
+ if (!equal) { |
+ thrower.TypeError("Descriptor property 'element' must be 'anyfunc'"); |
+ return; |
+ } |
+ } |
+ |
+ // The descriptor's 'initial'. |
+ int initial; |
+ { |
+ v8::MaybeLocal<v8::Value> maybe = |
+ descriptor->Get(context, v8_str(isolate, "initial")); |
+ v8::Local<v8::Value> value; |
+ if (!maybe.ToLocal(&value)) return; |
+ int64_t number; |
+ if (!value->IntegerValue(context).To(&number)) return; |
+ if (number < 0) { |
+ thrower.RangeError("Descriptor property 'initial' must not be negative"); |
+ return; |
+ } |
+ initial = static_cast<int>(number); |
+ if (initial != number) { |
+ thrower.RangeError("Descriptor property 'initial' too large"); |
+ return; |
+ } |
+ } |
+ |
+ // The descriptor's 'maximum'. |
+ Maybe<int> maximum = Nothing<int>(); |
+ { |
+ v8::MaybeLocal<v8::Value> maybe = |
+ descriptor->Get(context, v8_str(isolate, "maximum")); |
+ v8::Local<v8::Value> value; |
+ if (maybe.ToLocal(&value) && !value->IsUndefined()) { |
+ int64_t number; |
+ if (!value->IntegerValue(context).To(&number)) return; |
+ if (number < 0) { |
+ thrower.RangeError( |
+ "Descriptor property 'maximum' must not be negative"); |
+ return; |
+ } |
+ int num = static_cast<int>(number); |
+ if (num != number) { |
+ thrower.RangeError("Descriptor property 'maximum' too large"); |
+ return; |
+ } |
+ if (num < initial) { |
+ thrower.RangeError( |
+ "Descriptor property 'maximum' must not be smaller than 'initial'"); |
+ return; |
+ } |
+ maximum = Just(num); |
+ } |
+ } |
+ |
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
+ i::Handle<i::JSFunction> table_cons( |
+ i_isolate->native_context()->wasm_table_constructor()); |
+ i::Handle<i::JSObject> table_obj = |
+ i_isolate->factory()->NewJSObject(table_cons); |
+ i::Handle<i::FixedArray> fixed_array = |
+ i_isolate->factory()->NewFixedArray(initial); |
+ i::Object* null = i_isolate->heap()->null_value(); |
+ for (int i = 0; i < initial; ++i) fixed_array->set(i, null); |
+ table_obj->SetInternalField(0, *fixed_array); |
+ table_obj->SetInternalField(1, |
+ maximum.IsNothing() |
+ ? static_cast<i::Object*>(i_isolate->heap()->undefined_value()) |
+ : static_cast<i::Object*>(i::Smi::FromInt(maximum.ToChecked()))); |
+ i::Handle<i::Symbol> table_sym(i_isolate->native_context()->wasm_table_sym()); |
+ i::Object::SetProperty(table_obj, table_sym, table_obj, i::STRICT).Check(); |
+ |
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); |
+ return_value.Set(Utils::ToLocal(table_obj)); |
+} |
+ |
+void WebAssemblyMemory(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ v8::Isolate* isolate = args.GetIsolate(); |
+ HandleScope scope(isolate); |
+ ErrorThrower thrower(reinterpret_cast<i::Isolate*>(isolate), |
+ "WebAssembly.Module()"); |
+ |
+ if (args.Length() < 1 || !args[0]->IsObject()) { |
+ thrower.TypeError("Argument 0 must be a table descriptor"); |
+ return; |
+ } |
+ Local<Context> context = isolate->GetCurrentContext(); |
+ Local<v8::Object> descriptor = args[0]->ToObject(context).ToLocalChecked(); |
+ |
+ // The descriptor's 'initial'. |
+ int initial; |
+ { |
+ v8::MaybeLocal<v8::Value> maybe = |
+ descriptor->Get(context, v8_str(isolate, "initial")); |
+ v8::Local<v8::Value> value; |
+ if (!maybe.ToLocal(&value)) return; |
+ int64_t number; |
+ if (!value->IntegerValue(context).To(&number)) return; |
+ if (number < 0) { |
+ thrower.RangeError("Descriptor property 'inital' must not be negative"); |
+ return; |
+ } |
+ initial = static_cast<int>(number); |
+ if (initial != number || initial > 65536) { |
+ thrower.RangeError("Descriptor property 'initial' " |
+ "must not be larger than 65536 (4GiB)"); |
+ return; |
+ } |
+ } |
+ |
+ // The descriptor's 'maximum'. |
+ Maybe<int> maximum = Nothing<int>(); |
+ { |
+ v8::MaybeLocal<v8::Value> maybe = |
+ descriptor->Get(context, v8_str(isolate, "maximum")); |
+ v8::Local<v8::Value> value; |
+ if (maybe.ToLocal(&value) && !value->IsUndefined()) { |
+ int64_t number; |
+ if (!value->IntegerValue(context).To(&number)) return; |
+ if (number < 0) { |
+ thrower.RangeError( |
+ "Descriptor property 'maximum' must not be negative"); |
+ return; |
+ } |
+ int num = static_cast<int>(number); |
+ if (num != number || initial > 65536) { |
+ thrower.RangeError( |
+ "Descriptor property 'maximum' must not be larger than 65536 (4GiB)"); |
+ return; |
+ } |
+ if (num < initial) { |
+ thrower.RangeError( |
+ "Descriptor property 'maximum' must not be smaller than 'initial'"); |
+ return; |
+ } |
+ maximum = Just(num); |
+ } |
+ } |
+ |
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
+ i::Handle<i::JSFunction> memory_cons( |
+ i_isolate->native_context()->wasm_memory_constructor()); |
+ i::Handle<i::JSObject> memory_obj = |
+ i_isolate->factory()->NewJSObject(memory_cons); |
+ i::Handle<i::JSArrayBuffer> buffer = |
+ i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); |
+ size_t size = |
+ static_cast<size_t>(i::wasm::WasmModule::kPageSize) * |
+ static_cast<size_t>(initial); |
+ i::JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, size); |
+ memory_obj->SetInternalField(0, *buffer); |
+ memory_obj->SetInternalField(1, |
+ maximum.IsNothing() |
+ ? static_cast<i::Object*>(i_isolate->heap()->undefined_value()) |
+ : static_cast<i::Object*>(i::Smi::FromInt(maximum.ToChecked()))); |
+ i::Handle<i::Symbol> memory_sym( |
+ i_isolate->native_context()->wasm_memory_sym()); |
+ i::Object::SetProperty(memory_obj, memory_sym, memory_obj, i::STRICT).Check(); |
+ |
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); |
+ return_value.Set(Utils::ToLocal(memory_obj)); |
+} |
+ |
+void WebAssemblyTableGetLength( |
+ const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ // TODO(rossberg) |
+} |
+ |
+void WebAssemblyTableGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ // TODO(rossberg) |
+} |
+ |
+void WebAssemblyTableGet(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ // TODO(rossberg) |
+} |
+ |
+void WebAssemblyTableSet(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ // TODO(rossberg) |
+} |
+ |
+void WebAssemblyMemoryGrow(const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ // TODO(rossberg) |
+} |
+ |
+void WebAssemblyMemoryGetBuffer( |
+ const v8::FunctionCallbackInfo<v8::Value>& args) { |
+ v8::Isolate* isolate = args.GetIsolate(); |
+ Local<Context> context = isolate->GetCurrentContext(); |
+ i::Handle<i::Context> i_context = Utils::OpenHandle(*context); |
+ |
+ if (!BrandCheck(isolate, Utils::OpenHandle(*args.This()), |
+ i::Handle<i::Symbol>(i_context->wasm_memory_sym()), |
+ "Receiver is not a WebAssembly.Memory")) { |
+ return; |
+ } |
+ |
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
+ i::Handle<i::JSObject> receiver = |
+ i::Handle<i::JSObject>::cast(Utils::OpenHandle(*args.This())); |
+ i::Handle<i::Object> buffer(receiver->GetInternalField(0), i_isolate); |
+ DCHECK(buffer->IsJSArrayBuffer()); |
+ |
+ v8::ReturnValue<v8::Value> return_value = args.GetReturnValue(); |
+ return_value.Set(Utils::ToLocal(buffer)); |
+} |
} // namespace |
+ |
// TODO(titzer): we use the API to create the function template because the |
// internal guts are too ugly to replicate here. |
static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, |
@@ -300,11 +550,7 @@ static i::Handle<i::FunctionTemplateInfo> NewTemplate(i::Isolate* i_isolate, |
} |
namespace internal { |
-static Handle<String> v8_str(Isolate* isolate, const char* str) { |
- return isolate->factory()->NewStringFromAsciiChecked(str); |
-} |
- |
-static Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, |
+Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, |
const char* str, FunctionCallback func) { |
Handle<String> name = v8_str(isolate, str); |
Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); |
@@ -316,21 +562,41 @@ static Handle<JSFunction> InstallFunc(Isolate* isolate, Handle<JSObject> object, |
return function; |
} |
+Handle<JSFunction> InstallGetter( |
+ Isolate* isolate, Handle<JSObject> object, |
+ const char* str, FunctionCallback func) { |
+ Handle<String> name = v8_str(isolate, str); |
+ Handle<FunctionTemplateInfo> temp = NewTemplate(isolate, func); |
+ Handle<JSFunction> function = |
+ ApiNatives::InstantiateFunction(temp).ToHandleChecked(); |
+ v8::PropertyAttribute attributes = |
+ static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly); |
+ Utils::ToLocal(object)->SetAccessorProperty( |
+ Utils::ToLocal(name), Utils::ToLocal(function), |
+ Local<Function>(), attributes); |
+ return function; |
+} |
+ |
void WasmJs::SetupIsolateForWasm(Isolate* isolate) { |
- InstallWasmFunctionMap(isolate, isolate->native_context()); |
- InstallWasmModuleSymbol(isolate, isolate->global_object(), |
+ InstallWasmMaps(isolate, isolate->native_context()); |
+ InstallWasmConstructors(isolate, isolate->global_object(), |
isolate->native_context()); |
} |
-void WasmJs::InstallWasmModuleSymbol(Isolate* isolate, |
+void WasmJs::InstallWasmConstructors(Isolate* isolate, |
Handle<JSGlobalObject> global, |
Handle<Context> context) { |
Factory* factory = isolate->factory(); |
+ |
// Create private symbols. |
Handle<Symbol> module_sym = factory->NewPrivateSymbol(); |
Handle<Symbol> instance_sym = factory->NewPrivateSymbol(); |
+ Handle<Symbol> table_sym = factory->NewPrivateSymbol(); |
+ Handle<Symbol> memory_sym = factory->NewPrivateSymbol(); |
context->set_wasm_module_sym(*module_sym); |
context->set_wasm_instance_sym(*instance_sym); |
+ context->set_wasm_table_sym(*table_sym); |
+ context->set_wasm_memory_sym(*memory_sym); |
// Bind the WebAssembly object. |
Handle<String> name = v8_str(isolate, "WebAssembly"); |
@@ -348,13 +614,50 @@ void WasmJs::InstallWasmModuleSymbol(Isolate* isolate, |
InstallFunc(isolate, wasm_object, "Module", WebAssemblyModule); |
Handle<JSFunction> instance_constructor = |
InstallFunc(isolate, wasm_object, "Instance", WebAssemblyInstance); |
- i::Handle<i::Map> map = isolate->factory()->NewMap( |
- i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize); |
- module_constructor->set_prototype_or_initial_map(*map); |
- map->SetConstructor(*module_constructor); |
- |
+ Handle<JSFunction> table_constructor = |
+ InstallFunc(isolate, wasm_object, "Table", WebAssemblyTable); |
+ Handle<JSFunction> memory_constructor = |
+ InstallFunc(isolate, wasm_object, "Memory", WebAssemblyMemory); |
context->set_wasm_module_constructor(*module_constructor); |
context->set_wasm_instance_constructor(*instance_constructor); |
+ context->set_wasm_table_constructor(*table_constructor); |
+ context->set_wasm_memory_constructor(*memory_constructor); |
+ |
+ // Set up prototypes and instance maps. |
+ Handle<JSObject> module_proto = |
+ factory->NewJSObject(module_constructor, TENURED); |
+ i::Handle<i::Map> map = isolate->factory()->NewMap( |
+ i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + i::kPointerSize); |
+ JSFunction::SetInitialMap(module_constructor, map, module_proto); |
+ |
+ Handle<JSObject> table_proto = |
+ factory->NewJSObject(table_constructor, TENURED); |
+ map = isolate->factory()->NewMap( |
+ i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 2*i::kPointerSize); |
+ JSFunction::SetInitialMap(table_constructor, map, table_proto); |
+ |
+ Handle<JSObject> memory_proto = |
+ factory->NewJSObject(memory_constructor, TENURED); |
+ map = isolate->factory()->NewMap( |
+ i::JS_OBJECT_TYPE, i::JSObject::kHeaderSize + 2*i::kPointerSize); |
+ JSFunction::SetInitialMap(memory_constructor, map, memory_proto); |
+ |
+ // Add prototype properties. |
+ JSObject::AddProperty(module_proto, isolate->factory()->constructor_string(), |
+ module_constructor, DONT_ENUM); |
+ |
+ JSObject::AddProperty(table_proto, isolate->factory()->constructor_string(), |
+ table_constructor, DONT_ENUM); |
+ |
+ InstallGetter(isolate, table_proto, "length", WebAssemblyTableGetLength); |
+ InstallFunc(isolate, table_proto, "grow", WebAssemblyTableGrow); |
+ InstallFunc(isolate, table_proto, "get", WebAssemblyTableGet); |
+ InstallFunc(isolate, table_proto, "set", WebAssemblyTableSet); |
+ |
+ JSObject::AddProperty(memory_proto, isolate->factory()->constructor_string(), |
+ memory_constructor, DONT_ENUM); |
+ InstallFunc(isolate, memory_proto, "grow", WebAssemblyMemoryGrow); |
+ InstallGetter(isolate, memory_proto, "buffer", WebAssemblyMemoryGetBuffer); |
} |
void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { |
@@ -366,7 +669,7 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { |
// Setup wasm function map. |
Handle<Context> context(global->native_context(), isolate); |
- InstallWasmFunctionMap(isolate, context); |
+ InstallWasmMaps(isolate, context); |
if (!FLAG_expose_wasm) { |
return; |
@@ -399,10 +702,10 @@ void WasmJs::Install(Isolate* isolate, Handle<JSGlobalObject> global) { |
JSObject::AddProperty(wasm_object, name, value, attributes); |
} |
} |
- InstallWasmModuleSymbol(isolate, global, context); |
+ InstallWasmConstructors(isolate, global, context); |
} |
-void WasmJs::InstallWasmFunctionMap(Isolate* isolate, Handle<Context> context) { |
+void WasmJs::InstallWasmMaps(Isolate* isolate, Handle<Context> context) { |
if (!context->get(Context::WASM_FUNCTION_MAP_INDEX)->IsMap()) { |
// TODO(titzer): Move this to bootstrapper.cc?? |
// TODO(titzer): Also make one for strict mode functions? |