/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
using constructorGetterCallback = JS::Handle<JSObject*> (*)(JSContext*);
// Mapping of html tag and GetConstructorObjectHandle methods. #define HTML_TAG(_tag, _classname, _interfacename) \
HTML##_interfacename##Element_Binding::GetConstructorObjectHandle, #define HTML_OTHER(_tag) nullptr, // We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h // to index into this array. staticconst constructorGetterCallback sConstructorGetterCallback[] = {
HTMLUnknownElement_Binding::GetConstructorObjectHandle, #include"nsHTMLTagList.h" #undef HTML_TAG #undef HTML_OTHER
};
#define MSG_DEF(_name, _argc, _has_context, _exn, _str) \
static_assert( \
(_argc) < JS::MaxNumErrorArguments, #_name \ " must only have as many error arguments as the JS engine can support"); #include"mozilla/dom/Errors.msg" #undef MSG_DEF
// aErrorNumber needs to be unsigned, not an ErrNum, because the latter makes // va_start have undefined behavior, and we do not want undefined behavior. void binding_detail::ThrowErrorMessage(JSContext* aCx, constunsigned aErrorNumber, ...) {
va_list ap;
va_start(ap, aErrorNumber);
// Our first arg is the context arg. We want to replace nullptr with empty // string, leave empty string alone, and for anything else append ": " to the // end. See also the behavior of // TErrorResult::SetPendingExceptionWithMessage, which this is mirroring for // exceptions that are thrown directly, not via an ErrorResult. constchar* args[JS::MaxNumErrorArguments + 1];
size_t argCount = GetErrorArgCount(static_cast<ErrNum>(aErrorNumber));
MOZ_ASSERT(argCount > 0, "We have a context arg!");
nsAutoCString firstArg;
for (size_t i = 0; i < argCount; ++i) {
args[i] = va_arg(ap, constchar*); if (i == 0) { if (args[0] && *args[0]) {
firstArg.Append(args[0]);
firstArg.AppendLiteral(": ");
}
args[0] = firstArg.get();
}
}
staticbool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, bool aSecurityError, constchar* aInterfaceName) {
NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName); // This should only be called for DOM methods/getters/setters, which // are JSNative-backed functions, so we can assume that // JS_ValueToFunction and JS_GetFunctionDisplayId will both return // non-null and that JS_GetStringCharsZ returns non-null.
JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
MOZ_ASSERT(func);
JS::Rooted<JSString*> funcName(aCx); if (!JS_GetFunctionDisplayId(aCx, func, &funcName)) { returnfalse;
}
MOZ_ASSERT(funcName);
nsAutoJSString funcNameStr; if (!funcNameStr.init(aCx, funcName)) { returnfalse;
} if (aSecurityError) { returnThrow(aCx, NS_ERROR_DOM_SECURITY_ERR,
nsPrintfCString("Permission to call '%s' denied.",
NS_ConvertUTF16toUTF8(funcNameStr).get()));
}
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(
JSContext* aCx, constchar* context) {
AssertInOwningThread();
MOZ_ASSERT(mUnionState == HasMessage);
MOZ_ASSERT(mExtra.mMessage, "SetPendingExceptionWithMessage() can be called only once");
Message* message = mExtra.mMessage;
MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments()); if (dom::ErrorFormatHasContext[message->mErrorNumber]) {
MOZ_ASSERT(!message->mArgs.IsEmpty(), "How could we have no args here?");
MOZ_ASSERT(message->mArgs[0].IsEmpty(), "Context should not be set yet!"); if (context) { // Prepend our context and ": "; see API documentation.
message->mArgs[0].AssignASCII(context);
message->mArgs[0].AppendLiteral(": ");
}
} const uint32_t argCount = message->mArgs.Length(); constchar* args[JS::MaxNumErrorArguments + 1]; for (uint32_t i = 0; i < argCount; ++i) {
args[i] = message->mArgs.ElementAt(i).get();
}
args[argCount] = nullptr;
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx,
JS::Handle<JS::Value> exn) {
AssertInOwningThread();
MOZ_ASSERT(mMightHaveUnreportedJSException, "Why didn't you tell us you planned to throw a JS exception?");
ClearUnionData();
// Make sure mExtra.mJSException is initialized _before_ we try to root it. // But don't set it to exn yet, because we don't want to do that until after // we root.
JS::Value& exc = InitJSException(); if (!js::AddRawValueRoot(cx, &exc, "TErrorResult::mExtra::mJSException")) { // Don't use NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION, because that // indicates we have in fact rooted mExtra.mJSException.
mResult = NS_ERROR_OUT_OF_MEMORY;
} else {
exc = exn;
mResult = NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION; #ifdef DEBUG
mUnionState = HasJSException; #endif// DEBUG
}
}
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx) {
AssertInOwningThread();
MOZ_ASSERT(!mMightHaveUnreportedJSException, "Why didn't you tell us you planned to handle JS exceptions?");
MOZ_ASSERT(mUnionState == HasJSException);
JS::Rooted<JS::Value> exception(cx, mExtra.mJSException); if (JS_WrapValue(cx, &exception)) {
JS_SetPendingException(cx, exception);
}
mExtra.mJSException = exception; // If JS_WrapValue failed, not much we can do about it... No matter // what, go ahead and unroot mExtra.mJSException.
js::RemoveRawValueRoot(cx, &mExtra.mJSException);
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx, constchar* context) {
AssertInOwningThread();
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
MOZ_ASSERT(mExtra.mDOMExceptionInfo, "SetPendingDOMException() can be called only once");
if (context && !mExtra.mDOMExceptionInfo->mMessage.IsEmpty()) { // Prepend our context and ": "; see API documentation.
nsAutoCString prefix(context);
prefix.AppendLiteral(": ");
mExtra.mDOMExceptionInfo->mMessage.Insert(prefix, 0);
}
template <typename CleanupPolicy>
TErrorResult<CleanupPolicy>& TErrorResult<CleanupPolicy>::operator=(
TErrorResult<CleanupPolicy>&& aRHS) {
AssertInOwningThread();
aRHS.AssertInOwningThread(); // Clear out any union members we may have right now, before we // start writing to it.
ClearUnionData();
#ifdef DEBUG
mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
aRHS.mMightHaveUnreportedJSException = false; #endif if (aRHS.IsErrorWithMessage()) {
InitMessage(aRHS.mExtra.mMessage);
aRHS.mExtra.mMessage = nullptr;
} elseif (aRHS.IsJSException()) {
JSContext* cx = dom::danger::GetJSContext();
MOZ_ASSERT(cx);
JS::Value& exn = InitJSException(); if (!js::AddRawValueRoot(cx, &exn, "TErrorResult::mExtra::mJSException")) {
MOZ_CRASH("Could not root mExtra.mJSException, we're about to OOM");
}
mExtra.mJSException = aRHS.mExtra.mJSException;
aRHS.mExtra.mJSException.setUndefined();
js::RemoveRawValueRoot(cx, &aRHS.mExtra.mJSException);
} elseif (aRHS.IsDOMException()) {
InitDOMExceptionInfo(aRHS.mExtra.mDOMExceptionInfo);
aRHS.mExtra.mDOMExceptionInfo = nullptr;
} else { // Null out the union on both sides for hygiene purposes. This is purely // precautionary, so InitMessage/placement-new is unnecessary.
mExtra.mMessage = aRHS.mExtra.mMessage = nullptr;
}
// Note: It's important to do this last, since this affects the condition // checks above!
mResult = aRHS.mResult;
aRHS.mResult = NS_OK; return *this;
}
template <typename CleanupPolicy> bool TErrorResult<CleanupPolicy>::operator==(const ErrorResult& aRight) const { auto right = reinterpret_cast<const TErrorResult<CleanupPolicy>*>(&aRight);
if (mResult != right->mResult) { returnfalse;
}
if (IsJSException()) { // js exceptions are always non-equal returnfalse;
}
if (IsErrorWithMessage()) { return *mExtra.mMessage == *right->mExtra.mMessage;
}
if (IsDOMException()) { return *mExtra.mDOMExceptionInfo == *right->mExtra.mDOMExceptionInfo;
}
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::SuppressException() {
AssertInOwningThread();
WouldReportJSException();
ClearUnionData(); // We don't use AssignErrorCode, because we want to override existing error // states, which AssignErrorCode is not allowed to do.
mResult = NS_OK;
}
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx, constchar* context) {
AssertInOwningThread(); if (IsUncatchableException()) { // Note: ReportUncatchableException will clear any existing exception on cx.
JS::ReportUncatchableException(cx);
mResult = NS_OK; return;
} if (IsJSContextException()) { // Whatever we need to throw is on the JSContext already.
MOZ_ASSERT(JS_IsExceptionPending(cx));
mResult = NS_OK; return;
} if (IsErrorWithMessage()) {
SetPendingExceptionWithMessage(cx, context); return;
} if (IsJSException()) {
SetPendingJSException(cx); return;
} if (IsDOMException()) {
SetPendingDOMException(cx, context); return;
}
SetPendingGenericErrorException(cx);
}
template <typename CleanupPolicy> void TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx) {
AssertInOwningThread();
MOZ_ASSERT(mMightHaveUnreportedJSException, "Why didn't you tell us you planned to throw a JS exception?");
JS::Rooted<JS::Value> exn(cx); if (!JS_GetPendingException(cx, &exn)) {
ThrowUncatchableException(); return;
}
// This creates a JSFunction and sets its length and name properties in the // order that ECMAScript's CreateBuiltinFunction does. static JSObject* CreateBuiltinFunctionForConstructor(
JSContext* aCx, JSNative aNative, size_t aNativeReservedSlot, void* aNativeReserved, unsignedint aNargs, jsid aName,
JS::Handle<JSObject*> aProto) {
JSFunction* fun = js::NewFunctionByIdWithReservedAndProto(
aCx, aNative, aProto, aNargs, JSFUN_CONSTRUCTOR, aName); if (!fun) { return nullptr;
}
// Eagerly force creation of the .length and .name properties, because // SpiderMonkey creates them lazily (see // https://bugzilla.mozilla.org/show_bug.cgi?id=1629803). bool unused; if (!JS_HasProperty(aCx, constructor, "length", &unused) ||
!JS_HasProperty(aCx, constructor, "name", &unused)) { return nullptr;
}
staticbool InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp) {
JS::CallArgs args = JS::CallArgsFromVp(argc, vp); // If the thing we were passed is not an object, return false like // OrdinaryHasInstance does. if (!args.get(0).isObject()) {
args.rval().setBoolean(false); returntrue;
}
// If "this" is not an object, likewise return false (again, like // OrdinaryHasInstance). if (!args.thisv().isObject()) {
args.rval().setBoolean(false); returntrue;
}
// If "this" is not an interface object, likewise return false (again, like // OrdinaryHasInstance). But note that we should CheckedUnwrapStatic here, // because otherwise we won't get the right answers. // The static version is OK, because we're looking for interface objects, // which are not cross-origin objects.
JS::Rooted<JSObject*> thisObj(
cx, js::CheckedUnwrapStatic(&args.thisv().toObject())); if (!thisObj) { // Just fall back on the normal thing, in case it still happens to work.
args.rval().setBoolean(false); returntrue;
}
if (!IsInterfaceObject(thisObj)) {
args.rval().setBoolean(false); returntrue;
}
// If "this" is a constructor for an interface without a prototype, just fall // back. if (interfaceInfo->mPrototypeID == prototypes::id::_ID_Count) {
args.rval().setBoolean(false); returntrue;
}
if (properties->HasAttributes() &&
!DefinePrefable(cx, obj, properties->Attributes())) { returnfalse;
}
if (properties->HasConstants() &&
!DefinePrefable(cx, obj, properties->Constants())) { returnfalse;
}
}
if (chromeOnlyProperties) { if (chromeOnlyProperties->HasMethods() &&
!DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) { returnfalse;
}
if (chromeOnlyProperties->HasAttributes() &&
!DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) { returnfalse;
}
if (chromeOnlyProperties->HasConstants() &&
!DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) { returnfalse;
}
}
returntrue;
}
namespace binding_detail {
void CreateInterfaceObjects(
JSContext* cx, JS::Handle<JSObject*> global,
JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto, const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs, bool isConstructorChromeOnly, const Span<const LegacyFactoryFunction>& legacyFactoryFunctions,
JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties, const NativeProperties* chromeOnlyProperties, constchar* name, bool defineOnGlobal, constchar* const* unscopableNames, bool isGlobal, constchar* const* legacyWindowAliases) {
MOZ_ASSERT(protoClass || interfaceInfo, "Need at least a class or info!");
MOZ_ASSERT(
!((properties &&
(properties->HasMethods() || properties->HasAttributes())) ||
(chromeOnlyProperties && (chromeOnlyProperties->HasMethods() ||
chromeOnlyProperties->HasAttributes()))) ||
protoClass, "Methods or properties but no protoClass!");
MOZ_ASSERT(!((properties && (properties->HasStaticMethods() ||
properties->HasStaticAttributes())) ||
(chromeOnlyProperties &&
(chromeOnlyProperties->HasStaticMethods() ||
chromeOnlyProperties->HasStaticAttributes()))) ||
interfaceInfo, "Static methods but no info!");
MOZ_ASSERT(!protoClass == !protoCache, "If, and only if, there is an interface prototype object we need " "to cache it");
MOZ_ASSERT(bool(interfaceInfo) == bool(constructorCache), "If, and only if, there is an interface object we need to cache " "it");
MOZ_ASSERT(interfaceProto || !interfaceInfo, "Must have a interface proto if we plan to create an interface " "object");
JSObject* interface; if (interfaceInfo) {
interface = CreateInterfaceObject(
cx, global, interfaceProto, interfaceInfo,
(isChrome || !isConstructorChromeOnly) ? ctorNargs : 0,
legacyFactoryFunctions, proto, properties, chromeOnlyProperties,
nameStr, isChrome, defineOnGlobal, legacyWindowAliases); if (!interface) { if (protoCache) { // If we fail we need to make sure to clear the value of protoCache we // set above.
*protoCache = nullptr;
} return;
}
*constructorCache = interface;
}
}
if (!InitInterfaceOrNamespaceObject(
cx, namespaceObj, properties, chromeOnlyProperties,
nsContentUtils::ThreadsafeIsSystemCaller(cx))) { return;
}
if (defineOnGlobal && !DefineConstructor(cx, global, nameId, namespaceObj)) { return;
}
if (!DefineToStringTag(cx, namespaceObj, nameStr)) { return;
}
*namespaceCache = namespaceObj;
}
// Only set aAllowNativeWrapper to false if you really know you need it; if in // doubt use true. Setting it to false disables security wrappers. staticbool NativeInterface2JSObjectAndThrowIfFailed(
JSContext* aCx, JS::Handle<JSObject*> aScope,
JS::MutableHandle<JS::Value> aRetval, xpcObjectHelper& aHelper, const nsIID* aIID, bool aAllowNativeWrapper) {
js::AssertSameCompartment(aCx, aScope);
nsresult rv; // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need // on all threads.
nsWrapperCache* cache = aHelper.GetWrapperCache();
if (cache) {
JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper()); if (!obj) {
obj = cache->WrapObject(aCx, nullptr); if (!obj) { returnThrow(aCx, NS_ERROR_UNEXPECTED);
}
}
if (aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) { returnfalse;
}
aRetval.setObject(*obj); returntrue;
}
MOZ_ASSERT(NS_IsMainThread());
if (!XPCConvert::NativeInterface2JSObject(aCx, aRetval, aHelper, aIID,
aAllowNativeWrapper, &rv)) { // I can't tell if NativeInterface2JSObject throws JS exceptions // or not. This is a sloppy stab at the right semantics; the // method really ought to be fixed to behave consistently. if (!JS_IsExceptionPending(aCx)) { Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
} returnfalse;
} returntrue;
}
// nsISupports objects are special cased because DOM proxies are nsISupports // and have addProperty hooks that do more than wrapper preservation (so we // don't want to call them). if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
nsWrapperCache* cache = nullptr;
CallQueryInterface(native, &cache); if (cache) {
cache->PreserveWrapper(native);
} returntrue;
}
// The addProperty hook for WebIDL classes does wrapper preservation, and // nothing else, so call it, if present.
if (getter) { // If the class has a wrapper cache getter it must be a CC participant.
MOZ_RELEASE_ASSERT(domClass->mParticipant);
cache = getter(obj);
}
}
return cache && !cache->PreservingWrapper();
}
// Can only be called with a DOM JSClass. bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
uint32_t depth) { const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp); returnstatic_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
}
// Only set allowNativeWrapper to false if you really know you need it; if in // doubt use true. Setting it to false disables security wrappers. bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
xpcObjectHelper& helper, const nsIID* iid, bool allowNativeWrapper,
JS::MutableHandle<JS::Value> rval) { return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
allowNativeWrapper);
}
bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
JS::MutableHandle<JS::Value> aRetval) {
nsresult rv; if (!XPCVariant::VariantDataToJS(aCx, aVariant, &rv, aRetval)) { // Does it throw? Who knows if (!JS_IsExceptionPending(aCx)) { Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
} returnfalse;
}
// Index of the Prefable that contains the id for the current PropertyInfo.
uint32_t prefIndex = 0;
do { // We ignore whether the set of ids is enabled and just intern all the IDs, // because this is only done once per application runtime. const SpecT* spec = pref->specs; // Index of the property/function/constant spec for our current PropertyInfo // in the "specs" array of the relevant Prefable.
uint32_t specIndex = 0; do {
jsid id; if (!JS::PropertySpecNameToPermanentId(cx, ToPropertySpecName(spec->name),
&id)) { returnfalse;
}
infos->SetId(id);
infos->type = type;
infos->prefIndex = prefIndex;
infos->specIndex = specIndex++;
++infos;
} while ((++spec)->name);
++prefIndex;
} while ((++pref)->specs);
// Initialize and sort the index array.
uint16_t* indices = nativeProperties->sortedPropertyIndices; auto count = nativeProperties->propertyInfoCount; for (auto i = 0; i < count; ++i) {
indices[i] = i;
}
std::sort(indices, indices + count,
[infos = nativeProperties->PropertyInfos()](const uint16_t left, const uint16_t right) { // std::sort may call us with the same element by design but // PropertyInfo::Compare does not like that. if (left == right) { returnfalse;
} return PropertyInfo::Compare(infos[left], infos[right]) < 0;
});
static JSObject* XrayCreateFunction(JSContext* cx,
JS::Handle<JSObject*> wrapper,
JSNativeWrapper native, unsigned nargs,
JS::Handle<jsid> id) {
JSFunction* fun; if (id.isString()) {
fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
} else { // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved; // just use an empty name for lack of anything better.
fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
}
struct IdToIndexComparator { // The id we're searching for. const jsid& mId; // Whether we're searching for static operations. constbool mStatic; // The list of ids we're searching in. const PropertyInfo* mInfos;
JS::Rooted<jsid> getterId(cx); if (!JS::ToGetterId(cx, id, &getterId)) { returnfalse;
}
// Because of centralization, we need to make sure we fault in the JitInfos as // well. At present, until the JSAPI changes, the easiest way to do this is // wrap them up as functions ourselves.
// They all have getters, so we can just make it.
JS::Rooted<JSObject*> getter(
cx, XrayCreateFunction(cx, wrapper, attrSpec.u.accessors.getter.native, 0,
getterId)); if (!getter) { returnfalse;
}
JS::Rooted<JSObject*> setter(cx); if (attrSpec.u.accessors.setter.native.op) {
JS::Rooted<jsid> setterId(cx); if (!JS::ToSetterId(cx, id, &setterId)) { returnfalse;
}
// We have a setter! Make it.
setter = XrayCreateFunction(cx, wrapper, attrSpec.u.accessors.setter.native,
1, setterId); if (!setter) { returnfalse;
}
}
// Make sure we resolve for matched object type. switch (propertyInfo.type) { case eStaticMethod: case eStaticAttribute: if (type != eInterface && type != eNamespace) { returntrue;
} break; case eMethod: case eAttribute: if (type != eGlobalInstance && type != eInterfacePrototype) { returntrue;
} break; case eUnforgeableMethod: case eUnforgeableAttribute: if (!IsInstance(type)) { returntrue;
} break; case eConstant: if (IsInstance(type)) { returntrue;
} break;
}
staticbool ResolvePrototypeOrConstructor(
JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
size_t protoAndIfaceCacheIndex, unsigned attrs,
JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, bool& cacheOnHolder) {
JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj));
{
JSAutoRealm ar(cx, global);
ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); // This function is called when resolving the "constructor" and "prototype" // properties of Xrays for DOM prototypes and constructors respectively. // This means the relevant Xray exists, which means its _target_ exists. // And that means we managed to successfullly create the prototype or // constructor, respectively, and hence must have managed to create the // thing it's pointing to as well. So our entry slot must exist.
JSObject* protoOrIface =
protoAndIfaceCache.EntrySlotMustExist(protoAndIfaceCacheIndex);
MOZ_RELEASE_ASSERT(protoOrIface, "How can this object not exist?");
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 ist noch experimentell.