# include "js/friend/ErrorMessages.h"// js::GetErrorMessage, JSMSG_* # include "threading/ExclusiveData.h" # include "util/Text.h" # include "vm/GlobalObject.h" # include "vm/MutexIDs.h" # include "wasm/WasmJS.h"
// Component model names are encoded as UTF-8, and in fact an ASCII subset of // UTF-8, so this is fine. staticchar LowercaseNameChar(char c) { return ('A' <= c && c <= 'Z') ? c + ('a' - 'A') : c;
}
static mozilla::Span<constchar> TrimAttribute(mozilla::Span<constchar> name) { if (CharsStartsWith(name, attributeConstructor)) { return name.Subspan(attributeConstructor.Length());
} if (CharsStartsWith(name, attributeMethod)) { return name.Subspan(attributeMethod.Length());
} if (CharsStartsWith(name, attributeStatic)) { return name.Subspan(attributeStatic.Length());
} return name;
}
staticbool NameHasAttribute(mozilla::Span<constchar> name) { // The name should already be well-formed from parse time. return name.Length() == 0 || name.data()[0] == '[';
}
// We hash only the base part of the name, e.g. "foo" for "[constructor]foo".
HashNumber StronglyUniqueNameHasher::hash(const Lookup& aLookup) {
mozilla::Span<constchar> trimmed = TrimAttribute(aLookup);
HashNumber hash = 0; for (size_t i = 0; i < trimmed.Length(); i++) { char c = trimmed.data()[i]; if (c == '.') { break;
}
hash = mozilla::AddToHash(hash, LowercaseNameChar(trimmed.data()[i]));
} return hash;
}
// Rule 1: If one name is l and the other name is [constructor]l (for the // same label l), they are strongly-unique. bool newIsConstructor = CharsStartsWith(aLookup, attributeConstructor); bool existingIsConstructor = CharsStartsWith(keyBytes, attributeConstructor); if (newIsConstructor != existingIsConstructor &&
newTrimmed == existingTrimmed) { returnfalse;
}
// Rule 2: If one name is l and the other name is [*]l.l (for the same label l // and any annotation * with a dotted l.l name), they are not strongly-unique.
mozilla::Maybe<mozilla::Span<constchar>> plain;
mozilla::Maybe<mozilla::Span<constchar>> dotted; if (!NameHasAttribute(aLookup)) {
plain.emplace(aLookup);
} elseif (!NameHasAttribute(keyBytes)) {
plain.emplace(keyBytes);
} if (CharsStartsWith(aLookup, attributeMethod) ||
CharsStartsWith(aLookup, attributeStatic)) {
dotted.emplace(aLookup);
} elseif (CharsStartsWith(keyBytes, attributeMethod) ||
CharsStartsWith(keyBytes, attributeStatic)) {
dotted.emplace(keyBytes);
} if (plain.isSome() && dotted.isSome()) {
mozilla::Span<constchar> dottedTrimmed = TrimAttribute(dotted.value());
size_t indexOfDot = dottedTrimmed.IndexOf('.');
MOZ_RELEASE_ASSERT(indexOfDot != mozilla::Span<constchar>::npos); auto [before, after] = dottedTrimmed.SplitAt(indexOfDot);
after = after.Subspan(1); // The SplitAt method includes the dot. if (plain.value() == after && plain.value() == before) { returntrue;
}
}
// Rule 3: Lowercase the names, trim attributes, and compare directly. if (newTrimmed.Length() != existingTrimmed.Length()) { returnfalse;
} for (size_t i = 0; i < newTrimmed.Length(); i++) { if (LowercaseNameChar(newTrimmed[i]) !=
LowercaseNameChar(existingTrimmed[i])) { returnfalse;
}
} returntrue;
}
// Different sorts never match. if (sub.sort() != super.sort()) { returnfalse;
}
switch (sub.sort()) { case ComponentSort::Func: return sub.asFunc() == super.asFunc(); case ComponentSort::Type: return sub.asType() == super.asType(); case ComponentSort::Component: case ComponentSort::Instance: case ComponentSort::CoreModule: { // TODO(wasm-cm) returnfalse;
} break; default:
MOZ_CRASH("all valid sorts for externdesc should have been handled");
}
}
bool ComponentType::subResource(ComponentType* type) { // We still need a unique heap allocation so that two (sub resource) types // will not be equal.
ComponentTypeDef* def =
js_new<ComponentTypeDef>(ComponentTypeSchema(mozilla::Nothing())); if (!def) { returnfalse;
}
*type = ComponentType(ComponentTypeKind::SubResource, def); returntrue;
}
if (aFields.length() != bFields.length()) { returnfalse;
} for (size_t i = 0; i < aFields.length(); i++) { if (aFields[i] != bFields[i]) { returnfalse;
}
} returntrue;
},
[&](const ComponentVariantCaseVector& aCases) { if (!b.schema().is<ComponentVariantCaseVector>()) { returnfalse;
} const ComponentVariantCaseVector& bCases =
b.schema().as<ComponentVariantCaseVector>();
if (aCases.length() != bCases.length()) { returnfalse;
} for (size_t i = 0; i < aCases.length(); i++) { if (aCases[i] != bCases[i]) { returnfalse;
}
} returntrue;
},
[&](const ComponentTypeVector& aTypes) { if (!b.schema().is<ComponentTypeVector>()) { returnfalse;
} const ComponentTypeVector& bTypes =
b.schema().as<ComponentTypeVector>();
if (aTypes.length() != bTypes.length()) { returnfalse;
} for (size_t i = 0; i < aTypes.length(); i++) { if (aTypes[i] != bTypes[i]) { returnfalse;
}
} returntrue;
},
[&](const CacheableNameVector& aLabels) { if (!b.schema().is<CacheableNameVector>()) { returnfalse;
} const CacheableNameVector& bLabels =
b.schema().as<CacheableNameVector>();
if (aLabels.length() != bLabels.length()) { returnfalse;
} for (size_t i = 0; i < aLabels.length(); i++) { if (aLabels[i] != bLabels[i]) { returnfalse;
}
} returntrue;
},
[&](const ComponentResultType& aResult) { if (!b.schema().is<ComponentResultType>()) { returnfalse;
} const ComponentResultType& bResult =
b.schema().as<ComponentResultType>(); return ComponentResultType::equals(aResult, bResult);
},
[&](const ComponentFuncType& aFunc) { if (!b.schema().is<ComponentFuncType>()) { returnfalse;
} const ComponentFuncType& bFunc = b.schema().as<ComponentFuncType>(); return aFunc == bFunc;
},
[&](const ComponentResourceType& a) { // This method never considers resource types to be equal because this // is the wrong place to check for that kind of equality. Two // canonicalized ComponentTypes for resource types may be equal, because // they point at the same ComponentTypeDef (by pointer equality), but // this method has no concept of that. returnfalse;
});
}
// Primitives and resource types should not appear here; this is caught by the // default case. switch (t.kind()) { case ComponentTypeKind::Record: { const ComponentRecordFieldVector& fields = t.asRecord(); for (const ComponentRecordField& f : fields) {
hash = mozilla::AddToHash(hash, HashName(f.name));
hash = AddComponentTypeToHash(hash, f.type);
}
} break; case ComponentTypeKind::Variant: { const ComponentVariantCaseVector& cases = t.asVariant(); for (const ComponentVariantCase& c : cases) {
hash = mozilla::AddToHash(hash, HashName(c.name));
hash = AddMaybeComponentTypeToHash(hash, c.type);
}
} break; case ComponentTypeKind::List: {
hash = AddComponentTypeToHash(hash, t.asList());
} break; case ComponentTypeKind::Tuple: { const ComponentTypeVector& types = t.asTuple(); for (const ComponentType& t : types) {
hash = AddComponentTypeToHash(hash, t);
}
} break; case ComponentTypeKind::Flags: { const CacheableNameVector& labels = t.asFlags(); for (const CacheableName& label : labels) {
hash = mozilla::AddToHash(hash, HashName(label));
}
} break; case ComponentTypeKind::Enum: { const CacheableNameVector& cases = t.asEnum(); for (const CacheableName& c : cases) {
hash = mozilla::AddToHash(hash, HashName(c));
}
} break; case ComponentTypeKind::Option: {
hash = AddComponentTypeToHash(hash, t.asOption());
} break; case ComponentTypeKind::Result: { const ComponentResultType& rt = t.asResult();
hash = AddMaybeComponentTypeToHash(hash, rt.type);
hash = AddMaybeComponentTypeToHash(hash, rt.errorType);
} break; case ComponentTypeKind::Own: {
hash = AddComponentTypeToHash(hash, t.asOwn());
} break; case ComponentTypeKind::Borrow: {
hash = AddComponentTypeToHash(hash, t.asBorrow());
} break; case ComponentTypeKind::Func: { const ComponentFuncType& ft = t.asFunc();
MOZ_ASSERT(ft.paramTypes.length() == ft.paramNames.length()); for (size_t i = 0; i < ft.paramTypes.length(); i++) {
hash = mozilla::AddToHash(hash, HashName(ft.paramNames[i]));
hash = AddComponentTypeToHash(hash, ft.paramTypes[i]);
}
hash = AddMaybeComponentTypeToHash(hash, ft.resultType);
} break; case ComponentTypeKind::Component: case ComponentTypeKind::Instance: // TODO(wasm-cm): Component and instance types not yet implemented
MOZ_CRASH(); default:
MOZ_CRASH("should have been excluded from hashing");
}
return hash;
} bool ComponentTypeHasher::match(const ComponentType& a, const ComponentType& b) { // (eq i) bounds should be resolved to a unique type on type construction.
MOZ_ASSERT(a.kind() != ComponentTypeKind::Eq);
MOZ_ASSERT(b.kind() != ComponentTypeKind::Eq);
// Primitives and resource types should be special-cased during // canonicalization and should therefore never end up here.
MOZ_ASSERT(!ComponentTypeKindIsPrimitive(a.kind()) &&
a.kind() != ComponentTypeKind::Resource &&
a.kind() != ComponentTypeKind::SubResource);
MOZ_ASSERT(!ComponentTypeKindIsPrimitive(b.kind()) &&
b.kind() != ComponentTypeKind::Resource &&
b.kind() != ComponentTypeKind::SubResource);
// Primitives compare trivially and require no additional storage, therefore // they do not need to be explicitly stored. if (ComponentTypeKindIsPrimitive(type.kind())) {
MOZ_RELEASE_ASSERT(!type.typeDef());
*canonicalized = type; returntrue;
}
MOZ_RELEASE_ASSERT(type.typeDef());
// Resource types retain their uniqueness by skipping canonicalization. if (type.kind() == ComponentTypeKind::Resource ||
type.kind() == ComponentTypeKind::SubResource) {
*canonicalized = type; returntrue;
}
// All other types are hashed and deduplicated structurally. As long as all // types are canonicalized as they are parsed, this means that pointer // equality of a type's ComponentTypeDef is equivalent to structural equality. auto addPtr = canonicalTypes_.lookupForAdd(type); if (addPtr) {
*canonicalized = *addPtr; returntrue;
} if (!canonicalTypes_.add(addPtr, type)) { returnfalse;
}
*canonicalized = type; returntrue;
}
// TODO(wasm-cm): Handle (and test) the case where params or results exceed // the maximums set by the component model, at which point the ABI falls back // to passing values in memory. (Or maybe this will all change with lazy // lowering, who knows.)
if (!FlattenTypes(c, funcType.paramTypes, ¶ms)) { return mozilla::Nothing();
} if (funcType.resultType.isSome()) { if (!FlattenType(c, funcType.resultType.ref(), &results)) { return mozilla::Nothing();
}
}
bool wasm::FlattenTypes(const Component& c, const ComponentTypeVector& types,
ValTypeVector* result) { // Pre-reserve at least enough space for a bunch of primitives. We still may // exceed the capacity reserved here but at least we can avoid a little bit of // allocation. (Appends after this point are not to be considered infallible.) if (!result->reserve(types.length())) { returnfalse;
}
for (const ComponentType& t : types) { if (!FlattenType(c, t, result)) { returnfalse;
}
}
returntrue;
}
static ValType JoinVariantValType(ValType a, ValType b) {
MOZ_ASSERT(a.isNumber() && b.isNumber()); if (a == b) { return a;
} elseif ((a == ValType::i32() && b == ValType::f32()) ||
(a == ValType::f32() && b == ValType::i32())) { return ValType::i32();
} else { return ValType::i64();
}
}
bool wasm::FlattenType(const Component& c, const ComponentType& type,
ValTypeVector* result) { switch (type.kind()) { // Simple primitives case ComponentTypeKind::Bool: case ComponentTypeKind::U8: case ComponentTypeKind::U16: case ComponentTypeKind::U32: case ComponentTypeKind::S8: case ComponentTypeKind::S16: case ComponentTypeKind::S32: case ComponentTypeKind::Char: case ComponentTypeKind::Flags: case ComponentTypeKind::Enum: case ComponentTypeKind::Own: case ComponentTypeKind::Borrow: { if (!result->append(ValType::i32())) { returnfalse;
}
} break; case ComponentTypeKind::U64: case ComponentTypeKind::S64: { if (!result->append(ValType::i64())) { returnfalse;
}
} break; case ComponentTypeKind::F32: { if (!result->append(ValType::f32())) { returnfalse;
}
} break; case ComponentTypeKind::F64: { if (!result->append(ValType::f64())) { returnfalse;
}
} break;
// Strings are always two i32's case ComponentTypeKind::String: { if (!result->append(ValType::i32())) { returnfalse;
} if (!result->append(ValType::i32())) { returnfalse;
}
} break;
// Compound types have dedicated logic. Note that our data storage for some // types disagrees with the categories in the canonical ABI explainer, e.g. // we represent tuples as a vector of value types, not a record. case ComponentTypeKind::List: { // This will have to change when support is added for fixed-length lists. if (!result->append(ValType::i32())) { returnfalse;
} if (!result->append(ValType::i32())) { returnfalse;
}
} break; case ComponentTypeKind::Record: { if (!FlattenRecord(c, type.asRecord(), result)) { returnfalse;
}
} break; case ComponentTypeKind::Tuple: { if (!FlattenTypes(c, type.asTuple(), result)) { returnfalse;
}
} break; case ComponentTypeKind::Variant: { // Flatten the discriminant if (!result->append(ValType::i32())) { returnfalse;
}
// Flatten all the cases (overlapped, with joins) const ComponentVariantCaseVector& cases = type.asVariant();
size_t startIndex = result->length(); for (const ComponentVariantCase& case_ : cases) { if (!case_.type) { continue;
}
ValTypeVector caseFlattened; if (!FlattenType(c, *case_.type, &caseFlattened)) { returnfalse;
} for (size_t i = 0; i < caseFlattened.length(); i++) {
size_t existingIndex = startIndex + i; if (existingIndex < result->length()) { // Join the new type with the existing one.
(*result)[existingIndex] =
JoinVariantValType((*result)[existingIndex], caseFlattened[i]);
} else { // Append the new type to the overall list. if (!result->append(caseFlattened[i])) { returnfalse;
}
}
}
}
} break; case ComponentTypeKind::Option: {
ComponentType inner = type.asOption(); if (!result->append(ValType::i32())) { returnfalse;
} if (!FlattenType(c, inner, result)) { returnfalse;
}
} break; case ComponentTypeKind::Result: {
ComponentResultType inner = type.asResult(); // Result types are encoded just like a variant with two cases, but each // case may or may not have a type.
// Discriminant if (!result->append(ValType::i32())) { returnfalse;
}
// Payload(s)
size_t startIndex = result->length(); if (inner.type.isSome()) { if (!FlattenType(c, *inner.type, result)) { returnfalse;
}
} if (inner.errorType.isSome()) {
ValTypeVector errorFlattened; if (!FlattenType(c, *inner.errorType, &errorFlattened)) { returnfalse;
} for (size_t i = 0; i < errorFlattened.length(); i++) {
size_t existingIndex = startIndex + i; if (existingIndex < result->length()) {
(*result)[existingIndex] =
JoinVariantValType((*result)[existingIndex], errorFlattened[i]);
} else { if (!result->append(errorFlattened[i])) { returnfalse;
}
}
}
}
} break;
default:
MOZ_CRASH("should have been rejected when the func type was validated");
}
returntrue;
}
bool wasm::FlattenRecord(const Component& c, const ComponentRecordFieldVector& fields,
ValTypeVector* result) { for (const ComponentRecordField& field : fields) { if (!FlattenType(c, field.type, result)) { returnfalse;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.