diff options
Diffstat (limited to 'js/xpconnect/loader/ModuleEnvironmentProxy.cpp')
-rw-r--r-- | js/xpconnect/loader/ModuleEnvironmentProxy.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/js/xpconnect/loader/ModuleEnvironmentProxy.cpp b/js/xpconnect/loader/ModuleEnvironmentProxy.cpp new file mode 100644 index 0000000000..e70fcb3865 --- /dev/null +++ b/js/xpconnect/loader/ModuleEnvironmentProxy.cpp @@ -0,0 +1,237 @@ +/* -*- 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/. */ + +#include "ModuleEnvironmentProxy.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT +#include "mozilla/Maybe.h" // mozilla::Maybe + +#include <stddef.h> // size_t + +#include "js/Class.h" // JS::ObjectOpResult +#include "js/ErrorReport.h" // JS_ReportOutOfMemory +#include "js/GCVector.h" // JS::RootedVector +#include "js/Id.h" // JS::PropertyKey +#include "js/PropertyAndElement.h" // JS::IdVector, JS_HasPropertyById, JS_GetPropertyById, JS_Enumerate +#include "js/PropertyDescriptor.h" // JS::PropertyDescriptor, JS_GetOwnPropertyDescriptorById +#include "js/Proxy.h" // js::ProxyOptions, js::NewProxyObject, js::GetProxyPrivate +#include "js/RootingAPI.h" // JS::Rooted, JS::Handle, JS::MutableHandle +#include "js/TypeDecls.h" // JSContext, JSObject, JS::MutableHandleVector +#include "js/Value.h" // JS::Value +#include "js/friend/ErrorMessages.h" // JSMSG_* +#include "js/String.h" +#include "js/Modules.h" + +namespace mozilla { +namespace loader { + +struct ModuleEnvironmentProxyHandler : public js::BaseProxyHandler { + ModuleEnvironmentProxyHandler() : js::BaseProxyHandler(&gFamily, false) {} + + bool defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + JS::Handle<JS::PropertyDescriptor> aDesc, + JS::ObjectOpResult& aResult) const override { + return aResult.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE); + } + + bool getPrototype(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::MutableHandle<JSObject*> aProtop) const override { + aProtop.set(nullptr); + return true; + } + + bool setPrototype(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JSObject*> aProto, + JS::ObjectOpResult& aResult) const override { + if (!aProto) { + return aResult.succeed(); + } + return aResult.failCantSetProto(); + } + + bool getPrototypeIfOrdinary( + JSContext* aCx, JS::Handle<JSObject*> aProxy, bool* aIsOrdinary, + JS::MutableHandle<JSObject*> aProtop) const override { + *aIsOrdinary = false; + return true; + } + + bool setImmutablePrototype(JSContext* aCx, JS::Handle<JSObject*> aProxy, + bool* aSucceeded) const override { + *aSucceeded = true; + return true; + } + + bool preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::ObjectOpResult& aResult) const override { + aResult.succeed(); + return true; + } + + bool isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy, + bool* aExtensible) const override { + *aExtensible = false; + return true; + } + + bool set(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, JS::Handle<JS::Value> aValue, + JS::Handle<JS::Value> aReceiver, + JS::ObjectOpResult& aResult) const override { + return aResult.failReadOnly(); + } + + bool delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + JS::ObjectOpResult& aResult) const override { + return aResult.failCantDelete(); + } + + bool getOwnPropertyDescriptor( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) + const override; + bool has(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, bool* aBp) const override; + bool get(JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::Value> receiver, JS::Handle<JS::PropertyKey> aId, + JS::MutableHandle<JS::Value> aVp) const override; + bool ownPropertyKeys( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::MutableHandleVector<JS::PropertyKey> aProps) const override; + + private: + static JSObject* getEnvironment(JS::Handle<JSObject*> aProxy) { + return &js::GetProxyPrivate(aProxy).toObject(); + } + + static bool equalsNamespace(JSContext* aCx, JS::Handle<JS::PropertyKey> aId, + bool* aMatch) { + if (!aId.isString()) { + *aMatch = false; + return true; + } + return JS_StringEqualsLiteral(aCx, aId.toString(), "*namespace*", aMatch); + } + + public: + static const char gFamily; + static const ModuleEnvironmentProxyHandler gHandler; +}; + +const ModuleEnvironmentProxyHandler ModuleEnvironmentProxyHandler::gHandler; +const char ModuleEnvironmentProxyHandler::gFamily = 0; + +bool ModuleEnvironmentProxyHandler::getOwnPropertyDescriptor( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) const { + bool isNamespace; + if (!equalsNamespace(aCx, aId, &isNamespace)) { + return false; + } + if (isNamespace) { + aDesc.reset(); + return true; + } + + JS::Rooted<JSObject*> envObj(aCx, getEnvironment(aProxy)); + if (!JS_GetOwnPropertyDescriptorById(aCx, envObj, aId, aDesc)) { + return false; + } + + if (aDesc.get().isNothing()) { + return true; + } + + JS::PropertyDescriptor& desc = *aDesc.get(); + + desc.setConfigurable(false); + desc.setWritable(false); + desc.setEnumerable(true); + + return true; +} + +bool ModuleEnvironmentProxyHandler::has(JSContext* aCx, + JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + bool* aBp) const { + bool isNamespace; + if (!equalsNamespace(aCx, aId, &isNamespace)) { + return false; + } + if (isNamespace) { + *aBp = false; + return true; + } + + JS::Rooted<JSObject*> envObj(aCx, getEnvironment(aProxy)); + return JS_HasOwnPropertyById(aCx, envObj, aId, aBp); +} + +bool ModuleEnvironmentProxyHandler::get( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::Value> aReceiver, JS::Handle<JS::PropertyKey> aId, + JS::MutableHandle<JS::Value> aVp) const { + bool isNamespace; + if (!equalsNamespace(aCx, aId, &isNamespace)) { + return false; + } + if (isNamespace) { + aVp.setUndefined(); + return true; + } + + JS::Rooted<JSObject*> envObj(aCx, getEnvironment(aProxy)); + return JS_GetPropertyById(aCx, envObj, aId, aVp); +} + +bool ModuleEnvironmentProxyHandler::ownPropertyKeys( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::MutableHandleVector<JS::PropertyKey> aProps) const { + JS::Rooted<JSObject*> envObj(aCx, getEnvironment(aProxy)); + JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx)); + if (!JS_Enumerate(aCx, envObj, &ids)) { + return false; + } + + for (size_t i = 0; i < ids.length(); i++) { + bool isNamespace; + if (!equalsNamespace(aCx, ids[i], &isNamespace)) { + return false; + } + if (isNamespace) { + continue; + } + if (!aProps.append(ids[i])) { + JS_ReportOutOfMemory(aCx); + return false; + } + } + + return true; +} + +JSObject* CreateModuleEnvironmentProxy(JSContext* aCx, + JS::Handle<JSObject*> aModuleObj) { + js::ProxyOptions options; + options.setLazyProto(true); + + JS::Rooted<JSObject*> envObj(aCx, JS::GetModuleEnvironment(aCx, aModuleObj)); + if (!envObj) { + return nullptr; + } + + JS::Rooted<JS::Value> envVal(aCx, JS::ObjectValue(*envObj)); + return NewProxyObject(aCx, &ModuleEnvironmentProxyHandler::gHandler, envVal, + nullptr, options); +} + +} // namespace loader +} // namespace mozilla |