diff options
Diffstat (limited to '')
-rw-r--r-- | js/xpconnect/loader/JSMEnvironmentProxy.cpp | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/js/xpconnect/loader/JSMEnvironmentProxy.cpp b/js/xpconnect/loader/JSMEnvironmentProxy.cpp new file mode 100644 index 0000000000..cb21151033 --- /dev/null +++ b/js/xpconnect/loader/JSMEnvironmentProxy.cpp @@ -0,0 +1,259 @@ +/* -*- 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 "JSMEnvironmentProxy.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT +#include "mozilla/Maybe.h" // mozilla::Maybe + +#include <stddef.h> // size_t + +#include "jsapi.h" // JS_HasExtensibleLexicalEnvironment, JS_ExtensibleLexicalEnvironment +#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_HasOwnPropertyById, 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, JS::UndefinedValue, JS_UNINITIALIZED_LEXICAL +#include "js/friend/ErrorMessages.h" // JSMSG_* + +namespace mozilla { +namespace loader { + +struct JSMEnvironmentProxyHandler : public js::BaseProxyHandler { + JSMEnvironmentProxyHandler() : 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> aReceiver, 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* getGlobal(JSContext* aCx, JS::Handle<JSObject*> aProxy) { + JS::Rooted<JSObject*> globalObj(aCx, + &js::GetProxyPrivate(aProxy).toObject()); + return globalObj; + } + + public: + static const char gFamily; + static const JSMEnvironmentProxyHandler gHandler; +}; + +const JSMEnvironmentProxyHandler JSMEnvironmentProxyHandler::gHandler; +const char JSMEnvironmentProxyHandler::gFamily = 0; + +JSObject* ResolveModuleObjectPropertyById(JSContext* aCx, + JS::Handle<JSObject*> aModObj, + JS::Handle<JS::PropertyKey> aId) { + if (JS_HasExtensibleLexicalEnvironment(aModObj)) { + JS::Rooted<JSObject*> lexical(aCx, + JS_ExtensibleLexicalEnvironment(aModObj)); + bool found; + if (!JS_HasOwnPropertyById(aCx, lexical, aId, &found)) { + return nullptr; + } + if (found) { + return lexical; + } + } + return aModObj; +} + +JSObject* ResolveModuleObjectProperty(JSContext* aCx, + JS::Handle<JSObject*> aModObj, + const char* aName) { + if (JS_HasExtensibleLexicalEnvironment(aModObj)) { + JS::RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj)); + bool found; + if (!JS_HasOwnProperty(aCx, lexical, aName, &found)) { + return nullptr; + } + if (found) { + return lexical; + } + } + return aModObj; +} + +bool JSMEnvironmentProxyHandler::getOwnPropertyDescriptor( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> aDesc) const { + JS::Rooted<JSObject*> globalObj(aCx, getGlobal(aCx, aProxy)); + JS::Rooted<JSObject*> holder( + aCx, ResolveModuleObjectPropertyById(aCx, globalObj, aId)); + if (!JS_GetOwnPropertyDescriptorById(aCx, holder, aId, aDesc)) { + return false; + } + + if (aDesc.get().isNothing()) { + return true; + } + + JS::PropertyDescriptor& desc = *aDesc.get(); + + if (desc.hasValue()) { + if (desc.value().isMagic(JS_UNINITIALIZED_LEXICAL)) { + desc.setValue(JS::UndefinedValue()); + } + } + + desc.setConfigurable(false); + desc.setEnumerable(true); + if (!desc.isAccessorDescriptor()) { + desc.setWritable(false); + } + + return true; +} + +bool JSMEnvironmentProxyHandler::has(JSContext* aCx, + JS::Handle<JSObject*> aProxy, + JS::Handle<JS::PropertyKey> aId, + bool* aBp) const { + JS::Rooted<JSObject*> globalObj(aCx, getGlobal(aCx, aProxy)); + JS::Rooted<JSObject*> holder( + aCx, ResolveModuleObjectPropertyById(aCx, globalObj, aId)); + return JS_HasPropertyById(aCx, holder, aId, aBp); +} + +bool JSMEnvironmentProxyHandler::get(JSContext* aCx, + JS::Handle<JSObject*> aProxy, + JS::Handle<JS::Value> aReceiver, + JS::Handle<JS::PropertyKey> aId, + JS::MutableHandle<JS::Value> aVp) const { + JS::Rooted<JSObject*> globalObj(aCx, getGlobal(aCx, aProxy)); + JS::Rooted<JSObject*> holder( + aCx, ResolveModuleObjectPropertyById(aCx, globalObj, aId)); + if (!JS_GetPropertyById(aCx, holder, aId, aVp)) { + return false; + } + + if (aVp.isMagic(JS_UNINITIALIZED_LEXICAL)) { + aVp.setUndefined(); + } + + return true; +} + +bool JSMEnvironmentProxyHandler::ownPropertyKeys( + JSContext* aCx, JS::Handle<JSObject*> aProxy, + JS::MutableHandleVector<JS::PropertyKey> aProps) const { + JS::Rooted<JSObject*> globalObj(aCx, getGlobal(aCx, aProxy)); + JS::Rooted<JS::IdVector> globalIds(aCx, JS::IdVector(aCx)); + if (!JS_Enumerate(aCx, globalObj, &globalIds)) { + return false; + } + + for (size_t i = 0; i < globalIds.length(); i++) { + if (!aProps.append(globalIds[i])) { + JS_ReportOutOfMemory(aCx); + return false; + } + } + + JS::RootedObject lexicalEnv(aCx, JS_ExtensibleLexicalEnvironment(globalObj)); + JS::Rooted<JS::IdVector> lexicalIds(aCx, JS::IdVector(aCx)); + if (!JS_Enumerate(aCx, lexicalEnv, &lexicalIds)) { + return false; + } + + for (size_t i = 0; i < lexicalIds.length(); i++) { + if (!aProps.append(lexicalIds[i])) { + JS_ReportOutOfMemory(aCx); + return false; + } + } + + return true; +} + +JSObject* CreateJSMEnvironmentProxy(JSContext* aCx, + JS::Handle<JSObject*> aGlobalObj) { + js::ProxyOptions options; + options.setLazyProto(true); + + JS::Rooted<JS::Value> globalVal(aCx, JS::ObjectValue(*aGlobalObj)); + return NewProxyObject(aCx, &JSMEnvironmentProxyHandler::gHandler, globalVal, + nullptr, options); +} + +} // namespace loader +} // namespace mozilla |