/* -*- 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 // 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 aProxy, JS::Handle aId, JS::Handle aDesc, JS::ObjectOpResult& aResult) const override { return aResult.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE); } bool getPrototype(JSContext* aCx, JS::Handle aProxy, JS::MutableHandle aProtop) const override { aProtop.set(nullptr); return true; } bool setPrototype(JSContext* aCx, JS::Handle aProxy, JS::Handle aProto, JS::ObjectOpResult& aResult) const override { if (!aProto) { return aResult.succeed(); } return aResult.failCantSetProto(); } bool getPrototypeIfOrdinary( JSContext* aCx, JS::Handle aProxy, bool* aIsOrdinary, JS::MutableHandle aProtop) const override { *aIsOrdinary = false; return true; } bool setImmutablePrototype(JSContext* aCx, JS::Handle aProxy, bool* aSucceeded) const override { *aSucceeded = true; return true; } bool preventExtensions(JSContext* aCx, JS::Handle aProxy, JS::ObjectOpResult& aResult) const override { aResult.succeed(); return true; } bool isExtensible(JSContext* aCx, JS::Handle aProxy, bool* aExtensible) const override { *aExtensible = false; return true; } bool set(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, JS::Handle aValue, JS::Handle aReceiver, JS::ObjectOpResult& aResult) const override { return aResult.failReadOnly(); } bool delete_(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, JS::ObjectOpResult& aResult) const override { return aResult.failCantDelete(); } bool getOwnPropertyDescriptor( JSContext* aCx, JS::Handle aProxy, JS::Handle aId, JS::MutableHandle> aDesc) const override; bool has(JSContext* aCx, JS::Handle aProxy, JS::Handle aId, bool* aBp) const override; bool get(JSContext* aCx, JS::Handle aProxy, JS::Handle aReceiver, JS::Handle aId, JS::MutableHandle aVp) const override; bool ownPropertyKeys( JSContext* aCx, JS::Handle aProxy, JS::MutableHandleVector aProps) const override; private: static JSObject* getGlobal(JSContext* aCx, JS::Handle aProxy) { JS::Rooted 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 aModObj, JS::Handle aId) { if (JS_HasExtensibleLexicalEnvironment(aModObj)) { JS::Rooted 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 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 aProxy, JS::Handle aId, JS::MutableHandle> aDesc) const { JS::Rooted globalObj(aCx, getGlobal(aCx, aProxy)); JS::Rooted 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 aProxy, JS::Handle aId, bool* aBp) const { JS::Rooted globalObj(aCx, getGlobal(aCx, aProxy)); JS::Rooted holder( aCx, ResolveModuleObjectPropertyById(aCx, globalObj, aId)); return JS_HasPropertyById(aCx, holder, aId, aBp); } bool JSMEnvironmentProxyHandler::get(JSContext* aCx, JS::Handle aProxy, JS::Handle aReceiver, JS::Handle aId, JS::MutableHandle aVp) const { JS::Rooted globalObj(aCx, getGlobal(aCx, aProxy)); JS::Rooted 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 aProxy, JS::MutableHandleVector aProps) const { JS::Rooted globalObj(aCx, getGlobal(aCx, aProxy)); JS::Rooted 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 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 aGlobalObj) { js::ProxyOptions options; options.setLazyProto(true); JS::Rooted globalVal(aCx, JS::ObjectValue(*aGlobalObj)); return NewProxyObject(aCx, &JSMEnvironmentProxyHandler::gHandler, globalVal, nullptr, options); } } // namespace loader } // namespace mozilla