author | Peter Van der Beken <peterv@propagandism.org> |
Fri, 30 Mar 2012 21:42:20 -0700 | |
changeset 90770 | 1bdb337e3136c877e5d93b9c3ebac548bd7e4fff |
parent 90769 | 031949d40753cf8678ca9492c165a2ca885f330f |
child 90771 | b857d5215c55902a057377b80a992a08002741b1 |
push id | 22382 |
push user | bmo@edmorley.co.uk |
push date | Sat, 31 Mar 2012 21:44:34 +0000 |
treeherder | mozilla-central@bbe5086163c9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bent, bz, bholley, jlebar, khuey, peterv, sicking, smaug |
bugs | 740069 |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -89,18 +89,20 @@ #include "nsAboutProtocolUtils.h" #include "nsIClassInfo.h" #include "nsIURIFixup.h" #include "nsCDefaultURIFixup.h" #include "nsIChromeRegistry.h" #include "nsIContentSecurityPolicy.h" #include "nsIAsyncVerifyRedirectCallback.h" #include "mozilla/Preferences.h" +#include "mozilla/dom/bindings/Utils.h" using namespace mozilla; +using namespace mozilla::dom; static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID); nsIIOService *nsScriptSecurityManager::sIOService = nsnull; nsIXPConnect *nsScriptSecurityManager::sXPConnect = nsnull; nsIThreadJSContextStack *nsScriptSecurityManager::sJSContextStack = nsnull; nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull; JSRuntime *nsScriptSecurityManager::sRuntime = 0; @@ -2440,19 +2442,27 @@ nsScriptSecurityManager::doGetObjectPrin aAllowShortCircuit #else true #endif ); if (result) { break; } - } else if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | - JSCLASS_PRIVATE_IS_NSISUPPORTS))) { - nsISupports *priv = (nsISupports *) js::GetObjectPrivate(aObj); + } else { + nsISupports *priv; + if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE | + JSCLASS_PRIVATE_IS_NSISUPPORTS))) { + priv = (nsISupports *) js::GetObjectPrivate(aObj); + } else if ((jsClass->flags & JSCLASS_IS_DOMJSCLASS) && + bindings::DOMJSClass::FromJSClass(jsClass)->mDOMObjectIsISupports) { + priv = bindings::UnwrapDOMObject<nsISupports>(aObj, jsClass); + } else { + priv = nsnull; + } #ifdef DEBUG if (aAllowShortCircuit) { nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper = do_QueryInterface(priv); NS_ASSERTION(!xpcWrapper || !strcmp(jsClass->name, "XPCNativeWrapper"),
--- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -6575,26 +6575,27 @@ nsContentUtils::GetRootDocument(nsIDocum } // static void nsContentUtils::ReleaseWrapper(nsISupports* aScriptObjectHolder, nsWrapperCache* aCache) { if (aCache->PreservingWrapper()) { + // PreserveWrapper puts new DOM bindings in the JS holders hash, but they + // can also be in the DOM expando hash, so we need to try to remove them + // from both here. JSObject* obj = aCache->GetWrapperPreserveColor(); - if (aCache->IsDOMBinding()) { + if (aCache->IsDOMBinding() && obj) { JSCompartment *compartment = js::GetObjectCompartment(obj); xpc::CompartmentPrivate *priv = static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment)); priv->RemoveDOMExpandoObject(obj); } - else { - DropJSObjects(aScriptObjectHolder); - } + DropJSObjects(aScriptObjectHolder); aCache->SetPreservingWrapper(false); } } // static void nsContentUtils::TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
--- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -100,16 +100,17 @@ #include "nsIContentSecurityPolicy.h" #include "nsAsyncRedirectVerifyHelper.h" #include "jstypedarray.h" #include "nsStringBuffer.h" #include "nsDOMFile.h" #include "nsIFileChannel.h" #include "mozilla/Telemetry.h" #include "sampler.h" +#include "mozilla/dom/bindings/XMLHttpRequestBinding.h" #include "nsIDOMFormData.h" #include "nsWrapperCacheInlines.h" using namespace mozilla; using namespace mozilla::dom; #define LOAD_STR "load" @@ -478,16 +479,18 @@ nsXMLHttpRequest::nsXMLHttpRequest() mWarnAboutSyncHtml(false), mLoadLengthComputable(false), mLoadTotal(0), mFirstStartRequestSeen(false), mInLoadProgressEvent(false), mResultJSON(JSVAL_VOID), mResultArrayBuffer(nsnull) { nsLayoutStatics::AddRef(); + + SetIsDOMBinding(); #ifdef DEBUG StaticAssertions(); #endif } nsXMLHttpRequest::~nsXMLHttpRequest() { mState |= XML_HTTP_REQUEST_DELETED; @@ -535,42 +538,42 @@ nsXMLHttpRequest::Init() nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); nsCOMPtr<nsIPrincipal> subjectPrincipal; if (secMan) { nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); NS_ENSURE_SUCCESS(rv, rv); } NS_ENSURE_STATE(subjectPrincipal); - mPrincipal = subjectPrincipal; nsIScriptContext* context = GetScriptContextFromJSContext(cx); + nsCOMPtr<nsPIDOMWindow> window; if (context) { - nsCOMPtr<nsPIDOMWindow> window = - do_QueryInterface(context->GetGlobalObject()); - BindToOwner(window ? window->GetCurrentInnerWindow() : nsnull); + window = do_QueryInterface(context->GetGlobalObject()); + if (window) { + window = window->GetCurrentInnerWindow(); + } } + Construct(subjectPrincipal, window); return NS_OK; } /** * This Init method should only be called by C++ consumers. */ NS_IMETHODIMP nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal, nsIScriptContext* aScriptContext, nsPIDOMWindow* aOwnerWindow, nsIURI* aBaseURI) { NS_ENSURE_ARG_POINTER(aPrincipal); - - mPrincipal = aPrincipal; - BindToOwner(aOwnerWindow ? aOwnerWindow->GetCurrentInnerWindow() : nsnull); - mBaseURI = aBaseURI; - + Construct(aPrincipal, + aOwnerWindow ? aOwnerWindow->GetCurrentInnerWindow() : nsnull, + aBaseURI); return NS_OK; } /** * This Initialize method is called from XPConnect via nsIJSNativeInitializer. */ NS_IMETHODIMP nsXMLHttpRequest::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj, @@ -581,19 +584,19 @@ nsXMLHttpRequest::Initialize(nsISupports NS_WARNING("Unexpected nsIJSNativeInitializer owner"); return NS_OK; } // This XHR object is bound to a |window|, // so re-set principal and script context. nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(aOwner); NS_ENSURE_STATE(scriptPrincipal); - mPrincipal = scriptPrincipal->GetPrincipal(); - BindToOwner(owner); - return NS_OK; + + Construct(scriptPrincipal->GetPrincipal(), owner); + return NS_OK; } void nsXMLHttpRequest::ResetResponse() { mResponseXML = nsnull; mResponseBody.Truncate(); mResponseText.Truncate(); @@ -1082,16 +1085,25 @@ void nsXMLHttpRequest::StaticAssertions() { #define ASSERT_ENUM_EQUAL(_lc, _uc) \ MOZ_STATIC_ASSERT(\ bindings::prototypes::XMLHttpRequestResponseType::_lc \ == bindings::prototypes::XMLHttpRequestResponseType::value(XML_HTTP_RESPONSE_TYPE_ ## _uc), \ #_uc " should match") + ASSERT_ENUM_EQUAL(_empty, DEFAULT); + ASSERT_ENUM_EQUAL(arraybuffer, ARRAYBUFFER); + ASSERT_ENUM_EQUAL(blob, BLOB); + ASSERT_ENUM_EQUAL(document, DOCUMENT); + ASSERT_ENUM_EQUAL(json, JSON); + ASSERT_ENUM_EQUAL(text, TEXT); + ASSERT_ENUM_EQUAL(moz_chunked_text, CHUNKED_TEXT); + ASSERT_ENUM_EQUAL(moz_chunked_arraybuffer, CHUNKED_ARRAYBUFFER); + ASSERT_ENUM_EQUAL(moz_blob, MOZ_BLOB); #undef ASSERT_ENUM_EQUAL } #endif /* attribute AString responseType; */ NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType) { nsXMLHttpRequest::ResponseType responseType; @@ -1118,16 +1130,23 @@ NS_IMETHODIMP nsXMLHttpRequest::SetRespo } nsresult rv = NS_OK; SetResponseType(responseType, rv); return rv; } void +nsXMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aType, + nsresult& aRv) +{ + SetResponseType(ResponseType(aType), aRv); +} + +void nsXMLHttpRequest::SetResponseType(nsXMLHttpRequest::ResponseType aResponseType, nsresult& aRv) { // If the state is not OPENED or HEADERS_RECEIVED raise an // INVALID_STATE_ERR exception and terminate these steps. if (!(mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT | XML_HTTP_REQUEST_HEADERS_RECEIVED))) { aRv = NS_ERROR_DOM_INVALID_STATE_ERR; @@ -2701,25 +2720,25 @@ GetRequestBody(nsIVariant* aBody, nsIInp string.Adopt(data, len); return GetRequestBody(string, aResult, aContentType, aCharset); } /* static */ nsresult nsXMLHttpRequest::GetRequestBody(nsIVariant* aVariant, - const RequestBody* aBody, + const Nullable<RequestBody>& aBody, nsIInputStream** aResult, nsACString& aContentType, nsACString& aCharset) { if (aVariant) { return ::GetRequestBody(aVariant, aResult, aContentType, aCharset); } - const RequestBody& body = *aBody; + const RequestBody& body = aBody.Value(); RequestBody::Value value = body.GetValue(); switch (body.GetType()) { case nsXMLHttpRequest::RequestBody::ArrayBuffer: { return ::GetRequestBody(value.mArrayBuffer, aResult, aContentType, aCharset); } case nsXMLHttpRequest::RequestBody::Blob: { @@ -2759,21 +2778,21 @@ nsXMLHttpRequest::GetRequestBody(nsIVari NS_NOTREACHED("Default cases exist for a reason"); return NS_OK; } /* void send (in nsIVariant aBody); */ NS_IMETHODIMP nsXMLHttpRequest::Send(nsIVariant *aBody) { - return Send(aBody, nsnull); + return Send(aBody, Nullable<RequestBody>()); } nsresult -nsXMLHttpRequest::Send(nsIVariant* aVariant, const RequestBody* aBody) +nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody) { NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED); nsresult rv = CheckInnerWindowCorrectness(); NS_ENSURE_SUCCESS(rv, rv); // Return error if we're already processing a request if (XML_HTTP_REQUEST_SENT & mState) { @@ -2880,17 +2899,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari mUploadTotal = 0; // By default we don't have any upload, so mark upload complete. mUploadComplete = true; mErrorLoad = false; mLoadLengthComputable = false; mLoadTotal = 0; mUploadProgress = 0; mUploadProgressMax = 0; - if ((aVariant || aBody) && httpChannel && + if ((aVariant || !aBody.IsNull()) && httpChannel && !method.EqualsLiteral("GET")) { nsCAutoString charset; nsCAutoString defaultContentType; nsCOMPtr<nsIInputStream> postDataStream; rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream), defaultContentType, charset); @@ -3836,16 +3855,30 @@ nsXMLHttpRequest::GetInterface(const nsI return wwatch->GetPrompt(window, aIID, reinterpret_cast<void**>(aResult)); } return QueryInterface(aIID, aResult); } +JS::Value +nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSIID* aIID, nsresult& aRv) +{ + const nsID* iid = aIID->GetID(); + nsCOMPtr<nsISupports> result; + JS::Value v = JSVAL_NULL; + aRv = GetInterface(*iid, getter_AddRefs(result)); + NS_ENSURE_SUCCESS(aRv, JSVAL_NULL); + + JSObject* global = JS_GetGlobalForObject(aCx, GetWrapper()); + aRv = nsContentUtils::WrapNative(aCx, global, result, iid, &v); + return NS_SUCCEEDED(aRv) ? v : JSVAL_NULL; +} + nsXMLHttpRequestUpload* nsXMLHttpRequest::GetUpload() { if (!mUpload) { mUpload = new nsXMLHttpRequestUpload(this); } return mUpload; }
--- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -62,124 +62,83 @@ #include "nsIDOMNSEvent.h" #include "nsITimer.h" #include "nsIPrivateDOMEvent.h" #include "nsDOMProgressEvent.h" #include "nsDOMEventTargetHelper.h" #include "nsContentUtils.h" #include "nsDOMFile.h" #include "nsDOMBlobBuilder.h" +#include "nsIPrincipal.h" +#include "nsIScriptObjectPrincipal.h" +#include "mozilla/dom/bindings/XMLHttpRequestBinding.h" +#include "mozilla/dom/bindings/XMLHttpRequestUploadBinding.h" + +#include "mozilla/Assertions.h" class nsILoadGroup; class AsyncVerifyRedirectCallbackForwarder; class nsIUnicodeDecoder; class nsIDOMFormData; +#define IMPL_EVENT_HANDLER(_lowercase, _capitalized) \ + JSObject* GetOn##_lowercase() \ + { \ + return GetListenerAsJSObject(mOn##_capitalized##Listener); \ + } \ + void SetOn##_lowercase(JSContext* aCx, JSObject* aCallback, nsresult& aRv) \ + { \ + aRv = SetJSObjectListener(aCx, NS_LITERAL_STRING(#_lowercase), \ + mOn##_capitalized##Listener, \ + aCallback); \ + } + class nsXHREventTarget : public nsDOMEventTargetHelper, public nsIXMLHttpRequestEventTarget { public: + typedef mozilla::dom::bindings::prototypes::XMLHttpRequestResponseType::value + XMLHttpRequestResponseType; + virtual ~nsXHREventTarget() {} NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXHREventTarget, nsDOMEventTargetHelper) NS_DECL_NSIXMLHTTPREQUESTEVENTTARGET NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::) - JSObject* GetOnloadstart() - { - return GetListenerAsJSObject(mOnLoadStartListener); - } - JSObject* GetOnprogress() - { - return GetListenerAsJSObject(mOnProgressListener); - } - JSObject* GetOnabort() - { - return GetListenerAsJSObject(mOnAbortListener); - } - JSObject* GetOnerror() - { - return GetListenerAsJSObject(mOnErrorListener); - } - JSObject* GetOnload() - { - return GetListenerAsJSObject(mOnLoadListener); - } - JSObject* GetOntimeout() - { - return GetListenerAsJSObject(mOnTimeoutListener); - } - JSObject* GetOnloadend() - { - return GetListenerAsJSObject(mOnLoadendListener); - } - void SetOnloadstart(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("loadstart"), - mOnLoadStartListener, - aCallback); - } - void SetOnprogress(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("progress"), - mOnProgressListener, - aCallback); - } - void SetOnabort(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("abort"), mOnAbortListener, - aCallback); - } - void SetOnerror(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("error"), mOnErrorListener, - aCallback); - } - void SetOnload(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("load"), mOnLoadListener, - aCallback); - } - void SetOntimeout(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("timeout"), - mOnTimeoutListener, - aCallback); - } - void SetOnloadend(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("loadend"), - mOnLoadendListener, - aCallback); - } - + IMPL_EVENT_HANDLER(loadstart, LoadStart) + IMPL_EVENT_HANDLER(progress, Progress) + IMPL_EVENT_HANDLER(abort, Abort) + IMPL_EVENT_HANDLER(error, Error) + IMPL_EVENT_HANDLER(load, Load) + IMPL_EVENT_HANDLER(timeout, Timeout) + IMPL_EVENT_HANDLER(loadend, Loadend) + virtual void DisconnectFromOwner(); protected: static inline JSObject* GetListenerAsJSObject(nsDOMEventListenerWrapper* aWrapper) { nsCOMPtr<nsIXPConnectJSObjectHolder> holder = do_QueryInterface(aWrapper->GetInner()); JSObject* obj; return holder && NS_SUCCEEDED(holder->GetJSObject(&obj)) ? obj : nsnull; } - inline nsresult SetJSObjectListener(const nsAString& aType, + inline nsresult SetJSObjectListener(JSContext* aCx, + const nsAString& aType, nsRefPtr<nsDOMEventListenerWrapper>& aWrapper, JSObject* aCallback) { - nsresult rv; - nsIScriptContext* context = GetContextForEventHandlers(&rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr<nsIDOMEventListener> listener; if (aCallback) { - rv = nsContentUtils::XPConnect()->WrapJS(context->GetNativeContext(), - aCallback, - NS_GET_IID(nsIDOMEventListener), - getter_AddRefs(listener)); + nsresult rv = + nsContentUtils::XPConnect()->WrapJS(aCx, + aCallback, + NS_GET_IID(nsIDOMEventListener), + getter_AddRefs(listener)); NS_ENSURE_SUCCESS(rv, rv); } return RemoveAddEventListener(aType, aWrapper, listener); } nsRefPtr<nsDOMEventListenerWrapper> mOnLoadListener; nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener; @@ -192,22 +151,33 @@ protected: class nsXMLHttpRequestUpload : public nsXHREventTarget, public nsIXMLHttpRequestUpload { public: nsXMLHttpRequestUpload(nsDOMEventTargetHelper* aOwner) { BindToOwner(aOwner); + SetIsDOMBinding(); } NS_DECL_ISUPPORTS_INHERITED NS_FORWARD_NSIXMLHTTPREQUESTEVENTTARGET(nsXHREventTarget::) NS_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget::) NS_DECL_NSIXMLHTTPREQUESTUPLOAD + virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, + bool *triedToWrap) + { + return mozilla::dom::bindings::prototypes::XMLHttpRequestUpload::Wrap(cx, scope, this, triedToWrap); + } + nsISupports* GetParentObject() + { + return GetOwner(); + } + bool HasListeners() { return mListenerManager && mListenerManager->HasListeners(); } }; class nsXMLHttpRequest : public nsXHREventTarget, public nsIXMLHttpRequest, @@ -220,16 +190,53 @@ class nsXMLHttpRequest : public nsXHREve public nsIJSNativeInitializer, public nsITimerCallback { friend class nsXHRParseEndListener; public: nsXMLHttpRequest(); virtual ~nsXMLHttpRequest(); + virtual JSObject* WrapObject(JSContext *cx, JSObject *scope, + bool *triedToWrap) + { + return mozilla::dom::bindings::prototypes::XMLHttpRequest::Wrap(cx, scope, this, triedToWrap); + } + nsISupports* GetParentObject() + { + return GetOwner(); + } + + // The WebIDL parser converts constructors into methods called _Constructor. + static already_AddRefed<nsXMLHttpRequest> + _Constructor(nsISupports* aGlobal, nsresult& aRv) + { + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal); + nsCOMPtr<nsIScriptObjectPrincipal> principal = do_QueryInterface(aGlobal); + if (!window || ! principal) { + aRv = NS_ERROR_FAILURE; + return NULL; + } + + nsRefPtr<nsXMLHttpRequest> req = new nsXMLHttpRequest(); + req->Construct(principal->GetPrincipal(), window); + return req.forget(); + } + + void Construct(nsIPrincipal* aPrincipal, + nsPIDOMWindow* aOwnerWindow, + nsIURI* aBaseURI = NULL) + { + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT_IF(aOwnerWindow, aOwnerWindow->IsInnerWindow()); + mPrincipal = aPrincipal; + BindToOwner(aOwnerWindow); + mBaseURI = aBaseURI; + } + NS_DECL_ISUPPORTS_INHERITED // nsIXMLHttpRequest NS_DECL_NSIXMLHTTPREQUEST // nsIJSXMLHttpRequest NS_IMETHOD GetOnuploadprogress(nsIDOMEventListener** aOnuploadprogress); NS_IMETHOD SetOnuploadprogress(nsIDOMEventListener* aOnuploadprogress); @@ -260,26 +267,17 @@ public: NS_FORWARD_NSIDOMEVENTTARGET(nsXHREventTarget::) #ifdef DEBUG void StaticAssertions(); #endif // event handler - JSObject* GetOnreadystatechange() - { - return GetListenerAsJSObject(mOnReadystatechangeListener); - } - void SetOnreadystatechange(JSObject* aCallback, nsresult& aRv) - { - aRv = SetJSObjectListener(NS_LITERAL_STRING("readystatechange"), - mOnReadystatechangeListener, - aCallback); - } + IMPL_EVENT_HANDLER(readystatechange, Readystatechange) // states uint16_t GetReadyState(); // request void Open(const nsAString& aMethod, const nsAString& aUrl, bool aAsync, const nsAString& aUser, const nsAString& aPassword, nsresult& aRv) { @@ -363,31 +361,35 @@ private: } private: Type mType; Value mValue; }; static nsresult GetRequestBody(nsIVariant* aVariant, - const RequestBody* aBody, + const Nullable<RequestBody>& aBody, nsIInputStream** aResult, nsACString& aContentType, nsACString& aCharset); - nsresult Send(nsIVariant* aVariant, const RequestBody* aBody); + nsresult Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody); + nsresult Send(const Nullable<RequestBody>& aBody) + { + return Send(nsnull, aBody); + } nsresult Send(const RequestBody& aBody) { - return Send(nsnull, &aBody); + return Send(Nullable<RequestBody>(aBody)); } public: void Send(nsresult& aRv) { - aRv = Send(nsnull, nsnull); + aRv = Send(Nullable<RequestBody>()); } void Send(JSObject* aArrayBuffer, nsresult& aRv) { NS_ASSERTION(aArrayBuffer, "Null should go to string version"); aRv = Send(RequestBody(aArrayBuffer)); } void Send(nsIDOMBlob* aBlob, nsresult& aRv) { @@ -444,30 +446,38 @@ public: } } void GetAllResponseHeaders(nsString& aResponseHeaders); void OverrideMimeType(const nsAString& aMimeType) { // XXX Should we do some validation here? mOverrideMimeType = aMimeType; } + XMLHttpRequestResponseType GetResponseType() + { + return XMLHttpRequestResponseType(mResponseType); + } + void SetResponseType(XMLHttpRequestResponseType aType, nsresult& aRv); JS::Value GetResponse(JSContext* aCx, nsresult& aRv); void GetResponseText(nsString& aResponseText, nsresult& aRv); nsIDocument* GetResponseXML(nsresult& aRv); bool GetMozBackgroundRequest(); void SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv); bool GetMultipart(); void SetMultipart(bool aMultipart, nsresult& aRv); nsIChannel* GetChannel() { return mChannel; } + // We need a GetInterface callable from JS for chrome JS + JS::Value GetInterface(JSContext*aCx, nsIJSIID* aIID, nsresult& aRv); + // This creates a trusted readystatechange event, which is not cancelable and // doesn't bubble. static nsresult CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent); // For backwards compatibility aPosition should contain the headers for upload // and aTotalSize is LL_MAXUINT when unknown. Both those values are // used by nsXMLHttpProgressEvent. Normal progress event should not use // headers in aLoaded and aTotal is 0 when unknown. void DispatchProgressEvent(nsDOMEventTargetHelper* aTarget, @@ -707,16 +717,18 @@ protected: struct RequestHeader { nsCString header; nsCString value; }; nsTArray<RequestHeader> mModifiedRequestHeaders; }; +#undef IMPL_EVENT_HANDLER + // helper class to expose a progress DOM Event class nsXMLHttpProgressEvent : public nsIDOMProgressEvent, public nsIDOMLSProgressEvent, public nsIDOMNSEvent, public nsIPrivateDOMEvent { public:
--- a/content/events/src/nsDOMEventTargetHelper.h +++ b/content/events/src/nsDOMEventTargetHelper.h @@ -72,16 +72,37 @@ class nsDOMEventTargetHelper : public ns { public: nsDOMEventTargetHelper() : mOwner(nsnull), mHasOrHasHadOwner(false) {} virtual ~nsDOMEventTargetHelper(); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMEventTargetHelper) NS_DECL_NSIDOMEVENTTARGET + void AddEventListener(const nsAString& aType, + nsIDOMEventListener* aCallback, // XXX nullable + bool aCapture, Nullable<bool>& aWantsUntrusted, + nsresult& aRv) + { + aRv = AddEventListener(aType, aCallback, aCapture, + !aWantsUntrusted.IsNull() && aWantsUntrusted.Value(), + aWantsUntrusted.IsNull() ? 1 : 2); + } + void RemoveEventListener(const nsAString& aType, + nsIDOMEventListener* aCallback, + bool aCapture, nsresult& aRv) + { + aRv = RemoveEventListener(aType, aCallback, aCapture); + } + bool DispatchEvent(nsIDOMEvent* aEvent, nsresult& aRv) + { + bool result = false; + aRv = DispatchEvent(aEvent, &result); + return result; + } void GetParentObject(nsIScriptGlobalObject **aParentObject) { if (mOwner) { CallQueryInterface(mOwner, aParentObject); } else { *aParentObject = nsnull;
--- a/content/xbl/src/Makefile.in +++ b/content/xbl/src/Makefile.in @@ -85,11 +85,12 @@ LOCAL_INCLUDES = \ -I$(srcdir)/../../html/document/src \ -I$(srcdir)/../../xml/document/src \ -I$(srcdir)/../../xul/content/src \ -I$(srcdir)/../../xul/document/src \ -I$(srcdir)/../../events/src \ -I$(srcdir)/../../../layout/style \ -I$(srcdir)/../../../dom/base \ -I$(topsrcdir)/xpcom/ds \ + -I$(topsrcdir)/js/xpconnect/src \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -54,20 +54,23 @@ #include "nsIScriptSecurityManager.h" #include "nsContentUtils.h" #include "nsDOMJSUtils.h" #include "mozilla/Services.h" #include "xpcpublic.h" #include "mozilla/scache/StartupCache.h" #include "mozilla/scache/StartupCacheUtils.h" #include "nsCCUncollectableMarker.h" +#include "mozilla/dom/bindings/Utils.h" using namespace mozilla::scache; using namespace mozilla; +using mozilla::dom::bindings::DestroyProtoOrIfaceCache; + static const char kXBLCachePrefix[] = "xblcache"; static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); // An XBLDocumentInfo object has a special context associated with it which we can use to pre-compile // properties and methods of XBL bindings against. class nsXBLDocGlobalObject : public nsIScriptGlobalObject, public nsIScriptObjectPrincipal @@ -175,16 +178,18 @@ nsXBLDocGlobalObject_finalize(JSContext nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeThis)); if (sgo) sgo->OnFinalize(obj); // The addref was part of JSObject construction NS_RELEASE(nativeThis); + + DestroyProtoOrIfaceCache(obj); } static JSBool nsXBLDocGlobalObject_resolve(JSContext *cx, JSObject *obj, jsid id) { JSBool did_resolve = JS_FALSE; return JS_ResolveStandardClass(cx, obj, id, &did_resolve); }
--- a/content/xul/document/src/Makefile.in +++ b/content/xul/document/src/Makefile.in @@ -71,11 +71,13 @@ LOCAL_INCLUDES = -I$(srcdir)/../../../ba -I$(srcdir)/../../../../layout/base \ -I$(srcdir)/../../../../layout/generic \ -I$(srcdir)/../../../../layout/style \ -I$(srcdir)/../../../../layout/xul/base/src \ -I$(srcdir)/../../../xml/document/src \ -I$(srcdir)/../../../xbl/src \ -I$(srcdir)/../../../events/src \ -I$(topsrcdir)/xpcom/ds \ + -I$(topsrcdir)/js/xpconnect/src \ + -I$(topsrcdir)/dom/base \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp +++ b/content/xul/document/src/nsXULPrototypeDocument.cpp @@ -61,16 +61,19 @@ #include "mozilla/FunctionTimer.h" #include "nsIDOMScriptObjectFactory.h" #include "nsDOMCID.h" #include "nsNodeInfoManager.h" #include "nsContentUtils.h" #include "nsCCUncollectableMarker.h" #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext #include "xpcpublic.h" +#include "mozilla/dom/bindings/Utils.h" + +using mozilla::dom::bindings::DestroyProtoOrIfaceCache; static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); class nsXULPDGlobalObject : public nsIScriptGlobalObject, public nsIScriptObjectPrincipal { @@ -124,16 +127,18 @@ nsXULPDGlobalObject_finalize(JSContext * nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeThis)); if (sgo) { sgo->OnFinalize(obj); } // The addref was part of JSObject construction NS_RELEASE(nativeThis); + + DestroyProtoOrIfaceCache(obj); } JSBool nsXULPDGlobalObject_resolve(JSContext *cx, JSObject *obj, jsid id) { JSBool did_resolve = JS_FALSE;
--- a/dom/Makefile.in +++ b/dom/Makefile.in @@ -71,16 +71,17 @@ DIRS = \ $(NULL) DIRS += \ interfaces/apps \ $(NULL) DIRS += \ base \ + bindings \ battery \ contacts \ power \ settings \ sms \ src \ locales \ network \
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -52,16 +52,18 @@ #include "jsprvtd.h" // we are using private JS typedefs... #include "jsdbgapi.h" #include "WrapperFactory.h" #include "AccessCheck.h" #include "xpcprivate.h" #include "XPCWrapper.h" +#include "mozilla/dom/bindings/Common.h" + #include "nscore.h" #include "nsDOMClassInfo.h" #include "nsCRT.h" #include "nsCRTGlue.h" #include "nsIServiceManager.h" #include "nsICategoryManager.h" #include "nsIComponentRegistrar.h" #include "nsXPCOM.h" @@ -634,34 +636,32 @@ DOMCI_DATA(DOMConstructor, void) nsnull, \ nsnull, \ nsnull, \ _flags, \ true, \ 0, \ false, \ false, \ - NULL, \ NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \ }, #define NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA_WITH_NAME(_class, _name, \ _helper, _flags) \ { #_name, \ nsnull, \ { _helper::doCreate }, \ nsnull, \ nsnull, \ nsnull, \ _flags, \ true, \ 0, \ true, \ false, \ - NULL, \ NS_DEFINE_CLASSINFO_DATA_DEBUG(_class) \ }, #define NS_DEFINE_CLASSINFO_DATA(_class, _helper, _flags) \ NS_DEFINE_CLASSINFO_DATA_WITH_NAME(_class, _class, _helper, _flags) #define NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(_class, _helper, _flags) \ NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA_WITH_NAME(_class, _class, _helper, \ @@ -4460,17 +4460,21 @@ nsDOMClassInfo::Init() RegisterExternalClasses(); sDisableDocumentAllSupport = Preferences::GetBool("browser.dom.document.all.disabled"); sDisableGlobalScopePollutionSupport = Preferences::GetBool("browser.dom.global_scope_pollution.disabled"); - mozilla::dom::binding::Register(sClassInfoData); + // Proxy bindings + mozilla::dom::binding::Register(nameSpaceManager); + + // Non-proxy bindings + mozilla::dom::bindings::Register(nameSpaceManager); sIsInitialized = true; return NS_OK; } // static PRInt32 @@ -6566,16 +6570,25 @@ nsWindowSH::GlobalResolve(nsGlobalWindow return rv; } } if (name_struct->mType == nsGlobalNameStruct::eTypeInterface) { // We're resolving a name of a DOM interface for which there is no // direct DOM class, create a constructor object... + // Lookup new DOM bindings. + mozilla::dom::binding::DefineInterface define = + name_struct->mDefineDOMInterface; + if (define && mozilla::dom::binding::DefineConstructor(cx, obj, define, &rv)) { + *did_resolve = NS_SUCCEEDED(rv); + + return rv; + } + nsRefPtr<nsDOMConstructor> constructor; rv = nsDOMConstructor::Create(class_name, nsnull, name_struct, static_cast<nsPIDOMWindow*>(aWin), getter_AddRefs(constructor)); NS_ENSURE_SUCCESS(rv, rv); @@ -6623,17 +6636,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow if (!nsEventSource::PrefEnabled()) { return NS_OK; } } // Lookup new DOM bindings. if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) { mozilla::dom::binding::DefineInterface define = - sClassInfoData[name_struct->mDOMClassInfoID].mDefineDOMInterface; + name_struct->mDefineDOMInterface; if (define && mozilla::dom::binding::DefineConstructor(cx, obj, define, &rv)) { *did_resolve = NS_SUCCEEDED(rv); return rv; } } // Create the XPConnect prototype for our classinfo, PostCreateProto will @@ -6662,16 +6675,26 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *did_resolve = NS_SUCCEEDED(rv); return rv; } if (name_struct->mType == nsGlobalNameStruct::eTypeClassProto) { // We don't have a XPConnect prototype object, let ResolvePrototype create // one. + + // Lookup new DOM bindings. + mozilla::dom::binding::DefineInterface define = + name_struct->mDefineDOMInterface; + if (define && mozilla::dom::binding::DefineConstructor(cx, obj, define, &rv)) { + *did_resolve = NS_SUCCEEDED(rv); + + return rv; + } + return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, nsnull, name_struct, nameSpaceManager, nsnull, true, did_resolve); } if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) { const nsGlobalNameStruct *alias_struct = nameSpaceManager->GetConstructorProto(name_struct); @@ -7832,20 +7855,16 @@ nsEventTargetSH::PreCreate(nsISupports * return NS_OK; } NS_IMETHODIMP nsEventTargetSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, bool *_retval) { - if (id == sAddEventListener_id) { - return NS_OK; - } - nsEventTargetSH::PreserveWrapper(GetNative(wrapper, obj)); return NS_OK; } void nsEventTargetSH::PreserveWrapper(nsISupports *aNative) {
--- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -98,18 +98,16 @@ struct nsDOMClassInfoData // so be sure to mask if necessary! const nsIID *mProtoChainInterface; const nsIID **mInterfaces; PRUint32 mScriptableFlags : 31; // flags must not use more than 31 bits! PRUint32 mHasClassInterface : 1; PRUint32 mInterfacesBitmap; bool mChromeOnly; bool mDisabled; - // For new style DOM bindings. - mozilla::dom::binding::DefineInterface mDefineDOMInterface; #ifdef NS_DEBUG PRUint32 mDebugID; #endif }; struct nsExternalDOMClassInfoData : public nsDOMClassInfoData { const nsCID *mConstructorCID;
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -103,16 +103,17 @@ #define FORCE_PR_LOG 1 #endif #include "prlog.h" #include "prthread.h" #include "mozilla/FunctionTimer.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" +#include "mozilla/dom/bindings/Utils.h" #include "sampler.h" using namespace mozilla; const size_t gStackSize = 8192; #ifdef PR_LOGGING @@ -2001,22 +2002,26 @@ nsJSContext::GetGlobalObject() // If this assertion hits then it means that we have a window object as // our global, but we never called CreateOuterObject. NS_ASSERTION(inner == global, "Shouldn't be able to innerize here"); } #endif JSClass *c = JS_GetClass(global); - if (!c || ((~c->flags) & (JSCLASS_HAS_PRIVATE | - JSCLASS_PRIVATE_IS_NSISUPPORTS))) { + // Whenever we end up with globals that are JSCLASS_IS_DOMJSCLASS + // and have an nsISupports DOM object, we will need to modify this + // check here. + MOZ_ASSERT(!(c->flags & JSCLASS_IS_DOMJSCLASS)); + if ((~c->flags) & (JSCLASS_HAS_PRIVATE | + JSCLASS_PRIVATE_IS_NSISUPPORTS)) { return nsnull; } - - nsISupports *priv = (nsISupports *)js::GetObjectPrivate(global); + + nsISupports *priv = static_cast<nsISupports*>(js::GetObjectPrivate(global)); nsCOMPtr<nsIXPConnectWrappedNative> wrapped_native = do_QueryInterface(priv); nsCOMPtr<nsIScriptGlobalObject> sgo; if (wrapped_native) { // The global object is a XPConnect wrapped native, the native in // the wrapper might be the nsIScriptGlobalObject
--- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -56,16 +56,18 @@ #include "nsIXPConnect.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsIScriptSecurityManager.h" #include "nsPIDOMWindow.h" #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext +#include "mozilla/dom/bindings/Utils.h" + JSBool nsJSUtils::GetCallingLocation(JSContext* aContext, const char* *aFilename, PRUint32* aLineno) { // Get the current filename and line number JSStackFrame* frame = nsnull; JSScript* script = nsnull; do { @@ -94,30 +96,33 @@ nsJSUtils::GetCallingLocation(JSContext* } return JS_FALSE; } nsIScriptGlobalObject * nsJSUtils::GetStaticScriptGlobal(JSContext* aContext, JSObject* aObj) { - nsISupports* supports; JSClass* clazz; JSObject* glob = aObj; // starting point for search if (!glob) return nsnull; glob = JS_GetGlobalForObject(aContext, glob); NS_ABORT_IF_FALSE(glob, "Infallible returns null"); clazz = JS_GetClass(glob); - if (!clazz || - !(clazz->flags & JSCLASS_HAS_PRIVATE) || + // Whenever we end up with globals that are JSCLASS_IS_DOMJSCLASS + // and have an nsISupports DOM object, we will need to modify this + // check here. + MOZ_ASSERT(!(clazz->flags & JSCLASS_IS_DOMJSCLASS)); + nsISupports* supports; + if (!(clazz->flags & JSCLASS_HAS_PRIVATE) || !(clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) || !(supports = (nsISupports*)::JS_GetPrivate(glob))) { return nsnull; } // We might either have a window directly (e.g. if the global is a // sandbox whose script object principal pointer is a window), or an // XPCWrappedNative for a window. We could also have other
--- a/dom/base/nsScriptNameSpaceManager.cpp +++ b/dom/base/nsScriptNameSpaceManager.cpp @@ -798,8 +798,17 @@ nsScriptNameSpaceManager::Observe(nsISup // TODO: we could observe NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID // and NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID but we are safe without it. // See bug 600460. return NS_OK; } +void +nsScriptNameSpaceManager::RegisterDefineDOMInterface(const nsAString& aName, + mozilla::dom::binding::DefineInterface aDefineDOMInterface) +{ + nsGlobalNameStruct* s = LookupNameInternal(aName); + if (s) { + s->mDefineDOMInterface = aDefineDOMInterface; + } +}
--- a/dom/base/nsScriptNameSpaceManager.h +++ b/dom/base/nsScriptNameSpaceManager.h @@ -92,16 +92,19 @@ struct nsGlobalNameStruct union { PRInt32 mDOMClassInfoID; // eTypeClassConstructor nsIID mIID; // eTypeInterface, eTypeClassProto nsExternalDOMClassInfoData* mData; // eTypeExternalClassInfo ConstructorAlias* mAlias; // eTypeExternalConstructorAlias nsCID mCID; // All other types... }; + // For new style DOM bindings. + mozilla::dom::binding::DefineInterface mDefineDOMInterface; + private: // copy constructor }; class nsIScriptContext; class nsICategoryManager; @@ -161,17 +164,20 @@ public: const nsIID *aProtoChainInterface, const nsIID **aInterfaces, PRUint32 aScriptableFlags, bool aHasClassInterface, const nsCID *aConstructorCID); nsGlobalNameStruct* GetConstructorProto(const nsGlobalNameStruct* aStruct); -protected: + void RegisterDefineDOMInterface(const nsAString& aName, + mozilla::dom::binding::DefineInterface aDefineDOMInterface); + +private: // Adds a new entry to the hash and returns the nsGlobalNameStruct // that aKey will be mapped to. If mType in the returned // nsGlobalNameStruct is != eTypeNotInitialized, an entry for aKey // already existed. nsGlobalNameStruct *AddToHash(PLDHashTable *aTable, const char *aKey, const PRUnichar **aClassName = nsnull); nsresult FillHash(nsICategoryManager *aCategoryManager,
--- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -42,16 +42,26 @@ #include "nsCycleCollectionParticipant.h" struct JSObject; struct JSContext; class XPCWrappedNativeScope; typedef PRUptrdiff PtrBits; +namespace mozilla { +namespace dom { +namespace workers { + +class DOMBindingBase; + +} // namespace workers +} // namespace dom +} // namespace mozilla + #define NS_WRAPPERCACHE_IID \ { 0x6f3179a1, 0x36f7, 0x4a5c, \ { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } } /** * Class to store the wrapper for an object. This can only be used with objects * that only have one non-security wrapper at a time (for an XPCWrappedNative * this is usually ensured by setting an explicit parent in the PreCreate hook @@ -67,26 +77,28 @@ typedef PRUptrdiff PtrBits; * object in the cache. * * The cache can store 2 types of objects: * * If WRAPPER_IS_DOM_BINDING is not set (IsDOMBinding() returns false): * - a slim wrapper or the JSObject of an XPCWrappedNative wrapper * * If WRAPPER_IS_DOM_BINDING is set (IsDOMBinding() returns true): - * - a DOM binding object (proxy) + * - a DOM binding object (regular JS object or proxy) * * The finalizer for the wrapper clears the cache. * * A number of the methods are implemented in nsWrapperCacheInlines.h because we * have to include some JS headers that don't play nicely with the rest of the * codebase. Include nsWrapperCacheInlines.h if you need to call those methods. */ class nsWrapperCache { + friend class mozilla::dom::workers::DOMBindingBase; + public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID) nsWrapperCache() : mWrapperPtrBits(0) { } ~nsWrapperCache() { @@ -212,17 +224,17 @@ private: * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER, * NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS and * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER). */ enum { WRAPPER_BIT_PRESERVED = 1 << 0 }; /** * If this bit is set then the wrapper for the native object is a DOM binding - * (proxy). + * (regular JS object or proxy). */ enum { WRAPPER_IS_DOM_BINDING = 1 << 1 }; enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING) }; PtrBits mWrapperPtrBits; };
new file mode 100644 --- /dev/null +++ b/dom/bindings/BindingGen.py @@ -0,0 +1,73 @@ +# 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/. + +import os +import cPickle +import WebIDL +from Configuration import * +from Codegen import CGBindingRoot, replaceFileIfChanged +# import Codegen in general, so we can set a variable on it +import Codegen + +def generate_binding_header(config, outputprefix, webidlfile): + """ + |config| Is the configuration object. + |outputprefix| is a prefix to use for the header guards and filename. + """ + + filename = outputprefix + ".h" + root = CGBindingRoot(config, outputprefix, webidlfile) + if replaceFileIfChanged(filename, root.declare()): + print "Generating binding header: %s" % (filename) + +def generate_binding_cpp(config, outputprefix, webidlfile): + """ + |config| Is the configuration object. + |outputprefix| is a prefix to use for the header guards and filename. + """ + + filename = outputprefix + ".cpp" + root = CGBindingRoot(config, outputprefix, webidlfile) + if replaceFileIfChanged(filename, root.define()): + print "Generating binding implementation: %s" % (filename) + +def main(): + + # Parse arguments. + from optparse import OptionParser + usagestring = "usage: %prog [header|cpp] configFile outputPrefix webIDLFile" + o = OptionParser(usage=usagestring) + o.add_option("--verbose-errors", action='store_true', default=False, + help="When an error happens, display the Python traceback.") + o.add_option("--use-jsop-accessors", action='store_true', default=False, + dest='useJSOPAccessors', + help="Use JSPropertyOps instead of JSNatives for getters and setters") + (options, args) = o.parse_args() + Codegen.generateNativeAccessors = not options.useJSOPAccessors + + if len(args) != 4 or (args[0] != "header" and args[0] != "cpp"): + o.error(usagestring) + buildTarget = args[0] + configFile = os.path.normpath(args[1]) + outputPrefix = args[2] + webIDLFile = os.path.normpath(args[3]) + + # Load the parsing results + f = open('ParserResults.pkl', 'rb') + parserData = cPickle.load(f) + f.close() + + # Create the configuration data. + config = Configuration(configFile, parserData) + + # Generate the prototype classes. + if buildTarget == "header": + generate_binding_header(config, outputPrefix, webIDLFile); + elif buildTarget == "cpp": + generate_binding_cpp(config, outputPrefix, webIDLFile); + else: + assert False # not reached + +if __name__ == '__main__': + main()
new file mode 100644 --- /dev/null +++ b/dom/bindings/Bindings.conf @@ -0,0 +1,233 @@ +# DOM Bindings Configuration. +# +# The WebIDL interfaces are defined in dom/webidl. For each such interface, there +# is a corresponding entry in the configuration table below. The configuration +# table maps each interface name to a |descriptor| or list of |descriptor|s. +# +# Valid fields for all descriptors: +# * nativeType - The native type (concrete class or XPCOM interface) that +# instances of this interface will unwrap to (required). +# * headerFile - The file in which the nativeType is declared (defaults +# to an educated guess). +# * castable - Indicates whether the value in the wrapper can be cast to +# nativeType, or whether it needs to be QI-ed (defaults to True +# for everything but callback interfaces). +# * concrete - Indicates whether there exist objects with this interface as +# their primary interface (defaults to True). +# * prefable - Indicates whether this binding is subject to the about:config +# pref, or whether it's always enabled (defaults to False). +# * workers - Indicates whether the descriptor is intended to be used for +# worker threads (defaults to false). +# * customTrace - The native class will use a custom trace hook (defaults to +# true for workers, false otherwise). +# * customFinalize - The native class will use a custom finalize hook +# (defaults to true for workers, false otherwise). +# * notflattened - The native type does not have nsIClassInfo, so when +# wrapping it the right IID needs to be passed in. +# +# The following fields are either a string, an array (defaults to an empty +# array) or a dictionary with three possible keys (all, getterOnly and +# setterOnly) each having such an array as the value +# +# * infallible - attributes and methods specified in the .webidl file that +# cannot fail and therefore do not require the final nsresult& +# argument +# * implicitJSContext - attributes and methods specified in the .webidl file +# that require a JSContext as the first argument +# * resultNotAddRefed - attributes and methods specified in the .webidl file +# that do not AddRef the return value + +DOMInterfaces = { + +'XMLHttpRequest': [ +{ + 'nativeType': 'nsXMLHttpRequest', + 'prefable': True, + 'infallible': { + 'all': [ + 'readyState', 'withCredentials', 'abort', 'statusText', + 'getAllResponseHeaders', 'overrideMimeType', 'mozBackgroundRequest', + 'multipart', 'channel', 'upload', 'status' + ], + 'getterOnly': [ + 'responseType', 'timeout', 'onreadystatechange' + ] + }, + 'implicitJSContext': { + 'all': [ + 'response', 'getInterface' + ], + 'setterOnly': [ + 'onreadystatechange' + ] + }, + 'resultNotAddRefed': [ 'upload', 'responseXML' ] +}, +{ + 'workers': True, + 'nativeType': 'mozilla::dom::workers::XMLHttpRequest', + 'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequest.h', + 'infallible': [ + 'readyState', 'statusText' + ] +}], + +'XMLHttpRequestUpload': [ +{ + 'nativeType': 'nsXMLHttpRequestUpload', + 'headerFile': 'nsXMLHttpRequest.h', + 'prefable': True +}, +{ + 'workers': True, + 'nativeType': 'mozilla::dom::workers::XMLHttpRequestUpload', + 'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestUpload.h' +}], + +'MozChannel': [ +{ + 'nativeType': 'nsIChannel', + 'prefable': True, + 'castable': False, + 'notflattened': True +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +'InputStream': [ +{ + 'nativeType': 'nsIInputStream', + 'prefable': True, + 'castable': False, + 'notflattened': True +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +'Document': [ +{ + 'nativeType': 'nsIDocument', + 'prefable': True, + 'castable': False +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +'Blob': [ +{ + 'nativeType': 'nsIDOMBlob', + 'headerFile': 'nsIDOMFile.h', + 'prefable': True, + 'castable': False +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +'FormData': [ +{ + 'nativeType': 'nsIDOMFormData', + 'prefable': True, + 'castable': False +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +'EventTarget': [ +{ + 'nativeType': 'nsDOMEventTargetHelper', + 'hasInstanceInterface': 'nsIDOMEventTarget', + 'concrete': False, + 'prefable': True, +}, +{ + 'workers': True, + 'nativeType': 'mozilla::dom::workers::EventTarget', + 'headerFile': 'mozilla/dom/workers/bindings/EventTarget.h', + 'concrete': False +}], + +'Event': [ +{ + 'nativeType': 'nsIDOMEvent', + 'prefable': True, + 'castable': False +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +'EventListener': [ +{ + 'nativeType': 'nsIDOMEventListener', + 'prefable': True +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h' +}], + +'XMLHttpRequestEventTarget': [ +{ + 'nativeType': 'nsXHREventTarget', + 'headerFile': 'nsXMLHttpRequest.h', + 'concrete': False, + 'prefable': True, + 'infallible': { + 'getterOnly': [ + 'onabort', 'onerror', 'onload', 'onloadstart', 'onprogress', + 'ontimeout', 'onloadend' + ] + }, + 'implicitJSContext': { + 'setterOnly': [ + 'onabort', 'onerror', 'onload', 'onloadstart', 'onprogress', + 'ontimeout', 'onloadend' + ] + } +}, +{ + 'workers': True, + 'concrete': False, + 'nativeType': 'mozilla::dom::workers::XMLHttpRequestEventTarget', + 'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestEventTarget.h' +}], + +'IID': [ +{ + 'nativeType': 'nsIJSIID', + 'headerFile': 'xpcjsid.h', + 'prefable': True, + 'castable': False +}, +{ + 'workers': True, + 'nativeType': 'JSObject', + 'headerFile': 'jsapi.h', + 'castable': False +}], + +}
new file mode 100644 --- /dev/null +++ b/dom/bindings/Codegen.py @@ -0,0 +1,2982 @@ +# 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/. + +# Common codegen classes. + +import os +import string + +from WebIDL import * + +AUTOGENERATED_WARNING_COMMENT = \ + "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" +ADDPROPERTY_HOOK_NAME = '_AddProperty' +FINALIZE_HOOK_NAME = '_Finalize' +TRACE_HOOK_NAME = '_Trace' +CONSTRUCT_HOOK_NAME = '_Construct' +HASINSTANCE_HOOK_NAME = '_HasInstance' + +def replaceFileIfChanged(filename, newContents): + """ + Read a copy of the old file, so that we don't touch it if it hasn't changed. + Returns True if the file was updated, false otherwise. + """ + oldFileContents = "" + try: + oldFile = open(filename, 'rb') + oldFileContents = ''.join(oldFile.readlines()) + oldFile.close() + except: + pass + + if newContents == oldFileContents: + return False + + f = open(filename, 'wb') + f.write(newContents) + f.close() + +def toStringBool(arg): + return str(not not arg).lower() + +class CGThing(): + """ + Abstract base class for things that spit out code. + """ + def __init__(self): + pass # Nothing for now + def declare(self): + """Produce code for a header file.""" + assert(False) # Override me! + def define(self): + """Produce code for a cpp file.""" + assert(False) # Override me! + +class CGNativePropertyHooks(CGThing): + """ + Generate a NativePropertyHooks for a given descriptor + """ + def __init__(self, descriptor): + CGThing.__init__(self) + self.descriptor = descriptor + def declare(self): + return " extern const NativePropertyHooks NativeHooks;\n" + def define(self): + parent = self.descriptor.interface.parent + parentHooks = "&" + parent.identifier.name + "::NativeHooks" if parent else 'NULL' + return """ +const NativePropertyHooks NativeHooks = { ResolveProperty, EnumerateProperties, %s }; +""" % parentHooks + +class CGDOMJSClass(CGThing): + """ + Generate a DOMJSClass for a given descriptor + """ + def __init__(self, descriptor): + CGThing.__init__(self) + self.descriptor = descriptor + def declare(self): + return " extern DOMJSClass Class;\n" + def define(self): + traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL' + protoList = ['id::' + proto for proto in self.descriptor.prototypeChain] + # Pad out the list to the right length with _ID_Count so we + # guarantee that all the lists are the same length. _ID_Count + # is never the ID of any prototype, so it's safe to use as + # padding. + while len(protoList) < self.descriptor.config.maxProtoChainLength: + protoList.append('id::_ID_Count') + prototypeChainString = ', '.join(protoList) + return """ +DOMJSClass Class = { + { "%s", + JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1), + %s, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + %s, /* finalize */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* hasInstance */ + %s, /* trace */ + JSCLASS_NO_INTERNAL_MEMBERS + }, + { %s }, + -1, %s, DOM_OBJECT_SLOT, + &NativeHooks +}; +""" % (self.descriptor.interface.identifier.name, + ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers else 'JS_PropertyStub', + FINALIZE_HOOK_NAME, traceHook, prototypeChainString, + str(self.descriptor.nativeIsISupports).lower()) + +class CGPrototypeJSClass(CGThing): + def __init__(self, descriptor): + CGThing.__init__(self) + self.descriptor = descriptor + def declare(self): + # We're purely for internal consumption + return "" + def define(self): + return """ +static JSClass PrototypeClass = { + "%s Prototype", 0, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, /* finalize */ + NULL, /* checkAccess */ + NULL, /* call */ + NULL, /* construct */ + NULL, /* hasInstance */ + NULL, /* trace */ + JSCLASS_NO_INTERNAL_MEMBERS +}; +""" % (self.descriptor.interface.identifier.name) + +class CGInterfaceObjectJSClass(CGThing): + def __init__(self, descriptor): + CGThing.__init__(self) + self.descriptor = descriptor + def declare(self): + # We're purely for internal consumption + return "" + def define(self): + ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME + hasinstance = "NULL" if not self.descriptor.hasInstanceInterface else HASINSTANCE_HOOK_NAME + return """ +static JSClass InterfaceObjectClass = { + "Function", 0, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + NULL, /* finalize */ + NULL, /* checkAccess */ + %s, /* call */ + %s, /* construct */ + %s, /* hasInstance */ + NULL, /* trace */ + JSCLASS_NO_INTERNAL_MEMBERS +}; +""" % (ctorname, ctorname, hasinstance) + +class CGList(CGThing): + """ + Generate code for a list of GCThings. Just concatenates them together, with + an optional joiner string. "\n" is a common joiner. + """ + def __init__(self, children, joiner=""): + CGThing.__init__(self) + self.children = children + self.joiner = joiner + def append(self, child): + self.children.append(child) + def prepend(self, child): + self.children.insert(0, child) + def declare(self): + return self.joiner.join([child.declare() for child in self.children]) + def define(self): + return self.joiner.join([child.define() for child in self.children]) + +class CGGeneric(CGThing): + """ + A class that spits out a fixed string into the codegen. Can spit out a + separate string for the declaration too. + """ + def __init__(self, define="", declare=""): + self.declareText = declare + self.defineText = define + def declare(self): + return self.declareText + def define(self): + return self.defineText + +# We'll want to insert the indent at the beginnings of lines, but we +# don't want to indent empty lines. So only indent lines that have a +# non-newline character on them. +lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) +class CGIndenter(CGThing): + """ + A class that takes another CGThing and generates code that indents that + CGThing by some number of spaces. The default indent is two spaces. + """ + def __init__(self, child, indentLevel=2): + CGThing.__init__(self) + self.child = child + self.indent = " " * indentLevel + def declare(self): + decl = self.child.declare() + if decl is not "": + return re.sub(lineStartDetector, self.indent, decl) + else: + return "" + def define(self): + defn = self.child.define() + if defn is not "": + return re.sub(lineStartDetector, self.indent, defn) + else: + return "" + +class CGWrapper(CGThing): + """ + Generic CGThing that wraps other CGThings with pre and post text. + """ + def __init__(self, child, pre="", post="", declarePre=None, + declarePost=None, definePre=None, definePost=None, + declareOnly=False): + CGThing.__init__(self) + self.child = child + self.declarePre = declarePre or pre + self.declarePost = declarePost or post + self.definePre = definePre or pre + self.definePost = definePost or post + self.declareOnly = declareOnly + def declare(self): + return self.declarePre + self.child.declare() + self.declarePost + def define(self): + if self.declareOnly: + return '' + return self.definePre + self.child.define() + self.definePost + +class CGNamespace(CGWrapper): + def __init__(self, namespace, child, declareOnly=False): + pre = "namespace %s {\n" % namespace + post = "} // namespace %s\n" % namespace + CGWrapper.__init__(self, child, pre=pre, post=post, + declareOnly=declareOnly) + @staticmethod + def build(namespaces, child, declareOnly=False): + """ + Static helper method to build multiple wrapped namespaces. + """ + if not namespaces: + return child + return CGNamespace(namespaces[0], CGNamespace.build(namespaces[1:], + child), + declareOnly=declareOnly) + +class CGIncludeGuard(CGWrapper): + """ + Generates include guards for a header. + """ + def __init__(self, prefix, child): + """|prefix| is the filename without the extension.""" + define = 'mozilla_dom_bindings_%s_h__' % prefix + CGWrapper.__init__(self, child, + declarePre='#ifndef %s\n#define %s\n\n' % (define, define), + declarePost='\n#endif // %s\n' % define) + +class CGHeaders(CGWrapper): + """ + Generates the appropriate include statements. + """ + def __init__(self, descriptors, declareIncludes, defineIncludes, child): + """ + Builds a set of includes to cover |descriptors|. + + Also includes the files in |declareIncludes| in the header + file and the files in |defineIncludes| in the .cpp. + """ + + # Determine the filenames for which we need headers. + interfaceDeps = [d.interface for d in descriptors] + ancestors = [] + for iface in interfaceDeps: + while iface.parent: + ancestors.append(iface.parent) + iface = iface.parent + interfaceDeps.extend(ancestors) + bindingIncludes = set(self.getInterfaceFilename(d) for d in interfaceDeps) + + # Grab all the implementation declaration files we need. + implementationIncludes = set(d.headerFile for d in descriptors) + + # Now find all the things we'll need as arguments because we + # need to wrap or unwrap them. + bindingHeaders = set() + for d in descriptors: + members = [m for m in d.interface.members] + signatures = [s for m in members if m.isMethod() for s in m.signatures()] + types = [] + for s in signatures: + assert len(s) == 2 + (returnType, arguments) = s + types.append(returnType) + types.extend([a.type for a in arguments]) + + attrs = [a for a in members if a.isAttr()] + types.extend([a.type for a in attrs]) + + for t in types: + if t.unroll().isInterface(): + if t.unroll().isArrayBuffer(): + bindingHeaders.add("jstypedarray.h") + else: + typeDesc = d.getDescriptor(t.unroll().inner.identifier.name) + if typeDesc is not None: + implementationIncludes.add(typeDesc.headerFile) + bindingHeaders.add(self.getInterfaceFilename(typeDesc.interface)) + + # Let the machinery do its thing. + def _includeString(includes): + return ''.join(['#include "%s"\n' % i for i in includes]) + '\n' + CGWrapper.__init__(self, child, + declarePre=_includeString(declareIncludes), + definePre=_includeString(sorted(set(defineIncludes) | + bindingIncludes | + bindingHeaders | + implementationIncludes))) + @staticmethod + def getInterfaceFilename(interface): + basename = os.path.basename(interface.filename()) + return 'mozilla/dom/bindings/' + \ + basename.replace('.webidl', 'Binding.h') + +class Argument(): + """ + A class for outputting the type and name of an argument + """ + def __init__(self, argType, name): + self.argType = argType + self.name = name + def __str__(self): + return self.argType + ' ' + self.name + +class CGAbstractMethod(CGThing): + """ + An abstract class for generating code for a method. Subclasses + should override definition_body to create the actual code. + + descriptor is the descriptor for the interface the method is associated with + + name is the name of the method as a string + + returnType is the IDLType of the return value + + args is a list of Argument objects + + inline should be True to generate an inline method, whose body is + part of the declaration. + + static should be True to generate a static method, which only has + a definition. + """ + def __init__(self, descriptor, name, returnType, args, inline=False, static=False): + CGThing.__init__(self) + self.descriptor = descriptor + self.name = name + self.returnType = returnType + self.args = args + self.inline = inline + self.static = static + def _argstring(self): + return ', '.join([str(a) for a in self.args]) + def _decorators(self): + decorators = [] + if self.inline: + decorators.append('inline') + if self.static: + decorators.append('static') + decorators.append(self.returnType) + return ' '.join(decorators) + def declare(self): + if self.inline: + return self._define() + return "\n %s %s(%s);\n" % (self._decorators(), self.name, self._argstring()) + def _define(self): + return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue() + def define(self): + return "" if self.inline else self._define() + def definition_prologue(self): + maybeNewline = " " if self.inline else "\n" + return "\n%s%s%s(%s)\n{" % (self._decorators(), maybeNewline, + self.name, self._argstring()) + def definition_epilogue(self): + return "\n}\n" + def definition_body(self): + assert(False) # Override me! + +class CGAbstractStaticMethod(CGAbstractMethod): + """ + Abstract base class for codegen of implementation-only (no + declaration) static methods. + """ + def __init__(self, descriptor, name, returnType, args): + CGAbstractMethod.__init__(self, descriptor, name, returnType, args, + inline=False, static=True) + def declare(self): + # We only have implementation + return "" + +class CGAbstractClassHook(CGAbstractStaticMethod): + """ + Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw + 'this' unwrapping as it assumes that the unwrapped type is always known. + """ + def __init__(self, descriptor, name, returnType, args): + CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, + args) + + def definition_body_prologue(self): + return """ + MOZ_ASSERT(js::GetObjectJSClass(obj) == Class.ToJSClass()); + %s* self = UnwrapDOMObject<%s>(obj, Class.ToJSClass()); +""" % (self.descriptor.nativeType, self.descriptor.nativeType) + + def definition_body(self): + return self.definition_body_prologue() + self.generate_code() + + def generate_code(self): + # Override me + assert(False) + +class CGAddPropertyHook(CGAbstractClassHook): + """ + A hook for addProperty, used to preserve our wrapper from GC. + """ + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'), + Argument('jsid', 'id'), Argument('jsval*', 'vp')] + CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME, + 'JSBool', args) + + def generate_code(self): + return """ + JSCompartment* compartment = js::GetObjectCompartment(obj); + xpc::CompartmentPrivate* priv = + static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment)); + if (!priv->RegisterDOMExpandoObject(obj)) { + return false; + } + self->SetPreservingWrapper(true); + return true;""" + +class CGClassFinalizeHook(CGAbstractClassHook): + """ + A hook for finalize, used to release our native object. + """ + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj')] + CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, + 'void', args) + + def generate_code(self): + if self.descriptor.customFinalize: + return """ if (self) { + self->%s(%s); + }""" % (self.name, self.args[0].name) + if self.descriptor.workers: + release = "self->Release();" + else: + assert self.descriptor.nativeIsISupports + release = """ + XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); + if (rt) { + rt->DeferredRelease(NativeToSupports(self)); + } else { + NS_RELEASE(self); + }""" + return """ + self->ClearWrapper(); + %s""" % (release) + +class CGClassTraceHook(CGAbstractClassHook): + """ + A hook to trace through our native object; used for GC and CC + """ + def __init__(self, descriptor): + args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')] + CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void', + args) + + def generate_code(self): + return """ if (self) { + self->%s(%s); + }""" % (self.name, self.args[0].name) + +class CGClassConstructHook(CGAbstractStaticMethod): + """ + JS-visible constructor for our objects + """ + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')] + CGAbstractStaticMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME, + 'JSBool', args) + self._ctor = self.descriptor.interface.ctor() + + def define(self): + if not self._ctor: + return "" + return CGAbstractStaticMethod.define(self) + + def definition_body(self): + return self.generate_code() + + def generate_code(self): + preamble = """ + JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); +""" + preArgs = "" + if self.descriptor.workers: + preArgs = "cx, obj, " + else: + preamble += """ + nsISupports* global; + xpc_qsSelfRef globalRef; + { + nsresult rv; + JS::Value val = OBJECT_TO_JSVAL(obj); + rv = xpc_qsUnwrapArg<nsISupports>(cx, val, &global, &globalRef.ptr, &val); + if (NS_FAILED(rv)) { + return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS); + } + } +""" + preArgs = "global, " + + name = "_" + self._ctor.identifier.name + nativeName = "_" + MakeNativeName(self._ctor.identifier.name) + nativeName = self.descriptor.binaryNames.get(name, nativeName) + callGenerator = CGMethodCall(preArgs, nativeName, True, + self.descriptor, self._ctor, {}) + return preamble + callGenerator.define(); + +class CGClassHasInstanceHook(CGAbstractStaticMethod): + def __init__(self, descriptor): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'), + Argument('const jsval*', 'v'), Argument('JSBool*', 'bp')] + CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME, + 'JSBool', args) + + def define(self): + if not self.descriptor.hasInstanceInterface: + return "" + return CGAbstractStaticMethod.define(self) + + def definition_body(self): + return self.generate_code() + + def generate_code(self): + return """ if (!v->isObject()) { + *bp = false; + return true; + } + + jsval protov; + if (!JS_GetProperty(cx, obj, "prototype", &protov)) + return false; + if (!protov.isObject()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, + "%s"); + return false; + } + obj = &protov.toObject(); + + JSObject* instance = &v->toObject(); + JSObject* proto = JS_GetPrototype(instance); + while (proto) { + if (proto == obj) { + *bp = true; + return true; + } + proto = JS_GetPrototype(proto); + } + + nsISupports* native = + nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance); + nsCOMPtr<%s> qiResult = do_QueryInterface(native); + *bp = !!qiResult; + return true; +""" % (self.descriptor.name, self.descriptor.hasInstanceInterface) + +def isChromeOnly(m): + return m.extendedAttribute("ChromeOnly") + +class PropertyDefiner: + """ + A common superclass for defining things on prototype objects. + + Subclasses should implement generateArray to generate the actual arrays of + things we're defining. They should also set self.chrome to the list of + things exposed to chrome and self.regular to the list of things exposed to + web pages. self.chrome must be a superset of self.regular but also include + all the ChromeOnly stuff. + """ + def __init__(self, descriptor, name): + self.descriptor = descriptor + self.name = name + def hasChromeOnly(self): + return len(self.chrome) > len(self.regular) + def hasNonChromeOnly(self): + return len(self.regular) > 0 + def variableName(self, chrome): + if chrome and self.hasChromeOnly(): + return "sChrome" + self.name + if self.hasNonChromeOnly(): + return "s" + self.name + return "NULL" + def __str__(self): + str = self.generateArray(self.regular, self.variableName(False)) + if self.hasChromeOnly(): + str += self.generateArray(self.chrome, self.variableName(True)) + return str + +class MethodDefiner(PropertyDefiner): + """ + A class for defining methods on a prototype object. + """ + def __init__(self, descriptor, name, static): + PropertyDefiner.__init__(self, descriptor, name) + + # The length of a method is the maximum of the lengths of the + # argument lists of all its overloads. + def methodLength(method): + signatures = method.signatures() + return max([len(arguments) for (retType, arguments) in signatures]) + + methods = [m for m in descriptor.interface.members if + m.isMethod() and m.isStatic() == static] + self.chrome = [{"name": m.identifier.name, + "length": methodLength(m), + "flags": "JSPROP_ENUMERATE"} for m in methods] + self.regular = [{"name": m.identifier.name, + "length": methodLength(m), + "flags": "JSPROP_ENUMERATE"} + for m in methods if not isChromeOnly(m)] + if not descriptor.interface.parent and not static and not descriptor.workers: + self.chrome.append({"name": 'QueryInterface', + "length": 1, + "flags": "0"}) + self.regular.append({"name": 'QueryInterface', + "length": 1, + "flags": "0"}) + + if static: + if not descriptor.interface.hasInterfaceObject(): + # static methods go on the interface object + assert not self.hasChromeOnly() and not self.hasNonChromeOnly() + else: + if not descriptor.interface.hasInterfacePrototypeObject(): + # non-static methods go on the interface prototype object + assert not self.hasChromeOnly() and not self.hasNonChromeOnly() + + @staticmethod + def generateArray(array, name): + if len(array) == 0: + return "" + + funcdecls = [' JS_FN("%s", %s, %s, %s)' % + (m["name"], m["name"], m["length"], m["flags"]) + for m in array] + # And add our JS_FS_END + funcdecls.append(' JS_FS_END') + + return ("static JSFunctionSpec %s[] = {\n" + + ',\n'.join(funcdecls) + "\n" + + "};\n\n" + + "static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array)) + +class AttrDefiner(PropertyDefiner): + def __init__(self, descriptor, name): + PropertyDefiner.__init__(self, descriptor, name) + self.name = name + self.chrome = [m for m in descriptor.interface.members if m.isAttr()] + self.regular = [m for m in self.chrome if not isChromeOnly(m)] + + @staticmethod + def generateArray(array, name): + if len(array) == 0: + return "" + + def flags(attr): + flags = "JSPROP_SHARED | JSPROP_ENUMERATE" + if generateNativeAccessors: + flags = "JSPROP_NATIVE_ACCESSORS | " + flags + elif attr.readonly: + return "JSPROP_READONLY | " + flags + return flags + + def getter(attr): + return "get_" + attr.identifier.name + + def setter(attr): + if attr.readonly: + return "NULL" + return "set_" + attr.identifier.name + + attrdecls = [' { "%s", 0, %s, (JSPropertyOp)%s, (JSStrictPropertyOp)%s }' % + (attr.identifier.name, flags(attr), getter(attr), + setter(attr)) for attr in array] + attrdecls.append(' { 0, 0, 0, 0, 0 }') + + return ("static JSPropertySpec %s[] = {\n" + + ',\n'.join(attrdecls) + "\n" + + "};\n\n" + + "static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array)) + +class ConstDefiner(PropertyDefiner): + """ + A class for definining constants on the interface object + """ + def __init__(self, descriptor, name): + PropertyDefiner.__init__(self, descriptor, name) + self.name = name + self.chrome = [m for m in descriptor.interface.members if m.isConst()] + self.regular = [m for m in self.chrome if not isChromeOnly(m)] + + @staticmethod + def generateArray(array, name): + if len(array) == 0: + return "" + + constdecls = [' { "%s", %s }' % + (const.identifier.name, + convertConstIDLValueToJSVal(const.value)) + for const in array] + constdecls.append(' { 0, JSVAL_VOID }') + + return ("static ConstantSpec %s[] = {\n" + + ',\n'.join(constdecls) + "\n" + + "};\n\n" + + "static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array)) + +class PropertyArrays(): + def __init__(self, descriptor): + self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True) + self.methods = MethodDefiner(descriptor, "Methods", False) + self.attrs = AttrDefiner(descriptor, "Attributes") + self.consts = ConstDefiner(descriptor, "Constants") + + @staticmethod + def arrayNames(): + return [ "staticMethods", "methods", "attrs", "consts" ] + + def hasChromeOnly(self): + return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(), + self.arrayNames(), False) + def variableNames(self, chrome): + names = {} + for array in self.arrayNames(): + names[array] = getattr(self, array).variableName(chrome) + return names + def __str__(self): + define = "" + for array in self.arrayNames(): + define += str(getattr(self, array)) + return define + +class CGCreateInterfaceObjectsMethod(CGAbstractMethod): + """ + Generate the CreateInterfaceObjects method for an interface descriptor. + + properties should be a PropertyArrays instance. + """ + def __init__(self, descriptor, properties): + args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')] + CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'JSObject*', args) + self.properties = properties + def definition_body(self): + protoChain = self.descriptor.prototypeChain + if len(protoChain) == 1: + getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)" + else: + parentProtoName = self.descriptor.prototypeChain[-2] + getParentProto = "%s::GetProtoObject(aCx, aGlobal)" % (parentProtoName) + + needInterfaceObject = self.descriptor.interface.hasInterfaceObject() + needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject() + + # if we don't need to create anything, why are we generating this? + assert needInterfaceObject or needInterfacePrototypeObject + + idsToInit = [] + for var in self.properties.arrayNames(): + props = getattr(self.properties, var) + if props.hasNonChromeOnly(): + idsToInit.append(props.variableName(False)) + if props.hasChromeOnly() and not self.descriptor.workers: + idsToInit.append(props.variableName(True)) + initIds = "" + if len(idsToInit) > 0: + init = ' ||\n '.join(["!InitIds(aCx, %s, %s_ids)" % (varname, varname) + for varname in idsToInit]) + if len(idsToInit) > 1: + init = '(' + init + ')' + initIds = (" if (%s_ids[0] == JSID_VOID &&\n" + + " %s) {\n" + + " %s_ids[0] = JSID_VOID;\n" + " return NULL;\n" + " }\n\n") % (idsToInit[0], init, idsToInit[0]) + + getParentProto = (" JSObject* parentProto = %s;\n" + + " if (!parentProto) {\n" + + " return NULL;\n" + + " }") % getParentProto + + call = """return bindings::CreateInterfaceObjects(aCx, aGlobal, parentProto, + %s, %s, + %%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s, + %s);""" % ( + "&PrototypeClass" if needInterfacePrototypeObject else "NULL", + "&InterfaceObjectClass" if needInterfaceObject else "NULL", + '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL") + + if self.properties.hasChromeOnly(): + if self.descriptor.workers: + accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()" + else: + accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))" + chrome = """ + + if (%s) { + %s + } +""" % (accessCheck, call.replace("\n ", "\n ") % self.properties.variableNames(True)) + else: + chrome = "" + return initIds + getParentProto + chrome + "\n " + call % self.properties.variableNames(False) + +class CGGetPerInterfaceObject(CGAbstractMethod): + """ + A method for getting a per-interface object (a prototype object or interface + constructor object). + """ + def __init__(self, descriptor, name, idPrefix=""): + args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')] + CGAbstractMethod.__init__(self, descriptor, name, + 'JSObject*', args, inline=True) + self.id = idPrefix + "id::" + self.descriptor.name + def definition_body(self): + return """ + /* Make sure our global is sane. Hopefully we can remove this sometime */ + if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) { + return NULL; + } + /* Check to see whether the interface objects are already installed */ + JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal); + JSObject* cachedObject = protoOrIfaceArray[%s]; + if (!cachedObject) { + protoOrIfaceArray[%s] = cachedObject = CreateInterfaceObjects(aCx, aGlobal); + } + + /* cachedObject might _still_ be null, but that's OK */ + return cachedObject;""" % (self.id, self.id) + +class CGGetProtoObjectMethod(CGGetPerInterfaceObject): + """ + A method for getting the interface prototype object. + """ + def __init__(self, descriptor): + args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')] + CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject") + def definition_body(self): + return """ + /* Get the interface prototype object for this class. This will create the + object as needed. */""" + CGGetPerInterfaceObject.definition_body(self) + +class CGGetConstructorObjectMethod(CGGetPerInterfaceObject): + """ + A method for getting the interface constructor object. + """ + def __init__(self, descriptor): + args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')] + CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject", "constructors::") + def definition_body(self): + return """ + /* Get the interface object for this class. This will create the object as + needed. */""" + CGGetPerInterfaceObject.definition_body(self) + +def CheckPref(descriptor, scopeName, varName, retval, wrapperCache = None): + """ + Check whether bindings should be enabled for this descriptor. If not, set + varName to false and return retval. + """ + if not descriptor.prefable: + return "" + if wrapperCache: + wrapperCache = "%s->ClearIsDOMBinding();\n" % (wrapperCache) + else: + wrapperCache = "" + return """ + if (!%s->ParisBindingsEnabled()) { +%s %s = false; + return %s; + } +""" % (scopeName, wrapperCache, varName, retval) + +class CGDefineDOMInterfaceMethod(CGAbstractMethod): + """ + A method for resolve hooks to try to lazily define the interface object for + a given interface. + """ + def __init__(self, descriptor): + args = [Argument('JSContext*', 'aCx'), Argument('XPCWrappedNativeScope*', 'aScope'), + Argument('bool*', 'aEnabled')] + CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args) + + def declare(self): + if self.descriptor.workers: + return '' + return CGAbstractMethod.declare(self) + + def define(self): + if self.descriptor.workers: + return '' + return CGAbstractMethod.define(self) + + def definition_body(self): + if self.descriptor.interface.hasInterfacePrototypeObject(): + # We depend on GetProtoObject defining an interface constructor + # object as needed. + getter = "GetProtoObject" + else: + getter = "GetConstructorObject" + + return CheckPref(self.descriptor, "aScope", "*aEnabled", "false") + """ + *aEnabled = true; + return !!%s(aCx, aScope->GetGlobalJSObject());""" % (getter) + +class CGNativeToSupportsMethod(CGAbstractStaticMethod): + """ + A method to cast our native to an nsISupports. We do it by casting up the + interface chain in hopes of getting to something that singly-inherits from + nsISupports. + """ + def __init__(self, descriptor): + args = [Argument(descriptor.nativeType + '*', 'aNative')] + CGAbstractStaticMethod.__init__(self, descriptor, 'NativeToSupports', 'nsISupports*', args) + + def definition_body(self): + cast = "aNative" + whitespace = "" + addspace = "" + for proto in reversed(self.descriptor.prototypeChain[:-1]): + d = self.descriptor.getDescriptor(proto) + cast = "static_cast<%s*>(%s)" % (d.nativeType, whitespace + cast) + addspace += " " + whitespace = "\n " + addspace + return """ + return %s;""" % (cast) + +class CGWrapMethod(CGAbstractMethod): + def __init__(self, descriptor): + # XXX can we wrap if we don't have an interface prototype object? + assert descriptor.interface.hasInterfacePrototypeObject() + args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'), + Argument(descriptor.nativeType + '*', 'aObject'), + Argument('bool*', 'aTriedToWrap')] + CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) + + def definition_body(self): + if self.descriptor.workers: + return """ + *aTriedToWrap = true; + return aObject->GetJSObject();""" + + return """ + *aTriedToWrap = true; + + JSObject* parent = bindings::WrapNativeParent(aCx, aScope, aObject->GetParentObject()); + if (!parent) { + return NULL; + } + + JSAutoEnterCompartment ac; + if (js::GetGlobalForObjectCrossCompartment(parent) != aScope) { + if (!ac.enter(aCx, parent)) { + return NULL; + } + } + + XPCWrappedNativeScope* scope = + XPCWrappedNativeScope::FindInJSObjectScope(aCx, parent); + if (!scope) { + return NULL; + } +%s + JSObject* proto = GetProtoObject(aCx, scope->GetGlobalJSObject()); + if (!proto) { + return NULL; + } + + JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, parent); + if (!obj) { + return NULL; + } + + js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject)); + NS_ADDREF(aObject); + + aObject->SetWrapper(obj); + + return obj;""" % (CheckPref(self.descriptor, "scope", "*aTriedToWrap", "NULL", "aObject")) + +builtinNames = { + IDLType.Tags.bool: 'bool', + IDLType.Tags.int8: 'int8_t', + IDLType.Tags.int16: 'int16_t', + IDLType.Tags.int32: 'int32_t', + IDLType.Tags.int64: 'int64_t', + IDLType.Tags.uint8: 'uint8_t', + IDLType.Tags.uint16: 'uint16_t', + IDLType.Tags.uint32: 'uint32_t', + IDLType.Tags.uint64: 'uint64_t', + IDLType.Tags.float: 'float', + IDLType.Tags.double: 'double' +} + +class CastableObjectUnwrapper(): + """ + A class for unwrapping an object named by the "source" argument + based on the passed-in descriptor and storing it in a variable + called by the name in the "target" argument. + + codeOnFailure is the code to run if unwrapping fails. + """ + def __init__(self, descriptor, source, target, codeOnFailure): + assert descriptor.castable + self.substitution = { "type" : descriptor.nativeType, + "protoID" : "id::" + descriptor.name, + "source" : source, + "target" : target, + "codeOnFailure" : codeOnFailure } + + def __str__(self): + return string.Template( +""" { + nsresult rv = UnwrapObject<${protoID}>(cx, ${source}, &${target}); + if (NS_FAILED(rv)) { + ${codeOnFailure} + } + }""").substitute(self.substitution) + +class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper): + """ + As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails + """ + def __init__(self, descriptor, source, target): + CastableObjectUnwrapper.__init__(self, descriptor, source, target, + "return Throw<%s>(cx, rv);" % + toStringBool(not descriptor.workers)) + +class CallbackObjectUnwrapper: + """ + A class for unwrapping objects implemented in JS. + + |source| is the JSObject we want to use in native code. + |target| is an nsCOMPtr of the appropriate type in which we store the result. + """ + def __init__(self, descriptor, source, target, codeOnFailure=None): + if codeOnFailure is None: + codeOnFailure = ("return Throw<%s>(cx, rv);" % + toStringBool(not descriptor.workers)) + self.descriptor = descriptor + self.substitution = { "nativeType" : descriptor.nativeType, + "source" : source, + "target" : target, + "codeOnFailure" : codeOnFailure } + + def __str__(self): + if self.descriptor.workers: + return string.Template(""" + ${target} = ${source};""").substitute(self.substitution) + + return string.Template(""" + nsresult rv; + XPCCallContext ccx(JS_CALLER, cx); + if (!ccx.IsValid()) { + rv = NS_ERROR_XPC_BAD_CONVERT_JS; + ${codeOnFailure} + } + + const nsIID& iid = NS_GET_IID(${nativeType}); + nsRefPtr<nsXPCWrappedJS> wrappedJS; + rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid, + NULL, getter_AddRefs(wrappedJS)); + if (NS_FAILED(rv) || !wrappedJS) { + ${codeOnFailure} + } + + ${target} = do_QueryObject(wrappedJS.get()); + if (!${target}) { + ${codeOnFailure} + }""").substitute(self.substitution) + +def getArgumentConversionTemplate(type, descriptor): + if type.isSequence() or type.isArray(): + raise TypeError("Can't handle sequence or array arguments yet") + + if descriptor is not None: + assert(type.isInterface()) + # This is an interface that we implement as a concrete class + # or an XPCOM interface. + argIsPointer = type.nullable() or type.unroll().inner.isExternal() + if argIsPointer: + nameSuffix = "" + else: + nameSuffix = "_ptr" + + # If we're going to QI, we want an nsCOMPtr. But note that XPConnect + # unwrapping may or may not QI, and we don't know whether it will. So + # we use a raw pointer for the isExternal() case, and if a ref is needed + # it'll be handled by the xpc_qsSelfRef we put on the stack later. + if descriptor.castable or type.unroll().inner.isExternal() or descriptor.workers: + declType = " ${typeName}*" + else: + declType = " nsCOMPtr<${typeName}>" + template = declType + " ${name}%s;\n" % nameSuffix + + # We have to be very careful here to put anything that might need to + # hold references across the C++ call in |template| and not + # |templateBody|, since things in |templateBody| will go out of scope + # before the call happens. + templateBody = " if (${argVal}.isObject()) {" + if descriptor.castable: + templateBody += str(FailureFatalCastableObjectUnwrapper( + descriptor, + "&${argVal}.toObject()", + "${name}"+nameSuffix)).replace("\n", "\n ") + "\n" + elif descriptor.interface.isCallback(): + templateBody += str(CallbackObjectUnwrapper( + descriptor, + "&${argVal}.toObject()", + "${name}"+nameSuffix)) + "\n" + elif descriptor.workers: + templateBody += """ + ${name}%s = &${argVal}.toObject(); + MOZ_ASSERT(${name}%s); +""" % (nameSuffix, nameSuffix) + else: + template += " xpc_qsSelfRef tmpRef_${name};\n" + template += " jsval tmpVal_${name} = ${argVal};\n" + templateBody += """ + ${typeName}* tmp; + if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr, + &tmpVal_${name}))) { + return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS); + } + MOZ_ASSERT(tmp); + ${name}%s = tmp; +""" % (toStringBool(not descriptor.workers), nameSuffix) + + if type.nullable(): + templateBody += ( + " } else if (${argVal}.isNullOrUndefined()) {\n" + " ${name}%s = NULL;\n" % nameSuffix) + + templateBody += ( + " } else {\n" + " return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n" + " }\n" % toStringBool(not descriptor.workers)) + + template += templateBody + + if not argIsPointer: + template += " ${typeName} &${name} = *${name}_ptr;\n" + + return template + + if type.isArrayBuffer(): + template = ( + " JSObject* ${name};\n" + " if (${argVal}.isObject() && JS_IsArrayBufferObject(&${argVal}.toObject())) {\n" + " ${name} = &${argVal}.toObject();\n" + " }") + if type.nullable(): + template += ( + " else if (${argVal}.isNullOrUndefined()) {\n" + " ${name} = NULL;\n" + " }") + + template += ( + # XXXbz We don't know whether we're on workers, so play it safe + " else {\n" + " return Throw<false>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n" + " }") + + return template + + if type.isInterface(): + raise TypeError("Interface type with no descriptor: " + type) + + if type.isString(): + # XXXbz Need to figure out string behavior based on extended args? Also, how to + # detect them? + + # For nullable strings that are not otherwise annotated, null + # and undefined become null strings. + if type.nullable(): + nullBehavior = "eNull" + undefinedBehavior = "eNull" + else: + nullBehavior = "eDefaultNullBehavior" + undefinedBehavior = "eDefaultUndefinedBehavior" + + return ( + " xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n" + " xpc_qsDOMString::%s,\n" + " xpc_qsDOMString::%s);\n" + " if (!${name}.IsValid()) {\n" + " return false;\n" + " }\n" % (nullBehavior, undefinedBehavior)) + + if type.isEnum(): + if type.nullable(): + raise TypeError("We don't support nullable enumerated arguments " + "yet") + enum = type.inner.identifier.name + return ( + " %(enumtype)s ${name};\n" + " {\n" + " bool ok;\n" + " ${name} = static_cast<%(enumtype)s>(FindEnumStringIndex(cx, ${argVal}, %(values)s, &ok));\n" + " if (!ok) {\n" + " return false;\n" + " }\n" + " }" % { "enumtype" : enum + "::value", + "values" : enum + "::strings" }) + + if type.isCallback(): + # XXXbz we're going to assume that callback types are always + # nullable and always have [TreatNonCallableAsNull] for now. + return ( + " JSObject* ${name};\n" + " if (${argVal}.isObject() && JS_ObjectIsCallable(cx, &${argVal}.toObject())) {\n" + " ${name} = &${argVal}.toObject();\n" + " } else {\n" + " ${name} = NULL;\n" + " }\n") + + if type.isAny(): + return " JS::Value ${name} = ${argVal};\n" + + if not type.isPrimitive(): + raise TypeError("Need conversion for argument type '%s'" % type) + + tag = type.tag() + replacements = dict() + if type.nullable(): + replacements["declareArg"] = ( + " Nullable<${typeName}> ${name};\n" + " if (${argVal}.isNullOrUndefined()) {\n" + " ${name}.SetNull();\n" + " } else" + ) + replacements["finalValueSetter"] = "${name}.SetValue" + else: + replacements["declareArg"] = " ${typeName} ${name};\n" + replacements["finalValueSetter"] = "${name} = " + + replacements["intermediateCast"] = "" + + if tag == IDLType.Tags.bool: + replacements["jstype"] = "JSBool" + replacements["converter"] = "JS_ValueToBoolean" + elif tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, + IDLType.Tags.uint16, IDLType.Tags.int32, IDLType.Tags.uint32]: + # XXXbz need to add support for [EnforceRange] and [Clamp] + # The output of JS_ValueToECMAInt32 is determined as follows: + # 1) The value is converted to a double + # 2) Anything that's not a finite double returns 0 + # 3) The double is rounded towards zero to the nearest integer + # 4) The resulting integer is reduced mod 2^32. The output of this + # operation is an integer in the range [0, 2^32). + # 5) If the resulting number is >= 2^31, 2^32 is subtracted from it. + # + # The result of all this is a number in the range [-2^31, 2^31) + # + # WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types + # are defined in the same way, except that step 4 uses reduction mod + # 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5 + # is only done for the signed types. + # + # C/C++ define integer conversion semantics to unsigned types as taking + # your input integer mod (1 + largest value representable in the + # unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32, + # converting to the unsigned int of the relevant width will correctly + # perform step 4; in particular, the 2^32 possibly subtracted in step 5 + # will become 0. + # + # Once we have step 4 done, we're just going to assume 2s-complement + # representation and cast directly to the type we really want. + # + # So we can cast directly for all unsigned types and for int32_t; for + # the smaller-width signed types we need to cast through the + # corresponding unsigned type. + replacements["jstype"] = "int32_t" + replacements["converter"] = "JS::ToInt32" + if tag is IDLType.Tags.int8: + replacements["intermediateCast"] = "(uint8_t)" + elif tag is IDLType.Tags.int16: + replacements["intermediateCast"] = "(uint16_t)" + else: + replacements["intermediateCast"] = "" + elif tag is IDLType.Tags.int64: + # XXXbz this may not match what WebIDL says to do in terms of reducing + # mod 2^64. Should we check? + replacements["jstype"] = "PRInt64" + replacements["converter"] = "xpc_qsValueToInt64" + elif tag is IDLType.Tags.uint64: + # XXXbz this may not match what WebIDL says to do in terms of reducing + # mod 2^64. Should we check? + replacements["jstype"] = "PRUint64" + replacements["converter"] = "xpc_qsValueToUint64" + elif tag in [IDLType.Tags.float, IDLType.Tags.double]: + replacements["jstype"] = "double" + replacements["converter"] = "JS::ToNumber" + else: + raise TypeError("Unknown primitive type '%s'" % type); + + # We substitute the %(name)s things here. Our caller will + # substitute the ${name} things. + return (" %(jstype)s ${name}_jstype;\n" + "%(declareArg)s" # No leading whitespace or newline here, on purpose + " if (%(converter)s(cx, ${argVal}, &${name}_jstype)) {\n" + " %(finalValueSetter)s((${typeName})%(intermediateCast)s${name}_jstype);\n" + " } else {\n" + " return false;\n" + " }\n" % replacements) + +def convertConstIDLValueToJSVal(value): + if isinstance(value, IDLNullValue): + return "JSVAL_NULL" + tag = value.type.tag() + if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, + IDLType.Tags.uint16, IDLType.Tags.int32]: + return "INT_TO_JSVAL(%s)" % (value.value) + if tag == IDLType.Tags.uint32: + return "UINT_TO_JSVAL(%s)" % (value.value) + if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: + return "DOUBLE_TO_JSVAL(%s)" % (value.value) + if tag == IDLType.Tags.bool: + return "JSVAL_TRUE" if value.value else "JSVAL_FALSE" + if tag in [IDLType.Tags.float, IDLType.Tags.double]: + return "DOUBLE_TO_JSVAL(%s)" % (value.value) + raise TypeError("Const value of unhandled type: " + value.type) + +def convertIDLDefaultValueToJSVal(value): + if value.type: + tag = value.type.tag() + if tag == IDLType.Tags.domstring: + assert False # Not implemented! + return convertConstIDLValueToJSVal(value) + +unindenter = re.compile("^ ", re.MULTILINE) +class CGArgumentConverter(CGThing): + """ + A class that takes an IDL argument object, its index in the + argument list, and the argv and argc strings and generates code to + unwrap the argument to the right native type. + """ + def __init__(self, argument, index, argv, argc, descriptorProvider): + CGThing.__init__(self) + self.argument = argument + # XXXbz should optional jsval args get JSVAL_VOID? What about + # others? + self.replacementVariables = { + "index" : index, + "argc" : argc, + "argv" : argv, + "defaultValue" : "JSVAL_NULL", + "name" : "arg%d" % index + } + if argument.optional: + if argument.defaultValue: + self.replacementVariables["defaultValue"] = convertIDLDefaultValueToJSVal(argument.defaultValue) + self.replacementVariables["argVal"] = string.Template( + "(${index} < ${argc} ? ${argv}[${index}] : ${defaultValue})" + ).substitute(self.replacementVariables) + self.replacementVariables["argPtr"] = string.Template( + "(${index} < ${argc} ? &${argv}[${index}] : NULL)" + ).substitute(self.replacementVariables) + else: + self.replacementVariables["argVal"] = string.Template( + "${argv}[${index}]" + ).substitute(self.replacementVariables) + self.replacementVariables["argPtr"] = ( + "&" + self.replacementVariables["argVal"]) + self.descriptor = None + if argument.type.isPrimitive(): + self.replacementVariables["typeName"] = builtinNames[argument.type.tag()] + elif argument.type.isInterface() and not argument.type.isArrayBuffer(): + descriptor = descriptorProvider.getDescriptor( + argument.type.unroll().inner.identifier.name) + self.descriptor = descriptor + self.replacementVariables["typeName"] = descriptor.nativeType + + def define(self): + return string.Template( + re.sub(unindenter, + "", + getArgumentConversionTemplate(self.argument.type, + self.descriptor)) + ).substitute(self.replacementVariables) + +def getWrapTemplateForTypeImpl(type, result, descriptorProvider, + resultAlreadyAddRefed): + if type is None or type.isVoid(): + return """ + ${jsvalRef} = JSVAL_VOID; + return true;""" + + if type.isSequence() or type.isArray(): + raise TypeError("Can't handle sequence or array return values yet") + + if type.isInterface() and not type.isArrayBuffer(): + descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name) + wrappingCode = (""" + if (!%s) { + ${jsvalRef} = JSVAL_NULL; + return true; + }""" % result) if type.nullable() else "" + if descriptor.castable and not type.unroll().inner.isExternal(): + wrappingCode += """ + if (WrapNewBindingObject(cx, obj, %s, ${jsvalPtr})) { + return true; + }""" % result + if descriptor.workers: + # Worker bindings can only fail to wrap as a new-binding object + # if they already threw an exception + wrappingCode += """ + MOZ_ASSERT(JS_IsExceptionPending(cx)); + return false;""" + else: + # Try old-style wrapping for non-worker bindings + wrappingCode += """ + return HandleNewBindingWrappingFailure(cx, obj, %s, ${jsvalPtr});""" % result + else: + if descriptor.notflattened: + getIID = "&NS_GET_IID(%s), " % descriptor.nativeType + else: + getIID = "" + wrappingCode += """ + return WrapObject(cx, obj, %s, %s${jsvalPtr});""" % (result, getIID) + return wrappingCode + + if type.isString(): + if type.nullable(): + return """ + return xpc::StringToJsval(cx, %s, ${jsvalPtr});""" % result + else: + return """ + return xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr});""" % result + + if type.isEnum(): + if type.nullable(): + raise TypeError("We don't support nullable enumerated return types " + "yet") + return """ + MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s)); + JSString* result_str = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length); + if (!result_str) { + return false; + } + ${jsvalRef} = JS::StringValue(result_str); + return true;""" % { "result" : result, + "strings" : type.inner.identifier.name + "::strings" } + + if type.isCallback() and not type.isInterface(): + # XXXbz we're going to assume that callback types are always + # nullable and always have [TreatNonCallableAsNull] for now. + return """ + ${jsvalRef} = JS::ObjectOrNullValue(%s); + return true;""" % result + + if type.tag() == IDLType.Tags.any: + return """ + ${jsvalRef} = %s;\n + return true;""" % result + + if not type.isPrimitive(): + raise TypeError("Need to learn to wrap %s" % type) + + if type.nullable(): + return """ + if (%s.IsNull()) { + ${jsvalRef} = JSVAL_NULL; + return true; + } +%s""" % (result, getWrapTemplateForTypeImpl(type.inner, "%s.Value()" % result, + descriptorProvider, + resultAlreadyAddRefed)) + + tag = type.tag() + + if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, + IDLType.Tags.uint16, IDLType.Tags.int32]: + return """ + ${jsvalRef} = INT_TO_JSVAL(int32_t(%s)); + return true;""" % result + + elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float, + IDLType.Tags.double]: + # XXXbz will cast to double do the "even significand" thing that webidl + # calls for for 64-bit ints? Do we care? + return """ + return JS_NewNumberValue(cx, double(%s), ${jsvalPtr});""" % result + + elif tag == IDLType.Tags.uint32: + return """ + ${jsvalRef} = UINT_TO_JSVAL(%s); + return true;""" % result + + elif tag == IDLType.Tags.bool: + return """ + ${jsvalRef} = BOOLEAN_TO_JSVAL(%s); + return true;""" % result + + else: + raise TypeError("Need to learn to wrap primitive: %s" % type) + +def getWrapTemplateForType(type, descriptorProvider, resultAlreadyAddRefed): + return getWrapTemplateForTypeImpl(type, "result", descriptorProvider, + resultAlreadyAddRefed) + +class CGCallGenerator(CGThing): + """ + A class to generate an actual call to a C++ object. Assumes that the C++ + object is stored in a variable named "self". + """ + def __init__(self, errorReport, argCount, argsPre, returnType, + resultAlreadyAddRefed, descriptorProvider, nativeMethodName, static): + CGThing.__init__(self) + + isFallible = errorReport is not None + + args = CGList([CGGeneric("arg" + str(i)) for i in range(argCount)], ", ") + resultOutParam = returnType is not None and returnType.isString() + # Return values that go in outparams go here + if resultOutParam: + args.append(CGGeneric("result")) + if isFallible: + args.append(CGGeneric("rv")) + + if returnType is None or returnType.isVoid(): + # Nothing to declare + result = None + elif returnType.isPrimitive() and returnType.tag() in builtinNames: + result = CGGeneric(builtinNames[returnType.tag()]) + if returnType.nullable(): + result = CGWrapper(result, pre="Nullable<", post=">") + elif returnType.isString(): + result = CGGeneric("nsString") + elif returnType.isEnum(): + if returnType.nullable(): + raise TypeError("We don't support nullable enum return values") + result = CGGeneric(returnType.inner.identifier.name + "::value") + elif returnType.isInterface() and not returnType.isArrayBuffer(): + result = CGGeneric(descriptorProvider.getDescriptor( + returnType.unroll().inner.identifier.name).nativeType) + if resultAlreadyAddRefed: + result = CGWrapper(result, pre="nsRefPtr<", post=">") + else: + result = CGWrapper(result, post="*") + elif returnType.isCallback(): + # XXXbz we're going to assume that callback types are always + # nullable for now. + result = CGGeneric("JSObject*") + elif returnType.tag() is IDLType.Tags.any: + result = CGGeneric("JS::Value") + else: + raise TypeError("Don't know how to declare return value for %s" % + returnType) + + # Build up our actual call + self.cgRoot = CGList([], "\n") + + call = CGGeneric(nativeMethodName) + if static: + call = CGWrapper(call, pre="%s::" % (descriptorProvider.getDescriptor( + returnType.unroll().inner.identifier.name).nativeType)) + else: + call = CGWrapper(call, pre="self->") + call = CGList([call, CGWrapper(args, pre="(" + argsPre, post=");")]) + if result is not None: + result = CGWrapper(result, post=" result;") + self.cgRoot.prepend(result) + if not resultOutParam: + call = CGWrapper(call, pre="result = ") + + call = CGWrapper(call) + self.cgRoot.append(call) + + if isFallible: + self.cgRoot.prepend(CGGeneric("nsresult rv = NS_OK;")) + self.cgRoot.append(CGGeneric("if (NS_FAILED(rv)) {")) + self.cgRoot.append(CGIndenter(CGGeneric(errorReport))) + self.cgRoot.append(CGGeneric("}")) + + def define(self): + return self.cgRoot.define() + +class CGPerSignatureCall(CGThing): + """ + This class handles the guts of generating code for a particular + call signature. A call signature consists of four things: + + 1) A return type, which can be None to indicate that there is no + actual return value (e.g. this is an attribute setter) or an + IDLType if there's an IDL type involved (including |void|). + 2) An argument list, which is allowed to be empty. + 3) A name of a native method to call. + 4) Whether or not this method is static. + + We also need to know whether this is a method or a getter/setter + to do error reporting correctly. + + The idlNode parameter can be either a method or an attr. We can query + |idlNode.identifier| in both cases, so we can be agnostic between the two. + """ + # XXXbz For now each entry in the argument list is either an + # IDLArgument or a FakeArgument, but longer-term we may want to + # have ways of flagging things like JSContext* or optional_argc in + # there. + + def __init__(self, returnType, argsPre, arguments, nativeMethodName, static, + descriptor, idlNode, extendedAttributes, argConversionStartsAt=0): + CGThing.__init__(self) + self.returnType = returnType + self.descriptor = descriptor + self.idlNode = idlNode + self.extendedAttributes = extendedAttributes + # Default to already_AddRefed on the main thread, raw pointer in workers + self.resultAlreadyAddRefed = not descriptor.workers and not 'resultNotAddRefed' in self.extendedAttributes + self.argsPre = "cx, " if 'implicitJSContext' in self.extendedAttributes else "" + self.argsPre += argsPre + self.argCount = len(arguments) + if self.argCount > argConversionStartsAt: + # Insert our argv in there + cgThings = [CGGeneric(self.getArgvDecl())] + else: + cgThings = [] + cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(), + self.getArgc(), self.descriptor) for + i in range(argConversionStartsAt, self.argCount)]) + + cgThings.append(CGCallGenerator( + self.getErrorReport() if self.isFallible() else None, + self.argCount, self.argsPre, returnType, + self.resultAlreadyAddRefed, descriptor, nativeMethodName, + static)) + self.cgRoot = CGList(cgThings, "\n") + + def getArgv(self): + return "argv" if self.argCount > 0 else "" + def getArgvDecl(self): + return "\nJS::Value* argv = JS_ARGV(cx, vp);\n" + def getArgc(self): + return "argc" + + def isFallible(self): + return not 'infallible' in self.extendedAttributes + + def wrap_return_value(self): + resultTemplateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp'} + return string.Template( + re.sub(unindenter, + "", + getWrapTemplateForType(self.returnType, self.descriptor, + self.resultAlreadyAddRefed)) + ).substitute(resultTemplateValues) + + def getErrorReport(self): + return 'return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'\ + % (toStringBool(not self.descriptor.workers), + self.descriptor.interface.identifier.name, + self.idlNode.identifier.name) + + def define(self): + return (self.cgRoot.define() + self.wrap_return_value()) + +class CGSwitch(CGList): + """ + A class to generate code for a switch statement. + + Takes three constructor arguments: an expression, a list of cases, + and an optional default. + + Each case is a CGCase. The default is a CGThing for the body of + the default case, if any. + """ + def __init__(self, expression, cases, default=None): + CGList.__init__(self, [CGIndenter(c) for c in cases], "\n") + self.prepend(CGWrapper(CGGeneric(expression), + pre="switch (", post=") {")); + if default is not None: + self.append( + CGIndenter( + CGWrapper( + CGIndenter(default), + pre="default: {\n", + post="\n break;\n}" + ) + ) + ) + + self.append(CGGeneric("}")) + +class CGCase(CGList): + """ + A class to generate code for a case statement. + + Takes three constructor arguments: an expression, a CGThing for + the body (allowed to be None if there is no body), and an optional + argument (defaulting to False) for whether to fall through. + """ + def __init__(self, expression, body, fallThrough=False): + CGList.__init__(self, [], "\n") + self.append(CGWrapper(CGGeneric(expression), pre="case ", post=": {")) + if body is not None: + self.append(CGIndenter(body)) + if fallThrough: + self.append(CGIndenter(CGGeneric("/* Fall through */"))) + else: + self.append(CGIndenter(CGGeneric("break;"))) + self.append(CGGeneric("}")) + +class CGMethodCall(CGThing): + """ + A class to generate selection of a method signature from a set of + signatures and generation of a call to that signature. + """ + def __init__(self, argsPre, nativeMethodName, static, descriptor, method, + extendedAttributes): + CGThing.__init__(self) + + def requiredArgCount(signature): + arguments = signature[1] + if len(arguments) == 0: + return 0 + requiredArgs = len(arguments) + while requiredArgs and arguments[requiredArgs-1].optional: + requiredArgs -= 1 + return requiredArgs + + def maxSigLength(signatures): + return max([len(s[1]) for s in signatures]) + + def signaturesForArgCount(i, signatures): + return filter( + lambda s: len(s[1]) == i or (len(s[1]) > i and + s[1][i].optional), + signatures) + + def findDistinguishingIndex(argCount, signatures): + def isValidDistinguishingIndex(idx, signatures): + for firstSigIndex in range(0, len(signatures)): + for secondSigIndex in range(0, firstSigIndex): + firstType = signatures[firstSigIndex][1][idx].type + secondType = signatures[secondSigIndex][1][idx].type + if not firstType.isDistinguishableFrom(secondType): + return False + return True + for idx in range(0, argCount): + if isValidDistinguishingIndex(idx, signatures): + return idx + return -1 + + def getPerSignatureCall(signature, argConversionStartsAt=0): + return CGPerSignatureCall(signature[0], argsPre, signature[1], + nativeMethodName, static, descriptor, + method, extendedAttributes, + argConversionStartsAt) + + + signatures = method.signatures() + if len(signatures) == 1: + # Special case: we can just do a per-signature method call + # here for our one signature and not worry about switching + # on anything. + signature = signatures[0] + self.cgRoot = CGList([ CGIndenter(getPerSignatureCall(signature)) ]) + requiredArgs = requiredArgCount(signature) + if requiredArgs > 0: + self.cgRoot.prepend( + CGWrapper( + CGIndenter( + CGGeneric( + "if (argc < %d) {\n" + " return Throw<%s>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n" + "}" % (requiredArgs, + toStringBool(not descriptor.workers))) + ), + pre="\n", post="\n") + ) + return + + # Need to find the right overload + maxSigArgs = maxSigLength(signatures) + allowedArgCounts = [ i for i in range(0, maxSigArgs+1) + if len(signaturesForArgCount(i, signatures)) != 0 ] + + argCountCases = [] + for argCount in allowedArgCounts: + possibleSignatures = signaturesForArgCount(argCount, signatures) + if len(possibleSignatures) == 1: + # easy case! + signature = possibleSignatures[0] + + # (possibly) important optimization: if signature[1] has > + # argCount arguments and signature[1][argCount] is optional and + # there is only one signature for argCount+1, then the + # signature for argCount+1 is just ourselves and we can fall + # through. + if (len(signature[1]) > argCount and + signature[1][argCount].optional and + (argCount+1) in allowedArgCounts and + len(signaturesForArgCount(argCount+1, signatures)) == 1): + argCountCases.append( + CGCase(str(argCount), None, True)) + else: + argCountCases.append( + CGCase(str(argCount), getPerSignatureCall(signature))) + continue + + distinguishingIndex = findDistinguishingIndex(argCount, + possibleSignatures) + if distinguishingIndex == -1: + raise TypeError(("Signatures with %s arguments for " + + descriptor.interface.identifier.name + "." + + method.identifier.name + + " are not distinguishable") % argCount) + + for idx in range(0, distinguishingIndex): + firstSigType = possibleSignatures[0][1][idx].type + for sigIdx in range(1, len(possibleSignatures)): + if possibleSignatures[sigIdx][1][idx].type != firstSigType: + raise TypeError(("Signatures with %d arguments for " + + descriptor.interface.identifier.name + + "." + method.identifier.name + + " have different types at index %d" + + " which is before distinguishing" + + " index %d") % (argCount, + idx, + distinguishingIndex)) + + # Convert all our arguments up to the distinguishing index. + # Doesn't matter which of the possible signatures we use, since + # they all have the same types up to that point; just use + # possibleSignatures[0] + caseBody = [CGGeneric("JS::Value* argv_start = JS_ARGV(cx, vp);")] + caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i], + i, "argv_start", "argc", + descriptor) for i in + range(0, distinguishingIndex) ]) + + # Select the right overload from our set. + distinguishingArg = "argv_start[%d]" % distinguishingIndex + + def pickFirstSignature(condition, filterLambda): + sigs = filter(filterLambda, possibleSignatures) + assert len(sigs) < 2 + if len(sigs) > 0: + if condition is None: + caseBody.append( + getPerSignatureCall(sigs[0], distinguishingIndex)) + else: + caseBody.append(CGGeneric("if (" + condition + ") {")) + caseBody.append(CGIndenter( + getPerSignatureCall(sigs[0], distinguishingIndex))) + caseBody.append(CGGeneric("}")) + return True + return False + + # First check for null or undefined + pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg, + lambda s: s[1][distinguishingIndex].type.nullable()) + + # XXXbz Now we're supposed to check for distinguishingArg being + # an array or a platform object that supports indexed + # properties... skip that last for now. It's a bit of a pain. + pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject()" % + (distinguishingArg, distinguishingArg), + lambda s: + (s[1][distinguishingIndex].type.isArray() or + s[1][distinguishingIndex].type.isSequence() or + s[1][distinguishingIndex].type.isObject())) + + # Now check for distinguishingArg being a platform object. + # We can actually check separately for array buffers and + # other things. + # XXXbz Do we need to worry about security + # wrappers around the array buffer? + pickFirstSignature("%s.isObject() && JS_IsArrayBufferObject(&%s.toObject())" % + (distinguishingArg, distinguishingArg), + lambda s: (s[1][distinguishingIndex].type.isArrayBuffer() or + s[1][distinguishingIndex].type.isObject())) + + interfacesSigs = [ + s for s in possibleSignatures + if (s[1][distinguishingIndex].type.isObject() or + (s[1][distinguishingIndex].type.isInterface() and + not s[1][distinguishingIndex].type.isArrayBuffer() and + not s[1][distinguishingIndex].type.isCallback())) ] + # There might be more than one of these; we need to check + # which ones we unwrap to. + + if len(interfacesSigs) > 0: + caseBody.append(CGGeneric("if (%s.isObject() &&\n" + " IsPlatformObject(cx, &%s.toObject())) {" % + (distinguishingArg, distinguishingArg))) + for sig in interfacesSigs: + caseBody.append(CGIndenter(CGGeneric("do {"))); + type = sig[1][distinguishingIndex].type + + # XXXbz this duplicates some argument-unwrapping code! + interfaceDesc = descriptor.getDescriptor( + type.unroll().inner.identifier.name) + argIsPointer = (type.nullable() or + type.unroll().inner.isExternal()) + if argIsPointer: + nameSuffix = "" + else: + nameSuffix = "_ptr" + if (interfaceDesc.castable or + type.unroll().inner.isExternal() or + interfaceDesc.workers): + declType = " ${typeName}*" + else: + declType = " nsCOMPtr<${typeName}>" + template = declType + " ${name}%s;\n" % nameSuffix + if interfaceDesc.castable: + template += str(CastableObjectUnwrapper( + interfaceDesc, + "&${argVal}.toObject()", + "${name}"+nameSuffix, + "break;")) + "\n" + elif interfaceDesc.workers: + template += """ + ${name}%s = &${argVal}.toObject(); + MOZ_ASSERT(${name}%s); +""" % (nameSuffix, nameSuffix) + else: + template += " xpc_qsSelfRef tmpRef_${name};\n" + template += " jsval tmpVal_${name} = ${argVal};\n" + template += """ + ${typeName}* tmp; + if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr, + &tmpVal_${name}))) { + break; + } + MOZ_ASSERT(tmp); + ${name}%s = tmp; +""" % nameSuffix + + if not argIsPointer: + template += " ${typeName} &${name} = *${name}_ptr;\n" + + testCode = string.Template(template).substitute( + { + "typeName": interfaceDesc.nativeType, + "name" : "arg%d" % distinguishingIndex, + "argVal" : distinguishingArg + } + ) + caseBody.append(CGIndenter(CGGeneric(testCode))); + # If we got this far, we know we unwrapped to the right + # interface, so just do the call. Start conversion with + # distinguishingIndex + 1, since we already converted + # distinguishingIndex. + caseBody.append(CGIndenter(CGIndenter( + getPerSignatureCall(sig, distinguishingIndex + 1)))) + caseBody.append(CGIndenter(CGGeneric("} while (0);"))) + + caseBody.append(CGGeneric("}")) + + # Check for Date objects + # XXXbz Do we need to worry about security wrappers around the Date? + pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" % + (distinguishingArg, distinguishingArg), + lambda s: (s[1][distinguishingIndex].type.isDate() or + s[1][distinguishingIndex].type.isObject())) + + # Check for vanilla JS objects + # XXXbz Do we need to worry about security wrappers? + pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" % + (distinguishingArg, distinguishingArg), + lambda s: (s[1][distinguishingIndex].type.isCallback() or + s[1][distinguishingIndex].type.isDictionary() or + s[1][distinguishingIndex].type.isObject())) + + # The remaining cases are mutually exclusive. The + # pickFirstSignature calls are what change caseBody + # Check for strings or enums + if pickFirstSignature(None, + lambda s: (s[1][distinguishingIndex].type.isString() or + s[1][distinguishingIndex].type.isEnum())): + pass + # Check for primitives + elif pickFirstSignature(None, + lambda s: s[1][distinguishingIndex].type.isPrimitive()): + pass + # Check for "any" + elif pickFirstSignature(None, + lambda s: s[1][distinguishingIndex].type.isAny()): + pass + else: + # Just throw; we have no idea what we're supposed to + # do with this. + caseBody.append(CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);" % + toStringBool(not descriptor.workers))) + + argCountCases.append(CGCase(str(argCount), + CGList(caseBody, "\n"))) + + overloadCGThings = [] + overloadCGThings.append( + CGGeneric("unsigned argcount = NS_MIN(argc, %du);" % + maxSigArgs)) + overloadCGThings.append( + CGSwitch("argcount", + argCountCases, + CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);" % + toStringBool(not descriptor.workers)))) + overloadCGThings.append( + CGGeneric('MOZ_NOT_REACHED("We have an always-returning default case");\n' + 'return false;')) + self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings, "\n")), + pre="\n") + + def define(self): + return self.cgRoot.define() + +class CGGetterSetterCall(CGPerSignatureCall): + """ + A class to generate a native object getter or setter call for a + particular IDL getter or setter. + """ + def __init__(self, returnType, arguments, nativeMethodName, descriptor, + attr, extendedAttributes): + CGPerSignatureCall.__init__(self, returnType, "", arguments, + nativeMethodName, False, descriptor, attr, + extendedAttributes) + def getArgv(self): + if generateNativeAccessors: + return CGPerSignatureCall.getArgv(self) + return "vp" + +class CGGetterCall(CGGetterSetterCall): + """ + A class to generate a native object getter call for a particular IDL + getter. + """ + def __init__(self, returnType, nativeMethodName, descriptor, attr, + extendedAttributes): + CGGetterSetterCall.__init__(self, returnType, [], nativeMethodName, + descriptor, attr, extendedAttributes) + def getArgc(self): + if generateNativeAccessors: + return CGGetterSetterCall.getArgc() + return "0" + def getArgvDecl(self): + if generateNativeAccessors: + return CGPerSignatureCall.getArgvDecl(self) + # We just get our stuff from vp + return "" + +class FakeArgument(): + def __init__(self, type): + self.type = type + self.optional = False + +class CGSetterCall(CGGetterSetterCall): + """ + A class to generate a native object setter call for a particular IDL + setter. + """ + def __init__(self, argType, nativeMethodName, descriptor, attr, + extendedAttributes): + CGGetterSetterCall.__init__(self, None, [FakeArgument(argType)], + nativeMethodName, descriptor, attr, + extendedAttributes) + def wrap_return_value(self): + if generateNativeAccessors: + return CGGetterSetterCall.wrap_return_value(self) + # We have no return value + return "\nreturn true;" + def getArgc(self): + if generateNativeAccessors: + return CGGetterSetterCall.getArgc(self) + return "1" + def getArgvDecl(self): + if generateNativeAccessors: + return (CGPerSignatureCall.getArgvDecl(self) + + "jsval undef = JS::UndefinedValue();\n" + "if (argc == 0) {\n" + " argv = &undef;\n" + " argc = 1;\n" + "}") + # We just get our stuff from vp + return "" + +class CGAbstractBindingMethod(CGAbstractStaticMethod): + """ + Common class to generate the JSNatives for all our methods, getters, and + setters. This will generate the function declaration and unwrap the + |this| object. Subclasses are expected to override the generate_code + function to do the rest of the work. This function should return a + CGThing which is already properly indented. + """ + def __init__(self, descriptor, name, args, extendedAttributes): + self.extendedAttributes = extendedAttributes + CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args) + + def definition_body(self): + unwrapThis = CGGeneric( + str(FailureFatalCastableObjectUnwrapper(self.descriptor, "obj", "self"))) + return CGList([ self.getThis(), unwrapThis, + self.generate_code() ], "\n").define() + + def getThis(self): + return CGIndenter( + CGGeneric("JSObject* obj = JS_THIS_OBJECT(cx, vp);\n" + "if (!obj) {\n" + " return false;\n" + "}\n" + "\n" + "%s* self;" % self.descriptor.nativeType)) + + def generate_code(self): + assert(False) # Override me + +def MakeNativeName(name): + return name[0].upper() + name[1:] + +class CGNativeMethod(CGAbstractBindingMethod): + """ + A class for generating the C++ code for an IDL method.. + """ + def __init__(self, descriptor, method): + self.method = method + baseName = method.identifier.name + args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), + Argument('JS::Value*', 'vp')] + CGAbstractBindingMethod.__init__(self, descriptor, baseName, args, + descriptor.getExtendedAttributes(method)) + def generate_code(self): + name = self.method.identifier.name + nativeName = self.descriptor.binaryNames.get(name, MakeNativeName(name)) + return CGMethodCall("", nativeName, self.method.isStatic(), + self.descriptor, self.method, + self.extendedAttributes) + +class CGNativeGetter(CGAbstractBindingMethod): + """ + A class for generating the C++ code for an IDL attribute getter. + """ + def __init__(self, descriptor, attr): + self.attr = attr + name = 'get_' + attr.identifier.name + if generateNativeAccessors: + args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), + Argument('JS::Value*', 'vp')] + else: + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'), + Argument('jsid', 'id'), Argument('JS::Value*', 'vp')] + CGAbstractBindingMethod.__init__(self, descriptor, name, args, + descriptor.getExtendedAttributes(self.attr, getter=True)) + + def getThis(self): + if generateNativeAccessors: + return CGAbstractBindingMethod.getThis(self) + return CGIndenter( + CGGeneric("%s* self;" % self.descriptor.nativeType)) + + def generate_code(self): + + nativeMethodName = "Get" + MakeNativeName(self.attr.identifier.name) + return CGIndenter(CGGetterCall(self.attr.type, nativeMethodName, self.descriptor, + self.attr, self.extendedAttributes)) + +class CGNativeSetter(CGAbstractBindingMethod): + """ + A class for generating the C++ code for an IDL attribute setter. + """ + def __init__(self, descriptor, attr): + self.attr = attr + baseName = attr.identifier.name + name = 'set_' + attr.identifier.name + if generateNativeAccessors: + args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), + Argument('JS::Value*', 'vp')] + else: + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'), + Argument('jsid', 'id'), Argument('JSBool', 'strict'), + Argument('JS::Value*', 'vp')] + CGAbstractBindingMethod.__init__(self, descriptor, name, args, + descriptor.getExtendedAttributes(self.attr, setter=True)) + + def getThis(self): + if generateNativeAccessors: + return CGAbstractBindingMethod.getThis(self) + return CGIndenter( + CGGeneric("%s* self;" % self.descriptor.nativeType)) + + def generate_code(self): + nativeMethodName = "Set" + MakeNativeName(self.attr.identifier.name) + return CGIndenter(CGSetterCall(self.attr.type, nativeMethodName, self.descriptor, + self.attr, self.extendedAttributes)) + +def getEnumValueName(value): + # Some enum values can be empty strings. Others might have weird + # characters in them. Deal with the former by returning "_empty", + # deal with possible name collisions from that by throwing if the + # enum value is actually "_empty", and throw on any value + # containing chars other than [a-z] or '-' for now. Replace '-' with '_'. + value = value.replace('-', '_') + if value == "_empty": + raise SyntaxError('"_empty" is not an IDL enum value we support yet') + if value == "": + return "_empty" + if not re.match("^[a-z_]+$", value): + raise SyntaxError('Enum value "' + value + '" contains characters ' + 'outside [a-z_]') + return value + +class CGEnum(CGThing): + def __init__(self, enum): + CGThing.__init__(self) + self.enum = enum + + def declare(self): + return """ + enum value { + %s + }; + + extern const EnumEntry strings[%d]; +""" % (",\n ".join(map(getEnumValueName, self.enum.values())), + len(self.enum.values()) + 1) + + def define(self): + return """ + const EnumEntry strings[%d] = { + %s, + { NULL, 0 } + }; +""" % (len(self.enum.values()) + 1, + ",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()])) + +class ClassItem: + """ Use with CGClass """ + def __init__(self, name, visibility): + self.name = name + self.visibility = visibility + def declare(self, cgClass): + assert False + def define(self, cgClass): + assert False + +class ClassBase(ClassItem): + def __init__(self, name, visibility='public'): + ClassItem.__init__(self, name, visibility) + def declare(self, cgClass): + return '%s %s' % (self.visibility, self.name) + def define(self, cgClass): + # Only in the header + return '' + +class ClassMethod(ClassItem): + def __init__(self, name, returnType, args, inline=False, static=False, + virtual=False, const=False, bodyInHeader=False, + templateArgs=None, visibility='public', body=None): + self.returnType = returnType + self.args = args + self.inline = inline or bodyInHeader + self.static = static + self.virtual = virtual + self.const = const + self.bodyInHeader = bodyInHeader + self.templateArgs = templateArgs + self.body = body + ClassItem.__init__(self, name, visibility) + + def getDecorators(self, declaring): + decorators = [] + if self.inline: + decorators.append('inline') + if declaring: + if self.static: + decorators.append('static') + if self.virtual: + decorators.append('virtual') + if decorators: + return ' '.join(decorators) + ' ' + return '' + + def getBody(self): + # Override me or pass a string to constructor + assert self.body is not None + return self.body + + def declare(self, cgClass): + templateClause = 'template <%s>\n' % ', '.join(self.templateArgs) \ + if self.bodyInHeader and self.templateArgs else '' + args = ', '.join([str(a) for a in self.args]) + if self.bodyInHeader: + body = ' ' + self.getBody(); + body = body.replace('\n', '\n ').rstrip(' ') + body = '\n{\n' + body + '\n}' + else: + body = ';' + + return string.Template("""${templateClause}${decorators}${returnType} +${name}(${args})${const}${body} +""").substitute({ 'templateClause': templateClause, + 'decorators': self.getDecorators(True), + 'returnType': self.returnType, + 'name': self.name, + 'const': ' const' if self.const else '', + 'args': args, + 'body': body }) + + def define(self, cgClass): + if self.bodyInHeader: + return '' + + templateArgs = cgClass.templateArgs + if templateArgs: + if cgClass.templateSpecialization: + templateArgs = \ + templateArgs[len(cgClass.templateSpecialization):] + + if templateArgs: + templateClause = \ + 'template <%s>\n' % ', '.join([str(a) for a in templateArgs]) + else: + templateClause = '' + + args = ', '.join([str(a) for a in self.args]) + + body = ' ' + self.getBody() + body = body.replace('\n', '\n ').rstrip(' ') + + return string.Template("""${templateClause}${decorators}${returnType} +${className}::${name}(${args})${const} +{ +${body} +}\n +""").substitute({ 'templateClause': templateClause, + 'decorators': self.getDecorators(False), + 'returnType': self.returnType, + 'className': cgClass.getNameString(), + 'name': self.name, + 'args': args, + 'const': ' const' if self.const else '', + 'body': body }) + +class ClassMember(ClassItem): + def __init__(self, name, type, visibility="private", static=False, + body=None): + self.type = type; + self.static = static + self.body = body + ClassItem.__init__(self, name, visibility) + + def getBody(self): + assert self.body is not None + return self.body + + def declare(self, cgClass): + return '%s%s %s;\n' % ('static ' if self.static else '', self.type, + self.name) + + def define(self, cgClass): + if not self.static: + return '' + return '%s %s::%s = %s;\n' % (self.type, cgClass.getNameString(), + self.name, self.getBody()) + +class ClassTypedef(ClassItem): + def __init__(self, name, type, visibility="public"): + self.type = type + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + return 'typedef %s %s;\n' % (self.type, self.name) + + def define(self, cgClass): + # Only goes in the header + return '' + +class ClassEnum(ClassItem): + def __init__(self, name, entries, values=None, visibility="public"): + self.entries = entries + self.values = values + ClassItem.__init__(self, name, visibility) + + def declare(self, cgClass): + entries = [] + for i in range(0, len(self.entries)): + if i >= len(self.values): + entry = '%s' % self.entries[i] + else: + entry = '%s = %s' % (self.entries[i], self.values[i]) + entries.append(entry) + name = '' if not self.name else ' ' + self.name + return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries)) + + def define(self, cgClass): + # Only goes in the header + return '' + +class CGClass(CGThing): + def __init__(self, name, bases=[], members=[], methods=[], typedefs = [], + enums=[], templateArgs=[], templateSpecialization=[], + isStruct=False, indent=''): + CGThing.__init__(self) + self.name = name + self.bases = bases + self.members = members + self.methods = methods + self.typedefs = typedefs + self.enums = enums + self.templateArgs = templateArgs + self.templateSpecialization = templateSpecialization + self.isStruct = isStruct + self.indent = indent + self.defaultVisibility ='public' if isStruct else 'private' + + def getNameString(self): + className = self.name + if self.templateSpecialization: + className = className + \ + '<%s>' % ', '.join([str(a) for a + in self.templateSpecialization]) + return className + + def declare(self): + result = '' + if self.templateArgs: + templateArgs = [str(a) for a in self.templateArgs] + templateArgs = templateArgs[len(self.templateSpecialization):] + result = result + self.indent + 'template <%s>\n' \ + % ','.join([str(a) for a in templateArgs]) + + type = 'struct' if self.isStruct else 'class' + + if self.templateSpecialization: + specialization = \ + '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) + else: + specialization = '' + + result = result + '%s%s %s%s' \ + % (self.indent, type, self.name, specialization) + + if self.bases: + result = result + ' : %s' % ', '.join([d.declare(self) for d in self.bases]) + + result = result + '\n%s{\n' % self.indent + + def declareMembers(cgClass, memberList, defaultVisibility, itemCount, + separator=''): + members = { 'private': [], 'protected': [], 'public': [] } + + for member in memberList: + members[member.visibility].append(member) + + + if defaultVisibility == 'public': + order = [ 'public', 'protected', 'private' ] + else: + order = [ 'private', 'protected', 'public' ] + + result = '' + + lastVisibility = defaultVisibility + for visibility in order: + list = members[visibility] + if list: + if visibility != lastVisibility: + if itemCount: + result = result + '\n' + result = result + visibility + ':\n' + itemCount = 0 + for member in list: + if itemCount == 0: + result = result + ' ' + else: + result = result + separator + ' ' + declaration = member.declare(cgClass) + declaration = declaration.replace('\n', '\n ') + declaration = declaration.rstrip(' ') + result = result + declaration + itemCount = itemCount + 1 + lastVisibility = visibility + return (result, lastVisibility, itemCount) + + order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''), + (self.methods, '\n')] + + lastVisibility = self.defaultVisibility + itemCount = 0 + for (memberList, separator) in order: + (memberString, lastVisibility, itemCount) = \ + declareMembers(self, memberList, lastVisibility, itemCount, + separator) + if self.indent: + memberString = self.indent + memberString + memberString = memberString.replace('\n', '\n' + self.indent) + memberString = memberString.rstrip(' ') + result = result + memberString + + result = result + self.indent + '};\n\n' + return result + + def define(self): + def defineMembers(cgClass, memberList, itemCount, separator=''): + result = '' + for member in memberList: + if itemCount != 0: + result = result + separator + result = result + member.define(cgClass) + itemCount = itemCount + 1 + return (result, itemCount) + + order = [(self.members, '\n'), (self.methods, '\n')] + + result = '' + itemCount = 0 + for (memberList, separator) in order: + (memberString, itemCount) = defineMembers(self, memberList, + itemCount, separator) + result = result + memberString + return result + +class CGResolveProperty(CGAbstractMethod): + def __init__(self, descriptor, properties): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'), + Argument('jsid', 'id'), Argument('bool', 'set'), + Argument('JSPropertyDescriptor*', 'desc')] + CGAbstractMethod.__init__(self, descriptor, "ResolveProperty", "bool", args) + self.properties = properties + def definition_body(self): + str = "" + + varNames = self.properties.variableNames(True) + + methods = self.properties.methods + if methods.hasNonChromeOnly() or methods.hasChromeOnly(): + str += """ for (size_t i = 0; i < ArrayLength(%(methods)s_ids); ++i) { + if (id == %(methods)s_ids[i]) { + JSFunction *fun = JS_NewFunctionById(cx, %(methods)s[i].call, %(methods)s[i].nargs, 0, wrapper, id); + if (!fun) + return false; + JSObject *funobj = JS_GetFunctionObject(fun); + desc->value.setObject(*funobj); + desc->attrs = %(methods)s[i].flags; + desc->obj = wrapper; + desc->setter = nsnull; + desc->getter = nsnull; + return true; + } + } + +""" % varNames + + attrs = self.properties.attrs + if attrs.hasNonChromeOnly() or attrs.hasChromeOnly(): + str += """ for (size_t i = 0; i < ArrayLength(%(attrs)s_ids); ++i) { + if (id == %(attrs)s_ids[i]) { + desc->attrs = %(attrs)s[i].flags; + desc->obj = wrapper; + desc->setter = %(attrs)s[i].setter; + desc->getter = %(attrs)s[i].getter; + return true; + } + } + +""" % varNames + + return str + " return true;" + +class CGEnumerateProperties(CGAbstractMethod): + def __init__(self, descriptor, properties): + args = [Argument('JS::AutoIdVector&', 'props')] + CGAbstractMethod.__init__(self, descriptor, "EnumerateProperties", "bool", args) + self.properties = properties + def definition_body(self): + str = "" + + varNames = self.properties.variableNames(True) + + methods = self.properties.methods + if methods.hasNonChromeOnly() or methods.hasChromeOnly(): + str += """ for (size_t i = 0; i < sizeof(%(methods)s_ids); ++i) { + if ((%(methods)s[i].flags & JSPROP_ENUMERATE) && + !props.append(%(methods)s_ids[i])) { + return false; + } + } + +""" % varNames + + attrs = self.properties.attrs + if attrs.hasNonChromeOnly() or attrs.hasChromeOnly(): + str += """ for (size_t i = 0; i < sizeof(%(attrs)s_ids); ++i) { + if ((%(attrs)s[i].flags & JSPROP_ENUMERATE) && + !props.append(%(attrs)s_ids[i])) { + return false; + } + } + +""" % varNames + + return str + " return true;" + +class CGPrototypeTraitsClass(CGClass): + def __init__(self, descriptor, indent=''): + templateArgs = [Argument('prototypes::ID', 'PrototypeID')] + templateSpecialization = ['prototypes::id::' + descriptor.name] + enums = [ClassEnum('', ['Depth'], + [descriptor.interface.inheritanceDepth()])] + typedefs = [ClassTypedef('NativeType', descriptor.nativeType)] + CGClass.__init__(self, 'PrototypeTraits', indent=indent, + templateArgs=templateArgs, + templateSpecialization=templateSpecialization, + enums=enums, typedefs=typedefs, isStruct=True) + +class CGPrototypeIDMapClass(CGClass): + def __init__(self, descriptor, indent=''): + templateArgs = [Argument('class', 'ConcreteClass')] + templateSpecialization = [descriptor.nativeType] + enums = [ClassEnum('', ['PrototypeID'], + ['prototypes::id::' + descriptor.name])] + CGClass.__init__(self, 'PrototypeIDMap', indent=indent, + templateArgs=templateArgs, + templateSpecialization=templateSpecialization, + enums=enums, isStruct=True) + +class CGClassForwardDeclare(CGThing): + def __init__(self, name, isStruct=False): + CGThing.__init__(self) + self.name = name + self.isStruct = isStruct + def declare(self): + type = 'struct' if self.isStruct else 'class' + return '%s %s;\n' % (type, self.name) + def define(self): + # Header only + return '' + +def stripTrailingWhitespace(text): + lines = text.splitlines() + for i in range(len(lines)): + lines[i] = lines[i].rstrip() + return '\n'.join(lines) + +class CGDescriptor(CGThing): + def __init__(self, descriptor): + CGThing.__init__(self) + + assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject() + + cgThings = [] + if descriptor.interface.hasInterfacePrototypeObject(): + cgThings.extend([CGNativeMethod(descriptor, m) for m in + descriptor.interface.members if + m.isMethod() and not m.isStatic()]) + cgThings.extend([CGNativeGetter(descriptor, a) for a in + descriptor.interface.members if a.isAttr()]) + cgThings.extend([CGNativeSetter(descriptor, a) for a in + descriptor.interface.members if + a.isAttr() and not a.readonly]) + + if descriptor.concrete: + if not descriptor.workers: + cgThings.append(CGAddPropertyHook(descriptor)) + + # Always have a finalize hook, regardless of whether the class wants a + # custom hook. + if descriptor.nativeIsISupports: + cgThings.append(CGNativeToSupportsMethod(descriptor)) + cgThings.append(CGClassFinalizeHook(descriptor)) + + # Only generate a trace hook if the class wants a custom hook. + if (descriptor.customTrace): + cgThings.append(CGClassTraceHook(descriptor)) + + if descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject(): + cgThings.append(CGNativePropertyHooks(descriptor)) + if descriptor.concrete: + cgThings.append(CGDOMJSClass(descriptor)) + + if descriptor.interface.hasInterfaceObject(): + cgThings.append(CGClassConstructHook(descriptor)) + cgThings.append(CGClassHasInstanceHook(descriptor)) + cgThings.append(CGInterfaceObjectJSClass(descriptor)) + + if descriptor.interface.hasInterfacePrototypeObject(): + cgThings.append(CGPrototypeJSClass(descriptor)) + + properties = PropertyArrays(descriptor) + cgThings.append(CGGeneric(define=str(properties))) + cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties)) + if descriptor.interface.hasInterfacePrototypeObject(): + cgThings.append(CGIndenter(CGGetProtoObjectMethod(descriptor))) + else: + cgThings.append(CGIndenter(CGGetConstructorObjectMethod(descriptor))) + + if descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject(): + cgThings.append(CGResolveProperty(descriptor, properties)) + cgThings.append(CGEnumerateProperties(descriptor, properties)) + + if descriptor.interface.hasInterfaceObject(): + cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) + + if descriptor.concrete: + cgThings.append(CGWrapMethod(descriptor)) + + cgThings = CGList(cgThings) + cgThings = CGWrapper(cgThings, post='\n') + self.cgRoot = CGWrapper(CGNamespace(descriptor.name, cgThings), + post='\n') + + def declare(self): + return self.cgRoot.declare() + def define(self): + return self.cgRoot.define() + +class CGNamespacedEnum(CGThing): + def __init__(self, namespace, enumName, names, values, comment=""): + + if not values: + values = [] + + # Account for explicit enum values. + entries = [] + for i in range(0, len(names)): + if len(values) > i and values[i] is not None: + entry = "%s = %s" % (names[i], values[i]) + else: + entry = names[i] + entries.append(entry) + + # Append a Count. + entries.append('_' + enumName + '_Count') + + # Indent. + entries = [' ' + e for e in entries] + + # Build the enum body. + enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries)) + curr = CGGeneric(declare=enumstr) + + # Add some whitespace padding. + curr = CGWrapper(curr, pre='\n',post='\n') + + # Add the namespace. + curr = CGNamespace(namespace, curr) + + # Add the typedef + typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName) + curr = CGList([curr, CGGeneric(declare=typedef)]) + + # Save the result. + self.node = curr + + def declare(self): + return self.node.declare() + def define(self): + assert False # Only for headers. + +class CGRegisterProtos(CGAbstractMethod): + def __init__(self, config): + CGAbstractMethod.__init__(self, None, 'Register', 'void', + [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')]) + self.config = config + + def _defineMacro(self): + return """ +#define REGISTER_PROTO(_dom_class) \\ + aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), prototypes::_dom_class::DefineDOMInterface);\n\n""" + def _undefineMacro(self): + return "\n#undef REGISTER_PROTO" + def _registerProtos(self): + lines = ["REGISTER_PROTO(%s);" % desc.name + for desc in self.config.getDescriptors(hasInterfaceObject=True, + isExternal=False, + workers=False)] + return '\n'.join(lines) + '\n' + def definition_body(self): + return self._defineMacro() + self._registerProtos() + self._undefineMacro() + +class CGBindingRoot(CGThing): + """ + Root codegen class for binding generation. Instantiate the class, and call + declare or define to generate header or cpp code (respectively). + """ + def __init__(self, config, prefix, webIDLFile): + descriptors = config.getDescriptors(webIDLFile=webIDLFile, + hasInterfaceOrInterfacePrototypeObject=True) + + forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')] + + for x in descriptors: + nativeType = x.nativeType + components = x.nativeType.split('::') + declare = CGClassForwardDeclare(components[-1]) + if len(components) > 1: + declare = CGNamespace.build(components[:-1], + CGWrapper(declare, declarePre='\n', + declarePost='\n'), + declareOnly=True) + forwardDeclares.append(CGWrapper(declare, declarePost='\n')) + + forwardDeclares = CGList(forwardDeclares) + + descriptorsWithPrototype = filter(lambda d: d.interface.hasInterfacePrototypeObject(), + descriptors) + traitsClasses = [CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype] + + # We must have a 1:1 mapping here, skip for prototypes that have more + # than one concrete class implementation. + traitsClasses.extend([CGPrototypeIDMapClass(d) for d in descriptorsWithPrototype + if d.uniqueImplementation]) + + # Wrap all of that in our namespaces. + if len(traitsClasses) > 0: + traitsClasses = CGNamespace.build(['mozilla', 'dom', 'bindings'], + CGWrapper(CGList(traitsClasses), + declarePre='\n'), + declareOnly=True) + traitsClasses = CGWrapper(traitsClasses, declarePost='\n') + else: + traitsClasses = CGGeneric() + + # Do codegen for all the descriptors and enums. + cgthings = [CGWrapper(CGNamespace.build([e.identifier.name], + CGEnum(e)), + post="\n") for e in config.getEnums(webIDLFile)] + cgthings.extend([CGDescriptor(x) for x in descriptors]) + curr = CGList(cgthings) + + # Wrap all of that in our namespaces. + curr = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'], + CGWrapper(curr, pre="\n")) + + curr = CGList([forwardDeclares, traitsClasses, curr]) + + # Add header includes. + curr = CGHeaders(descriptors, + ['mozilla/dom/bindings/Utils.h', + 'DOMJSClass.h'], + ['mozilla/dom/bindings/Nullable.h', + 'XPCQuickStubs.h', + 'AccessCheck.h', + 'WorkerPrivate.h', + 'nsContentUtils.h'], + curr) + + # Add include guards. + curr = CGIncludeGuard(prefix, curr) + + # Add the auto-generated comment. + curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) + + # Store the final result. + self.root = curr + + def declare(self): + return stripTrailingWhitespace(self.root.declare()) + def define(self): + return stripTrailingWhitespace(self.root.define()) + + +class GlobalGenRoots(): + """ + Roots for global codegen. + + To generate code, call the method associated with the target, and then + call the appropriate define/declare method. + """ + + @staticmethod + def PrototypeList(config): + + # Prototype ID enum. + protos = [d.name for d in config.getDescriptors(hasInterfacePrototypeObject=True)] + idEnum = CGNamespacedEnum('id', 'ID', protos, [0]) + idEnum = CGList([idEnum]) + idEnum.append(CGGeneric(declare="const unsigned MaxProtoChainLength = " + + str(config.maxProtoChainLength) + ";\n\n")) + + # Wrap all of that in our namespaces. + idEnum = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'], + CGWrapper(idEnum, pre='\n')) + idEnum = CGWrapper(idEnum, post='\n') + + curr = CGList([idEnum]) + + # Constructor ID enum. + constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True, + hasInterfacePrototypeObject=False)] + idEnum = CGNamespacedEnum('id', 'ID', constructors, [0]) + + # Wrap all of that in our namespaces. + idEnum = CGNamespace.build(['mozilla', 'dom', 'bindings', 'constructors'], + CGWrapper(idEnum, pre='\n')) + idEnum = CGWrapper(idEnum, post='\n') + + curr.append(idEnum) + + traitsDecl = CGGeneric(declare=""" +template <prototypes::ID PrototypeID> +struct PrototypeTraits; + +template <class ConcreteClass> +struct PrototypeIDMap; +""") + + traitsDecl = CGNamespace.build(['mozilla', 'dom', 'bindings'], + CGWrapper(traitsDecl, post='\n')) + + curr.append(traitsDecl) + + # Add include guards. + curr = CGIncludeGuard('PrototypeList', curr) + + # Add the auto-generated comment. + curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) + + # Done. + return curr + + @staticmethod + def Common(config): + + # TODO - Generate the methods we want + curr = CGRegisterProtos(config) + + # Wrap all of that in our namespaces. + curr = CGNamespace.build(['mozilla', 'dom', 'bindings'], + CGWrapper(curr, post='\n')) + curr = CGWrapper(curr, post='\n') + + # Add the includes + defineIncludes = [CGHeaders.getInterfaceFilename(desc.interface) + for desc in config.getDescriptors(hasInterfaceObject=True, + workers=False)] + defineIncludes.append('nsScriptNameSpaceManager.h') + curr = CGHeaders([], [], defineIncludes, curr) + + # Add include guards. + curr = CGIncludeGuard('Common', curr) + + # Done. + return curr
new file mode 100644 --- /dev/null +++ b/dom/bindings/Configuration.py @@ -0,0 +1,186 @@ +# 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/. + +autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" + +class Configuration: + """ + Represents global configuration state based on IDL parse data and + the configuration file. + """ + def __init__(self, filename, parseData): + + # Read the configuration file. + glbl = {} + execfile(filename, glbl) + config = glbl['DOMInterfaces'] + + # Build descriptors for all the interfaces we have in the parse data. + # This allows callers to specify a subset of interfaces by filtering + # |parseData|. + self.descriptors = [] + self.interfaces = {} + self.maxProtoChainLength = 0; + for thing in parseData: + if not thing.isInterface(): continue + iface = thing + if iface.identifier.name not in config: continue + self.interfaces[iface.identifier.name] = iface + entry = config[iface.identifier.name] + if not isinstance(entry, list): + assert isinstance(entry, dict) + entry = [entry] + self.descriptors.extend([Descriptor(self, iface, x) for x in entry]) + + # Mark the descriptors for which only a single nativeType implements + # an interface. + for descriptor in self.descriptors: + intefaceName = descriptor.interface.identifier.name + otherDescriptors = [d for d in self.descriptors + if d.interface.identifier.name == intefaceName] + descriptor.uniqueImplementation = len(otherDescriptors) == 1 + + self.enums = [e for e in parseData if e.isEnum()] + + # Keep the descriptor list sorted for determinism. + self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) + + def getInterface(self, ifname): + return self.interfaces[ifname] + def getDescriptors(self, **filters): + """Gets the descriptors that match the given filters.""" + curr = self.descriptors + for key, val in filters.iteritems(): + if key == 'webIDLFile': + getter = lambda x: x.interface.filename() + elif key == 'hasInterfaceObject': + getter = lambda x: (not x.interface.isExternal() and + x.interface.hasInterfaceObject()) + elif key == 'hasInterfacePrototypeObject': + getter = lambda x: (not x.interface.isExternal() and + x.interface.hasInterfacePrototypeObject()) + elif key == 'hasInterfaceOrInterfacePrototypeObject': + getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject() + elif key == 'isCallback': + getter = lambda x: x.interface.isCallback() + elif key == 'isExternal': + getter = lambda x: x.interface.isExternal() + else: + getter = lambda x: getattr(x, key) + curr = filter(lambda x: getter(x) == val, curr) + return curr + def getEnums(self, webIDLFile): + return filter(lambda e: e.filename() == webIDLFile, self.enums) + +class Descriptor: + """ + Represents a single descriptor for an interface. See Bindings.conf. + """ + def __init__(self, config, interface, desc): + self.config = config + self.interface = interface + + # Read the desc, and fill in the relevant defaults. + self.nativeType = desc['nativeType'] + self.hasInstanceInterface = desc.get('hasInstanceInterface', None) + + headerDefault = self.nativeType + headerDefault = headerDefault.split("::")[-1] + ".h" + self.headerFile = desc.get('headerFile', headerDefault) + + castableDefault = not self.interface.isCallback() + self.castable = desc.get('castable', castableDefault) + + self.notflattened = desc.get('notflattened', False) + + # If we're concrete, we need to crawl our ancestor interfaces and mark + # them as having a concrete descendant. + self.concrete = desc.get('concrete', True) + if self.concrete: + iface = self.interface + while iface: + iface.setUserData('hasConcreteDescendant', True) + iface = iface.parent + + self.prefable = desc.get('prefable', False) + + self.workers = desc.get('workers', False) + self.nativeIsISupports = not self.workers + self.customTrace = desc.get('customTrace', self.workers) + self.customFinalize = desc.get('customFinalize', self.workers) + + def make_name(name): + return name + "_workers" if self.workers else name + self.name = make_name(interface.identifier.name) + + # self.extendedAttributes is a dict of dicts, keyed on + # all/getterOnly/setterOnly and then on member name. Values are an + # array of extended attributes. + self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} } + + def addExtendedAttribute(attribute, config): + def add(key, members, attribute): + for member in members: + self.extendedAttributes[key].setdefault(member, []).append(attribute) + + if isinstance(config, dict): + for key in ['all', 'getterOnly', 'setterOnly']: + add(key, config.get(key, []), attribute) + elif isinstance(config, list): + add('all', config, attribute) + else: + assert isinstance(config, string) + add('all', [config], attribute) + + for attribute in ['infallible', 'implicitJSContext', 'resultNotAddRefed']: + addExtendedAttribute(attribute, desc.get(attribute, {})) + + self.binaryNames = desc.get('binaryNames', {}) + + # Build the prototype chain. + self.prototypeChain = [] + parent = interface + while parent: + self.prototypeChain.insert(0, make_name(parent.identifier.name)) + parent = parent.parent + config.maxProtoChainLength = max(config.maxProtoChainLength, + len(self.prototypeChain)) + + def hasInterfaceOrInterfacePrototypeObject(self): + + # Forward-declared interfaces don't need either interface object or + # interface prototype object as they're going to use QI (on main thread) + # or be passed as a JSObject (on worker threads). + if self.interface.isExternal(): + return False + + return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject() + + def getDescriptor(self, interfaceName): + """ + Gets the appropriate descriptor for the given interface name given the + context of the current descriptor. This selects the appropriate + implementation for cases like workers. + """ + iface = self.config.getInterface(interfaceName) + descriptors = self.config.getDescriptors(interface=iface) + + # The only filter we currently have is workers vs non-workers. + matches = filter(lambda x: x.workers is self.workers, descriptors) + + # After filtering, we should have exactly one result. + if len(matches) is not 1: + raise TypeError("For " + interfaceName + " found " + + str(len(matches)) + " matches"); + return matches[0] + + def getExtendedAttributes(self, member, getter=False, setter=False): + name = member.identifier.name + if member.isMethod(): + return self.extendedAttributes['all'].get(name, []) + + assert member.isAttr() + assert bool(getter) != bool(setter) + key = 'getterOnly' if getter else 'setterOnly' + return self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
new file mode 100644 --- /dev/null +++ b/dom/bindings/DOMJSClass.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* 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/. */ + +#ifndef mozilla_dom_bindings_DOMJSClass_h +#define mozilla_dom_bindings_DOMJSClass_h + +#include "jsapi.h" +#include "jsfriendapi.h" + +#include "mozilla/dom/bindings/PrototypeList.h" // auto-generated + +// For non-global objects we use slot 0 for holding the raw object. +#define DOM_OBJECT_SLOT 0 + +// All DOM globals must have two slots at DOM_GLOBAL_OBJECT_SLOT and +// DOM_PROTOTYPE_SLOT. We have to start at 1 past JSCLASS_GLOBAL_SLOT_COUNT +// because XPConnect uses that one. +#define DOM_GLOBAL_OBJECT_SLOT (JSCLASS_GLOBAL_SLOT_COUNT + 1) +#define DOM_PROTOTYPE_SLOT (JSCLASS_GLOBAL_SLOT_COUNT + 2) + +// We use these flag bits for the new bindings. +#define JSCLASS_IS_DOMJSCLASS JSCLASS_USERBIT1 +#define JSCLASS_DOM_GLOBAL JSCLASS_USERBIT2 + +namespace mozilla { +namespace dom { +namespace bindings { + +typedef bool +(* ResolveProperty)(JSContext* cx, JSObject* wrapper, jsid id, bool set, + JSPropertyDescriptor* desc); +typedef bool +(* EnumerateProperties)(JS::AutoIdVector& props); + +struct NativePropertyHooks +{ + ResolveProperty mResolveProperty; + EnumerateProperties mEnumerateProperties; + + const NativePropertyHooks *mProtoHooks; +}; + +// Special JSClass for reflected DOM objects. +struct DOMJSClass +{ + // It would be nice to just inherit from JSClass, but that precludes pure + // compile-time initialization of the form |DOMJSClass = {...};|, since C++ + // only allows brace initialization for aggregate/POD types. + JSClass mBase; + + // A list of interfaces that this object implements, in order of decreasing + // derivedness. + const prototypes::ID mInterfaceChain[prototypes::id::_ID_Count]; + + // We cache the VTable index of GetWrapperCache for objects that support it. + // + // -1 indicates that GetWrapperCache is not implemented on the underlying object. + // XXXkhuey this is unused and needs to die. + const int16_t mGetWrapperCacheVTableOffset; + + // We store the DOM object in a reserved slot whose index is mNativeSlot. + // Sometimes it's an nsISupports and sometimes it's not; this class tells + // us which it is. + const bool mDOMObjectIsISupports; + + // The slot to use to get the object reference from the object + const size_t mNativeSlot; + + const NativePropertyHooks* mNativeHooks; + + static DOMJSClass* FromJSClass(JSClass* base) { + MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS); + return reinterpret_cast<DOMJSClass*>(base); + } + static const DOMJSClass* FromJSClass(const JSClass* base) { + MOZ_ASSERT(base->flags & JSCLASS_IS_DOMJSCLASS); + return reinterpret_cast<const DOMJSClass*>(base); + } + + static DOMJSClass* FromJSClass(js::Class* base) { + return FromJSClass(Jsvalify(base)); + } + static const DOMJSClass* FromJSClass(const js::Class* base) { + return FromJSClass(Jsvalify(base)); + } + + JSClass* ToJSClass() { return &mBase; } +}; + +inline JSObject** +GetProtoOrIfaceArray(JSObject* global) +{ + MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL); + return static_cast<JSObject**>( + js::GetReservedSlot(global, DOM_PROTOTYPE_SLOT).toPrivate()); +} + +} // namespace bindings +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_bindings_DOMJSClass_h */
new file mode 100644 --- /dev/null +++ b/dom/bindings/GlobalGen.py @@ -0,0 +1,81 @@ +# 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/. + +# We do one global pass over all the WebIDL to generate our prototype enum +# and generate information for subsequent phases. + +import os +import cStringIO +import WebIDL +import cPickle +from Configuration import * +from Codegen import GlobalGenRoots, replaceFileIfChanged +# import Codegen in general, so we can set a variable on it +import Codegen + +def generate_file(config, name, action): + + root = getattr(GlobalGenRoots, name)(config) + if action is 'declare': + filename = name + '.h' + code = root.declare() + else: + assert action is 'define' + filename = name + '.cpp' + code = root.define() + + if replaceFileIfChanged(filename, code): + print "Generating %s" % (filename) + else: + print "%s hasn't changed - not touching it" % (filename) + +def main(): + # Parse arguments. + from optparse import OptionParser + usageString = "usage: %prog [options] webidldir [files]" + o = OptionParser(usage=usageString) + o.add_option("--cachedir", dest='cachedir', default=None, + help="Directory in which to cache lex/parse tables.") + o.add_option("--verbose-errors", action='store_true', default=False, + help="When an error happens, display the Python traceback.") + o.add_option("--use-jsop-accessors", action='store_true', default=False, + dest='useJSOPAccessors', + help="Use JSPropertyOps instead of JSNatives for getters and setters") + (options, args) = o.parse_args() + Codegen.generateNativeAccessors = not options.useJSOPAccessors + + if len(args) < 2: + o.error(usageString) + + configFile = args[0] + baseDir = args[1] + fileList = args[2:] + + # Parse the WebIDL. + parser = WebIDL.Parser(options.cachedir) + for filename in fileList: + fullPath = os.path.normpath(os.path.join(baseDir, filename)) + f = open(fullPath, 'rb') + lines = f.readlines() + f.close() + parser.parse(''.join(lines), fullPath) + parserResults = parser.finish() + + # Write the parser results out to a pickle. + resultsFile = open('ParserResults.pkl', 'wb') + cPickle.dump(parserResults, resultsFile, -1) + resultsFile.close() + + # Load the configuration. + config = Configuration(configFile, parserResults) + + # Generate the prototype list. + generate_file(config, 'PrototypeList', 'declare') + + # Generate the common code. + generate_file(config, 'Common', 'declare') + generate_file(config, 'Common', 'define') + +if __name__ == '__main__': + main()
new file mode 100644 --- /dev/null +++ b/dom/bindings/Makefile.in @@ -0,0 +1,122 @@ +# 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/. + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +MODULE = dom +LIBRARY_NAME = dombindings_s +LIBXUL_LIBRARY = 1 +FORCE_STATIC_LIB = 1 +EXPORT_LIBRARY = 1 + +include $(topsrcdir)/config/config.mk + +# Define USE_JSOP_ACCESSORS to a nonempty string like "yes" to use them +USE_JSOP_ACCESSORS = +ifdef USE_JSOP_ACCESSORS +DEFINES += -DUSE_JSOP_ACCESSORS +ACCESSOR_OPT = --use-jsop-accessors +else +ACCESSOR_OPT = +endif + +# Need this to find all our DOM source files. +include $(topsrcdir)/dom/dom-config.mk + +include $(topsrcdir)/dom/webidl/WebIDL.mk + +binding_include_path := mozilla/dom/bindings +binding_header_files := $(subst .webidl,Binding.h,$(webidl_files)) +binding_cpp_files := $(subst .webidl,Binding.cpp,$(webidl_files)) + +globalgen_targets := \ + PrototypeList.h \ + Common.h \ + Common.cpp \ + $(NULL) + +CPPSRCS = \ + $(binding_cpp_files) \ + $(filter %.cpp, $(globalgen_targets)) \ + Utils.cpp \ + $(NULL) + +EXPORTS_NAMESPACES = $(binding_include_path) + +EXPORTS_$(binding_include_path) = \ + DOMJSClass.h \ + PrototypeList.h \ + Common.h \ + Nullable.h \ + Utils.h \ + $(binding_header_files) \ + $(NULL) + +LOCAL_INCLUDES += -I$(topsrcdir)/js/xpconnect/src \ + -I$(topsrcdir)/js/xpconnect/wrappers + +TEST_DIRS += test + +include $(topsrcdir)/config/rules.mk + +bindinggen_dependencies := \ + BindingGen.py \ + Bindings.conf \ + Configuration.py \ + Codegen.py \ + ParserResults.pkl \ + $(GLOBAL_DEPS) \ + $(NULL) + +$(binding_header_files): %Binding.h: $(bindinggen_dependencies) \ + $(webidl_base)/%.webidl \ + $(NULL) + $(PYTHON) $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/other-licenses/ply -I$(srcdir)/parser \ + $(srcdir)/BindingGen.py $(ACCESSOR_OPT) header $(srcdir)/Bindings.conf $*Binding \ + $(webidl_base)/$*.webidl + +$(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \ + $(webidl_base)/%.webidl \ + $(NULL) + $(PYTHON) $(topsrcdir)/config/pythonpath.py \ + -I$(topsrcdir)/other-licenses/ply -I$(srcdir)/parser \ + $(srcdir)/BindingGen.py $(ACCESSOR_OPT) cpp $(srcdir)/Bindings.conf $*Binding \ + $(webidl_base)/$*.webidl + +$(globalgen_targets): ParserResults.pkl + +CACHE_DIR = _cache + +globalgen_dependencies := \ + GlobalGen.py \ + Bindings.conf \ + Configuration.py \ + Codegen.py \ + $(CACHE_DIR)/.done \ + $(GLOBAL_DEPS) \ + $(NULL) + +$(CACHE_DIR)/.done: + $(MKDIR) -p $(CACHE_DIR) + @$(TOUCH) $@ + +ParserResults.pkl: $(globalgen_dependencies) \ + $(addprefix $(webidl_base)/, $(webidl_files)) + $(PYTHON_PATH) -I$(topsrcdir)/other-licenses/ply -I$(srcdir)/parser \ + $(srcdir)/GlobalGen.py $(ACCESSOR_OPT) $(srcdir)/Bindings.conf $(webidl_base) \ + --cachedir=$(CACHE_DIR) \ + $(webidl_files) + +GARBAGE += \ + $(binding_header_files) \ + $(binding_cpp_files) \ + $(globalgen_targets) \ + ParserResults.pkl \ + webidlyacc.py \ + parser.out \ + $(NULL)
new file mode 100644 --- /dev/null +++ b/dom/bindings/Nullable.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* vim: set ts=2 sw=2 et tw=79: */ +/* 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/. */ + +#ifndef mozilla_dom_bindings_Nullable_h +#define mozilla_dom_bindings_Nullable_h + +#include "mozilla/Assertions.h" + +namespace mozilla { +namespace dom { +namespace bindings { + +// Support for nullable types +template <typename T> +struct Nullable +{ +private: + T mValue; + bool mIsNull; + +public: + Nullable() + : mIsNull(true) + {} + + Nullable(T aValue) + : mValue(aValue) + , mIsNull(false) + {} + + void SetValue(T aValue) { + mValue = aValue; + mIsNull = false; + } + + void SetNull() { + mIsNull = true; + } + + T Value() const { + MOZ_ASSERT(!mIsNull); + return mValue; + } + + bool IsNull() const { + return mIsNull; + } +}; + +} // namespace bindings +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_bindings_Nullable_h */
new file mode 100644 --- /dev/null +++ b/dom/bindings/Utils.cpp @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* vim: set ts=2 sw=2 et tw=79: */ +/* 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 "Utils.h" + +namespace mozilla { +namespace dom { +namespace bindings { + +static bool +DefineConstants(JSContext* cx, JSObject* obj, ConstantSpec* cs) +{ + for (; cs->name; ++cs) { + JSBool ok = + JS_DefineProperty(cx, obj, cs->name, cs->value, NULL, NULL, + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); + if (!ok) { + return false; + } + } + return true; +} + +static JSObject* +CreateInterfaceObject(JSContext* cx, JSObject* global, + JSClass* constructorClass, JSObject* proto, + JSFunctionSpec* staticMethods, ConstantSpec* constants, + const char* name) +{ + JSObject* functionProto = JS_GetFunctionPrototype(cx, global); + if (!functionProto) { + return NULL; + } + + JSObject* constructor = + JS_NewObject(cx, constructorClass, functionProto, global); + if (!constructor) { + return NULL; + } + + if (staticMethods && !JS_DefineFunctions(cx, constructor, staticMethods)) { + return NULL; + } + + if (constants && !DefineConstants(cx, constructor, constants)) { + return NULL; + } + + if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) { + return NULL; + } + + // This is Enumerable: False per spec. + if (!JS_DefineProperty(cx, global, name, OBJECT_TO_JSVAL(constructor), NULL, + NULL, 0)) { + return NULL; + } + + return constructor; +} + +static JSObject* +CreateInterfacePrototypeObject(JSContext* cx, JSObject* global, + JSObject* parentProto, JSClass* protoClass, + JSFunctionSpec* methods, + JSPropertySpec* properties, + ConstantSpec* constants) +{ + JSObject* ourProto = JS_NewObject(cx, protoClass, parentProto, global); + if (!ourProto) { + return NULL; + } + + if (methods && !JS_DefineFunctions(cx, ourProto, methods)) { + return NULL; + } + + if (properties && !JS_DefineProperties(cx, ourProto, properties)) { + return NULL; + } + + if (constants && !DefineConstants(cx, ourProto, constants)) { + return NULL; + } + + return ourProto; +} + +JSObject* +CreateInterfaceObjects(JSContext *cx, JSObject *global, JSObject *parentProto, + JSClass *protoClass, JSClass *constructorClass, + JSFunctionSpec *methods, JSPropertySpec *properties, + ConstantSpec *constants, JSFunctionSpec *staticMethods, + const char* name) +{ + MOZ_ASSERT(protoClass || constructorClass, "Need at least one class!"); + MOZ_ASSERT(!(methods || properties) || protoClass, + "Methods or properties but no protoClass!"); + MOZ_ASSERT(!staticMethods || constructorClass, + "Static methods but no constructorClass!"); + MOZ_ASSERT(bool(name) == bool(constructorClass), + "Must have name precisely when we have an interface object"); + + JSObject* proto; + if (protoClass) { + proto = CreateInterfacePrototypeObject(cx, global, parentProto, protoClass, + methods, properties, constants); + if (!proto) { + return NULL; + } + } + else { + proto = NULL; + } + + JSObject* interface; + if (constructorClass) { + interface = CreateInterfaceObject(cx, global, constructorClass, proto, + staticMethods, constants, name); + if (!interface) { + return NULL; + } + } + + return protoClass ? proto : interface; +} + +static bool +NativeInterface2JSObjectAndThrowIfFailed(XPCLazyCallContext& aLccx, + JSContext* aCx, + JS::Value* aRetval, + xpcObjectHelper& aHelper, + const nsIID* aIID, + bool aAllowNativeWrapper) +{ + nsresult rv; + if (!XPCConvert::NativeInterface2JSObject(aLccx, aRetval, NULL, aHelper, aIID, + NULL, 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<true>(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); + } + return false; + } + return true; +} + +bool +DoHandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope, + nsISupports* value, JS::Value* vp) +{ + if (JS_IsExceptionPending(cx)) { + return false; + } + + XPCLazyCallContext lccx(JS_CALLER, cx, scope); + + if (value) { + xpcObjectHelper helper(value); + return NativeInterface2JSObjectAndThrowIfFailed(lccx, cx, vp, helper, NULL, + true); + } + + return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS); +} + +// 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, JSObject* scope, xpcObjectHelper &helper, + const nsIID* iid, bool allowNativeWrapper, JS::Value* rval) +{ + XPCLazyCallContext lccx(JS_CALLER, cx, scope); + + if (!NativeInterface2JSObjectAndThrowIfFailed(lccx, cx, rval, helper, iid, + allowNativeWrapper)) { + return false; + } + +#ifdef DEBUG + JSObject* jsobj = JSVAL_TO_OBJECT(*rval); + if (jsobj && !js::GetObjectParent(jsobj)) + NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL, + "Why did we recreate this wrapper?"); +#endif + + return true; +} + +JSBool +QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::Value thisv = JS_THIS(cx, vp); + if (thisv == JSVAL_NULL) + return false; + + JSObject* obj = JSVAL_TO_OBJECT(thisv); + JSClass* clasp = js::GetObjectJSClass(obj); + if (!IsDOMClass(clasp)) { + return Throw<true>(cx, NS_ERROR_FAILURE); + } + + nsISupports* native = UnwrapDOMObject<nsISupports>(obj, clasp); + + if (argc < 1) { + return Throw<true>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS); + } + + JS::Value* argv = JS_ARGV(cx, vp); + if (!argv[0].isObject()) { + return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS); + } + + nsIJSIID* iid; + xpc_qsSelfRef iidRef; + if (NS_FAILED(xpc_qsUnwrapArg<nsIJSIID>(cx, argv[0], &iid, &iidRef.ptr, + &argv[0]))) { + return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS); + } + MOZ_ASSERT(iid); + + if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) { + nsresult rv; + nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv); + if (NS_FAILED(rv)) { + return Throw<true>(cx, rv); + } + + return WrapObject(cx, obj, ci, &NS_GET_IID(nsIClassInfo), vp); + } + + // Lie, otherwise we need to check classinfo or QI + *vp = thisv; + return true; +} + +} // namespace bindings +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/bindings/Utils.h @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ +/* vim: set ts=2 sw=2 et tw=79: */ +/* 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/. */ + +#ifndef mozilla_dom_bindings_Utils_h__ +#define mozilla_dom_bindings_Utils_h__ + +#include "mozilla/dom/bindings/DOMJSClass.h" + +#include "jsapi.h" +#include "jstypedarray.h" + +#include "XPCQuickStubs.h" +#include "XPCWrapper.h" +#include "nsTraceRefcnt.h" +#include "nsWrapperCacheInlines.h" + +namespace mozilla { +namespace dom { +namespace bindings { + +template<bool mainThread> +inline bool +Throw(JSContext* cx, nsresult rv) +{ + // XXX Introduce exception machinery. + if (mainThread) { + XPCThrower::Throw(rv, cx); + } else { + if (!JS_IsExceptionPending(cx)) { + JS_ReportError(cx, "Exception thrown (nsresult = %x).", rv); + } + } + return false; +} + +template<bool mainThread> +inline bool +ThrowMethodFailedWithDetails(JSContext* cx, nsresult rv, + const char* /* ifaceName */, + const char* /* memberName */) +{ + return Throw<mainThread>(cx, rv); +} + +inline bool +IsDOMClass(const JSClass* clasp) +{ + return clasp->flags & JSCLASS_IS_DOMJSCLASS; +} + +template <class T> +inline T* +UnwrapDOMObject(JSObject* obj, const JSClass* clasp) +{ + MOZ_ASSERT(IsDOMClass(clasp)); + MOZ_ASSERT(JS_GetClass(obj) == clasp); + + size_t slot = DOMJSClass::FromJSClass(clasp)->mNativeSlot; + MOZ_ASSERT((slot == DOM_OBJECT_SLOT && + !(clasp->flags & JSCLASS_DOM_GLOBAL)) || + (slot == DOM_GLOBAL_OBJECT_SLOT && + (clasp->flags & JSCLASS_DOM_GLOBAL))); + + JS::Value val = js::GetReservedSlot(obj, slot); + // XXXbz/khuey worker code tries to unwrap interface objects (which have + // nothing here). That needs to stop. + // XXX We don't null-check UnwrapObject's result; aren't we going to crash + // anyway? + if (val.isUndefined()) { + return NULL; + } + + return static_cast<T*>(val.toPrivate()); +} + +template <class T> +inline T* +UnwrapDOMObject(JSObject* obj, const js::Class* clasp) +{ + return UnwrapDOMObject<T>(obj, Jsvalify(clasp)); +} + +// Some callers don't want to set an exception when unwrappin fails +// (for example, overload resolution uses unwrapping to tell what sort +// of thing it's looking at). +template <prototypes::ID PrototypeID, class T> +inline nsresult +UnwrapObject(JSContext* cx, JSObject* obj, T** value) +{ + /* First check to see whether we have a DOM object */ + JSClass* clasp = js::GetObjectJSClass(obj); + if (!IsDOMClass(clasp)) { + /* Maybe we have a security wrapper or outer window? */ + if (!js::IsWrapper(obj)) { + /* Not a DOM object, not a wrapper, just bail */ + return NS_ERROR_XPC_BAD_CONVERT_JS; + } + + obj = XPCWrapper::Unwrap(cx, obj, false); + if (!obj) { + return NS_ERROR_XPC_SECURITY_MANAGER_VETO; + } + MOZ_ASSERT(!js::IsWrapper(obj)); + clasp = js::GetObjectJSClass(obj); + if (!IsDOMClass(clasp)) { + /* We don't have a DOM object */ + return NS_ERROR_XPC_BAD_CONVERT_JS; + } + } + + MOZ_ASSERT(IsDOMClass(clasp)); + + /* This object is a DOM object. Double-check that it is safely + castable to T by checking whether it claims to inherit from the + class identified by protoID. */ + DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp); + if (domClass->mInterfaceChain[PrototypeTraits<PrototypeID>::Depth] == + PrototypeID) { + *value = UnwrapDOMObject<T>(obj, clasp); + return NS_OK; + } + + /* It's the wrong sort of DOM object */ + return NS_ERROR_XPC_BAD_CONVERT_JS; +} + +inline bool +IsArrayLike(JSContext* cx, JSObject* obj) +{ + MOZ_ASSERT(obj); + // For simplicity, check for security wrappers up front + if (js::IsWrapper(obj)) { + obj = XPCWrapper::Unwrap(cx, obj, false); + if (!obj) { + // Let's say it's not + return false; + } + } + + // XXXbz need to detect platform objects (including listbinding + // ones) with indexGetters here! + return JS_IsArrayObject(cx, obj); +} + +inline bool +IsPlatformObject(JSContext* cx, JSObject* obj) +{ + // XXXbz Should be treating list-binding objects as platform objects + // too? The one consumer so far wants non-array-like platform + // objects, so listbindings that have an indexGetter should test + // false from here. Maybe this function should have a different + // name? + MOZ_ASSERT(obj); + // Fast-path the common case + JSClass* clasp = js::GetObjectJSClass(obj); + if (IsDOMClass(clasp)) { + return true; + } + // Now for simplicity check for security wrappers before anything else + if (js::IsWrapper(obj)) { + obj = XPCWrapper::Unwrap(cx, obj, false); + if (!obj) { + // Let's say it's not + return false; + } + clasp = js::GetObjectJSClass(obj); + } + return IS_WRAPPER_CLASS(js::Valueify(clasp)) || IsDOMClass(clasp) || + JS_IsArrayBufferObject(obj); +} + +template <class T> +inline nsresult +UnwrapObject(JSContext* cx, JSObject* obj, T* *value) +{ + return UnwrapObject<static_cast<prototypes::ID>( + PrototypeIDMap<T>::PrototypeID)>(cx, obj, value); +} + +const size_t kProtoOrIfaceCacheCount = + prototypes::id::_ID_Count + constructors::id::_ID_Count; + +inline void +AllocateProtoOrIfaceCache(JSObject* obj) +{ + MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL); + MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined()); + + // Important: The () at the end ensure zero-initialization + JSObject** protoOrIfaceArray = new JSObject*[kProtoOrIfaceCacheCount](); + + js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT, + JS::PrivateValue(protoOrIfaceArray)); +} + +inline void +DestroyProtoOrIfaceCache(JSObject* obj) +{ + MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL); + + JSObject** protoOrIfaceArray = GetProtoOrIfaceArray(obj); + + delete [] protoOrIfaceArray; +} + +struct ConstantSpec +{ + const char* name; + JS::Value value; +}; + +/* + * Create a DOM interface object (if constructorClass is non-null) and/or a + * DOM interface prototype object (if protoClass is non-null). + * + * parentProto is the prototype to use for the interface prototype object. + * protoClass is the JSClass to use for the interface prototype object. + * This is null if we should not create an interface prototype + * object. + * constructorClass is the JSClass to use for the interface object. + * This is null if we should not create an interface object. + * methods and properties are to be defined on the interface prototype object; + * these arguments are allowed to be null if there are no + * methods or properties respectively. + * constants are to be defined on the interface object and on the interface + * prototype object; allowed to be null if there are no constants. + * staticMethods are to be defined on the interface object; allowed to be null + * if there are no static methods. + * + * At least one of protoClass and constructorClass should be non-null. + * If constructorClass is non-null, the resulting interface object will be + * defined on the given global with property name |name|, which must also be + * non-null. + * + * returns the interface prototype object if protoClass is non-null, else it + * returns the interface object. + */ +JSObject* +CreateInterfaceObjects(JSContext* cx, JSObject* global, JSObject* parentProto, + JSClass* protoClass, JSClass* constructorClass, + JSFunctionSpec* methods, JSPropertySpec* properties, + ConstantSpec* constants, JSFunctionSpec* staticMethods, + const char* name); + +template <class T> +inline bool +WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp) +{ + JSObject* obj = value->GetWrapper(); + if (obj && js::GetObjectCompartment(obj) == js::GetObjectCompartment(scope)) { + *vp = JS::ObjectValue(*obj); + return true; + } + + if (!obj) { + bool triedToWrap; + obj = value->WrapObject(cx, scope, &triedToWrap); + if (!obj) { + // At this point, obj is null, so just return false. We could + // try to communicate triedToWrap to the caller, but in practice + // callers seem to be testing JS_IsExceptionPending(cx) to + // figure out whether WrapObject() threw instead. + return false; + } + } + + // Now make sure that |obj| is wrapped for the compartment of |scope| + // correctly. That means entering the compartment of |scope|. + JSAutoEnterCompartment ac; + if (!ac.enter(cx, scope)) { + return false; + } + *vp = JS::ObjectValue(*obj); + return JS_WrapValue(cx, vp); +} + +// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr). +template <template <class> class SmartPtr, class T> +inline bool +WrapNewBindingObject(JSContext* cx, JSObject* scope, const SmartPtr<T>& value, + JS::Value* vp) +{ + return WrapNewBindingObject(cx, scope, value.get(), vp); +} + +/** + * A method to handle new-binding wrap failure, by possibly falling back to + * wrapping as a non-new-binding object. + */ +bool +DoHandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope, + nsISupports* value, JS::Value* vp); + +/** + * An easy way to call the above when you have a value which + * multiply-inherits from nsISupports. + */ +template <class T> +bool +HandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope, T* value, + JS::Value* vp) +{ + nsCOMPtr<nsISupports> val; + CallQueryInterface(value, getter_AddRefs(val)); + return DoHandleNewBindingWrappingFailure(cx, scope, val, vp); +} + +// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr). +template <template <class> class SmartPtr, class T> +MOZ_ALWAYS_INLINE bool +HandleNewBindingWrappingFailure(JSContext* cx, JSObject* scope, + const SmartPtr<T>& value, JS::Value* vp) +{ + return HandleNewBindingWrappingFailure(cx, scope, value.get(), vp); +} + +struct EnumEntry { + const char* value; + size_t length; +}; + +inline int +FindEnumStringIndex(JSContext* cx, JS::Value v, const EnumEntry* values, bool* ok) +{ + // JS_StringEqualsAscii is slow as molasses, so don't use it here. + JSString* str = JS_ValueToString(cx, v); + if (!str) { + *ok = false; + return 0; + } + JS::Anchor<JSString*> anchor(str); + size_t length; + const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length); + if (!chars) { + *ok = false; + return 0; + } + int i = 0; + for (const EnumEntry* value = values; value->value; ++value, ++i) { + if (length != value->length) { + continue; + } + + bool equal = true; + const char* val = value->value; + for (size_t j = 0; j != length; ++j) { + if (unsigned(val[j]) != unsigned(chars[j])) { + equal = false; + break; + } + } + + if (equal) { + *ok = true; + return i; + } + } + + // XXX we don't know whether we're on the main thread, so play it safe + *ok = Throw<false>(cx, NS_ERROR_XPC_BAD_CONVERT_JS); + return 0; +} + +inline nsWrapperCache* +GetWrapperCache(nsWrapperCache* cache) +{ + return cache; +} + +// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't +// try to use it without fixing that first. +class nsGlobalWindow; +inline nsWrapperCache* +GetWrapperCache(nsGlobalWindow* not_allowed); + +inline nsWrapperCache* +GetWrapperCache(void* p) +{ + return NULL; +} + +// 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, JSObject* scope, xpcObjectHelper &helper, + const nsIID* iid, bool allowNativeWrapper, JS::Value* rval); + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, T* p, nsWrapperCache* cache, + const nsIID* iid, JS::Value* vp) +{ + if (xpc_FastGetCachedWrapper(cache, scope, vp)) + return true; + qsObjectHelper helper(p, cache); + return XPCOMObjectToJsval(cx, scope, helper, iid, true, vp); +} + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, T* p, const nsIID* iid, + JS::Value* vp) +{ + return WrapObject(cx, scope, p, GetWrapperCache(p), iid, vp); +} + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, T* p, JS::Value* vp) +{ + return WrapObject(cx, scope, p, NULL, vp); +} + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, nsCOMPtr<T> &p, const nsIID* iid, + JS::Value* vp) +{ + return WrapObject(cx, scope, p.get(), iid, vp); +} + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, nsCOMPtr<T> &p, JS::Value* vp) +{ + return WrapObject(cx, scope, p, NULL, vp); +} + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, nsRefPtr<T> &p, const nsIID* iid, + JS::Value* vp) +{ + return WrapObject(cx, scope, p.get(), iid, vp); +} + +template<class T> +inline bool +WrapObject(JSContext* cx, JSObject* scope, nsRefPtr<T> &p, JS::Value* vp) +{ + return WrapObject(cx, scope, p, NULL, vp); +} + +template<> +inline bool +WrapObject<JSObject>(JSContext* cx, JSObject* scope, JSObject* p, JS::Value* vp) +{ + vp->setObjectOrNull(p); + return true; +} + +template<class T> +static inline JSObject* +WrapNativeParent(JSContext* cx, JSObject* scope, T* p) +{ + if (!p) + return scope; + + nsWrapperCache* cache = GetWrapperCache(p); + JSObject* obj; + if (cache && (obj = cache->GetWrapper())) { +#ifdef DEBUG + qsObjectHelper helper(p, cache); + JS::Value debugVal; + + bool ok = XPCOMObjectToJsval(cx, scope, helper, NULL, false, &debugVal); + NS_ASSERTION(ok && JSVAL_TO_OBJECT(debugVal) == obj, + "Unexpected object in nsWrapperCache"); +#endif + return obj; + } + + qsObjectHelper helper(p, cache); + JS::Value v; + return XPCOMObjectToJsval(cx, scope, helper, NULL, false, &v) ? + JSVAL_TO_OBJECT(v) : + NULL; +} + +// Spec needs a name property +template <typename Spec> +static bool +InitIds(JSContext* cx, Spec* specs, jsid* ids) +{ + Spec* spec = specs; + do { + JSString *str = ::JS_InternString(cx, spec->name); + if (!str) { + return false; + } + + *ids = INTERNED_STRING_TO_JSID(cx, str); + } while (++ids, (++spec)->name); + + return true; +} + +JSBool +QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp); + +} // namespace bindings +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_bindings_Utils_h__ */
new file mode 100644 --- /dev/null +++ b/dom/bindings/parser/README @@ -0,0 +1,1 @@ +A WebIDL parser written in Python to be used in Mozilla. \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/dom/bindings/parser/UPSTREAM @@ -0,0 +1,1 @@ +http://dev.w3.org/cvsweb/~checkout~/2006/webapi/WebIDL/Overview.html?rev=1.409;content-type=text%2Fhtml%3b+charset=utf-8 \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/dom/bindings/parser/WebIDL.py @@ -0,0 +1,2872 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is WebIDL Parser. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Kyle Huey <me@kylehuey.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +""" A WebIDL parser. """ + +from ply import lex, yacc +import re + +# Machinery + +def parseInt(literal): + string = literal + sign = 0 + base = 0 + + if string[0] == '-': + sign = -1 + string = string[1:] + else: + sign = 1 + + if string[0] == '0' and len(string) > 1: + if string[1] == 'x' or string[1] == 'X': + base = 16 + string = string[2:] + else: + base = 8 + string = string[1:] + else: + base = 10 + + value = int(string, base) + return value * sign + +# Magic for creating enums +def M_add_class_attribs(attribs): + def foo(name, bases, dict_): + for v, k in attribs: + dict_[k] = v + return type(name, bases, dict_) + return foo + +def enum(*names): + class Foo(object): + __metaclass__ = M_add_class_attribs(enumerate(names)) + def __setattr__(self, name, value): # this makes it read-only + raise NotImplementedError + return Foo() + +class WebIDLError(Exception): + def __init__(self, message, location, warning=False): + self.message = message + self.location = location + self.warning = warning + + def __str__(self): + return "%s: %s%s%s" % (self.warning and 'warning' or 'error', + self.message, ", " if self.location else "", + self.location) + +class Location(object): + _line = None + + def __init__(self, lexer, lineno, lexpos, filename): + self._lineno = lineno + self._lexpos = lexpos + self._lexdata = lexer.lexdata + self._file = filename if filename else "<unknown>" + + def __eq__(self, other): + return self._lexpos == other._lexpos and \ + self._file == other._file + + def resolve(self): + if self._line: + return + + startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1 + endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80) + self._line = self._lexdata[startofline:endofline] + self._colno = self._lexpos - startofline + + def pointerline(self): + def i(): + for i in xrange(0, self._colno): + yield " " + yield "^" + + return "".join(i()) + + def get(self): + self.resolve() + return "%s line %s:%s" % (self._file, self._lineno, self._colno) + + def __str__(self): + self.resolve() + return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno, + self._line, self.pointerline()) + +class BuiltinLocation(object): + def __init__(self, text): + self.msg = text + + def get(self): + return self.msg + + def __str__(self): + return self.get() + + +# Data Model + +class IDLObject(object): + def __init__(self, location): + self.location = location + self.userData = dict() + + def filename(self): + return self.location._file + + def isInterface(self): + return False + + def isEnum(self): + return False + + def isCallback(self): + return False + + def isType(self): + return False + + def getUserData(self, key, default): + return self.userData.get(key, default) + + def setUserData(self, key, value): + self.userData[key] = value + + def addExtendedAttributes(self, attrs): + assert False # Override me! + + def handleExtendedAttribute(self, attr, value): + assert False # Override me! + +class IDLScope(IDLObject): + def __init__(self, location, parentScope, identifier): + IDLObject.__init__(self, location) + + self.parentScope = parentScope + if identifier: + assert isinstance(identifier, IDLIdentifier) + self._name = identifier + else: + self._name = None + + self._dict = {} + + def __str__(self): + return self.QName() + + def QName(self): + if self._name: + return self._name.QName() + "::" + return "::" + + def ensureUnique(self, identifier, object): + assert isinstance(identifier, IDLUnresolvedIdentifier) + assert not object or isinstance(object, IDLObjectWithIdentifier) + assert not object or object.identifier == identifier + + if identifier.name in self._dict: + if not object: + return + + # ensureUnique twice with the same object is not allowed + assert object != self._dict[identifier.name] + + replacement = self.resolveIdentifierConflict(self, identifier, + self._dict[identifier.name], + object) + self._dict[identifier.name] = replacement + return + + assert object + + self._dict[identifier.name] = object + + def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): + if isinstance(originalObject, IDLExternalInterface) and \ + isinstance(newObject, IDLExternalInterface) and \ + originalObject.identifier.name == newObject.identifier.name: + return originalObject + + # Default to throwing, derived classes can override. + conflictdesc = "\n\t%s at %s\n\t%s at %s" % \ + (originalObject, originalObject.location, newObject, newObject.location) + + raise WebIDLError( + "Multiple unresolvable definitions of identifier '%s' in scope '%s%s" + % (identifier.name, str(self), conflictdesc), "") + + def _lookupIdentifier(self, identifier): + return self._dict[identifier.name] + + def lookupIdentifier(self, identifier): + assert isinstance(identifier, IDLIdentifier) + assert identifier.scope == self + return self._lookupIdentifier(identifier) + +class IDLIdentifier(IDLObject): + def __init__(self, location, scope, name): + IDLObject.__init__(self, location) + + self.name = name + assert isinstance(scope, IDLScope) + self.scope = scope + + def __str__(self): + return self.QName() + + def QName(self): + return self.scope.QName() + self.name + + def __hash__(self): + return self.QName().__hash__() + + def __eq__(self, other): + return self.QName() == other.QName() + + def object(self): + return self.scope.lookupIdentifier(self) + +class IDLUnresolvedIdentifier(IDLObject): + def __init__(self, location, name, allowDoubleUnderscore = False, + allowForbidden = False): + IDLObject.__init__(self, location) + + assert len(name) > 0 + + if name[:2] == "__" and not allowDoubleUnderscore: + raise WebIDLError("Identifiers beginning with __ are reserved", + location) + if name[0] == '_' and not allowDoubleUnderscore: + name = name[1:] + if name in ["prototype", "constructor", "toString"] and not allowForbidden: + raise WebIDLError("Cannot use reserved identifier '%s'" % (name), + location) + + self.name = name + + def __str__(self): + return self.QName() + + def QName(self): + return "<unresolved scope>::" + self.name + + def resolve(self, scope, object): + assert isinstance(scope, IDLScope) + assert not object or isinstance(object, IDLObjectWithIdentifier) + assert not object or object.identifier == self + + scope.ensureUnique(self, object) + + identifier = IDLIdentifier(self.location, scope, self.name) + if object: + object.identifier = identifier + return identifier + +class IDLObjectWithIdentifier(IDLObject): + def __init__(self, location, parentScope, identifier): + IDLObject.__init__(self, location) + + assert isinstance(identifier, IDLUnresolvedIdentifier) + + self.identifier = identifier + + if parentScope: + self.resolve(parentScope) + + def resolve(self, parentScope): + assert isinstance(parentScope, IDLScope) + assert isinstance(self.identifier, IDLUnresolvedIdentifier) + self.identifier.resolve(parentScope, self) + +class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope): + def __init__(self, location, parentScope, identifier): + assert isinstance(identifier, IDLUnresolvedIdentifier) + + IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) + IDLScope.__init__(self, location, parentScope, self.identifier) + +class IDLParentPlaceholder(IDLObjectWithIdentifier): + def __init__(self, location, identifier): + assert isinstance(identifier, IDLUnresolvedIdentifier) + IDLObjectWithIdentifier.__init__(self, location, None, identifier) + + def finish(self, scope): + try: + scope._lookupIdentifier(self.identifier) + except: + raise WebIDLError("Unresolved type '%s'." % self.identifier, self.location) + + iface = self.identifier.resolve(scope, None) + return scope.lookupIdentifier(iface) + +class IDLExternalInterface(IDLObjectWithIdentifier): + def __init__(self, location, parentScope, identifier): + assert isinstance(identifier, IDLUnresolvedIdentifier) + assert isinstance(parentScope, IDLScope) + self.parent = None + IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) + IDLObjectWithIdentifier.resolve(self, parentScope) + + def finish(self, scope): + pass + + def isExternal(self): + return True + + def isInterface(self): + return True + + def addExtendedAttributes(self, attrs): + assert len(attrs) == 0 + + def resolve(self, parentScope): + pass + +class IDLInterface(IDLObjectWithScope): + def __init__(self, location, parentScope, name, parent, members): + assert isinstance(parentScope, IDLScope) + assert isinstance(name, IDLUnresolvedIdentifier) + assert not parent or isinstance(parent, IDLParentPlaceholder) + + self.parent = parent + self._callback = False + + self.members = list(members) # clone the list + assert iter(self.members) # Assert it's iterable + + IDLObjectWithScope.__init__(self, location, parentScope, name) + + def __str__(self): + return "Interface '%s'" % self.identifier.name + + def ctor(self): + identifier = IDLUnresolvedIdentifier(self.location, "constructor", + allowForbidden=True) + try: + return self._lookupIdentifier(identifier) + except: + return None + + def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): + assert isinstance(scope, IDLScope) + assert isinstance(originalObject, IDLInterfaceMember) + assert isinstance(newObject, IDLInterfaceMember) + + if originalObject.tag != IDLInterfaceMember.Tags.Method or \ + newObject.tag != IDLInterfaceMember.Tags.Method: + # Call the base class method, which will throw + IDLScope.resolveIdentifierConflict(self, identifier, originalObject, + newObject) + assert False # Not reached + + retval = originalObject.addOverload(newObject) + # Might be a ctor, which isn't in self.members + if newObject in self.members: + self.members.remove(newObject) + return retval + + def finish(self, scope): + if hasattr(self, "_finished"): + return + + self._finished = True + + assert not self.parent or isinstance(self.parent, IDLParentPlaceholder) + parent = self.parent.finish(scope) if self.parent else None + assert not parent or isinstance(parent, IDLInterface) + + self.parent = parent + + assert iter(self.members) + members = None + + if self.parent: + self.parent.finish(scope) + assert iter(self.parent.members) + + members = list(self.parent.members) + members.extend(self.members) + else: + members = list(self.members) + + SpecialType = enum( + 'NamedGetter', + 'NamedSetter', + 'NamedCreator', + 'NamedDeleter', + 'IndexedGetter', + 'IndexedSetter', + 'IndexedCreator', + 'IndexedDeleter' + ) + + specialMembersSeen = [False for i in range(8)] + + def memberNotOnParentChain(member, iface): + assert iface + + if not iface.parent: + return True + + assert isinstance(iface.parent, IDLInterface) + if member in iface.parent.members: + return False + return memberNotOnParentChain(member, iface.parent) + + for member in members: + if memberNotOnParentChain(member, self): + member.resolve(self) + + if member.tag == IDLInterfaceMember.Tags.Method: + if member.isGetter(): + if member.isNamed(): + if specialMembersSeen[SpecialType.NamedGetter]: + raise WebIDLError("Multiple named getters on %s" % (self), + self.location) + specialMembersSeen[SpecialType.NamedGetter] = True + else: + assert member.isIndexed() + if specialMembersSeen[SpecialType.IndexedGetter]: + raise WebIDLError("Multiple indexed getters on %s" % (self), + self.location) + specialMembersSeen[SpecialType.IndexedGetter] = True + if member.isSetter(): + if member.isNamed(): + if specialMembersSeen[SpecialType.NamedSetter]: + raise WebIDLError("Multiple named setters on %s" % (self), + self.location) + specialMembersSeen[SpecialType.NamedSetter] = True + else: + assert member.isIndexed() + if specialMembersSeen[SpecialType.IndexedSetter]: + raise WebIDLError("Multiple indexed setters on %s" % (self), + self.location) + specialMembersSeen[SpecialType.IndexedSetter] = True + if member.isCreator(): + if member.isNamed(): + if specialMembersSeen[SpecialType.NamedCreator]: + raise WebIDLError("Multiple named creators on %s" % (self), + self.location) + specialMembersSeen[SpecialType.NamedCreator] = True + else: + assert member.isIndexed() + if specialMembersSeen[SpecialType.IndexedCreator]: + raise WebIDLError("Multiple indexed creators on %s" % (self), + self.location) + specialMembersSeen[SpecialType.IndexedCreator] = True + if member.isDeleter(): + if member.isNamed(): + if specialMembersSeen[SpecialType.NamedDeleter]: + raise WebIDLError("Multiple named deleters on %s" % (self), + self.location) + specialMembersSeen[SpecialType.NamedDeleter] = True + else: + assert member.isIndexed() + if specialMembersSeen[SpecialType.IndexedDeleter]: + raise WebIDLError("Multiple indexed Deleters on %s" % (self), + self.location) + specialMembersSeen[SpecialType.IndexedDeleter] = True + + for member in self.members: + member.finish(scope) + + def isInterface(self): + return True + + def isExternal(self): + return False + + def setCallback(self, value): + self._callback = value + + def isCallback(self): + return self._callback + + def inheritanceDepth(self): + depth = 0 + parent = self.parent + while parent: + depth = depth + 1 + parent = parent.parent + return depth + + def hasConstants(self): + return reduce(lambda b, m: b or m.isConst(), self.members, False) + + def hasInterfaceObject(self): + if self.isCallback(): + return self.hasConstants() + return not hasattr(self, "_noInterfaceObject") + + def hasInterfacePrototypeObject(self): + return not self.isCallback() + + def addExtendedAttributes(self, attrs): + self._extendedAttrDict = {} + for attr in attrs: + attrlist = list(attr) + identifier = attrlist.pop(0) + + # Special cased attrs + if identifier == "TreatNonCallableAsNull": + raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces", + self.location) + elif identifier == "NoInterfaceObject": + if self.ctor(): + raise WebIDLError("Constructor and NoInterfaceObject are incompatible", + self.location) + + self._noInterfaceObject = True + elif identifier == "Constructor": + if not self.hasInterfaceObject(): + raise WebIDLError("Constructor and NoInterfaceObject are incompatible", + self.location) + + args = attrlist[0] if len(attrlist) else [] + + retType = IDLWrapperType(self.location, self) + + identifier = IDLUnresolvedIdentifier(self.location, "constructor", + allowForbidden=True) + + method = IDLMethod(self.location, identifier, retType, args, + False, False, False, False, False, False, + False, False) + + method.resolve(self) + + self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True + +class IDLEnum(IDLObjectWithIdentifier): + def __init__(self, location, parentScope, name, values): + assert isinstance(parentScope, IDLScope) + assert isinstance(name, IDLUnresolvedIdentifier) + + if len(values) != len(set(values)): + raise WebIDLError("Enum %s has multiple identical strings" % name.name, location) + + IDLObjectWithIdentifier.__init__(self, location, parentScope, name) + self._values = values + + def values(self): + return self._values + + def finish(self, scope): + pass + + def isEnum(self): + return True + + def addExtendedAttributes(self, attrs): + assert len(attrs) == 0 + +class IDLType(IDLObject): + Tags = enum( + # The integer types + 'int8', + 'uint8', + 'int16', + 'uint16', + 'int32', + 'uint32', + 'int64', + 'uint64', + # Additional primitive types + 'bool', + 'float', + 'double', + # Other types + 'any', + 'domstring', + 'object', + 'date', + 'void', + # Funny stuff + 'interface', + 'dictionary', + 'enum', + 'callback' + ) + + def __init__(self, location, name): + IDLObject.__init__(self, location) + self.name = name + self.builtin = False + + def __eq__(self, other): + return other and self.name == other.name and self.builtin == other.builtin + + def __str__(self): + return str(self.name) + + def isType(self): + return True + + def nullable(self): + return False + + def isPrimitive(self): + return False + + def isString(self): + return False + + def isVoid(self): + return self.name == "Void" + + def isSequence(self): + return False + + def isArray(self): + return False + + def isArrayBuffer(self): + return False + + def isDictionary(self): + return False + + def isInterface(self): + return False + + def isAny(self): + return self.tag() == IDLType.Tags.any + + def isDate(self): + return self.tag() == IDLType.Tags.date + + def isObject(self): + return self.tag() == IDLType.Tags.object + + def isComplete(self): + return True + + def tag(self): + assert False # Override me! + + def treatNonCallableAsNull(self): + if not (self.nullable() and self.tag() == IDLType.Tags.callback): + raise WebIDLError("Type %s cannot be TreatNonCallableAsNull" % self, + self.location) + + return hasattr(self, "_treatNonCallableAsNull") + + def markTreatNonCallableAsNull(self): + assert not self.treatNonCallableAsNull() + self._treatNonCallableAsNull = True + + def addExtendedAttributes(self, attrs): + assert len(attrs) == 0 + + def resolveType(self, parentScope): + pass + + def unroll(self): + return self + + def isDistinguishableFrom(self, other): + raise TypeError("Can't tell whether a generic type is or is not " + "distinguishable from other things") + +class IDLUnresolvedType(IDLType): + """ + Unresolved types are interface types + """ + + def __init__(self, location, name): + IDLType.__init__(self, location, name) + + def isComplete(self): + return False + + def complete(self, scope): + obj = None + try: + obj = scope._lookupIdentifier(self.name) + except: + raise WebIDLError("Unresolved type '%s'." % self.name, self.location) + + assert obj + if obj.isType(): + return obj + + name = self.name.resolve(scope, None) + return IDLWrapperType(self.location, obj) + + def isDistinguishableFrom(self, other): + raise TypeError("Can't tell whether an unresolved type is or is not " + "distinguishable from other things") + +class IDLNullableType(IDLType): + def __init__(self, location, innerType): + assert not innerType.isVoid() + assert not innerType.nullable() + assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] + + IDLType.__init__(self, location, innerType.name) + self.inner = innerType + self.builtin = False + + def __eq__(self, other): + return isinstance(other, IDLNullableType) and self.inner == other.inner + + def __str__(self): + return self.inner.__str__() + "OrNull" + + def nullable(self): + return True + + def isCallback(self): + return self.inner.isCallback() + + def isPrimitive(self): + return self.inner.isPrimitive() + + def isString(self): + return self.inner.isString() + + def isVoid(self): + return False + + def isSequence(self): + return self.inner.isSequence() + + def isArray(self): + return self.inner.isArray() + + def isDictionary(self): + return self.inner.isDictionary() + + def isInterface(self): + return self.inner.isInterface() + + def isEnum(self): + return self.inner.isEnum() + + def tag(self): + return self.inner.tag() + + def resolveType(self, parentScope): + assert isinstance(parentScope, IDLScope) + self.inner.resolveType(parentScope) + + def isComplete(self): + return self.inner.isComplete() + + def complete(self, scope): + self.inner = self.inner.complete(scope) + self.name = self.inner.name + return self + + def unroll(self): + return self.inner + + def isDistinguishableFrom(self, other): + if other.nullable(): + # Can't tell which type null should become + return False + return self.inner.isDistinguishableFrom(other) + +class IDLSequenceType(IDLType): + def __init__(self, location, parameterType): + assert not parameterType.isVoid() + + IDLType.__init__(self, location, parameterType.name) + self.inner = parameterType + self.builtin = False + + def __eq__(self, other): + return isinstance(other, IDLSequenceType) and self.inner == other.inner + + def __str__(self): + return self.inner.__str__() + "Sequence" + + def nullable(self): + return False + + def isPrimitive(self): + return self.inner.isPrimitive() + + def isString(self): + return self.inner.isString() + + def isVoid(self): + return False + + def isSequence(self): + return True + + def isArray(self): + return self.inner.isArray() + + def isDictionary(self): + return self.inner.isDictionary() + + def isInterface(self): + return self.inner.isInterface() + + def isEnum(self): + return self.inner.isEnum(); + + def tag(self): + return self.inner.tag() + + def resolveType(self, parentScope): + assert isinstance(parentScope, IDLScope) + self.inner.resolveType(parentScope) + + def isComplete(self): + return self.inner.isComplete() + + def complete(self, scope): + self.inner = self.inner.complete(scope) + return self + + def unroll(self): + return self.inner + + def isDistinguishableFrom(self, other): + return (other.isPrimitive() or other.isString() or other.isEnum() or + other.isDictionary() or other.isDate() or + # XXXbz we should also be checking for indexed + # properties on interfaces + (other.isInterface() and not other.isCallback() and + not other.isArrayBuffer())) + +class IDLArrayType(IDLType): + def __init__(self, location, parameterType): + assert not parameterType.isVoid() + if parameterType.isSequence(): + raise WebIDLError("Array type cannot parameterize over a sequence type", + location) + if parameterType.isDictionary(): + raise WebIDLError("Array type cannot parameterize over a dictionary type", + location) + + IDLType.__init__(self, location, parameterType.name) + self.inner = parameterType + self.builtin = False + + def __eq__(self, other): + return isinstance(other, IDLArrayType) and self.inner == other.inner + + def __str__(self): + return self.inner.__str__() + "Array" + + def nullable(self): + return False + + def isPrimitive(self): + return self.inner.isPrimitive() + + def isString(self): + return self.inner.isString() + + def isVoid(self): + return False + + def isSequence(self): + assert not self.inner.isSequence() + return self.inner.isSequence() + + def isArray(self): + return True + + def isDictionary(self): + assert not self.inner.isDictionary() + return self.inner.isDictionary() + + def isInterface(self): + return self.inner.isInterface() + + def isEnum(self): + return self.inner.isEnum() + + def tag(self): + return self.inner.tag() + + def resolveType(self, parentScope): + assert isinstance(parentScope, IDLScope) + self.inner.resolveType(parentScope) + + def isComplete(self): + return self.inner.isComplete() + + def complete(self, scope): + self.inner = self.inner.complete(scope) + return self + + def unroll(self): + return self.inner + + def isDistinguishableFrom(self, other): + return (other.isPrimitive() or other.isString() or other.isEnum() or + other.isDictionary() or other.isDate() or + # XXXbz we should also be checking for indexed + # properties on interfaces + (other.isInterface() and not other.isCallback() and + not other.isArrayBuffer())) + +class IDLTypedefType(IDLType, IDLObjectWithIdentifier): + def __init__(self, location, innerType, name): + IDLType.__init__(self, location, innerType.name) + + identifier = IDLUnresolvedIdentifier(location, name) + + IDLObjectWithIdentifier.__init__(self, location, None, identifier) + + self.inner = innerType + self.name = name + self.builtin = False + + def __eq__(self, other): + return isinstance(other, IDLTypedefType) and self.inner == other.inner + + def __str__(self): + return self.identifier.name + + def nullable(self): + return self.inner.nullable() + + def isPrimitive(self): + return self.inner.isPrimitive() + + def isString(self): + return self.inner.isString() + + def isVoid(self): + return self.inner.isVoid() + + def isSequence(self): + return self.inner.isSequence() + + def isArray(self): + return self.inner.isArray() + + def isDictionary(self): + return self.inner.isDictionary() + + def isInterface(self): + return self.inner.isInterface() + + def resolve(self, parentScope): + assert isinstance(parentScope, IDLScope) + IDLObjectWithIdentifier.resolve(self, parentScope) + + def tag(self): + return self.inner.tag() + + def unroll(self): + return self.inner + + def isDistinguishableFrom(self, other): + return self.inner.isDistinguishableFrom(other) + +class IDLWrapperType(IDLType): + def __init__(self, location, inner): + IDLType.__init__(self, location, inner.identifier.name) + self.inner = inner + self.name = inner.identifier + self.builtin = False + + def __eq__(self, other): + return other and self.name == other.name and self.builtin == other.builtin + + def __str__(self): + return str(self.name.name) + " (Wrapper)" + + def nullable(self): + return False + + def isPrimitive(self): + return False + + def isString(self): + return False + + def isVoid(self): + return False + + def isSequence(self): + return False + + def isArray(self): + return False + + def isDictionary(self): + return False + + def isInterface(self): + return isinstance(self.inner, IDLInterface) or \ + isinstance(self.inner, IDLExternalInterface) + + def isEnum(self): + return isinstance(self.inner, IDLEnum) + + def isComplete(self): + return True + + def tag(self): + if self.isInterface(): + return IDLType.Tags.interface + elif self.isEnum(): + return IDLType.Tags.enum + else: + assert False + + def isDistinguishableFrom(self, other): + assert self.isInterface() or self.isEnum() + if self.isEnum(): + return (other.isInterface() or other.isObject() or + other.isCallback() or other.isDictionary() or + other.isSequence() or other.isArray() or + other.isDate()) + if other.isPrimitive() or other.isString() or other.isEnum(): + return True + # XXXbz need to check that the interfaces can't be implemented + # by the same object + if other.isInterface(): + return (self != other and + (not self.isCallback() or not other.isCallback())) + if other.isDictionary() or other.isCallback(): + return not self.isCallback() + if other.isSequence() or other.isArray(): + # XXXbz should also check self for enumerated properties + # and the like + return not self.isCallback() + +class IDLBuiltinType(IDLType): + + Types = enum( + # The integer types + 'byte', + 'octet', + 'short', + 'unsigned_short', + 'long', + 'unsigned_long', + 'long_long', + 'unsigned_long_long', + # Additional primitive types + 'boolean', + 'float', + 'double', + # Other types + 'any', + 'domstring', + 'object', + 'date', + 'void', + # Funny stuff + 'ArrayBuffer' + ) + + TagLookup = { + Types.byte: IDLType.Tags.int8, + Types.octet: IDLType.Tags.uint8, + Types.short: IDLType.Tags.int16, + Types.unsigned_short: IDLType.Tags.uint16, + Types.long: IDLType.Tags.int32, + Types.unsigned_long: IDLType.Tags.uint32, + Types.long_long: IDLType.Tags.int64, + Types.unsigned_long_long: IDLType.Tags.uint64, + Types.boolean: IDLType.Tags.bool, + Types.float: IDLType.Tags.float, + Types.double: IDLType.Tags.double, + Types.any: IDLType.Tags.any, + Types.domstring: IDLType.Tags.domstring, + Types.object: IDLType.Tags.object, + Types.date: IDLType.Tags.date, + Types.void: IDLType.Tags.void, + Types.ArrayBuffer: IDLType.Tags.interface + } + + def __init__(self, location, name, type): + IDLType.__init__(self, location, name) + self.builtin = True + self.type = type + + def isPrimitive(self): + return self.type <= IDLBuiltinType.Types.double + + def isString(self): + return self.type == IDLBuiltinType.Types.domstring + + def isInteger(self): + return self.type <= IDLBuiltinType.Types.unsigned_long_long + + def isArrayBuffer(self): + return self.type == IDLBuiltinType.Types.ArrayBuffer + + def isInterface(self): + # ArrayBuffers are interface types per the TypedArray spec, + # but we handle them as builtins because SpiderMonkey implements + # ArrayBuffers. + return self.type == IDLBuiltinType.Types.ArrayBuffer + + def isFloat(self): + return self.type == IDLBuiltinType.Types.float or \ + self.type == IDLBuiltinType.Types.double + + def tag(self): + return IDLBuiltinType.TagLookup[self.type] + + def isDistinguishableFrom(self, other): + if self.isPrimitive() or self.isString(): + return (other.isInterface() or other.isObject() or + other.isCallback() or other.isDictionary() or + other.isSequence() or other.isArray() or + other.isDate()) + if self.isAny(): + # Can't tell "any" apart from anything + return False + if self.isObject(): + return other.isPrimitive() or other.isString() or other.isEnum() + if self.isDate(): + return (other.isPrimitive() or other.isString() or other.isEnum() or + other.isInterface() or other.isCallback() or + other.isDictionary() or other.isSequence() or + other.isArray()) + if self.isVoid(): + return not other.isVoid() + # Not much else we could be! + assert self.isArrayBuffer() + # Like interfaces, but we know we're not a callback and we + # know that we have indexed properties. + # XXXbz this should be checking for indexed properties on + # other when other.isInterface() + return (other.isPrimitive() or other.isString() or other.isEnum() or + other.isCallback() or other.isDictionary() or other.isDate() or + (other.isInterface() and not other.isArrayBuffer())) + +BuiltinTypes = { + IDLBuiltinType.Types.byte: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte", + IDLBuiltinType.Types.byte), + IDLBuiltinType.Types.octet: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Octet", + IDLBuiltinType.Types.octet), + IDLBuiltinType.Types.short: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Short", + IDLBuiltinType.Types.short), + IDLBuiltinType.Types.unsigned_short: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedShort", + IDLBuiltinType.Types.unsigned_short), + IDLBuiltinType.Types.long: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Long", + IDLBuiltinType.Types.long), + IDLBuiltinType.Types.unsigned_long: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLong", + IDLBuiltinType.Types.unsigned_long), + IDLBuiltinType.Types.long_long: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "LongLong", + IDLBuiltinType.Types.long_long), + IDLBuiltinType.Types.unsigned_long_long: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLongLong", + IDLBuiltinType.Types.unsigned_long_long), + IDLBuiltinType.Types.boolean: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Boolean", + IDLBuiltinType.Types.boolean), + IDLBuiltinType.Types.float: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float", + IDLBuiltinType.Types.float), + IDLBuiltinType.Types.double: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Double", + IDLBuiltinType.Types.double), + IDLBuiltinType.Types.any: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Any", + IDLBuiltinType.Types.any), + IDLBuiltinType.Types.domstring: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "String", + IDLBuiltinType.Types.domstring), + IDLBuiltinType.Types.object: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object", + IDLBuiltinType.Types.object), + IDLBuiltinType.Types.date: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date", + IDLBuiltinType.Types.date), + IDLBuiltinType.Types.void: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void", + IDLBuiltinType.Types.void), + IDLBuiltinType.Types.ArrayBuffer: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBuffer", + IDLBuiltinType.Types.ArrayBuffer) + } + + +integerTypeSizes = { + IDLBuiltinType.Types.byte: (-128, 127), + IDLBuiltinType.Types.octet: (0, 255), + IDLBuiltinType.Types.short: (-32768, 32767), + IDLBuiltinType.Types.unsigned_short: (0, 65535), + IDLBuiltinType.Types.long: (-2147483648, 2147483647), + IDLBuiltinType.Types.unsigned_long: (0, 4294967295), + IDLBuiltinType.Types.long_long: (-9223372036854775808, + 9223372036854775807), + IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615) + } + +def matchIntegerValueToType(value): + for type, extremes in integerTypeSizes.items(): + (min, max) = extremes + if value <= max and value >= min: + return BuiltinTypes[type] + + return None + +def checkDistinguishability(argset1, argset2): + assert isinstance(argset1, list) and isinstance(argset2, list) + +class IDLValue(IDLObject): + def __init__(self, location, type, value): + IDLObject.__init__(self, location) + self.type = type + assert isinstance(type, IDLType) + + self.value = value + + def coerceToType(self, type, location): + if type == self.type: + return self # Nothing to do + + # If the type allows null, rerun this matching on the inner type + if type.nullable(): + innerValue = self.coerceToType(type.inner, location) + return IDLValue(self.location, type, innerValue.value) + + # Else, see if we can coerce to 'type'. + if self.type.isInteger(): + if not self.type.isInteger(): + raise WebIDLError("Cannot coerce type %s to type %s." % + (self.type, type), location) + + # We're both integer types. See if we fit. + + (min, max) = integerTypeSizes[type.type] + if self.value <= max and self.value >= min: + # Promote + return IDLValue(self.location, type, self.value) + else: + raise WebIDLError("Value %s is out of range for type %s." % + (self.value, type), location) + else: + pass + + assert False # Not implemented! + +class IDLNullValue(IDLObject): + def __init__(self, location): + IDLObject.__init__(self, location) + self.type = None + self.value = None + + def coerceToType(self, type, location): + if not isinstance(type, IDLNullableType): + raise WebIDLError("Cannot coerce null value to type %s." % type, + location) + + nullValue = IDLNullValue(self.location) + nullValue.type = type + return nullValue + + +class IDLInterfaceMember(IDLObjectWithIdentifier): + + Tags = enum( + 'Const', + 'Attr', + 'Method' + ) + + def __init__(self, location, identifier, tag): + IDLObjectWithIdentifier.__init__(self, location, None, identifier) + self.tag = tag + + def isMethod(self): + return self.tag == IDLInterfaceMember.Tags.Method + + def isAttr(self): + return self.tag == IDLInterfaceMember.Tags.Attr + + def isConst(self): + return self.tag == IDLInterfaceMember.Tags.Const + + def addExtendedAttributes(self, attrs): + self._extendedAttrDict = {} + for attr in attrs: + attrlist = list(attr) + identifier = attrlist.pop(0) + self.handleExtendedAttribute(identifier, attrlist) + self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True + + def handleExtendedAttribute(self, name, list): + pass + + def extendedAttribute(self, name): + return self._extendedAttrDict.get(name, None) + +class IDLConst(IDLInterfaceMember): + def __init__(self, location, identifier, type, value): + IDLInterfaceMember.__init__(self, location, identifier, + IDLInterfaceMember.Tags.Const) + + assert isinstance(type, IDLType) + self.type = type + + # The value might not match the type + coercedValue = value.coerceToType(self.type, location) + assert coercedValue + + self.value = coercedValue + + def __str__(self): + return "'%s' const '%s'" % (self.type, self.identifier) + + def finish(self, scope): + assert self.type.isComplete() + +class IDLAttribute(IDLInterfaceMember): + def __init__(self, location, identifier, type, readonly, inherit): + IDLInterfaceMember.__init__(self, location, identifier, + IDLInterfaceMember.Tags.Attr) + + assert isinstance(type, IDLType) + self.type = type + self.readonly = readonly + self.inherit = inherit + + if readonly and inherit: + raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'", + self.location) + + def __str__(self): + return "'%s' attribute '%s'" % (self.type, self.identifier) + + def finish(self, scope): + if not self.type.isComplete(): + t = self.type.complete(scope) + + assert not isinstance(t, IDLUnresolvedType) + assert not isinstance(t.name, IDLUnresolvedIdentifier) + self.type = t + + def handleExtendedAttribute(self, name, list): + if name == "TreatNonCallableAsNull": + self.type.markTreatNonCallableAsNull(); + IDLInterfaceMember.handleExtendedAttribute(self, name, list) + + def resolve(self, parentScope): + assert isinstance(parentScope, IDLScope) + self.type.resolveType(parentScope) + IDLObjectWithIdentifier.resolve(self, parentScope) + +class IDLArgument(IDLObjectWithIdentifier): + def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False): + IDLObjectWithIdentifier.__init__(self, location, None, identifier) + + assert isinstance(type, IDLType) + self.type = type + + if defaultValue: + defaultValue = defaultValue.coerceToType(type, location) + assert defaultValue + + self.optional = optional + self.defaultValue = defaultValue + self.variadic = variadic + + assert not variadic or optional + + def addExtendedAttributes(self, attrs): + assert len(attrs) == 0 + +class IDLCallbackType(IDLType, IDLObjectWithScope): + def __init__(self, location, parentScope, identifier, returnType, arguments): + assert isinstance(returnType, IDLType) + + IDLType.__init__(self, location, identifier.name) + + self._returnType = returnType + # Clone the list + self._arguments = list(arguments) + + IDLObjectWithScope.__init__(self, location, parentScope, identifier) + + for (returnType, arguments) in self.signatures(): + for argument in arguments: + argument.resolve(self) + + def isCallback(self): + return True + + def signatures(self): + return [(self._returnType, self._arguments)] + + def tag(self): + return IDLType.Tags.callback + + def finish(self, scope): + if not self._returnType.isComplete(): + type = returnType.complete(scope) + + assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type.name, IDLUnresolvedIdentifier) + self._returnType = type + + for argument in self._arguments: + if argument.type.isComplete(): + continue + + type = argument.type.complete(scope) + + assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type.name, IDLUnresolvedIdentifier) + argument.type = type + + def isDistinguishableFrom(self, other): + return (other.isPrimitive() or other.isString() or other.isEnum() or + (other.isInterface() and not other.isCallback()) or + other.isDate()) + +class IDLMethod(IDLInterfaceMember, IDLScope): + + Special = enum( + 'None', + 'Getter', + 'Setter', + 'Creator', + 'Deleter', + 'LegacyCaller', + 'Stringifier', + 'Static' + ) + + TypeSuffixModifier = enum( + 'None', + 'QMark', + 'Brackets' + ) + + NamedOrIndexed = enum( + 'Neither', + 'Named', + 'Indexed' + ) + + def __init__(self, location, identifier, returnType, arguments, + static, getter, setter, creator, deleter, specialType, legacycaller, + stringifier): + IDLInterfaceMember.__init__(self, location, identifier, + IDLInterfaceMember.Tags.Method) + + self._hasOverloads = False + + assert isinstance(returnType, IDLType) + self._returnType = [returnType] + + assert isinstance(static, bool) + self._static = static + assert isinstance(getter, bool) + self._getter = getter + assert isinstance(setter, bool) + self._setter = setter + assert isinstance(creator, bool) + self._creator = creator + assert isinstance(deleter, bool) + self._deleter = deleter + assert isinstance(legacycaller, bool) + self._legacycaller = legacycaller + assert isinstance(stringifier, bool) + self._stringifier = stringifier + self._specialType = specialType + + # Clone the list + self._arguments = [list(arguments)] + + self.assertSignatureConstraints() + + def __str__(self): + return "Method '%s'" % self.identifier + + def assertSignatureConstraints(self): + if self._getter or self._deleter: + assert len(self._arguments) == 1 + assert self._arguments[0][0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \ + self._arguments[0][0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long] + assert not self._arguments[0][0].optional and not self._arguments[0][0].variadic + assert not self._returnType[0].isVoid() + + if self._setter or self._creator: + assert len(self._arguments[0]) == 2 + assert self._arguments[0][0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \ + self._arguments[0][0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long] + assert not self._arguments[0][0].optional and not self._arguments[0][0].variadic + assert not self._arguments[0][1].optional and not self._arguments[0][1].variadic + assert self._arguments[0][1].type == self._returnType[0] + + if self._stringifier: + assert len(self._arguments[0]) == 0 + assert self._returnType[0] == BuiltinTypes[IDLBuiltinType.Types.domstring] + + inOptionalArguments = False + sawVariadicArgument = False + + assert len(self._arguments) == 1 + arguments = self._arguments[0] + + for argument in arguments: + # Only the last argument can be variadic + assert not sawVariadicArgument + # Once we see an optional argument, there can't be any non-optional + # arguments. + if inOptionalArguments: + assert argument.optional + inOptionalArguments = argument.optional + sawVariadicArgument = argument.variadic + + def isStatic(self): + return self._static + + def isGetter(self): + return self._getter + + def isSetter(self): + return self._setter + + def isCreator(self): + return self._creator + + def isDeleter(self): + return self._deleter + + def isNamed(self): + assert self._specialType == IDLMethod.NamedOrIndexed.Named or \ + self._specialType == IDLMethod.NamedOrIndexed.Indexed + return self._specialType == IDLMethod.NamedOrIndexed.Named + + def isIndexed(self): + assert self._specialType == IDLMethod.NamedOrIndexed.Named or \ + self._specialType == IDLMethod.NamedOrIndexed.Indexed + return self._specialType == IDLMethod.NamedOrIndexed.Indexed + + def isLegacycaller(self): + return self._legacycaller + + def isStringifier(self): + return self._stringifier + + def hasOverloads(self): + return self._hasOverloads + + def resolve(self, parentScope): + assert isinstance(parentScope, IDLScope) + IDLObjectWithIdentifier.resolve(self, parentScope) + IDLScope.__init__(self, self.location, parentScope, self.identifier) + for (returnType, arguments) in self.signatures(): + for argument in arguments: + argument.resolve(self) + + def addOverload(self, method): + checkDistinguishability(self._arguments, method._arguments) + + assert len(method._returnType) == 1 + assert len(method._arguments) == 1 + + self._returnType.extend(method._returnType) + self._arguments.extend(method._arguments) + + self._hasOverloads = True + + if self.isStatic() != method.isStatic(): + raise WebIDLError("Overloaded identifier %s appears with different values of the 'static' attribute" % method1.identifier, + method.location) + + if self.isLegacycaller() != method.isLegacycaller(): + raise WebIDLError("Overloaded identifier %s appears with different values of the 'legacycaller' attribute" % method1.identifier, + method.location) + + # Can't overload special things! + assert not self.isGetter() + assert not method.isGetter() + assert not self.isSetter() + assert not method.isSetter() + assert not self.isCreator() + assert not method.isCreator() + assert not self.isDeleter() + assert not method.isDeleter() + assert not self.isStringifier() + assert not method.isStringifier() + + return self + + def signatures(self): + assert len(self._returnType) == len(self._arguments) + return zip(self._returnType, self._arguments) + + def finish(self, scope): + for index, returnType in enumerate(self._returnType): + if returnType.isComplete(): + continue + + type = returnType.complete(scope) + + assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type.name, IDLUnresolvedIdentifier) + self._returnType[index] = type + + for arguments in self._arguments: + for argument in arguments: + if argument.type.isComplete(): + continue + + type = argument.type.complete(scope) + + assert not isinstance(type, IDLUnresolvedType) + assert not isinstance(type.name, IDLUnresolvedIdentifier) + argument.type = type + +# Parser + +class Tokenizer(object): + tokens = [ + "INTEGER", + "FLOAT", + "IDENTIFIER", + "STRING", + "WHITESPACE", + "OTHER" + ] + + def t_INTEGER(self, t): + r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)' + try: + t.value = parseInt(t.value) + except: + raise WebIDLError("Invalid integer literal", + Location(lexer=self.lexer, + lineno=self.lexer.lineno, + lexpos=self.lexer.lexpos, + filename=self._filename)) + return t + + def t_FLOAT(self, t): + r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)' + assert False + return t + + def t_IDENTIFIER(self, t): + r'[A-Z_a-z][0-9A-Z_a-z]*' + t.type = self.keywords.get(t.value, 'IDENTIFIER') + return t + + def t_STRING(self, t): + r'"[^"]*"' + t.value = t.value[1:-1] + return t + + def t_WHITESPACE(self, t): + r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+' + pass + + def t_ELLIPSIS(self, t): + r'\.\.\.' + t.type = self.keywords.get(t.value) + return t + + def t_OTHER(self, t): + r'[^\t\n\r 0-9A-Z_a-z]' + t.type = self.keywords.get(t.value, 'OTHER') + return t + + keywords = { + "module": "MODULE", + "interface": "INTERFACE", + "partial": "PARTIAL", + "dictionary": "DICTIONARY", + "exception": "EXCEPTION", + "enum": "ENUM", + "callback": "CALLBACK", + "typedef": "TYPEDEF", + "implements": "IMPLEMENTS", + "const": "CONST", + "null": "NULL", + "true": "TRUE", + "false": "FALSE", + "stringifier": "STRINGIFIER", + "attribute": "ATTRIBUTE", + "readonly": "READONLY", + "inherit": "INHERIT", + "static": "STATIC", + "getter": "GETTER", + "setter": "SETTER", + "creator": "CREATOR", + "deleter": "DELETER", + "legacycaller": "LEGACYCALLER", + "optional": "OPTIONAL", + "...": "ELLIPSIS", + "::": "SCOPE", + "Date": "DATE", + "DOMString": "DOMSTRING", + "any": "ANY", + "boolean": "BOOLEAN", + "byte": "BYTE", + "double": "DOUBLE", + "float": "FLOAT_", + "long": "LONG", + "object": "OBJECT", + "octet": "OCTET", + "optional": "OPTIONAL", + "sequence": "SEQUENCE", + "short": "SHORT", + "unsigned": "UNSIGNED", + "void": "VOID", + ":": "COLON", + ";": "SEMICOLON", + "{": "LBRACE", + "}": "RBRACE", + "(": "LPAREN", + ")": "RPAREN", + "[": "LBRACKET", + "]": "RBRACKET", + "?": "QUESTIONMARK", + ",": "COMMA", + "=": "EQUALS", + "<": "LT", + ">": "GT", + "ArrayBuffer": "ARRAYBUFFER" + } + + tokens.extend(keywords.values()) + + def t_error(self, t): + raise WebIDLError("Unrecognized Input", + Location(lexer=self.lexer, + lineno=self.lexer.lineno, + lexpos=self.lexer.lexpos, + filename = self.filename)) + + def __init__(self, outputdir): + self.lexer = lex.lex(object=self, + outputdir=outputdir, + lextab='webidllex', + reflags=re.DOTALL) + +class Parser(Tokenizer): + def getLocation(self, p, i): + return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename) + + def globalScope(self): + return self._globalScope + + # The p_Foo functions here must match the WebIDL spec's grammar. + # It's acceptable to split things at '|' boundaries. + def p_Definitions(self, p): + """ + Definitions : ExtendedAttributeList Definition Definitions + """ + if p[2]: + p[0] = [p[2]] + p[2].addExtendedAttributes(p[1]) + else: + assert not p[1] + p[0] = [] + + p[0].extend(p[3]) + + def p_DefinitionsEmpty(self, p): + """ + Definitions : + """ + p[0] = [] + + def p_Definition(self, p): + """ + Definition : CallbackOrInterface + | PartialInterface + | Dictionary + | Exception + | Enum + | Typedef + | ImplementsStatement + """ + p[0] = p[1] + assert p[1] # We might not have implemented something ... + + def p_CallbackOrInterfaceCallback(self, p): + """ + CallbackOrInterface : CALLBACK CallbackRestOrInterface + """ + if p[2].isInterface(): + assert isinstance(p[2], IDLInterface) + p[2].setCallback(True) + + p[0] = p[2] + + def p_CallbackOrInterfaceInterface(self, p): + """ + CallbackOrInterface : Interface + """ + p[0] = p[1] + + def p_CallbackRestOrInterface(self, p): + """ + CallbackRestOrInterface : CallbackRest + | Interface + """ + assert p[1] + p[0] = p[1] + + def p_Interface(self, p): + """ + Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + + members = p[5] + p[0] = IDLInterface(location, self.globalScope(), identifier, p[3], members) + + def p_InterfaceForwardDecl(self, p): + """ + Interface : INTERFACE IDENTIFIER SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + + try: + if self.globalScope()._lookupIdentifier(identifier): + p[0] = self.globalScope()._lookupIdentifier(identifier) + return + except: + pass + + p[0] = IDLExternalInterface(location, self.globalScope(), identifier) + + def p_PartialInterface(self, p): + """ + PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON + """ + pass + + def p_Inheritance(self, p): + """ + Inheritance : COLON ScopedName + """ + p[0] = IDLParentPlaceholder(self.getLocation(p, 2), p[2]) + + def p_InheritanceEmpty(self, p): + """ + Inheritance : + """ + pass + + def p_InterfaceMembers(self, p): + """ + InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers + """ + p[0] = [p[2]] if p[2] else [] + + assert not p[1] or p[2] + p[2].addExtendedAttributes(p[1]) + + p[0].extend(p[3]) + + def p_InterfaceMembersEmpty(self, p): + """ + InterfaceMembers : + """ + p[0] = [] + + def p_InterfaceMember(self, p): + """ + InterfaceMember : Const + | AttributeOrOperation + """ + p[0] = p[1] + + def p_Dictionary(self, p): + """ + Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON + """ + pass + + def p_DictionaryMembers(self, p): + """ + DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers + | + """ + pass + + def p_DictionaryMember(self, p): + """ + DictionaryMember : Type IDENTIFIER DefaultValue SEMICOLON + """ + pass + + def p_DefaultValue(self, p): + """ + DefaultValue : EQUALS ConstValue + | + """ + if len(p) > 1: + p[0] = p[2] + else: + p[0] = None + + def p_Exception(self, p): + """ + Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON + """ + pass + + def p_Enum(self, p): + """ + Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON + """ + location = self.getLocation(p, 1) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) + + values = p[4] + assert values + p[0] = IDLEnum(location, self.globalScope(), identifier, values) + + def p_EnumValueList(self, p): + """ + EnumValueList : STRING EnumValues + """ + p[0] = [p[1]] + p[0].extend(p[2]) + + def p_EnumValues(self, p): + """ + EnumValues : COMMA STRING EnumValues + """ + p[0] = [p[2]] + p[0].extend(p[3]) + + def p_EnumValuesEmpty(self, p): + """ + EnumValues : + """ + p[0] = [] + + def p_CallbackRest(self, p): + """ + CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON + """ + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) + p[0] = IDLCallbackType(self.getLocation(p, 1), self.globalScope(), + identifier, p[3], p[5]) + + def p_ExceptionMembers(self, p): + """ + ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers + | + """ + pass + + def p_Typedef(self, p): + """ + Typedef : TYPEDEF Type IDENTIFIER SEMICOLON + """ + typedef = IDLTypedefType(self.getLocation(p, 1), p[2], p[3]) + typedef.resolve(self.globalScope()) + p[0] = typedef + + def p_ImplementsStatement(self, p): + """ + ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON + """ + pass + + def p_Const(self, p): + """ + Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON + """ + location = self.getLocation(p, 1) + type = p[2] + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) + value = p[5] + p[0] = IDLConst(location, identifier, type, value) + + def p_ConstValueBoolean(self, p): + """ + ConstValue : BooleanLiteral + """ + location = self.getLocation(p, 1) + booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean] + p[0] = IDLValue(location, booleanType, p[1]) + + def p_ConstValueInteger(self, p): + """ + ConstValue : INTEGER + """ + location = self.getLocation(p, 1) + + # We don't know ahead of time what type the integer literal is. + # Determine the smallest type it could possibly fit in and use that. + integerType = matchIntegerValueToType(p[1]) + if integerType == None: + raise WebIDLError("Integer literal out of range", location) + + p[0] = IDLValue(location, integerType, p[1]) + + def p_ConstValueFloat(self, p): + """ + ConstValue : FLOAT + """ + assert False + pass + + def p_ConstValueString(self, p): + """ + ConstValue : STRING + """ + assert False + pass + + def p_ConstValueNull(self, p): + """ + ConstValue : NULL + """ + p[0] = IDLNullValue(self.getLocation(p, 1)) + + def p_BooleanLiteralTrue(self, p): + """ + BooleanLiteral : TRUE + """ + p[0] = True + + def p_BooleanLiteralFalse(self, p): + """ + BooleanLiteral : FALSE + """ + p[0] = False + + def p_AttributeOrOperationStringifier(self, p): + """ + AttributeOrOperation : STRINGIFIER StringifierAttributeOrOperation + """ + assert False # Not implemented + pass + + def p_AttributeOrOperation(self, p): + """ + AttributeOrOperation : Attribute + | Operation + """ + p[0] = p[1] + + def p_StringifierAttributeOrOperation(self, p): + """ + StringifierAttributeOrOperation : Attribute + | OperationRest + | SEMICOLON + """ + pass + + def p_Attribute(self, p): + """ + Attribute : Inherit ReadOnly ATTRIBUTE AttributeType IDENTIFIER SEMICOLON + """ + location = self.getLocation(p, 3) + inherit = p[1] + readonly = p[2] + t = p[4] + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5]) + p[0] = IDLAttribute(location, identifier, t, readonly, inherit) + + def p_ReadOnly(self, p): + """ + ReadOnly : READONLY + """ + p[0] = True + + def p_ReadOnlyEmpty(self, p): + """ + ReadOnly : + """ + p[0] = False + + def p_Inherit(self, p): + """ + Inherit : INHERIT + """ + p[0] = True + + def p_InheritEmpty(self, p): + """ + Inherit : + """ + p[0] = False + + def p_Operation(self, p): + """ + Operation : Qualifiers OperationRest + """ + qualifiers = p[1] + + # Disallow duplicates in the qualifier set + if not len(set(qualifiers)) == len(qualifiers): + raise WebIDLError("Duplicate qualifiers are not allowed", + self.getLocation(p, 1)) + + static = True if IDLMethod.Special.Static in p[1] else False + # If static is there that's all that's allowed. This is disallowed + # by the parser, so we can assert here. + assert not static or len(qualifiers) == 1 + + getter = True if IDLMethod.Special.Getter in p[1] else False + setter = True if IDLMethod.Special.Setter in p[1] else False + creator = True if IDLMethod.Special.Creator in p[1] else False + deleter = True if IDLMethod.Special.Deleter in p[1] else False + legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False + + if getter or deleter: + if setter or creator: + raise WebIDLError("getter and deleter are incompatible with setter and creator", + self.getLocation(p, 1)) + + (returnType, identifier, arguments) = p[2] + + assert isinstance(returnType, IDLType) + + specialType = IDLMethod.NamedOrIndexed.Neither + + if getter or deleter: + if len(arguments) != 1: + raise WebIDLError("%s has wrong number of arguments" % + ("getter" if getter else "deleter"), + self.getLocation(p, 2)) + argType = arguments[0].type + if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: + specialType = IDLMethod.NamedOrIndexed.Named + elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: + specialType = IDLMethod.NamedOrIndexed.Indexed + else: + raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" % + ("getter" if getter else "deleter"), + arguments[0].location) + if arguments[0].optional or arguments[0].variadic: + raise WebIDLError("%s cannot have %s argument" % + ("getter" if getter else "deleter", + "optional" if arguments[0].optional else "variadic"), + arguments[0].location) + if returnType.isVoid(): + raise WebIDLError("%s cannot have void return type" % + ("getter" if getter else "deleter"), + self.getLocation(p, 2)) + if setter or creator: + if len(arguments) != 2: + raise WebIDLError("%s has wrong number of arguments" % + ("setter" if setter else "creator"), + self.getLocation(p, 2)) + argType = arguments[0].type + if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: + specialType = IDLMethod.NamedOrIndexed.Named + elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: + specialType = IDLMethod.NamedOrIndexed.Indexed + else: + raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" % + ("setter" if setter else "creator"), + arguments[0].location) + if arguments[0].optional or arguments[0].variadic: + raise WebIDLError("%s cannot have %s argument" % + ("setter" if setter else "creator", + "optional" if arguments[0].optional else "variadic"), + arguments[0].location) + if arguments[1].optional or arguments[1].variadic: + raise WebIDLError("%s cannot have %s argument" % + ("setter" if setter else "creator", + "optional" if arguments[1].optional else "variadic"), + arguments[1].location) + if returnType.isVoid(): + raise WebIDLError("%s cannot have void return type" % + ("setter" if setter else "creator"), + self.getLocation(p, 2)) + if not arguments[1].type == returnType: + raise WebIDLError("%s return type and second argument type must match" % + ("setter" if setter else "creator"), + self.getLocation(p, 2)) + + inOptionalArguments = False + variadicArgument = False + for argument in arguments: + # Only the last argument can be variadic + if variadicArgument: + raise WebIDLError("Only the last argument can be variadic", + variadicArgument.location) + # Once we see an optional argument, there can't be any non-optional + # arguments. + if inOptionalArguments and not argument.optional: + raise WebIDLError("Cannot have a non-optional argument following an optional argument", + argument.location) + inOptionalArguments = argument.optional + variadicArgument = argument if argument.variadic else None + + # identifier might be None. This is only permitted for special methods. + # NB: Stringifiers are handled elsewhere. + if not identifier: + if not getter and not setter and not creator and \ + not deleter and not legacycaller: + raise WebIDLError("Identifier required for non-special methods", + self.getLocation(p, 2)) + + location = BuiltinLocation("<auto-generated-identifier>") + identifier = IDLUnresolvedIdentifier(location, "__%s%s%s%s%s%s" % + ("named" if specialType == IDLMethod.NamedOrIndexed.Named else \ + "indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "", + "getter" if getter else "", + "setter" if setter else "", + "deleter" if deleter else "", + "creator" if creator else "", + "legacycaller" if legacycaller else ""), allowDoubleUnderscore=True) + + method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments, + static, getter, setter, creator, deleter, specialType, + legacycaller, False) + p[0] = method + + def p_QualifiersStatic(self, p): + """ + Qualifiers : STATIC + """ + p[0] = [IDLMethod.Special.Static] + + def p_QualifiersSpecials(self, p): + """ + Qualifiers : Specials + """ + p[0] = p[1] + + def p_Specials(self, p): + """ + Specials : Special Specials + """ + p[0] = [p[1]] + p[0].extend(p[2]) + + def p_SpecialsEmpty(self, p): + """ + Specials : + """ + p[0] = [] + + def p_SpecialGetter(self, p): + """ + Special : GETTER + """ + p[0] = IDLMethod.Special.Getter + + def p_SpecialSetter(self, p): + """ + Special : SETTER + """ + p[0] = IDLMethod.Special.Setter + + def p_SpecialCreator(self, p): + """ + Special : CREATOR + """ + p[0] = IDLMethod.Special.Creator + + def p_SpecialDeleter(self, p): + """ + Special : DELETER + """ + p[0] = IDLMethod.Special.Deleter + + def p_SpecialLegacyCaller(self, p): + """ + Special : LEGACYCALLER + """ + p[0] = IDLMethod.Special.LegacyCaller + + def p_OperationRest(self, p): + """ + OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON + """ + p[0] = (p[1], p[2], p[4]) + + def p_OptionalIdentifier(self, p): + """ + OptionalIdentifier : IDENTIFIER + """ + p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) + + def p_OptionalIdentifierEmpty(self, p): + """ + OptionalIdentifier : + """ + pass + + def p_ArgumentList(self, p): + """ + ArgumentList : Argument Arguments + """ + p[0] = [p[1]] if p[1] else [] + p[0].extend(p[2]) + + def p_ArgumentListEmpty(self, p): + """ + ArgumentList : + """ + p[0] = [] + + def p_Arguments(self, p): + """ + Arguments : COMMA Argument Arguments + """ + p[0] = [p[2]] if p[2] else [] + p[0].extend(p[3]) + + def p_ArgumentsEmpty(self, p): + """ + Arguments : + """ + p[0] = [] + + def p_Argument(self, p): + """ + Argument : ExtendedAttributeList Optional Type Ellipsis IDENTIFIER DefaultValue + """ + t = p[3] + assert isinstance(t, IDLType) + identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5]) + + optional = p[2] + variadic = p[4] + defaultValue = p[6] + + if not optional and defaultValue: + raise WebIDLError("Mandatory arguments can't have a default value.", + self.getLocation(p, 6)) + + if variadic: + if optional: + raise WebIDLError("Variadic arguments should not be marked optional.", + self.getLocation(p, 2)) + optional = variadic + + p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic) + p[0].addExtendedAttributes(p[1]) + + def p_Optional(self, p): + """ + Optional : OPTIONAL + """ + p[0] = True + + def p_OptionalEmpty(self, p): + """ + Optional : + """ + p[0] = False + + def p_Ellipsis(self, p): + """ + Ellipsis : ELLIPSIS + """ + p[0] = True + + def p_EllipsisEmpty(self, p): + """ + Ellipsis : + """ + p[0] = False + + def p_ExceptionMember(self, p): + """ + ExceptionMember : Const + | ExceptionField + """ + pass + + def p_ExceptionField(self, p): + """ + ExceptionField : AttributeType IDENTIFIER SEMICOLON + """ + pass + + def p_ExtendedAttributeList(self, p): + """ + ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET + """ + p[0] = [p[2]] + if p[3]: + p[0].extend(p[3]) + + def p_ExtendedAttributeListEmpty(self, p): + """ + ExtendedAttributeList : + """ + p[0] = [] + + def p_ExtendedAttribute(self, p): + """ + ExtendedAttribute : ExtendedAttributeNoArgs + | ExtendedAttributeArgList + | ExtendedAttributeIdent + | ExtendedAttributeNamedArgList + """ + p[0] = p[1] + + def p_ExtendedAttributeEmpty(self, p): + """ + ExtendedAttribute : + """ + pass + + def p_ExtendedAttributes(self, p): + """ + ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes + """ + p[0] = [p[2]] if p[2] else [] + p[0].extend(p[3]) + + def p_ExtendedAttributesEmpty(self, p): + """ + ExtendedAttributes : + """ + p[0] = [] + + def p_Other(self, p): + """ + Other : INTEGER + | FLOAT + | IDENTIFIER + | STRING + | OTHER + | ELLIPSIS + | COLON + | SCOPE + | SEMICOLON + | LT + | EQUALS + | GT + | QUESTIONMARK + | DATE + | DOMSTRING + | ANY + | ATTRIBUTE + | BOOLEAN + | BYTE + | LEGACYCALLER + | CONST + | CREATOR + | DELETER + | DOUBLE + | EXCEPTION + | FALSE + | FLOAT_ + | GETTER + | IMPLEM