[modules] Implement namespace imports.
This implements namespace imports (import * as foo from "bar"), except for the @@iterator property on namespace objects (to be done later). R=adamk@chromium.org BUG=v8:1569 Review-Url: https://codereview.chromium.org/2388153003 Cr-Commit-Position: refs/heads/master@{#40096}
This commit is contained in:
parent
707934cf9e
commit
57ba0ae10e
@ -202,6 +202,68 @@ Handle<AccessorInfo> Accessors::ArrayLengthInfo(
|
||||
attributes);
|
||||
}
|
||||
|
||||
//
|
||||
// Accessors::ModuleNamespaceToStringTag
|
||||
//
|
||||
|
||||
void Accessors::ModuleNamespaceToStringTagGetter(
|
||||
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
|
||||
info.GetReturnValue().Set(
|
||||
Utils::ToLocal(isolate->factory()->NewStringFromAsciiChecked("Module")));
|
||||
}
|
||||
|
||||
Handle<AccessorInfo> Accessors::ModuleNamespaceToStringTagInfo(
|
||||
Isolate* isolate, PropertyAttributes attributes) {
|
||||
Handle<Name> name = isolate->factory()->to_string_tag_symbol();
|
||||
return MakeAccessor(isolate, name, &ModuleNamespaceToStringTagGetter, nullptr,
|
||||
attributes);
|
||||
}
|
||||
|
||||
//
|
||||
// Accessors::ModuleNamespaceEntry
|
||||
//
|
||||
|
||||
void Accessors::ModuleNamespaceEntryGetter(
|
||||
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
|
||||
HandleScope scope(isolate);
|
||||
JSModuleNamespace* holder =
|
||||
JSModuleNamespace::cast(*Utils::OpenHandle(*info.Holder()));
|
||||
Handle<Object> result;
|
||||
if (!holder->GetExport(Handle<String>::cast(Utils::OpenHandle(*name)))
|
||||
.ToHandle(&result)) {
|
||||
isolate->OptionalRescheduleException(false);
|
||||
} else {
|
||||
info.GetReturnValue().Set(Utils::ToLocal(result));
|
||||
}
|
||||
}
|
||||
|
||||
void Accessors::ModuleNamespaceEntrySetter(
|
||||
v8::Local<v8::Name> name, v8::Local<v8::Value> val,
|
||||
const v8::PropertyCallbackInfo<void>& info) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
|
||||
HandleScope scope(isolate);
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<JSModuleNamespace> holder =
|
||||
Handle<JSModuleNamespace>::cast(Utils::OpenHandle(*info.Holder()));
|
||||
|
||||
if (info.ShouldThrowOnError()) {
|
||||
isolate->Throw(*factory->NewTypeError(
|
||||
MessageTemplate::kStrictReadOnlyProperty, Utils::OpenHandle(*name),
|
||||
i::Object::TypeOf(isolate, holder), holder));
|
||||
isolate->OptionalRescheduleException(false);
|
||||
} else {
|
||||
info.GetReturnValue().Set(Utils::ToLocal(factory->ToBoolean(false)));
|
||||
}
|
||||
}
|
||||
|
||||
Handle<AccessorInfo> Accessors::ModuleNamespaceEntryInfo(
|
||||
Isolate* isolate, Handle<String> name, PropertyAttributes attributes) {
|
||||
return MakeAccessor(isolate, name, &ModuleNamespaceEntryGetter,
|
||||
&ModuleNamespaceEntrySetter, attributes);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Accessors::StringLength
|
||||
|
@ -30,6 +30,7 @@ class AccessorInfo;
|
||||
V(FunctionName) \
|
||||
V(FunctionLength) \
|
||||
V(FunctionPrototype) \
|
||||
V(ModuleNamespaceToStringTag) \
|
||||
V(ScriptColumnOffset) \
|
||||
V(ScriptCompilationType) \
|
||||
V(ScriptContextData) \
|
||||
@ -48,10 +49,11 @@ class AccessorInfo;
|
||||
V(StringLength)
|
||||
|
||||
#define ACCESSOR_SETTER_LIST(V) \
|
||||
V(ReconfigureToDataProperty) \
|
||||
V(ArrayLengthSetter) \
|
||||
V(ErrorStackSetter) \
|
||||
V(FunctionPrototypeSetter)
|
||||
V(FunctionPrototypeSetter) \
|
||||
V(ModuleNamespaceEntrySetter) \
|
||||
V(ReconfigureToDataProperty)
|
||||
|
||||
// Accessors contains all predefined proxy accessors.
|
||||
|
||||
@ -74,6 +76,12 @@ class Accessors : public AllStatic {
|
||||
ACCESSOR_SETTER_LIST(ACCESSOR_SETTER_DECLARATION)
|
||||
#undef ACCESSOR_SETTER_DECLARATION
|
||||
|
||||
static void ModuleNamespaceEntryGetter(
|
||||
v8::Local<v8::Name> name,
|
||||
const v8::PropertyCallbackInfo<v8::Value>& info);
|
||||
static Handle<AccessorInfo> ModuleNamespaceEntryInfo(
|
||||
Isolate* isolate, Handle<String> name, PropertyAttributes attributes);
|
||||
|
||||
enum DescriptorId {
|
||||
#define ACCESSOR_INFO_DECLARATION(name) \
|
||||
k##name##Getter, \
|
||||
|
@ -208,6 +208,7 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
|
||||
case JS_DATE_TYPE:
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
case JS_GENERATOR_OBJECT_TYPE:
|
||||
case JS_MODULE_NAMESPACE_TYPE:
|
||||
case JS_ARRAY_BUFFER_TYPE:
|
||||
case JS_ARRAY_TYPE:
|
||||
case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp type.
|
||||
|
@ -2102,6 +2102,25 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
Context::JS_SET_FUN_INDEX);
|
||||
}
|
||||
|
||||
{ // -- J S M o d u l e N a m e s p a c e
|
||||
Handle<Map> map =
|
||||
factory->NewMap(JS_MODULE_NAMESPACE_TYPE, JSModuleNamespace::kSize);
|
||||
Map::SetPrototype(map, isolate->factory()->null_value());
|
||||
native_context()->set_js_module_namespace_map(*map);
|
||||
|
||||
// Install @@toStringTag.
|
||||
PropertyAttributes attribs =
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
|
||||
Handle<AccessorInfo> toStringTag =
|
||||
Accessors::ModuleNamespaceToStringTagInfo(isolate, attribs);
|
||||
AccessorConstantDescriptor d(factory->to_string_tag_symbol(), toStringTag,
|
||||
attribs);
|
||||
Map::EnsureDescriptorSlack(map, 1);
|
||||
map->AppendDescriptor(&d);
|
||||
|
||||
// TODO(neis): Implement and install @@iterator.
|
||||
}
|
||||
|
||||
{ // -- I t e r a t o r R e s u l t
|
||||
Handle<Map> map =
|
||||
factory->NewMap(JS_OBJECT_TYPE, JSIteratorResult::kSize);
|
||||
|
@ -203,6 +203,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
|
||||
case JS_DATE_TYPE:
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
case JS_GENERATOR_OBJECT_TYPE:
|
||||
case JS_MODULE_NAMESPACE_TYPE:
|
||||
case JS_ARRAY_BUFFER_TYPE:
|
||||
case JS_ARRAY_TYPE:
|
||||
case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp type.
|
||||
|
@ -177,6 +177,7 @@ enum ContextLookupFlags {
|
||||
js_array_fast_holey_double_elements_map_index) \
|
||||
V(JS_MAP_FUN_INDEX, JSFunction, js_map_fun) \
|
||||
V(JS_MAP_MAP_INDEX, Map, js_map_map) \
|
||||
V(JS_MODULE_NAMESPACE_MAP, Map, js_module_namespace_map) \
|
||||
V(JS_SET_FUN_INDEX, JSFunction, js_set_fun) \
|
||||
V(JS_SET_MAP_INDEX, Map, js_set_map) \
|
||||
V(JS_WEAK_MAP_FUN_INDEX, JSFunction, js_weak_map_fun) \
|
||||
|
@ -1735,6 +1735,10 @@ void Factory::NewJSArrayStorage(Handle<JSArray> array,
|
||||
array->set_length(Smi::FromInt(length));
|
||||
}
|
||||
|
||||
Handle<JSModuleNamespace> Factory::NewJSModuleNamespace() {
|
||||
Handle<Map> map = isolate()->js_module_namespace_map();
|
||||
return Handle<JSModuleNamespace>::cast(NewJSObjectFromMap(map));
|
||||
}
|
||||
|
||||
Handle<JSGeneratorObject> Factory::NewJSGeneratorObject(
|
||||
Handle<JSFunction> function) {
|
||||
@ -1765,10 +1769,11 @@ Handle<Module> Factory::NewModule(Handle<SharedFunctionInfo> code) {
|
||||
|
||||
Handle<Module> module = Handle<Module>::cast(NewStruct(MODULE_TYPE));
|
||||
module->set_code(*code);
|
||||
module->set_exports(*exports);
|
||||
module->set_requested_modules(*requested_modules);
|
||||
module->set_flags(0);
|
||||
module->set_embedder_data(isolate()->heap()->undefined_value());
|
||||
module->set_exports(*exports);
|
||||
module->set_flags(0);
|
||||
module->set_module_namespace(isolate()->heap()->undefined_value());
|
||||
module->set_requested_modules(*requested_modules);
|
||||
return module;
|
||||
}
|
||||
|
||||
|
@ -486,6 +486,8 @@ class Factory final {
|
||||
|
||||
Handle<JSGeneratorObject> NewJSGeneratorObject(Handle<JSFunction> function);
|
||||
|
||||
Handle<JSModuleNamespace> NewJSModuleNamespace();
|
||||
|
||||
Handle<Module> NewModule(Handle<SharedFunctionInfo> code);
|
||||
|
||||
Handle<JSArrayBuffer> NewJSArrayBuffer(
|
||||
|
@ -107,6 +107,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
|
||||
case JS_ARGUMENTS_TYPE:
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
case JS_GENERATOR_OBJECT_TYPE:
|
||||
case JS_MODULE_NAMESPACE_TYPE:
|
||||
case JS_VALUE_TYPE:
|
||||
case JS_DATE_TYPE:
|
||||
case JS_ARRAY_TYPE:
|
||||
|
@ -678,6 +678,9 @@ void BytecodeGenerator::GenerateBytecodeBody() {
|
||||
// Visit declarations within the function scope.
|
||||
VisitDeclarations(scope()->declarations());
|
||||
|
||||
// Emit initializing assignments for module namespace imports (if any).
|
||||
VisitModuleNamespaceImports();
|
||||
|
||||
// Perform a stack-check before the body.
|
||||
builder()->StackCheck(info()->literal()->start_position());
|
||||
|
||||
@ -877,6 +880,24 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitModuleNamespaceImports() {
|
||||
if (!scope()->is_module_scope()) return;
|
||||
|
||||
RegisterAllocationScope register_scope(this);
|
||||
Register module_request = register_allocator()->NewRegister();
|
||||
|
||||
ModuleDescriptor* descriptor = scope()->AsModuleScope()->module();
|
||||
for (auto entry : descriptor->namespace_imports()) {
|
||||
builder()
|
||||
->LoadLiteral(Smi::FromInt(entry->module_request))
|
||||
.StoreAccumulatorInRegister(module_request)
|
||||
.CallRuntime(Runtime::kGetModuleNamespace, module_request);
|
||||
Variable* var = scope()->LookupLocal(entry->local_name);
|
||||
DCHECK_NOT_NULL(var);
|
||||
VisitVariableAssignment(var, Token::INIT, FeedbackVectorSlot::Invalid());
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitDeclarations(
|
||||
ZoneList<Declaration*>* declarations) {
|
||||
RegisterAllocationScope register_scope(this);
|
||||
|
@ -143,6 +143,7 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
ObjectLiteralProperty* property,
|
||||
Register value_out);
|
||||
void VisitForInAssignment(Expression* expr, FeedbackVectorSlot slot);
|
||||
void VisitModuleNamespaceImports();
|
||||
|
||||
// Visit the header/body of a loop iteration.
|
||||
void VisitIterationHeader(IterationStatement* stmt,
|
||||
|
@ -468,6 +468,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3) {
|
||||
case JS_VALUE_TYPE:
|
||||
case JS_DATE_TYPE:
|
||||
case JS_ARRAY_TYPE:
|
||||
case JS_MODULE_NAMESPACE_TYPE:
|
||||
case JS_TYPED_ARRAY_TYPE:
|
||||
case JS_DATA_VIEW_TYPE:
|
||||
case JS_SET_TYPE:
|
||||
|
@ -140,6 +140,9 @@ void HeapObject::HeapObjectVerify() {
|
||||
case JS_ARRAY_TYPE:
|
||||
JSArray::cast(this)->JSArrayVerify();
|
||||
break;
|
||||
case JS_MODULE_NAMESPACE_TYPE:
|
||||
JSModuleNamespace::cast(this)->JSModuleNamespaceVerify();
|
||||
break;
|
||||
case JS_SET_TYPE:
|
||||
JSSet::cast(this)->JSSetVerify();
|
||||
break;
|
||||
@ -919,7 +922,13 @@ void PromiseContainer::PromiseContainerVerify() {
|
||||
after_debug_event()->ObjectVerify();
|
||||
}
|
||||
|
||||
void JSModuleNamespace::JSModuleNamespaceVerify() {
|
||||
CHECK(IsJSModuleNamespace());
|
||||
module()->ObjectVerify();
|
||||
}
|
||||
|
||||
void Module::ModuleVerify() {
|
||||
Isolate* isolate = GetIsolate();
|
||||
CHECK(IsModule());
|
||||
CHECK(code()->IsSharedFunctionInfo() || code()->IsJSFunction());
|
||||
code()->ObjectVerify();
|
||||
@ -928,6 +937,8 @@ void Module::ModuleVerify() {
|
||||
VerifySmiField(kFlagsOffset);
|
||||
embedder_data()->ObjectVerify();
|
||||
CHECK(shared()->name()->IsSymbol());
|
||||
CHECK(module_namespace()->IsUndefined(isolate) ||
|
||||
module_namespace()->IsJSModuleNamespace());
|
||||
// TODO(neis): Check more.
|
||||
}
|
||||
|
||||
|
@ -3277,6 +3277,7 @@ CAST_ACCESSOR(JSGlobalProxy)
|
||||
CAST_ACCESSOR(JSMap)
|
||||
CAST_ACCESSOR(JSMapIterator)
|
||||
CAST_ACCESSOR(JSMessageObject)
|
||||
CAST_ACCESSOR(JSModuleNamespace)
|
||||
CAST_ACCESSOR(JSObject)
|
||||
CAST_ACCESSOR(JSProxy)
|
||||
CAST_ACCESSOR(JSReceiver)
|
||||
@ -5706,8 +5707,11 @@ BOOL_ACCESSORS(PrototypeInfo, bit_field, should_be_fast_map, kShouldBeFastBit)
|
||||
ACCESSORS(ContextExtension, scope_info, ScopeInfo, kScopeInfoOffset)
|
||||
ACCESSORS(ContextExtension, extension, Object, kExtensionOffset)
|
||||
|
||||
ACCESSORS(JSModuleNamespace, module, Module, kModuleOffset)
|
||||
|
||||
ACCESSORS(Module, code, Object, kCodeOffset)
|
||||
ACCESSORS(Module, exports, ObjectHashTable, kExportsOffset)
|
||||
ACCESSORS(Module, module_namespace, HeapObject, kModuleNamespaceOffset)
|
||||
ACCESSORS(Module, requested_modules, FixedArray, kRequestedModulesOffset)
|
||||
SMI_ACCESSORS(Module, flags, kFlagsOffset)
|
||||
BOOL_ACCESSORS(Module, flags, evaluated, kEvaluatedBit)
|
||||
@ -6664,6 +6668,8 @@ bool JSGeneratorObject::is_executing() const {
|
||||
return continuation() == kGeneratorExecuting;
|
||||
}
|
||||
|
||||
TYPE_CHECKER(JSModuleNamespace, JS_MODULE_NAMESPACE_TYPE)
|
||||
|
||||
ACCESSORS(JSValue, value, Object, kValueOffset)
|
||||
|
||||
|
||||
|
184
src/objects.cc
184
src/objects.cc
@ -6556,7 +6556,7 @@ Maybe<bool> JSReceiver::DefineOwnProperty(Isolate* isolate,
|
||||
return JSProxy::DefineOwnProperty(isolate, Handle<JSProxy>::cast(object),
|
||||
key, desc, should_throw);
|
||||
}
|
||||
// TODO(jkummerow): Support Modules (ES6 9.4.6.6)
|
||||
// TODO(neis): Special case for JSModuleNamespace?
|
||||
|
||||
// OrdinaryDefineOwnProperty, by virtue of calling
|
||||
// DefineOwnPropertyIgnoreAttributes, can handle arguments (ES6 9.4.4.2)
|
||||
@ -18218,6 +18218,9 @@ Object* ObjectHashTable::Lookup(Handle<Object> key) {
|
||||
return Lookup(isolate, key, Smi::cast(hash)->value());
|
||||
}
|
||||
|
||||
Object* ObjectHashTable::ValueAt(int entry) {
|
||||
return get(EntryToValueIndex(entry));
|
||||
}
|
||||
|
||||
Object* ObjectHashTable::Lookup(Handle<Object> key, int32_t hash) {
|
||||
return Lookup(GetIsolate(), key, hash);
|
||||
@ -19565,6 +19568,23 @@ bool JSReceiver::HasProxyInPrototype(Isolate* isolate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MaybeHandle<Object> JSModuleNamespace::GetExport(Handle<String> name) {
|
||||
Isolate* isolate = name->GetIsolate();
|
||||
|
||||
Handle<Object> object(module()->exports()->Lookup(name), isolate);
|
||||
if (object->IsTheHole(isolate)) {
|
||||
return isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
Handle<Object> value(Handle<Cell>::cast(object)->value(), isolate);
|
||||
if (value->IsTheHole(isolate)) {
|
||||
THROW_NEW_ERROR(
|
||||
isolate, NewReferenceError(MessageTemplate::kNotDefined, name), Object);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
@ -19596,6 +19616,35 @@ class UnorderedStringSet
|
||||
StringHandleEqual(), zone_allocator<Handle<String>>(zone)) {}
|
||||
};
|
||||
|
||||
class UnorderedModuleSet
|
||||
: public std::unordered_set<Handle<Module>, HandleValueHash<Module>,
|
||||
ModuleHandleEqual,
|
||||
zone_allocator<Handle<Module>>> {
|
||||
public:
|
||||
explicit UnorderedModuleSet(Zone* zone)
|
||||
: std::unordered_set<Handle<Module>, HandleValueHash<Module>,
|
||||
ModuleHandleEqual, zone_allocator<Handle<Module>>>(
|
||||
2 /* bucket count */, HandleValueHash<Module>(),
|
||||
ModuleHandleEqual(), zone_allocator<Handle<Module>>(zone)) {}
|
||||
};
|
||||
|
||||
class UnorderedStringMap
|
||||
: public std::unordered_map<
|
||||
Handle<String>, Handle<Object>, HandleValueHash<String>,
|
||||
StringHandleEqual,
|
||||
zone_allocator<std::pair<const Handle<String>, Handle<Object>>>> {
|
||||
public:
|
||||
explicit UnorderedStringMap(Zone* zone)
|
||||
: std::unordered_map<
|
||||
Handle<String>, Handle<Object>, HandleValueHash<String>,
|
||||
StringHandleEqual,
|
||||
zone_allocator<std::pair<const Handle<String>, Handle<Object>>>>(
|
||||
2 /* bucket count */, HandleValueHash<String>(),
|
||||
StringHandleEqual(),
|
||||
zone_allocator<std::pair<const Handle<String>, Handle<Object>>>(
|
||||
zone)) {}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class Module::ResolveSet
|
||||
@ -19919,5 +19968,138 @@ MaybeHandle<Object> Module::Evaluate(Handle<Module> module) {
|
||||
return Execution::Call(isolate, resume, generator, 0, nullptr);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void FetchStarExports(Handle<Module> module, Zone* zone,
|
||||
UnorderedModuleSet* visited) {
|
||||
DCHECK(module->code()->IsJSFunction()); // Instantiated.
|
||||
|
||||
bool cycle = !visited->insert(module).second;
|
||||
if (cycle) return;
|
||||
|
||||
Isolate* isolate = module->GetIsolate();
|
||||
Handle<ObjectHashTable> exports(module->exports(), isolate);
|
||||
UnorderedStringMap more_exports(zone);
|
||||
|
||||
// TODO(neis): Only allocate more_exports if there are star exports.
|
||||
// Maybe split special_exports into indirect_exports and star_exports.
|
||||
|
||||
Handle<FixedArray> special_exports(module->info()->special_exports(),
|
||||
isolate);
|
||||
for (int i = 0, n = special_exports->length(); i < n; ++i) {
|
||||
Handle<ModuleInfoEntry> entry(
|
||||
ModuleInfoEntry::cast(special_exports->get(i)), isolate);
|
||||
if (!entry->export_name()->IsUndefined(isolate)) {
|
||||
continue; // Indirect export.
|
||||
}
|
||||
|
||||
int module_request = Smi::cast(entry->module_request())->value();
|
||||
Handle<Module> requested_module(
|
||||
Module::cast(module->requested_modules()->get(module_request)),
|
||||
isolate);
|
||||
|
||||
// Recurse.
|
||||
FetchStarExports(requested_module, zone, visited);
|
||||
|
||||
// Collect all of [requested_module]'s exports that must be added to
|
||||
// [module]'s exports (i.e. to [exports]). We record these in
|
||||
// [more_exports]. Ambiguities (conflicting exports) are marked by mapping
|
||||
// the name to undefined instead of a Cell.
|
||||
Handle<ObjectHashTable> requested_exports(requested_module->exports(),
|
||||
isolate);
|
||||
for (int i = 0, n = requested_exports->Capacity(); i < n; ++i) {
|
||||
Handle<Object> key(requested_exports->KeyAt(i), isolate);
|
||||
if (!requested_exports->IsKey(isolate, *key)) continue;
|
||||
Handle<String> name = Handle<String>::cast(key);
|
||||
|
||||
if (name->Equals(isolate->heap()->default_string())) continue;
|
||||
if (!exports->Lookup(name)->IsTheHole(isolate)) continue;
|
||||
|
||||
Handle<Cell> cell(Cell::cast(requested_exports->ValueAt(i)), isolate);
|
||||
auto insert_result = more_exports.insert(std::make_pair(name, cell));
|
||||
if (!insert_result.second) {
|
||||
auto it = insert_result.first;
|
||||
if (*it->second == *cell || it->second->IsUndefined(isolate)) {
|
||||
// We already recorded this mapping before, or the name is already
|
||||
// known to be ambiguous. In either case, there's nothing to do.
|
||||
} else {
|
||||
DCHECK(it->second->IsCell());
|
||||
// Different star exports provide different cells for this name, hence
|
||||
// mark the name as ambiguous.
|
||||
it->second = isolate->factory()->undefined_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy [more_exports] into [exports].
|
||||
for (const auto& elem : more_exports) {
|
||||
if (elem.second->IsUndefined(isolate)) continue; // Ambiguous export.
|
||||
DCHECK(!elem.first->Equals(isolate->heap()->default_string()));
|
||||
DCHECK(elem.second->IsCell());
|
||||
exports = ObjectHashTable::Put(exports, elem.first, elem.second);
|
||||
}
|
||||
module->set_exports(*exports);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Handle<JSModuleNamespace> Module::GetModuleNamespace(Handle<Module> module,
|
||||
int module_request) {
|
||||
Isolate* isolate = module->GetIsolate();
|
||||
Handle<Module> requested_module(
|
||||
Module::cast(module->requested_modules()->get(module_request)), isolate);
|
||||
return Module::GetModuleNamespace(requested_module);
|
||||
}
|
||||
|
||||
Handle<JSModuleNamespace> Module::GetModuleNamespace(Handle<Module> module) {
|
||||
Isolate* isolate = module->GetIsolate();
|
||||
|
||||
Handle<HeapObject> object(module->module_namespace(), isolate);
|
||||
if (!object->IsUndefined(isolate)) {
|
||||
// Namespace object already exists.
|
||||
return Handle<JSModuleNamespace>::cast(object);
|
||||
}
|
||||
|
||||
// Create the namespace object (initially empty).
|
||||
Handle<JSModuleNamespace> ns = isolate->factory()->NewJSModuleNamespace();
|
||||
ns->set_module(*module);
|
||||
module->set_module_namespace(*ns);
|
||||
|
||||
// Collect the export names.
|
||||
Zone zone(isolate->allocator());
|
||||
UnorderedModuleSet visited(&zone);
|
||||
FetchStarExports(module, &zone, &visited);
|
||||
Handle<ObjectHashTable> exports(module->exports(), isolate);
|
||||
ZoneVector<Handle<String>> names(&zone);
|
||||
names.reserve(exports->NumberOfElements());
|
||||
for (int i = 0, n = exports->Capacity(); i < n; ++i) {
|
||||
Handle<Object> key(exports->KeyAt(i), isolate);
|
||||
if (!exports->IsKey(isolate, *key)) continue;
|
||||
DCHECK(exports->ValueAt(i)->IsCell());
|
||||
names.push_back(Handle<String>::cast(key));
|
||||
}
|
||||
DCHECK_EQ(names.size(), exports->NumberOfElements());
|
||||
|
||||
// Sort them alphabetically.
|
||||
struct {
|
||||
bool operator()(Handle<String> a, Handle<String> b) {
|
||||
return String::Compare(a, b) == ComparisonResult::kLessThan;
|
||||
}
|
||||
} StringLess;
|
||||
std::sort(names.begin(), names.end(), StringLess);
|
||||
|
||||
// Create the corresponding properties in the namespace object.
|
||||
PropertyAttributes attr = DONT_DELETE;
|
||||
for (const auto& name : names) {
|
||||
JSObject::SetAccessor(
|
||||
ns, Accessors::ModuleNamespaceEntryInfo(isolate, name, attr))
|
||||
.Check();
|
||||
}
|
||||
JSObject::PreventExtensions(ns, THROW_ON_ERROR).ToChecked();
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -71,6 +71,7 @@
|
||||
// - JSValue
|
||||
// - JSDate
|
||||
// - JSMessageObject
|
||||
// - JSModuleNamespace
|
||||
// - JSProxy
|
||||
// - FixedArrayBase
|
||||
// - ByteArray
|
||||
@ -416,6 +417,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
|
||||
V(JS_ARGUMENTS_TYPE) \
|
||||
V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
|
||||
V(JS_GENERATOR_OBJECT_TYPE) \
|
||||
V(JS_MODULE_NAMESPACE_TYPE) \
|
||||
V(JS_GLOBAL_OBJECT_TYPE) \
|
||||
V(JS_GLOBAL_PROXY_TYPE) \
|
||||
V(JS_API_OBJECT_TYPE) \
|
||||
@ -717,6 +719,7 @@ enum InstanceType {
|
||||
JS_ARGUMENTS_TYPE,
|
||||
JS_CONTEXT_EXTENSION_OBJECT_TYPE,
|
||||
JS_GENERATOR_OBJECT_TYPE,
|
||||
JS_MODULE_NAMESPACE_TYPE,
|
||||
JS_ARRAY_TYPE,
|
||||
JS_ARRAY_BUFFER_TYPE,
|
||||
JS_TYPED_ARRAY_TYPE,
|
||||
@ -886,6 +889,7 @@ class LayoutDescriptor;
|
||||
class LiteralsArray;
|
||||
class LookupIterator;
|
||||
class FieldType;
|
||||
class Module;
|
||||
class ModuleDescriptor;
|
||||
class ModuleInfoEntry;
|
||||
class ModuleInfo;
|
||||
@ -970,6 +974,7 @@ template <class C> inline bool Is(Object* obj);
|
||||
V(JSObject) \
|
||||
V(JSContextExtensionObject) \
|
||||
V(JSGeneratorObject) \
|
||||
V(JSModuleNamespace) \
|
||||
V(Map) \
|
||||
V(DescriptorArray) \
|
||||
V(FrameArray) \
|
||||
@ -3951,6 +3956,9 @@ class ObjectHashTable: public HashTable<ObjectHashTable,
|
||||
Object* Lookup(Handle<Object> key, int32_t hash);
|
||||
Object* Lookup(Isolate* isolate, Handle<Object> key, int32_t hash);
|
||||
|
||||
// Returns the value at entry.
|
||||
Object* ValueAt(int entry);
|
||||
|
||||
// Adds (or overwrites) the value associated with the given key.
|
||||
static Handle<ObjectHashTable> Put(Handle<ObjectHashTable> table,
|
||||
Handle<Object> key,
|
||||
@ -7863,6 +7871,29 @@ class JSGeneratorObject: public JSObject {
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(JSGeneratorObject);
|
||||
};
|
||||
|
||||
// When importing a module namespace (import * as foo from "bar"), a
|
||||
// JSModuleNamespace object (representing module "bar") is created and bound to
|
||||
// the declared variable (foo). A module can have at most one namespace object.
|
||||
class JSModuleNamespace : public JSObject {
|
||||
public:
|
||||
DECLARE_CAST(JSModuleNamespace)
|
||||
DECLARE_VERIFIER(JSModuleNamespace)
|
||||
|
||||
// The actual module whose namespace is being represented.
|
||||
DECL_ACCESSORS(module, Module)
|
||||
|
||||
// Retrieve the value exported by [module] under the given [name]. If there is
|
||||
// no such export, return Just(undefined). If the export is uninitialized,
|
||||
// schedule an exception and return Nothing.
|
||||
MUST_USE_RESULT MaybeHandle<Object> GetExport(Handle<String> name);
|
||||
|
||||
static const int kModuleOffset = JSObject::kHeaderSize;
|
||||
static const int kSize = kModuleOffset + kPointerSize;
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(JSModuleNamespace);
|
||||
};
|
||||
|
||||
// A Module object is a mapping from export names to cells
|
||||
// This is still very much in flux.
|
||||
class Module : public Struct {
|
||||
@ -7871,13 +7902,15 @@ class Module : public Struct {
|
||||
DECLARE_VERIFIER(Module)
|
||||
DECLARE_PRINTER(Module)
|
||||
|
||||
// The code representing this Module, either a
|
||||
// SharedFunctionInfo or a JSFunction depending
|
||||
// on whether it's been instantiated.
|
||||
// The code representing this Module, either a SharedFunctionInfo or a
|
||||
// JSFunction depending on whether it's been instantiated.
|
||||
DECL_ACCESSORS(code, Object)
|
||||
|
||||
DECL_ACCESSORS(exports, ObjectHashTable)
|
||||
|
||||
// The namespace object (or undefined).
|
||||
DECL_ACCESSORS(module_namespace, HeapObject)
|
||||
|
||||
// [[RequestedModules]]: Modules imported or re-exported by this module.
|
||||
// Corresponds 1-to-1 to the module specifier strings in
|
||||
// ModuleInfo::module_requests.
|
||||
@ -7887,7 +7920,7 @@ class Module : public Struct {
|
||||
// are only evaluated a single time.
|
||||
DECL_BOOLEAN_ACCESSORS(evaluated)
|
||||
|
||||
// Storage for [[Evaluated]]
|
||||
// Storage for [[Evaluated]].
|
||||
DECL_INT_ACCESSORS(flags)
|
||||
|
||||
// Embedder-specified data
|
||||
@ -7920,12 +7953,18 @@ class Module : public Struct {
|
||||
static Handle<Object> LoadImport(Handle<Module> module, Handle<String> name,
|
||||
int module_request);
|
||||
|
||||
// Get the namespace object for [module_request] of [module]. If it doesn't
|
||||
// exist yet, it is created.
|
||||
static Handle<JSModuleNamespace> GetModuleNamespace(Handle<Module> module,
|
||||
int module_request);
|
||||
|
||||
static const int kCodeOffset = HeapObject::kHeaderSize;
|
||||
static const int kExportsOffset = kCodeOffset + kPointerSize;
|
||||
static const int kRequestedModulesOffset = kExportsOffset + kPointerSize;
|
||||
static const int kFlagsOffset = kRequestedModulesOffset + kPointerSize;
|
||||
static const int kEmbedderDataOffset = kFlagsOffset + kPointerSize;
|
||||
static const int kSize = kEmbedderDataOffset + kPointerSize;
|
||||
static const int kModuleNamespaceOffset = kEmbedderDataOffset + kPointerSize;
|
||||
static const int kSize = kModuleNamespaceOffset + kPointerSize;
|
||||
|
||||
private:
|
||||
enum { kEvaluatedBit };
|
||||
@ -7934,6 +7973,10 @@ class Module : public Struct {
|
||||
static void CreateIndirectExport(Handle<Module> module, Handle<String> name,
|
||||
Handle<ModuleInfoEntry> entry);
|
||||
|
||||
// Get the namespace object for [module]. If it doesn't exist yet, it is
|
||||
// created.
|
||||
static Handle<JSModuleNamespace> GetModuleNamespace(Handle<Module> module);
|
||||
|
||||
// The [must_resolve] argument indicates whether or not an exception should be
|
||||
// thrown in case the module does not provide an export named [name]
|
||||
// (including when a cycle is detected). An exception is always thrown in the
|
||||
|
@ -960,6 +960,14 @@ RUNTIME_FUNCTION(Runtime_CreateDataProperty) {
|
||||
return *value;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_GetModuleNamespace) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_SMI_ARG_CHECKED(module_request, 0);
|
||||
Handle<Module> module(isolate->context()->module());
|
||||
return *Module::GetModuleNamespace(module, module_request);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_LoadModuleExport) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
@ -972,9 +980,9 @@ RUNTIME_FUNCTION(Runtime_LoadModuleImport) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 2);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Smi, module_request, 1);
|
||||
CONVERT_SMI_ARG_CHECKED(module_request, 1);
|
||||
Handle<Module> module(isolate->context()->module());
|
||||
return *Module::LoadImport(module, name, module_request->value());
|
||||
return *Module::LoadImport(module, name, module_request);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StoreModuleExport) {
|
||||
|
@ -420,6 +420,7 @@ namespace internal {
|
||||
F(CreateIterResultObject, 2, 1) \
|
||||
F(IsAccessCheckNeeded, 1, 1) \
|
||||
F(CreateDataProperty, 3, 1) \
|
||||
F(GetModuleNamespace, 1, 1) \
|
||||
F(LoadModuleExport, 1, 1) \
|
||||
F(LoadModuleImport, 2, 1) \
|
||||
F(StoreModuleExport, 2, 1)
|
||||
|
@ -78,7 +78,7 @@ bytecodes: [
|
||||
/* 15 S> */ B(LdrUndefined), R(0),
|
||||
B(CreateArrayLiteral), U8(0), U8(0), U8(9),
|
||||
B(Star), R(1),
|
||||
B(CallJSRuntime), U8(141), R(0), U8(2),
|
||||
B(CallJSRuntime), U8(142), R(0), U8(2),
|
||||
/* 44 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
|
82
test/mjsunit/modules-namespace1.js
Normal file
82
test/mjsunit/modules-namespace1.js
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
|
||||
let ja = 42;
|
||||
export {ja as yo};
|
||||
export const bla = "blaa";
|
||||
export {foo as foo_again};
|
||||
// See further below for the actual star import that declares "foo".
|
||||
|
||||
// The object itself.
|
||||
assertEquals("object", typeof foo);
|
||||
assertThrows(() => foo = 666, TypeError);
|
||||
assertFalse(Reflect.isExtensible(foo));
|
||||
assertTrue(Reflect.preventExtensions(foo));
|
||||
assertThrows(() => Reflect.apply(foo, {}, []));
|
||||
assertThrows(() => Reflect.construct(foo, {}, []));
|
||||
assertSame(null, Reflect.getPrototypeOf(foo));
|
||||
// TODO(neis): The next one should be False.
|
||||
assertTrue(Reflect.setPrototypeOf(foo, null));
|
||||
assertFalse(Reflect.setPrototypeOf(foo, {}));
|
||||
assertSame(null, Reflect.getPrototypeOf(foo));
|
||||
// TODO(neis): The next one should include @@iterator at the end.
|
||||
assertEquals(
|
||||
["bla", "foo_again", "yo", Symbol.toStringTag],
|
||||
Reflect.ownKeys(foo));
|
||||
|
||||
// Its "yo" property.
|
||||
assertEquals(
|
||||
{value: 42, enumerable: true, configurable: false, writable: true},
|
||||
Reflect.getOwnPropertyDescriptor(foo, "yo"));
|
||||
assertFalse(Reflect.deleteProperty(foo, "yo"));
|
||||
assertTrue(Reflect.has(foo, "yo"));
|
||||
// TODO(neis): The next three should be False.
|
||||
assertTrue(Reflect.set(foo, "yo", true));
|
||||
assertTrue(Reflect.defineProperty(foo, "yo",
|
||||
Reflect.getOwnPropertyDescriptor(foo, "yo")));
|
||||
assertTrue(Reflect.defineProperty(foo, "yo", {}));
|
||||
assertFalse(Reflect.defineProperty(foo, "yo", {get() {return 1}}));
|
||||
assertEquals(42, Reflect.get(foo, "yo"));
|
||||
assertEquals(43, (ja++, foo.yo));
|
||||
|
||||
// Its "foo_again" property.
|
||||
assertSame(foo, foo.foo_again);
|
||||
|
||||
// Its @@toStringTag property.
|
||||
assertTrue(Reflect.has(foo, Symbol.toStringTag));
|
||||
assertEquals("string", typeof Reflect.get(foo, Symbol.toStringTag));
|
||||
assertEquals(
|
||||
{value: "Module", configurable: true, writable: false, enumerable: false},
|
||||
Reflect.getOwnPropertyDescriptor(foo, Symbol.toStringTag));
|
||||
|
||||
// TODO(neis): Its @@iterator property.
|
||||
// assertTrue(Reflect.has(foo, Symbol.iterator));
|
||||
// assertEquals("function", typeof Reflect.get(foo, Symbol.iterator));
|
||||
// assertEquals(["bla", "yo"], [...foo]);
|
||||
// assertThrows(() => (42, foo[Symbol.iterator])(), TypeError);
|
||||
// assertSame(foo[Symbol.iterator]().__proto__,
|
||||
// ([][Symbol.iterator]()).__proto__.__proto__);
|
||||
|
||||
// TODO(neis): Clarify spec w.r.t. other symbols.
|
||||
|
||||
// Nonexistant properties.
|
||||
let nonexistant = ["gaga", 123, Symbol('')];
|
||||
for (let key of nonexistant) {
|
||||
assertSame(undefined, Reflect.getOwnPropertyDescriptor(foo, key));
|
||||
assertTrue(Reflect.deleteProperty(foo, key));
|
||||
assertFalse(Reflect.set(foo, key, true));
|
||||
assertSame(undefined, Reflect.get(foo, key));
|
||||
assertFalse(Reflect.defineProperty(foo, key, {get() {return 1}}));
|
||||
assertFalse(Reflect.has(foo, key));
|
||||
}
|
||||
|
||||
// The actual star import that we are testing. Namespace imports are
|
||||
// initialized before evaluation
|
||||
import * as foo from "modules-namespace1.js";
|
||||
|
||||
// There can be only one namespace object.
|
||||
import * as bar from "modules-namespace1.js";
|
||||
assertSame(foo, bar);
|
22
test/mjsunit/modules-namespace2.js
Normal file
22
test/mjsunit/modules-namespace2.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
|
||||
// TODO(neis): Symbol.iterator
|
||||
assertEquals(
|
||||
["b", "c", "get_a", "ns2", "set_a", "zzz", Symbol.toStringTag],
|
||||
Reflect.ownKeys(ns));
|
||||
// assertEquals(["b", "c", "get_a", "ns2", "set_a", "zzz"], [...ns]);
|
||||
|
||||
import * as foo from "modules-skip-1.js";
|
||||
assertSame(foo.a, ns.b);
|
||||
assertSame(foo.a, ns.c);
|
||||
assertSame(foo.get_a, ns.get_a);
|
||||
assertSame(foo.set_a, ns.set_a);
|
||||
assertEquals(123, ns.zzz);
|
||||
|
||||
assertSame(ns, ns.ns2.ns);
|
||||
import * as ns from "modules-skip-namespace.js";
|
||||
export {ns};
|
11
test/mjsunit/modules-namespace3.js
Normal file
11
test/mjsunit/modules-namespace3.js
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
|
||||
import * as foo from "modules-namespace3.js";
|
||||
export * from "modules-namespace3.js";
|
||||
export var bar;
|
||||
assertEquals(["bar", "default"], Object.getOwnPropertyNames(foo));
|
||||
export default function() {};
|
37
test/mjsunit/modules-namespace4.js
Normal file
37
test/mjsunit/modules-namespace4.js
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// MODULE
|
||||
|
||||
import * as foo from "modules-namespace4.js";
|
||||
|
||||
assertSame(undefined, a);
|
||||
assertThrows(() => b, ReferenceError);
|
||||
assertThrows(() => c, ReferenceError);
|
||||
assertEquals(45, d());
|
||||
|
||||
assertSame(undefined, foo.a);
|
||||
assertThrows(() => foo.b, ReferenceError);
|
||||
assertThrows(() => foo.c, ReferenceError);
|
||||
assertEquals(45, foo.d());
|
||||
assertThrows(() => foo.default, ReferenceError);
|
||||
assertSame(undefined, foo.doesnotexist);
|
||||
|
||||
export var a = 42;
|
||||
export let b = 43;
|
||||
export const c = 44;
|
||||
export function d() { return 45 };
|
||||
export default 46;
|
||||
|
||||
assertEquals(42, a);
|
||||
assertEquals(43, b);
|
||||
assertEquals(44, c);
|
||||
assertEquals(45, d());
|
||||
|
||||
assertEquals(42, foo.a);
|
||||
assertEquals(43, foo.b);
|
||||
assertEquals(44, foo.c);
|
||||
assertEquals(45, foo.d());
|
||||
assertEquals(46, foo.default);
|
||||
assertSame(undefined, foo.doesnotexist);
|
@ -5,3 +5,4 @@
|
||||
export {a as b, default} from "modules-skip-1.js";
|
||||
import {a as tmp} from "modules-skip-1.js";
|
||||
export {tmp as c};
|
||||
export const zzz = 999;
|
||||
|
13
test/mjsunit/modules-skip-namespace.js
Normal file
13
test/mjsunit/modules-skip-namespace.js
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
//assertEquals(
|
||||
// ["ns", Symbol.toStringTag, Symbol.iterator], Reflect.ownKeys(ns2));
|
||||
//assertEquals(["ns"], [...ns2]);
|
||||
|
||||
export * from "modules-skip-4.js";
|
||||
export * from "modules-skip-5.js";
|
||||
export var zzz = 123;
|
||||
export {ns2};
|
||||
import * as ns2 from "modules-namespace2.js";
|
Loading…
Reference in New Issue
Block a user