Compare commits

..
Author SHA1 Message Date
Moonchild 14f9893f1c Issue #1442 - WIP: Response.body handling.
This doesn't build due to unknown code state at Mozilla. The BZ bug landed hg
commit has typoes in it so can't have been what landed because it wouldn't be
compile-able. hg history lists a backup but no re-land on the JS part as well
that doesn't make sense.

On top, further work apparently involved 100 refactoring commits to come to
Mozilla's final state. I'm signing off here.
2020-11-12 15:54:24 +00:00
Moonchild 142ed0c544 Issue #1442 - Part 10: Stream shutdown handling. 2020-11-06 23:26:39 +00:00
Moonchild db0aa7989c Issue #1442 - Part 9: Fetch implementation of streams.
+worker-friendly pref checking for the DOM API.
2020-11-06 13:47:47 +00:00
Moonchild 46b9c17c57 Issue #1442 - Part 8: Implement DOM Response().body as ReadableStream. 2020-11-05 15:42:40 +00:00
Moonchild da102ef405 Issue #1442 - Part 7: binding codegen naming alignment for Streams. 2020-11-05 15:32:55 +00:00
Moonchild 336ffb82fd Issue #1442 - Part 6: Implement WebIDL bindings for Streams. 2020-11-05 15:14:06 +00:00
Moonchild 64e1117cba Merge branch 'master' into fetchstreams-work
# Conflicts:
#	js/src/moz.build

Includes re-application of streams addition to moz.build files.
2020-11-05 13:26:08 +00:00
Moonchild bda38927be Issue #1442 - Part 5: Create JSAPI ductwork to handle ReadableStream. 2020-11-04 16:04:43 +00:00
Moonchild 83eb44d357 Issue #1442 - Part 4: Add Streams API implementation in C++ in SpiderMonkey. 2020-11-03 21:25:02 +00:00
Moonchild dff20616b7 Issue #1442 - Part 3: Add a run-time preference for toggling the streams API.
This was entirely way too complicated to toggle a feature.
2020-10-31 20:27:19 +00:00
Moonchild bc8b279e5f Revert "Issue #1442 - Part 2: Implement streams API as self-hosted JS."
This reverts commit bb35b25ef9.
2020-10-31 19:08:34 +00:00
Moonchild bb35b25ef9 Issue #1442 - Part 2: Implement streams API as self-hosted JS. 2020-10-31 03:19:54 +00:00
Moonchild 1059830a73 Issue #1442 - Part 1: Change ArrayBufferCopyData self-hosting intrinsic to take
a start offset for the destination.

This will be needed to manage stream chunks.
2020-10-31 01:32:41 +00:00
247 changed files with 13896 additions and 11694 deletions

No files matched your search

-3
View File
@@ -161,8 +161,6 @@ def old_configure_options(*options):
'--enable-b2g-camera',
'--enable-b2g-ril',
'--enable-bundled-fonts',
'--enable-chrome-compression',
'--enable-chrome-format',
'--enable-clang-plugin',
'--enable-content-sandbox',
'--enable-cookies',
@@ -264,7 +262,6 @@ def old_configure_options(*options):
'--with-app-name',
'--with-arch',
'--with-branding',
'--with-chrome-name',
'--with-crashreporter-enable-percent',
'--with-cross-lib',
'--with-debug-label',
-1
View File
@@ -86,7 +86,6 @@ included_inclnames_to_ignore = set([
'unicode/udat.h', # ICU
'unicode/udatpg.h', # ICU
'unicode/uenum.h', # ICU
'unicode/uniset.h', # ICU
'unicode/unorm.h', # ICU
'unicode/unum.h', # ICU
'unicode/unumsys.h', # ICU
+1 -1
View File
@@ -216,7 +216,7 @@ endif # NO_PROFILE_GUIDED_OPTIMIZE
MAKE_JARS_FLAGS = \
-t $(topsrcdir) \
-f $(MOZ_CHROME_FILE_FORMAT) \
-f $(MOZ_JAR_MAKER_FILE_FORMAT) \
$(NULL)
ifdef USE_EXTENSION_MANIFEST
+1
View File
@@ -526,6 +526,7 @@ hardware/lights.h
hardware/power.h
hardware_legacy/power.h
hardware_legacy/uevent.h
hardware_legacy/vibrator.h
#endif
HIToolbox/HIToolbox.h
hlink.h
+1 -1
View File
@@ -22,7 +22,7 @@ REF_MANIFEST = $(if $(USE_EXTENSION_MANIFEST),chrome.manifest,test.manifest)
check-%::
if test -d $(FINAL_TARGET); then rm -rf $(FINAL_TARGET); fi;
$(MAKE) realchrome MOZ_CHROME_FILE_FORMAT=$*
$(MAKE) realchrome MOZ_JAR_MAKER_FILE_FORMAT=$*
@echo 'Comparing manifests...'
@if ! sort $(MY_MANIFEST) | diff --text -U 0 $(srcdir)/../$(REF_MANIFEST).$* - ; then \
echo 'TEST-UNEXPECTED-FAIL | config/tests/$(REF_MANIFEST).$* | differing content in manifest!' ; \
+1 -1
View File
@@ -94,7 +94,7 @@ exports.menuitems = [
},
key: {
id: "browserToolbox",
modifiers: isMac ? "accel,alt" : "accel,shift",
modifiers: "accel,alt,shift",
keytext: true
}
},
+192 -1
View File
@@ -97,6 +97,11 @@
namespace mozilla {
namespace dom {
static bool sVibratorEnabled = false;
static uint32_t sMaxVibrateMS = 0;
static uint32_t sMaxVibrateListLen = 0;
static const char* kVibrationPermissionType = "vibration";
static void
AddPermission(nsIPrincipal* aPrincipal, const char* aType, uint32_t aPermission,
uint32_t aExpireType, int64_t aExpireTime)
@@ -147,7 +152,12 @@ GetPermission(nsIPrincipal* aPrincipal, const char* aType)
void
Navigator::Init()
{
// Add any Preferences::Add*VarCache(&sPref, "pref", default) here if needed.
Preferences::AddBoolVarCache(&sVibratorEnabled,
"dom.vibrator.enabled", true);
Preferences::AddUintVarCache(&sMaxVibrateMS,
"dom.vibrator.max_vibrate_ms", 10000);
Preferences::AddUintVarCache(&sMaxVibrateListLen,
"dom.vibrator.max_vibrate_list_len", 128);
}
Navigator::Navigator(nsPIDOMWindowInner* aWindow)
@@ -695,6 +705,82 @@ Navigator::RefreshMIMEArray()
}
}
namespace {
class VibrateWindowListener : public nsIDOMEventListener
{
public:
VibrateWindowListener(nsPIDOMWindowInner* aWindow, nsIDocument* aDocument)
{
mWindow = do_GetWeakReference(aWindow);
mDocument = do_GetWeakReference(aDocument);
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
aDocument->AddSystemEventListener(visibilitychange,
this, /* listener */
true, /* use capture */
false /* wants untrusted */);
}
void RemoveListener();
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
private:
virtual ~VibrateWindowListener()
{
}
nsWeakPtr mWindow;
nsWeakPtr mDocument;
};
NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
static bool
MayVibrate(nsIDocument* doc) {
// Hidden documents cannot start or stop a vibration.
return (doc && !doc->Hidden());
}
NS_IMETHODIMP
VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
if (!MayVibrate(doc)) {
// It's important that we call CancelVibrate(), not Vibrate() with an
// empty list, because Vibrate() will fail if we're no longer focused, but
// CancelVibrate() will succeed, so long as nobody else has started a new
// vibration pattern.
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
hal::CancelVibrate(window);
RemoveListener();
gVibrateWindowListener = nullptr;
// Careful: The line above might have deleted |this|!
}
return NS_OK;
}
void
VibrateWindowListener::RemoveListener()
{
nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
if (!target) {
return;
}
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
target->RemoveSystemEventListener(visibilitychange, this,
true /* use capture */);
}
} // namespace
void
Navigator::AddIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
{
@@ -723,6 +809,111 @@ Navigator::RemoveIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
}
}
void
Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent)
{
MOZ_ASSERT(NS_IsMainThread());
nsTArray<uint32_t> pattern;
pattern.SwapElements(mRequestedVibrationPattern);
if (!mWindow) {
return;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
if (!MayVibrate(doc)) {
return;
}
if (aPermitted) {
// Add a listener to cancel the vibration if the document becomes hidden,
// and remove the old visibility listener, if there was one.
if (!gVibrateWindowListener) {
// If gVibrateWindowListener is null, this is the first time we've vibrated,
// and we need to register a listener to clear gVibrateWindowListener on
// shutdown.
ClearOnShutdown(&gVibrateWindowListener);
} else {
gVibrateWindowListener->RemoveListener();
}
gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
hal::Vibrate(pattern, mWindow);
}
if (aPersistent) {
AddPermission(doc->NodePrincipal(), kVibrationPermissionType,
aPermitted ? nsIPermissionManager::ALLOW_ACTION :
nsIPermissionManager::DENY_ACTION,
nsIPermissionManager::EXPIRE_SESSION, 0);
}
}
bool
Navigator::Vibrate(uint32_t aDuration)
{
AutoTArray<uint32_t, 1> pattern;
pattern.AppendElement(aDuration);
return Vibrate(pattern);
}
bool
Navigator::Vibrate(const nsTArray<uint32_t>& aPattern)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return false;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
if (!MayVibrate(doc)) {
return false;
}
nsTArray<uint32_t> pattern(aPattern);
if (pattern.Length() > sMaxVibrateListLen) {
pattern.SetLength(sMaxVibrateListLen);
}
for (size_t i = 0; i < pattern.Length(); ++i) {
pattern[i] = std::min(sMaxVibrateMS, pattern[i]);
}
// The spec says we check sVibratorEnabled after we've done the sanity
// checking on the pattern.
if (!sVibratorEnabled) {
return true;
}
mRequestedVibrationPattern.SwapElements(pattern);
uint32_t permission = GetPermission(mWindow, kVibrationPermissionType);
if (permission == nsIPermissionManager::ALLOW_ACTION ||
mRequestedVibrationPattern.IsEmpty() ||
(mRequestedVibrationPattern.Length() == 1 &&
mRequestedVibrationPattern[0] == 0)) {
// Always allow cancelling vibration and respect session permissions.
SetVibrationPermission(true /* permitted */, false /* persistent */);
return true;
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (!obs || permission == nsIPermissionManager::DENY_ACTION) {
// Abort without observer service or on denied session permission.
SetVibrationPermission(false /* permitted */, false /* persistent */);
return true;
}
// Request user permission.
obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);
return true;
}
//*****************************************************************************
// Pointer Events interface
//*****************************************************************************
+3
View File
@@ -149,6 +149,9 @@ public:
// NavigatorBinding::ClearCachedUserAgentValue(this);
void ClearUserAgentCache();
bool Vibrate(uint32_t aDuration);
bool Vibrate(const nsTArray<uint32_t>& aDuration);
void SetVibrationPermission(bool aPermitted, bool aPersistent);
uint32_t MaxTouchPoints();
void GetAppCodeName(nsString& aAppCodeName, ErrorResult& aRv)
{
+19
View File
@@ -8527,6 +8527,25 @@ nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
return workerPrivate->PushEnabled();
}
// static
bool
nsContentUtils::StreamsEnabled(JSContext* aCx, JSObject* aObj)
{
if (NS_IsMainThread()) {
return Preferences::GetBool("dom.streams.enabled", false);
}
using namespace workers;
// Otherwise, check the pref via the WorkerPrivate
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
if (!workerPrivate) {
return false;
}
return workerPrivate->StreamsEnabled();
}
// static
bool
nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel)
+2
View File
@@ -2622,6 +2622,8 @@ public:
static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader);
static bool PushEnabled(JSContext* aCx, JSObject* aObj);
static bool StreamsEnabled(JSContext* aCx, JSObject* aObj);
static bool IsNonSubresourceRequest(nsIChannel* aChannel);
-13
View File
@@ -1384,8 +1384,6 @@ nsTreeSanitizer::SanitizeChildren(nsINode* aRoot)
nsAutoString styleText;
nsContentUtils::GetNodeTextContent(node, false, styleText);
RemoveAllAttributesFromDescendants(elt);
nsAutoString sanitizedStyle;
nsCOMPtr<nsIURI> baseURI = node->GetBaseURI();
if (SanitizeStyleSheet(styleText,
@@ -1481,17 +1479,6 @@ nsTreeSanitizer::RemoveAllAttributes(nsIContent* aElement)
}
}
void nsTreeSanitizer::RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement) {
nsIContent* node = aElement->GetFirstChild();
while (node) {
if (node->IsElement()) {
mozilla::dom::Element* elt = node->AsElement();
RemoveAllAttributes(elt);
}
node = node->GetNextNode(aElement);
}
}
void
nsTreeSanitizer::InitializeStatics()
{
-6
View File
@@ -183,12 +183,6 @@ class MOZ_STACK_CLASS nsTreeSanitizer {
*/
void RemoveAllAttributes(nsIContent* aElement);
/**
* Removes all attributes from the descendants of an element but not from
* the element itself.
*/
void RemoveAllAttributesFromDescendants(mozilla::dom::Element* aElement);
/**
* The whitelist of HTML elements.
*/
+3
View File
@@ -714,6 +714,7 @@ DOMInterfaces = {
'headers': 'headers_',
'referrerPolicy': 'referrerPolicy_'
},
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text' ],
},
'ResizeObservation': {
@@ -733,6 +734,8 @@ DOMInterfaces = {
'Response': {
'binaryNames': { 'headers': 'headers_' },
'implicitJSContext': [ 'arrayBuffer', 'blob', 'formData', 'json', 'text',
'clone', 'cloneUnfiltered' ],
},
'RGBColor': {
+90 -74
View File
@@ -1156,7 +1156,10 @@ class CGHeaders(CGWrapper):
# just include their header if we need to have functions
# taking references to them declared in that header.
headerSet = declareIncludes
headerSet.add("mozilla/dom/TypedArray.h")
if unrolled.isReadableStream():
headerSet.add("mozilla/dom/ReadableStream.h")
else:
headerSet.add("mozilla/dom/TypedArray.h")
else:
try:
typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
@@ -1371,7 +1374,10 @@ def UnionTypes(unionTypes, config):
elif f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
headers.add("mozilla/dom/TypedArray.h")
if f.isReadableStream():
headers.add("mozilla/dom/ReadableStream.h")
else:
headers.add("mozilla/dom/TypedArray.h")
else:
try:
typeDesc = config.getDescriptor(f.inner.identifier.name)
@@ -1457,7 +1463,10 @@ def UnionConversions(unionTypes, config):
elif f.isInterface():
if f.isSpiderMonkeyInterface():
headers.add("jsfriendapi.h")
headers.add("mozilla/dom/TypedArray.h")
if f.isReadableStream():
headers.add("mozilla/dom/ReadableStream.h")
else:
headers.add("mozilla/dom/TypedArray.h")
elif f.inner.isExternal():
try:
typeDesc = config.getDescriptor(f.inner.identifier.name)
@@ -5593,8 +5602,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isSpiderMonkeyInterface():
assert not isEnforceRange and not isClamp
name = type.unroll().name # unroll() because it may be nullable
arrayType = CGGeneric(name)
declType = arrayType
interfaceType = CGGeneric(name)
declType = interfaceType
if type.nullable():
declType = CGTemplatedType("Nullable", declType)
objRef = "${declName}.SetValue()"
@@ -5618,22 +5627,23 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# This is a bit annoying. In a union we don't want to have a
# holder, since unions don't support that. But if we're optional we
# want to have a holder, so that the callee doesn't see
# Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
# optional and use a RootedTypedArray otherwise.
# Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
# holder if we're optional and use a RootedSpiderMonkeyInterface
# otherwise.
if isOptional:
holderType = CGTemplatedType("TypedArrayRooter", arrayType)
# If our typed array is nullable, this will set the Nullable to
# be not-null, but that's ok because we make an explicit
# SetNull() call on it as needed if our JS value is actually
# null. XXXbz Because "Maybe" takes const refs for constructor
# arguments, we can't pass a reference here; have to pass a
# pointer.
holderType = CGTemplatedType("SpiderMonkeyInterfaceRooter", interfaceType)
# If our SpiderMonkey interface is nullable, this will set the
# Nullable to be not-null, but that's ok because we make an
# explicit SetNull() call on it as needed if our JS value is
# actually null. XXXbz Because "Maybe" takes const refs for
# constructor arguments, we can't pass a reference here; have
# to pass a pointer.
holderArgs = "cx, &%s" % objRef
declArgs = None
else:
holderType = None
holderArgs = None
declType = CGTemplatedType("RootedTypedArray", declType)
declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
declArgs = "cx"
else:
holderType = None
@@ -6382,7 +6392,7 @@ def getMaybeWrapValueFuncForType(type):
if type.nullable():
return "MaybeWrapObjectOrNullValue"
return "MaybeWrapObjectValue"
# Spidermonkey interfaces are never DOM objects. Neither are sequences or
# SpiderMonkey interfaces are never DOM objects. Neither are sequences or
# dictionaries, since those are always plain JS objects.
if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
if type.nullable():
@@ -6402,7 +6412,8 @@ recordWrapLevel = 0
def getWrapTemplateForType(type, descriptorProvider, result, successCode,
returnsNewObject, exceptionCode, typedArraysAreStructs,
returnsNewObject, exceptionCode,
spiderMonkeyInterfacesAreStructs,
isConstructorRetval=False):
"""
Reflect a C++ value stored in "result", of IDL type "type" into JS. The
@@ -6412,8 +6423,9 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
doing a 'break' if the entire conversion template is inside a block that
the 'break' will exit).
If typedArraysAreStructs is true, then if the type is a typed array,
"result" is one of the dom::TypedArray subclasses, not a JSObject*.
If spiderMonkeyInterfacesAreStructs is true, then if the type is a
SpiderMonkey interface, "result" is one of the
dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
The resulting string should be used with string.Template. It
needs the following keys when substituting:
@@ -6508,7 +6520,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode,
returnsNewObject, exceptionCode,
typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs)
code = fill(
"""
@@ -6540,7 +6552,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'returnsNewObject': returnsNewObject,
'exceptionCode': exceptionCode,
'obj': "returnArray",
'typedArraysAreStructs': typedArraysAreStructs
'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
})
sequenceWrapLevel -= 1
code = fill(
@@ -6594,7 +6606,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
'returnsNewObject': returnsNewObject,
'exceptionCode': exceptionCode,
'obj': "returnObj",
'typedArraysAreStructs': typedArraysAreStructs
'spiderMonkeyInterfacesAreStructs': spiderMonkeyInterfacesAreStructs
})
recordWrapLevel -= 1
if type.keyType.isByteString():
@@ -6760,7 +6772,7 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
return (head + _setValue(result, wrapAsType=type), False)
if (type.isObject() or (type.isSpiderMonkeyInterface() and
not typedArraysAreStructs)):
not spiderMonkeyInterfacesAreStructs)):
# See comments in GetOrCreateDOMReflector explaining why we need
# to wrap here.
if type.nullable():
@@ -6779,21 +6791,21 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
type.isDate() or
(type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
(type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)):
raise TypeError("Need to learn to wrap %s" % type)
if type.nullable():
recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
"%s.Value()" % result, successCode,
returnsNewObject, exceptionCode,
typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs)
return ("if (%s.IsNull()) {\n" % result +
indent(setNull()) +
"}\n" +
recTemplate, recInfal)
if type.isSpiderMonkeyInterface():
assert typedArraysAreStructs
assert spiderMonkeyInterfacesAreStructs
# See comments in GetOrCreateDOMReflector explaining why we need
# to wrap here.
# NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
@@ -6875,7 +6887,7 @@ def wrapForType(type, descriptorProvider, templateValues):
templateValues.get('successCode', None),
templateValues.get('returnsNewObject', False),
templateValues.get('exceptionCode', "return false;\n"),
templateValues.get('typedArraysAreStructs', False),
templateValues.get('spiderMonkeyInterfacesAreStructs', False),
isConstructorRetval=templateValues.get('isConstructorRetval', False))[0]
defaultValues = {'obj': 'obj'}
@@ -8215,10 +8227,10 @@ class CGMethodCall(CGThing):
# "object" arg.
# First grab all the overloads that have a non-callback interface
# (which includes typed arrays and arraybuffers) at the
# distinguishing index. We can also include the ones that have an
# "object" here, since if those are present no other object-typed
# argument will be.
# (which includes SpiderMonkey interfaces) at the distinguishing
# index. We can also include the ones that have an "object" here,
# since if those are present no other object-typed argument will
# be.
objectSigs = [
s for s in possibleSignatures
if (distinguishingType(s).isObject() or
@@ -8249,16 +8261,17 @@ class CGMethodCall(CGThing):
# There might be more than one thing in objectSigs; we need to check
# which ones we unwrap to.
if len(objectSigs) > 0:
# Here it's enough to guard on our argument being an object. The
# code for unwrapping non-callback interfaces, typed arrays,
# sequences, and Dates will just bail out and move on to
# the next overload if the object fails to unwrap correctly,
# while "object" accepts any object anyway. We could even not
# do the isObject() check up front here, but in cases where we
# have multiple object overloads it makes sense to do it only
# once instead of for each overload. That will also allow the
# unwrapping test to skip having to do codegen for the
# null-or-undefined case, which we already handled above.
# Here it's enough to guard on our argument being an object.
# The code for unwrapping non-callback interfaces, spiderMonkey
# interfaces, sequences, and Dates will just bail out and move
# on to the next overload if the object fails to unwrap
# correctly, while "object" accepts any object anyway. We
# could even not do the isObject() check up front here, but in
# cases where we have multiple object overloads it makes sense
# to do it only once instead of for each overload. That will
# also allow the unwrapping test to skip having to do codegen
# for the null-or-undefined case, which we already handled
# above.
caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
distinguishingArg))
for sig in objectSigs:
@@ -10271,7 +10284,7 @@ class CGUnionStruct(CGThing):
"jsvalHandle": "rval",
"obj": "scopeObj",
"result": val,
"typedArraysAreStructs": True
"spiderMonkeyInterfacesAreStructs": True
})
return CGGeneric(wrapCode)
@@ -13251,7 +13264,7 @@ class CGDictionary(CGThing):
# 'obj' can just be allowed to be the string "obj", since that
# will be our dictionary object, which is presumably itself in
# the right scope.
'typedArraysAreStructs': True
'spiderMonkeyInterfacesAreStructs': True
})
conversion = CGGeneric(innerTemplate)
conversion = CGWrapper(conversion,
@@ -13711,7 +13724,7 @@ class ForwardDeclarationBuilder:
except NoSuchDescriptorError:
pass
# Note: Spidermonkey interfaces are typedefs, so can't be
# Note: SpiderMonkey interfaces are typedefs, so can't be
# forward-declared
elif t.isPromise():
self.addInMozillaDom("Promise")
@@ -14026,7 +14039,7 @@ class CGBindingRoot(CGThing):
return {desc.getDescriptor(desc.interface.parent.identifier.name)}
for x in dependencySortObjects(jsImplemented, getParentDescriptor,
lambda d: d.interface.identifier.name):
cgthings.append(CGCallbackInterface(x, typedArraysAreStructs=True))
cgthings.append(CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True))
cgthings.append(CGJSImplClass(x))
# And make sure we have the right number of newlines at the end
@@ -14088,14 +14101,13 @@ class CGBindingRoot(CGThing):
class CGNativeMember(ClassMethod):
def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
typedArraysAreStructs=True, variadicIsSequence=False,
resultNotAddRefed=False,
virtual=False,
override=False):
spiderMonkeyInterfacesAreStructs=True,
variadicIsSequence=False, resultNotAddRefed=False,
virtual=False, override=False):
"""
If typedArraysAreStructs is false, typed arrays will be passed as
JS::Handle<JSObject*>. If it's true they will be passed as one of the
dom::TypedArray subclasses.
If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
will be passed as JS::Handle<JSObject*>. If it's true they will be
passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
If passJSBitsAsNeeded is false, we don't automatically pass in a
JSContext* or a JSObject* based on the return and argument types. We
@@ -14106,7 +14118,7 @@ class CGNativeMember(ClassMethod):
self.extendedAttrs = extendedAttrs
self.resultAlreadyAddRefed = not resultNotAddRefed
self.passJSBitsAsNeeded = passJSBitsAsNeeded
self.typedArraysAreStructs = typedArraysAreStructs
self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
self.variadicIsSequence = variadicIsSequence
breakAfterSelf = "\n" if breakAfter else ""
ClassMethod.__init__(self, name,
@@ -14413,7 +14425,7 @@ class CGNativeMember(ClassMethod):
False, False)
if type.isSpiderMonkeyInterface():
if not self.typedArraysAreStructs:
if not self.spiderMonkeyInterfacesAreStructs:
return "JS::Handle<JSObject*>", False, False
# Unroll for the name, in case we're nullable.
@@ -15585,16 +15597,16 @@ class CGFastCallback(CGClass):
class CGCallbackInterface(CGCallback):
def __init__(self, descriptor, typedArraysAreStructs=False):
def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
iface = descriptor.interface
attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
getters = [CallbackGetter(a, descriptor, typedArraysAreStructs)
getters = [CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
for a in attrs]
setters = [CallbackSetter(a, descriptor, typedArraysAreStructs)
setters = [CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
for a in attrs if not a.readonly]
methods = [m for m in iface.members
if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
methods = [CallbackOperation(m, sig, descriptor, typedArraysAreStructs)
methods = [CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
for m in methods for sig in m.signatures()]
if iface.isJSImplemented() and iface.ctor():
sigs = descriptor.interface.ctor().signatures()
@@ -15639,7 +15651,8 @@ class CallbackMember(CGNativeMember):
# CallSetup already handled the unmark-gray bits for us. we don't have
# anything better to use for 'obj', really...
def __init__(self, sig, name, descriptorProvider, needThisHandling,
rethrowContentException=False, typedArraysAreStructs=False,
rethrowContentException=False,
spiderMonkeyInterfacesAreStructs=False,
wrapScope='CallbackKnownNotGray()'):
"""
needThisHandling is True if we need to be able to accept a specified
@@ -15674,7 +15687,7 @@ class CallbackMember(CGNativeMember):
extendedAttrs={},
passJSBitsAsNeeded=False,
visibility=visibility,
typedArraysAreStructs=typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
# We have to do all the generation of our body now, because
# the caller relies on us throwing if we can't manage it.
self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
@@ -15791,7 +15804,7 @@ class CallbackMember(CGNativeMember):
'obj': self.wrapScope,
'returnsNewObject': False,
'exceptionCode': self.exceptionCode,
'typedArraysAreStructs': self.typedArraysAreStructs
'spiderMonkeyInterfacesAreStructs': self.spiderMonkeyInterfacesAreStructs
})
except MethodNotNewObjectError as err:
raise TypeError("%s being passed as an argument to %s but is not "
@@ -15895,10 +15908,11 @@ class CallbackMember(CGNativeMember):
class CallbackMethod(CallbackMember):
def __init__(self, sig, name, descriptorProvider, needThisHandling,
rethrowContentException=False, typedArraysAreStructs=False):
rethrowContentException=False,
spiderMonkeyInterfacesAreStructs=False):
CallbackMember.__init__(self, sig, name, descriptorProvider,
needThisHandling, rethrowContentException,
typedArraysAreStructs=typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@@ -15957,12 +15971,12 @@ class CallbackOperationBase(CallbackMethod):
"""
def __init__(self, signature, jsName, nativeName, descriptor,
singleOperation, rethrowContentException=False,
typedArraysAreStructs=False):
spiderMonkeyInterfacesAreStructs=False):
self.singleOperation = singleOperation
self.methodName = descriptor.binaryNameFor(jsName)
CallbackMethod.__init__(self, signature, nativeName, descriptor,
singleOperation, rethrowContentException,
typedArraysAreStructs=typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getThisDecl(self):
if not self.singleOperation:
@@ -16013,7 +16027,8 @@ class CallbackOperation(CallbackOperationBase):
"""
Codegen actual WebIDL operations on callback interfaces.
"""
def __init__(self, method, signature, descriptor, typedArraysAreStructs):
def __init__(self, method, signature, descriptor,
spiderMonkeyInterfacesAreStructs):
self.ensureASCIIName(method)
self.method = method
jsName = method.identifier.name
@@ -16022,7 +16037,7 @@ class CallbackOperation(CallbackOperationBase):
MakeNativeName(descriptor.binaryNameFor(jsName)),
descriptor, descriptor.interface.isSingleOperationInterface(),
rethrowContentException=descriptor.interface.isJSImplemented(),
typedArraysAreStructs=typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getPrettyName(self):
return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
@@ -16033,13 +16048,14 @@ class CallbackAccessor(CallbackMember):
"""
Shared superclass for CallbackGetter and CallbackSetter.
"""
def __init__(self, attr, sig, name, descriptor, typedArraysAreStructs):
def __init__(self, attr, sig, name, descriptor,
spiderMonkeyInterfacesAreStructs):
self.ensureASCIIName(attr)
self.attrName = attr.identifier.name
CallbackMember.__init__(self, sig, name, descriptor,
needThisHandling=False,
rethrowContentException=descriptor.interface.isJSImplemented(),
typedArraysAreStructs=typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs)
def getPrettyName(self):
return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
@@ -16047,12 +16063,12 @@ class CallbackAccessor(CallbackMember):
class CallbackGetter(CallbackAccessor):
def __init__(self, attr, descriptor, typedArraysAreStructs):
def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
CallbackAccessor.__init__(self, attr,
(attr.type, []),
callbackGetterName(attr, descriptor),
descriptor,
typedArraysAreStructs)
spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self):
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
@@ -16074,12 +16090,12 @@ class CallbackGetter(CallbackAccessor):
class CallbackSetter(CallbackAccessor):
def __init__(self, attr, descriptor, typedArraysAreStructs):
def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
CallbackAccessor.__init__(self, attr,
(BuiltinTypes[IDLBuiltinType.Types.void],
[FakeArgument(attr.type, attr)]),
callbackSetterName(attr, descriptor),
descriptor, typedArraysAreStructs)
descriptor, spiderMonkeyInterfacesAreStructs)
def getRvalDecl(self):
# We don't need an rval
@@ -16114,7 +16130,7 @@ class CGJSImplInitOperation(CallbackOperationBase):
"__init", "__Init", descriptor,
singleOperation=False,
rethrowContentException=True,
typedArraysAreStructs=True)
spiderMonkeyInterfacesAreStructs=True)
def getPrettyName(self):
return "__init"
+28
View File
@@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_ReadableStream_h
#define mozilla_dom_ReadableStream_h
#include "mozilla/dom/SpiderMonkeyInterface.h"
namespace mozilla {
namespace dom {
struct ReadableStream : public SpiderMonkeyInterfaceObjectStorage
{
inline bool Init(JSObject* obj)
{
MOZ_ASSERT(!inited());
mImplObj = mWrappedObj = js::UnwrapReadableStream(obj);
return inited();
}
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_ReadableStream_h */
+144
View File
@@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_SpiderMonkeyInterface_h
#define mozilla_dom_SpiderMonkeyInterface_h
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/TracingAPI.h"
namespace mozilla {
namespace dom {
/*
* Class that just handles the JSObject storage and tracing for spidermonkey
* interfaces
*/
struct SpiderMonkeyInterfaceObjectStorage
{
protected:
JSObject* mImplObj;
JSObject* mWrappedObj;
SpiderMonkeyInterfaceObjectStorage()
: mImplObj(nullptr),
mWrappedObj(nullptr)
{
}
SpiderMonkeyInterfaceObjectStorage(SpiderMonkeyInterfaceObjectStorage&& aOther)
: mImplObj(aOther.mImplObj),
mWrappedObj(aOther.mWrappedObj)
{
aOther.mImplObj = nullptr;
aOther.mWrappedObj = nullptr;
}
public:
inline void TraceSelf(JSTracer* trc)
{
JS::UnsafeTraceRoot(trc, &mImplObj, "SpiderMonkeyInterfaceObjectStorage.mImplObj");
JS::UnsafeTraceRoot(trc, &mWrappedObj, "SpiderMonkeyInterfaceObjectStorage.mWrappedObj");
}
inline bool inited() const
{
return !!mImplObj;
}
inline bool WrapIntoNewCompartment(JSContext* cx)
{
return JS_WrapObject(cx,
JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
}
inline JSObject *Obj() const
{
MOZ_ASSERT(inited());
return mWrappedObj;
}
private:
SpiderMonkeyInterfaceObjectStorage(const SpiderMonkeyInterfaceObjectStorage&) = delete;
};
// A class for rooting an existing SpiderMonkey Interface struct
template<typename InterfaceType>
class MOZ_RAII SpiderMonkeyInterfaceRooter : private JS::CustomAutoRooter
{
public:
template <typename CX>
SpiderMonkeyInterfaceRooter(const CX& cx,
InterfaceType* aInterface MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mInterface(aInterface)
{
}
virtual void trace(JSTracer* trc) override
{
mInterface->TraceSelf(trc);
}
private:
SpiderMonkeyInterfaceObjectStorage* const mInterface;
};
// And a specialization for dealing with nullable SpiderMonkey interfaces
template<typename Inner> struct Nullable;
template<typename InterfaceType>
class MOZ_RAII SpiderMonkeyInterfaceRooter<Nullable<InterfaceType>> :
private JS::CustomAutoRooter
{
public:
template <typename CX>
SpiderMonkeyInterfaceRooter(const CX& cx,
Nullable<InterfaceType>* aInterface MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mInterface(aInterface)
{
}
virtual void trace(JSTracer* trc) override
{
if (!mInterface->IsNull()) {
mInterface->Value().TraceSelf(trc);
}
}
private:
Nullable<InterfaceType>* const mInterface;
};
// Class for easily setting up a rooted SpiderMonkey interface object on the
// stack
template<typename InterfaceType>
class MOZ_RAII RootedSpiderMonkeyInterface final : public InterfaceType,
private SpiderMonkeyInterfaceRooter<InterfaceType>
{
public:
template <typename CX>
explicit RootedSpiderMonkeyInterface(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: InterfaceType(),
SpiderMonkeyInterfaceRooter<InterfaceType>(cx, this
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
template <typename CX>
RootedSpiderMonkeyInterface(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: InterfaceType(obj),
SpiderMonkeyInterfaceRooter<InterfaceType>(cx, this
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_SpiderMonkeyInterface_h */
+7 -127
View File
@@ -6,51 +6,15 @@
#ifndef mozilla_dom_TypedArray_h
#define mozilla_dom_TypedArray_h
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "mozilla/Attributes.h"
#include "mozilla/Move.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/SpiderMonkeyInterface.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace dom {
/*
* Class that just handles the JSObject storage and tracing for typed arrays
*/
struct TypedArrayObjectStorage : AllTypedArraysBase {
protected:
JSObject* mTypedObj;
JSObject* mWrappedObj;
TypedArrayObjectStorage()
: mTypedObj(nullptr),
mWrappedObj(nullptr)
{
}
TypedArrayObjectStorage(TypedArrayObjectStorage&& aOther)
: mTypedObj(aOther.mTypedObj),
mWrappedObj(aOther.mWrappedObj)
{
aOther.mTypedObj = nullptr;
aOther.mWrappedObj = nullptr;
}
public:
inline void TraceSelf(JSTracer* trc)
{
JS::UnsafeTraceRoot(trc, &mTypedObj, "TypedArray.mTypedObj");
JS::UnsafeTraceRoot(trc, &mWrappedObj, "TypedArray.mWrappedObj");
}
private:
TypedArrayObjectStorage(const TypedArrayObjectStorage&) = delete;
};
/*
* Various typed array classes for argument conversion. We have a base class
* that has a way of initializing a TypedArray from an existing typed array, and
@@ -60,7 +24,9 @@ private:
template<typename T,
JSObject* UnwrapArray(JSObject*),
void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**)>
struct TypedArray_base : public TypedArrayObjectStorage {
struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
AllTypedArraysBase
{
typedef T element_type;
TypedArray_base()
@@ -72,7 +38,7 @@ struct TypedArray_base : public TypedArrayObjectStorage {
}
TypedArray_base(TypedArray_base&& aOther)
: TypedArrayObjectStorage(Move(aOther)),
: SpiderMonkeyInterfaceObjectStorage(Move(aOther)),
mData(aOther.mData),
mLength(aOther.mLength),
mShared(aOther.mShared),
@@ -94,14 +60,10 @@ public:
inline bool Init(JSObject* obj)
{
MOZ_ASSERT(!inited());
mTypedObj = mWrappedObj = UnwrapArray(obj);
mImplObj = mWrappedObj = UnwrapArray(obj);
return inited();
}
inline bool inited() const {
return !!mTypedObj;
}
// About shared memory:
//
// Any DOM TypedArray as well as any DOM ArrayBufferView that does
@@ -173,22 +135,11 @@ public:
return mLength;
}
inline JSObject *Obj() const {
MOZ_ASSERT(inited());
return mWrappedObj;
}
inline bool WrapIntoNewCompartment(JSContext* cx)
{
return JS_WrapObject(cx,
JS::MutableHandle<JSObject*>::fromMarkedLocation(&mWrappedObj));
}
inline void ComputeLengthAndData() const
{
MOZ_ASSERT(inited());
MOZ_ASSERT(!mComputed);
GetLengthAndDataAndSharedness(mTypedObj, &mLength, &mShared, &mData);
GetLengthAndDataAndSharedness(mImplObj, &mLength, &mShared, &mData);
mComputed = true;
}
@@ -363,77 +314,6 @@ class TypedArrayCreator
const ArrayType& mArray;
};
// A class for rooting an existing TypedArray struct
template<typename ArrayType>
class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter
{
public:
template <typename CX>
TypedArrayRooter(const CX& cx,
ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mArray(aArray)
{
}
virtual void trace(JSTracer* trc) override
{
mArray->TraceSelf(trc);
}
private:
TypedArrayObjectStorage* const mArray;
};
// And a specialization for dealing with nullable typed arrays
template<typename Inner> struct Nullable;
template<typename ArrayType>
class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > :
private JS::CustomAutoRooter
{
public:
template <typename CX>
TypedArrayRooter(const CX& cx,
Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
mArray(aArray)
{
}
virtual void trace(JSTracer* trc) override
{
if (!mArray->IsNull()) {
mArray->Value().TraceSelf(trc);
}
}
private:
Nullable<ArrayType>* const mArray;
};
// Class for easily setting up a rooted typed array object on the stack
template<typename ArrayType>
class MOZ_RAII RootedTypedArray final : public ArrayType,
private TypedArrayRooter<ArrayType>
{
public:
template <typename CX>
explicit RootedTypedArray(const CX& cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
ArrayType(),
TypedArrayRooter<ArrayType>(cx, this
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
template <typename CX>
RootedTypedArray(const CX& cx, JSObject* obj MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
ArrayType(obj),
TypedArrayRooter<ArrayType>(cx, this
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
{
}
};
} // namespace dom
} // namespace mozilla
+2
View File
@@ -38,9 +38,11 @@ EXPORTS.mozilla.dom += [
'NonRefcountedDOMObject.h',
'Nullable.h',
'PrimitiveConversions.h',
'ReadableStream.h',
'Record.h',
'RootedDictionary.h',
'SimpleGlobalObject.h',
'SpiderMonkeyInterface.h',
'StructuredClone.h',
'ToJSValue.h',
'TypedArray.h',
+31 -8
View File
@@ -2048,6 +2048,9 @@ class IDLType(IDLObject):
def isRecord(self):
return False
def isReadableStream(self):
return False
def isArrayBuffer(self):
return False
@@ -2075,12 +2078,12 @@ class IDLType(IDLObject):
def isSpiderMonkeyInterface(self):
""" Returns a boolean indicating whether this type is an 'interface'
type that is implemented in Spidermonkey. At the moment, this
only returns true for the types from the TypedArray spec. """
type that is implemented in SpiderMonkey. """
return self.isInterface() and (self.isArrayBuffer() or
self.isArrayBufferView() or
self.isSharedArrayBuffer() or
self.isTypedArray())
self.isTypedArray() or
self.isReadableStream())
def isDictionary(self):
return False
@@ -2269,6 +2272,9 @@ class IDLNullableType(IDLParameterizedType):
def isRecord(self):
return self.inner.isRecord()
def isReadableStream(self):
return self.inner.isReadableStream()
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@@ -2628,6 +2634,9 @@ class IDLTypedefType(IDLType):
def isRecord(self):
return self.inner.isRecord()
def isReadableStream(self):
return self.inner.isReadableStream()
def isDictionary(self):
return self.inner.isDictionary()
@@ -2939,7 +2948,8 @@ class IDLBuiltinType(IDLType):
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array'
'Float64Array',
'ReadableStream',
)
TagLookup = {
@@ -2974,7 +2984,8 @@ class IDLBuiltinType(IDLType):
Types.Int32Array: IDLType.Tags.interface,
Types.Uint32Array: IDLType.Tags.interface,
Types.Float32Array: IDLType.Tags.interface,
Types.Float64Array: IDLType.Tags.interface
Types.Float64Array: IDLType.Tags.interface,
Types.ReadableStream: IDLType.Tags.interface,
}
def __init__(self, location, name, type):
@@ -3021,6 +3032,9 @@ class IDLBuiltinType(IDLType):
return (self._typeTag >= IDLBuiltinType.Types.Int8Array and
self._typeTag <= IDLBuiltinType.Types.Float64Array)
def isReadableStream(self):
return self._typeTag == IDLBuiltinType.Types.ReadableStream
def isInterface(self):
# TypedArray things are interface types per the TypedArray spec,
# but we handle them as builtins because SpiderMonkey implements
@@ -3028,7 +3042,8 @@ class IDLBuiltinType(IDLType):
return (self.isArrayBuffer() or
self.isArrayBufferView() or
self.isSharedArrayBuffer() or
self.isTypedArray())
self.isTypedArray() or
self.isReadableStream())
def isNonCallbackInterface(self):
# All the interfaces we can be are non-callback
@@ -3098,6 +3113,7 @@ class IDLBuiltinType(IDLType):
# that's not an ArrayBuffer or a callback interface
(self.isArrayBuffer() and not other.isArrayBuffer()) or
(self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or
(self.isReadableStream() and not other.isReadableStream()) or
# ArrayBufferView is distinguishable from everything
# that's not an ArrayBufferView or typed array.
(self.isArrayBufferView() and not other.isArrayBufferView() and
@@ -3207,7 +3223,10 @@ BuiltinTypes = {
IDLBuiltinType.Types.Float32Array),
IDLBuiltinType.Types.Float64Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
IDLBuiltinType.Types.Float64Array)
IDLBuiltinType.Types.Float64Array),
IDLBuiltinType.Types.ReadableStream:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ReadableStream",
IDLBuiltinType.Types.ReadableStream),
}
@@ -5236,7 +5255,8 @@ class Tokenizer(object):
"maplike": "MAPLIKE",
"setlike": "SETLIKE",
"iterable": "ITERABLE",
"namespace": "NAMESPACE"
"namespace": "NAMESPACE",
"ReadableStream": "READABLESTREAM",
}
tokens.extend(keywords.values())
@@ -6424,6 +6444,7 @@ class Parser(Tokenizer):
NonAnyType : PrimitiveType Null
| ARRAYBUFFER Null
| SHAREDARRAYBUFFER Null
| READABLESTREAM Null
| OBJECT Null
"""
if p[1] == "object":
@@ -6432,6 +6453,8 @@ class Parser(Tokenizer):
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
elif p[1] == "SharedArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
elif p[1] == "ReadableStream":
type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
else:
type = BuiltinTypes[p[1]]
+2 -2
View File
@@ -5947,7 +5947,7 @@ void
CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
double aDy, ErrorResult& aError)
{
RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited);
@@ -5963,7 +5963,7 @@ CanvasRenderingContext2D::PutImageData(ImageData& aImageData, double aDx,
double aDirtyHeight,
ErrorResult& aError)
{
RootedTypedArray<Uint8ClampedArray> arr(RootingCx());
RootedSpiderMonkeyInterface<Uint8ClampedArray> arr(RootingCx());
DebugOnly<bool> inited = arr.Init(aImageData.GetDataObject());
MOZ_ASSERT(inited);
+9 -9
View File
@@ -303,13 +303,13 @@ WebGLContext::FromDomElem(const char* funcName, TexImageTarget target, uint32_t
uint32_t height, uint32_t depth, const dom::Element& elem,
ErrorResult* const out_error)
{
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
if (canvas->IsWriteOnly()) {
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
}
if (elem.IsHTMLElement(nsGkAtoms::canvas)) {
const dom::HTMLCanvasElement* canvas = static_cast<const dom::HTMLCanvasElement*>(&elem);
if (canvas->IsWriteOnly()) {
out_error->Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
}
uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
@@ -475,7 +475,7 @@ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
GLsizei depth, GLint border, const webgl::PackingInfo& pi,
const TexImageSource& src)
{
dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
depth, border, pi, src, &scopedArr);
if (!blob)
@@ -491,7 +491,7 @@ WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint lev
const webgl::PackingInfo& pi, const TexImageSource& src)
{
const GLint border = 0;
dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
dom::RootedSpiderMonkeyInterface<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
depth, border, pi, src, &scopedArr);
if (!blob)
+2 -2
View File
@@ -1434,7 +1434,7 @@ public:
mDataIsJwk = false;
// Try ArrayBuffer
RootedTypedArray<ArrayBuffer> ab(aCx);
RootedSpiderMonkeyInterface<ArrayBuffer> ab(aCx);
if (ab.Init(aKeyData)) {
if (!mKeyData.Assign(ab)) {
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
@@ -1443,7 +1443,7 @@ public:
}
// Try ArrayBufferView
RootedTypedArray<ArrayBufferView> abv(aCx);
RootedSpiderMonkeyInterface<ArrayBufferView> abv(aCx);
if (abv.Init(aKeyData)) {
if (!mKeyData.Assign(abv)) {
mEarlyRv = NS_ERROR_DOM_OPERATION_ERR;
+239 -7
View File
@@ -5,6 +5,7 @@
#include "Fetch.h"
#include "FetchConsumer.h"
#include "FetchStream.h"
#include "nsIDocument.h"
#include "nsIGlobalObject.h"
@@ -23,6 +24,7 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BodyUtil.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/FetchDriver.h"
@@ -36,6 +38,7 @@
#include "mozilla/dom/Response.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/workers/ServiceWorkerManager.h"
#include "FetchObserver.h"
@@ -840,7 +843,7 @@ ExtractFromURLSearchParams(const URLSearchParams& aParams,
} // namespace
nsresult
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength)
@@ -878,7 +881,7 @@ ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDa
}
nsresult
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength)
@@ -916,9 +919,54 @@ ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUS
return NS_ERROR_FAILURE;
}
nsresult
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(!*aStream);
// ReadableStreams should be handled by
// BodyExtractorReadableStream::GetAsStream.
MOZ_ASSERT(!aBodyInit.IsReadableStream());
if (aBodyInit.IsArrayBuffer()) {
const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
return ExtractFromArrayBuffer(buf, aStream, aContentLength);
}
if (aBodyInit.IsArrayBufferView()) {
const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
return ExtractFromArrayBufferView(buf, aStream, aContentLength);
}
if (aBodyInit.IsBlob()) {
const Blob& blob = aBodyInit.GetAsBlob();
return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
}
if (aBodyInit.IsFormData()) {
FormData& form = aBodyInit.GetAsFormData();
return ExtractFromFormData(form, aStream, aContentType, aContentLength);
}
if (aBodyInit.IsUSVString()) {
nsAutoString str;
str.Assign(aBodyInit.GetAsUSVString());
return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
}
if (aBodyInit.IsURLSearchParams()) {
URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
}
NS_NOTREACHED("Should never reach here");
return NS_ERROR_FAILURE;
}
template <class Derived>
FetchBody<Derived>::FetchBody()
: mWorkerPrivate(nullptr)
, mReadableStreamBody(nullptr)
, mBodyUsed(false)
{
if (!NS_IsMainThread()) {
@@ -938,9 +986,46 @@ FetchBody<Derived>::~FetchBody()
{
}
template <class Derived>
bool
FetchBody<Derived>::BodyUsed() const
{
if (mBodyUsed) {
return true;
}
// If this object is disturbed or locked, return false.
if (mReadableStreamBody) {
AutoJSAPI jsapi;
// If we start tracking ownership, we need something like this.
//if (!jsapi.Init(mOwner)) {
// return true;
//}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> body(cx, mReadableStreamBody);
if (JS::ReadableStreamIsDisturbed(body) ||
JS::ReadableStreamIsLocked(body) ||
!JS::ReadableStreamIsReadable(body)) {
return true;
}
}
return false;
}
template
bool
FetchBody<Request>::BodyUsed() const;
template
bool
FetchBody<Response>::BodyUsed() const;
template <class Derived>
already_AddRefed<Promise>
FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv)
{
RefPtr<AbortSignal> signal = DerivedClass()->GetSignal();
if (signal && signal->Aborted()) {
@@ -955,9 +1040,20 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
SetBodyUsed();
// If we already created a ReadableStreamBody we have to lock it now because
// it may have been shared with other objects..
if (mReadableStreamBody) {
JS::Rooted<JSObject*> body(aCx, mReadableStreamBody);
LockStream(aCx, body, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
}
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();
RefPtr<Promise> promise =
FetchBodyConsumer<Derived>::Create(DerivedClass()->GetParentObject(),
this, signal, aType, aRv);
FetchBodyConsumer<Derived>::Create(global, this, signal, aType, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@@ -967,11 +1063,11 @@ FetchBody<Derived>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv)
template
already_AddRefed<Promise>
FetchBody<Request>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
FetchBody<Request>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
template
already_AddRefed<Promise>
FetchBody<Response>::ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
FetchBody<Response>::ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
template <class Derived>
void
@@ -1001,5 +1097,141 @@ template
void
FetchBody<Response>::SetMimeType();
template <class Derived>
void
FetchBody<Derived>::SetReadableStreamBody(JSObject* aBody)
{
MOZ_ASSERT(!mReadableStreamBody);
MOZ_ASSERT(aBody);
mReadableStreamBody = aBody;
}
template
void
FetchBody<Request>::SetReadableStreamBody(JSObject* aBody);
template
void
FetchBody<Response>::SetReadableStreamBody(JSObject* aBody);
template <class Derived>
void
FetchBody<Derived>::GetBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
ErrorResult& aRv)
{
nsCOMPtr<nsIInputStream> inputStream;
DerivedClass()->GetBody(getter_AddRefs(inputStream));
if (!inputStream) {
aBodyOut.set(nullptr);
return;
}
if (!mReadableStreamBody) {
JS::Rooted<JSObject*> body(aCx,
FetchStream::Create(aCx,
DerivedClass()->GetParentObject(),
inputStream,
aRv));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_ASSERT(body);
// If the body has been already consumed, we close the stream.
if (BodyUsed()) {
LockStream(aCx, body, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
mReadableStreamBody = body;
}
aBodyOut.set(mReadableStreamBody);
}
template
void
FetchBody<Request>::GetBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aMessage,
ErrorResult& aRv);
template
void
FetchBody<Response>::GetBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aMessage,
ErrorResult& aRv);
template <class Derived>
void
FetchBody<Derived>::LockStream(JSContext* aCx,
JS::HandleObject aStream,
ErrorResult& aRv)
{
// XXXMC: TODO
}
template
void
FetchBody<Request>::LockStream(JSContext* aCx,
JS::HandleObject aStream,
ErrorResult& aRv);
template
void
FetchBody<Response>::LockStream(JSContext* aCx,
JS::HandleObject aStream,
ErrorResult& aRv);
template <class Derived>
void
FetchBody<Derived>::MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(!BodyUsed());
if (!mReadableStreamBody) {
return;
}
JS::Rooted<JSObject*> stream(aCx, mReadableStreamBody);
// If this is a ReadableStream with an external source, this has been
// generated by a Fetch. In this case, Fetch will be able to recreate it
// again when GetBody() is called.
if (JS::ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource) {
aBodyOut.set(nullptr);
return;
}
JS::Rooted<JSObject*> branch1(aCx);
JS::Rooted<JSObject*> branch2(aCx);
if (!JS::ReadableStreamTee(aCx, stream, &branch1, &branch2)) {
aRv.StealExceptionFromJSContext(aCx);
return;
}
mReadableStreamBody = branch1;
aBodyOut.set(branch2);
}
template
void
FetchBody<Request>::MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aMessage,
ErrorResult& aRv);
template
void
FetchBody<Response>::MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aMessage,
ErrorResult& aRv);
} // namespace dom
} // namespace mozilla
+260 -207
View File
@@ -1,207 +1,260 @@
/* -*- Mode: C++; tab-width: 8; 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_Fetch_h
#define mozilla_dom_Fetch_h
#include "nsAutoPtr.h"
#include "nsIStreamLoader.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsProxyRelease.h"
#include "nsString.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RequestBinding.h"
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
class BlobImpl;
class InternalRequest;
class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
class RequestOrUSVString;
namespace workers {
class WorkerPrivate;
} // namespace workers
already_AddRefed<Promise>
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
const RequestInit& aInit, ErrorResult& aRv);
nsresult
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
/*
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
* Stores content type in out param aContentType.
*/
nsresult
ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength);
/*
* Non-owning version.
*/
nsresult
ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength);
template <class Derived> class FetchBodyConsumer;
enum FetchConsumeType
{
CONSUME_ARRAYBUFFER,
CONSUME_BLOB,
CONSUME_FORMDATA,
CONSUME_JSON,
CONSUME_TEXT,
};
/*
* FetchBody's body consumption uses nsIInputStreamPump to read from the
* underlying stream to a block of memory, which is then adopted by
* ContinueConsumeBody() and converted to the right type based on the JS
* function called.
*
* Use of the nsIInputStreamPump complicates things on the worker thread.
* The solution used here is similar to WebSockets.
* The difference is that we are only interested in completion and not data
* events, and nsIInputStreamPump can only deliver completion on the main thread.
*
* Before starting the pump on the main thread, we addref the FetchBody to keep
* it alive. Then we add a feature, to track the status of the worker.
*
* ContinueConsumeBody() is the function that cleans things up in both success
* and error conditions and so all callers call it with the appropriate status.
*
* Once the read is initiated on the main thread there are two possibilities.
*
* 1) Pump finishes before worker has finished Running.
* In this case we adopt the data and dispatch a runnable to the worker,
* which derefs FetchBody and removes the feature and resolves the Promise.
*
* 2) Pump still working while worker has stopped Running.
* The feature is Notify()ed and ContinueConsumeBody() is called with
* NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
* ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
* held by it) until pump->Cancel() is called. OnStreamComplete() will not
* do anything if the error code is NS_BINDING_ABORTED, so we don't have to
* worry about keeping anything alive.
*
* The pump is always released on the main thread.
*/
template <class Derived>
class FetchBody
{
public:
friend class FetchBodyConsumer<Derived>;
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
bool
BodyUsed() const { return mBodyUsed; }
already_AddRefed<Promise>
ArrayBuffer(ErrorResult& aRv)
{
return ConsumeBody(CONSUME_ARRAYBUFFER, aRv);
}
already_AddRefed<Promise>
Blob(ErrorResult& aRv)
{
return ConsumeBody(CONSUME_BLOB, aRv);
}
already_AddRefed<Promise>
FormData(ErrorResult& aRv)
{
return ConsumeBody(CONSUME_FORMDATA, aRv);
}
already_AddRefed<Promise>
Json(ErrorResult& aRv)
{
return ConsumeBody(CONSUME_JSON, aRv);
}
already_AddRefed<Promise>
Text(ErrorResult& aRv)
{
return ConsumeBody(CONSUME_TEXT, aRv);
}
// Utility public methods accessed by various runnables.
void
SetBodyUsed()
{
mBodyUsed = true;
}
const nsCString&
MimeType() const
{
return mMimeType;
}
virtual AbortSignal*
GetSignal() const = 0;
protected:
FetchBody();
// Always set whenever the FetchBody is created on the worker thread.
workers::WorkerPrivate* mWorkerPrivate;
virtual ~FetchBody();
void
SetMimeType();
private:
Derived*
DerivedClass() const
{
return static_cast<Derived*>(const_cast<FetchBody*>(this));
}
already_AddRefed<Promise>
ConsumeBody(FetchConsumeType aType, ErrorResult& aRv);
bool
IsOnTargetThread()
{
return NS_IsMainThread() == !mWorkerPrivate;
}
void
AssertIsOnTargetThread()
{
MOZ_ASSERT(IsOnTargetThread());
}
// Only ever set once, always on target thread.
bool mBodyUsed;
nsCString mMimeType;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Fetch_h
/* -*- Mode: C++; tab-width: 8; 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_Fetch_h
#define mozilla_dom_Fetch_h
#include "nsAutoPtr.h"
#include "nsIStreamLoader.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsProxyRelease.h"
#include "nsString.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RequestBinding.h"
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString;
class BlobImpl;
class InternalRequest;
class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
struct ReadableStream;
class RequestOrUSVString;
namespace workers {
class WorkerPrivate;
} // namespace workers
already_AddRefed<Promise>
FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
const RequestInit& aInit, ErrorResult& aRv);
nsresult
UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
/* Deal with unwieldy long webIDL-generated type names */
namespace fetch {
typedef ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams BodyInit;
typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrReadableStreamOrUSVString ResponseBodyInit;
typedef OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams OwningBodyInit;
};
/*
* Creates an nsIInputStream based on the fetch specifications 'extract a byte
* stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
* Stores content type in out param aContentType.
*/
nsresult
ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength);
/*
* Non-owning version.
*/
nsresult
ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength);
/*
* Non-owning version. This method should go away when BodyInit will contain
* ReadableStream.
*/
nsresult
ExtractByteStreamFromBody(const fetch::ResponseBodyInit& aBodyInit,
nsIInputStream** aStream,
nsCString& aContentType,
uint64_t& aContentLength);
template <class Derived> class FetchBodyConsumer;
enum FetchConsumeType
{
CONSUME_ARRAYBUFFER,
CONSUME_BLOB,
CONSUME_FORMDATA,
CONSUME_JSON,
CONSUME_TEXT,
};
class FetchStreamHolder
{
public:
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
virtual void NullifyStream() = 0;
};
/*
* FetchBody's body consumption uses nsIInputStreamPump to read from the
* underlying stream to a block of memory, which is then adopted by
* ContinueConsumeBody() and converted to the right type based on the JS
* function called.
*
* Use of the nsIInputStreamPump complicates things on the worker thread.
* The solution used here is similar to WebSockets.
* The difference is that we are only interested in completion and not data
* events, and nsIInputStreamPump can only deliver completion on the main thread.
*
* Before starting the pump on the main thread, we addref the FetchBody to keep
* it alive. Then we add a feature, to track the status of the worker.
*
* ContinueConsumeBody() is the function that cleans things up in both success
* and error conditions and so all callers call it with the appropriate status.
*
* Once the read is initiated on the main thread there are two possibilities.
*
* 1) Pump finishes before worker has finished Running.
* In this case we adopt the data and dispatch a runnable to the worker,
* which derefs FetchBody and removes the feature and resolves the Promise.
*
* 2) Pump still working while worker has stopped Running.
* The feature is Notify()ed and ContinueConsumeBody() is called with
* NS_BINDING_ABORTED. We first Cancel() the pump using a sync runnable to
* ensure that mFetchBody remains alive (since mConsumeBodyPump is strongly
* held by it) until pump->Cancel() is called. OnStreamComplete() will not
* do anything if the error code is NS_BINDING_ABORTED, so we don't have to
* worry about keeping anything alive.
*
* The pump is always released on the main thread.
*/
template <class Derived>
class FetchBody : public FetchStreamHolder
{
public:
friend class FetchBodyConsumer<Derived>;
bool
BodyUsed() const;
already_AddRefed<Promise>
ArrayBuffer(JSContext* aCx, ErrorResult& aRv)
{
return ConsumeBody(aCx, CONSUME_ARRAYBUFFER, aRv);
}
already_AddRefed<Promise>
Blob(JSContext* aCx, ErrorResult& aRv)
{
return ConsumeBody(aCx, CONSUME_BLOB, aRv);
}
already_AddRefed<Promise>
FormData(JSContext* aCx, ErrorResult& aRv)
{
return ConsumeBody(aCx, CONSUME_FORMDATA, aRv);
}
already_AddRefed<Promise>
Json(JSContext* aCx, ErrorResult& aRv)
{
return ConsumeBody(aCx, CONSUME_JSON, aRv);
}
already_AddRefed<Promise>
Text(JSContext* aCx, ErrorResult& aRv)
{
return ConsumeBody(aCx, CONSUME_TEXT, aRv);
}
void
GetBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
ErrorResult& aRv);
// If the body contains a ReadableStream body object, this method produces a
// tee() of it.
void
MaybeTeeReadableStreamBody(JSContext* aCx,
JS::MutableHandle<JSObject*> aBodyOut,
ErrorResult& aRv);
// Utility public methods accessed by various runnables.
void
SetBodyUsed()
{
mBodyUsed = true;
}
const nsCString&
MimeType() const
{
return mMimeType;
}
void
NullifyStream() override
{
mReadableStreamBody = nullptr;
}
virtual AbortSignal*
GetSignal() const = 0;
protected:
FetchBody();
// Always set whenever the FetchBody is created on the worker thread.
workers::WorkerPrivate* mWorkerPrivate;
// This is the ReadableStream exposed to content. Its underlying source is a FetchStream object.
JS::Heap<JSObject*> mReadableStreamBody;
virtual ~FetchBody();
void
SetMimeType();
void
SetReadableStreamBody(JSObject* aBody);
private:
Derived*
DerivedClass() const
{
return static_cast<Derived*>(const_cast<FetchBody*>(this));
}
already_AddRefed<Promise>
ConsumeBody(JSContext* aCx, FetchConsumeType aType, ErrorResult& aRv);
void
LockStream(JSContext* aCx, JS::HandleObject aStream, ErrorResult& aRv);
bool
IsOnTargetThread()
{
return NS_IsMainThread() == !mWorkerPrivate;
}
void
AssertIsOnTargetThread()
{
MOZ_ASSERT(IsOnTargetThread());
}
// Only ever set once, always on target thread.
bool mBodyUsed;
nsCString mMimeType;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Fetch_h
+553
View File
@@ -0,0 +1,553 @@
/* -*- Mode: C; tab-width: 8; 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/. */
#include "FetchStream.h"
#include "nsNetCID.h"
#include "nsITransport.h"
#include "nsIStreamTransportService.h"
#include "nsProxyRelease.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "Workers.h"
#include "mozilla/dom/DOMError.h"
#define FETCH_STREAM_FLAG 0
static NS_DEFINE_CID(kStreamTransportServiceCID,
NS_STREAMTRANSPORTSERVICE_CID);
namespace mozilla {
namespace dom {
using namespace workers;
namespace {
class FetchStreamWorkerHolder final : public WorkerHolder
{
public:
explicit FetchStreamWorkerHolder(FetchStream* aStream)
: WorkerHolder()
, mStream(aStream)
, mWasNotified(false)
{}
bool Notify(Status aStatus) override
{
if (!mWasNotified) {
mWasNotified = true;
mStream->Close();
}
return true;
}
WorkerPrivate* GetWorkerPrivate() const
{
return mWorkerPrivate;
}
private:
RefPtr<FetchStream> mStream;
bool mWasNotified;
};
class FetchStreamWorkerHolderShutdown final : public WorkerControlRunnable
{
public:
FetchStreamWorkerHolderShutdown(WorkerPrivate* aWorkerPrivate,
UniquePtr<WorkerHolder>&& aHolder,
nsCOMPtr<nsIGlobalObject>&& aGlobal,
RefPtr<FetchStreamHolder>&& aStreamHolder)
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
, mHolder(Move(aHolder))
, mGlobal(Move(aGlobal))
, mStreamHolder(Move(aStreamHolder))
{}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
mHolder = nullptr;
mGlobal = nullptr;
mStreamHolder->NullifyStream();
mStreamHolder = nullptr;
return true;
}
// This runnable starts from a JS Thread. We need to disable a couple of
// assertions by overriding the following methods.
bool
PreDispatch(WorkerPrivate* aWorkerPrivate) override
{
return true;
}
void
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
{}
private:
UniquePtr<WorkerHolder> mHolder;
nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<FetchStreamHolder> mStreamHolder;
};
} // anonymous
NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback, nsIObserver,
nsISupportsWeakReference)
/* static */ JSObject*
FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(aCx);
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
RefPtr<FetchStream> stream = new FetchStream(aGlobal, aStreamHolder, aInputStream);
if (NS_IsMainThread()) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (NS_WARN_IF(!os)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
aRv = os->AddObserver(stream, DOM_WINDOW_DESTROYED_TOPIC, true);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} else {
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
UniquePtr<FetchStreamWorkerHolder> holder(
new FetchStreamWorkerHolder(stream));
if (NS_WARN_IF(!holder->HoldWorker(workerPrivate, Closing))) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
// Note, this will create a ref-cycle between the holder and the stream.
// The cycle is broken when the stream is closed or the worker begins
// shutting down.
stream->mWorkerHolder = Move(holder);
}
if (!JS::HasReadableStreamCallbacks(aCx)) {
JS::SetReadableStreamCallbacks(aCx,
&FetchStream::RequestDataCallback,
&FetchStream::WriteIntoReadRequestCallback,
&FetchStream::CancelCallback,
&FetchStream::ClosedCallback,
&FetchStream::ErroredCallback,
&FetchStream::FinalizeCallback);
}
JS::Rooted<JSObject*> body(aCx,
JS::NewReadableExternalSourceStreamObject(aCx, stream, FETCH_STREAM_FLAG));
if (!body) {
aRv.StealExceptionFromJSContext(aCx);
return nullptr;
}
stream->mReadableStream = body;
// JS engine will call the finalize callback.
NS_ADDREF(stream.get());
return body;
}
/* static */ void
FetchStream::RequestDataCallback(JSContext* aCx,
JS::HandleObject aStream,
void* aUnderlyingSource,
uint8_t aFlags,
size_t aDesiredSize)
{
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
MOZ_DIAGNOSTIC_ASSERT(JS::ReadableStreamIsDisturbed(aStream));
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWaiting ||
stream->mState == eChecking);
if (stream->mState == eChecking) {
// If we are looking for more data, there is nothing else we should do:
// let's move this checking operation in a reading.
MOZ_ASSERT(stream->mInputStream);
stream->mState = eReading;
return;
}
stream->mState = eReading;
if (!stream->mInputStream) {
// This is the first use of the stream. Let's convert the
// mOriginalInputStream into an nsIAsyncInputStream.
MOZ_ASSERT(stream->mOriginalInputStream);
bool nonBlocking = false;
nsresult rv = stream->mOriginalInputStream->IsNonBlocking(&nonBlocking);
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
nsCOMPtr<nsIAsyncInputStream> asyncStream =
do_QueryInterface(stream->mOriginalInputStream);
if (!nonBlocking || !asyncStream) {
nsCOMPtr<nsIStreamTransportService> sts =
do_GetService(kStreamTransportServiceCID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
nsCOMPtr<nsITransport> transport;
rv = sts->CreateInputTransport(stream->mOriginalInputStream,
/* aStartOffset */ 0,
/* aReadLimit */ -1,
/* aCloseWhenDone */ true,
getter_AddRefs(transport));
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
nsCOMPtr<nsIInputStream> wrapper;
rv = transport->OpenInputStream(/* aFlags */ 0,
/* aSegmentSize */ 0,
/* aSegmentCount */ 0,
getter_AddRefs(wrapper));
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
asyncStream = do_QueryInterface(wrapper);
}
stream->mInputStream = asyncStream;
stream->mOriginalInputStream = nullptr;
}
MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
MOZ_DIAGNOSTIC_ASSERT(!stream->mOriginalInputStream);
nsresult rv =
stream->mInputStream->AsyncWait(stream, 0, 0, stream->mOwningEventTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
// All good.
}
/* static */ void
FetchStream::WriteIntoReadRequestCallback(JSContext* aCx,
JS::HandleObject aStream,
void* aUnderlyingSource,
uint8_t aFlags, void* aBuffer,
size_t aLength, size_t* aByteWritten)
{
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
MOZ_DIAGNOSTIC_ASSERT(aBuffer);
MOZ_DIAGNOSTIC_ASSERT(aByteWritten);
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream);
MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWriting);
stream->mState = eChecking;
uint32_t written;
nsresult rv =
stream->mInputStream->Read(static_cast<char*>(aBuffer), aLength, &written);
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
*aByteWritten = written;
if (written == 0) {
stream->CloseAndReleaseObjects(aCx, aStream);
return;
}
rv = stream->mInputStream->AsyncWait(stream, 0, 0, stream->mOwningEventTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
stream->ErrorPropagation(aCx, aStream, rv);
return;
}
// All good.
}
/* static */ JS::Value
FetchStream::CancelCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags,
JS::HandleValue aReason)
{
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource);
if (stream->mInputStream) {
stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
}
stream->ReleaseObjects();
return JS::UndefinedValue();
}
/* static */ void
FetchStream::ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags)
{
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
}
/* static */ void
FetchStream::ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags,
JS::HandleValue aReason)
{
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
}
void
FetchStream::FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags)
{
MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource);
MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG);
// This can be called in any thread.
RefPtr<FetchStream> stream =
dont_AddRef(static_cast<FetchStream*>(aUnderlyingSource));
stream->ReleaseObjects();
}
FetchStream::FetchStream(nsIGlobalObject* aGlobal,
FetchStreamHolder* aStreamHolder,
nsIInputStream* aInputStream)
: mState(eWaiting)
, mGlobal(aGlobal)
, mStreamHolder(aStreamHolder)
, mOriginalInputStream(aInputStream)
, mOwningEventTarget(nullptr)
, mReadableStream(nullptr)
{
MOZ_DIAGNOSTIC_ASSERT(aInputStream);
MOZ_DIAGNOSTIC_ASSERT(aStreamHolder);
}
FetchStream::~FetchStream()
{
}
void
FetchStream::ErrorPropagation(JSContext* aCx, JS::HandleObject aStream,
nsresult aError)
{
// Nothing to do.
if (mState == eClosed) {
return;
}
// Let's close the stream.
if (aError == NS_BASE_STREAM_CLOSED) {
CloseAndReleaseObjects(aCx, aStream);
return;
}
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
// Let's use a generic error.
RefPtr<DOMError> error = new DOMError(window, NS_ERROR_DOM_TYPE_ERR);
JS::Rooted<JS::Value> errorValue(aCx);
if (ToJSValue(aCx, error, &errorValue)) {
JS::ReadableStreamError(aCx, aStream, errorValue);
}
}
NS_IMETHODIMP
FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
{
MOZ_DIAGNOSTIC_ASSERT(aStream);
// Already closed. We have nothing else to do here.
if (mState == eClosed) {
return NS_OK;
}
MOZ_DIAGNOSTIC_ASSERT(mInputStream);
MOZ_DIAGNOSTIC_ASSERT(mState == eReading || mState == eChecking);
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
// Without JSContext we are not able to close the stream or to propagate the
// error.
return NS_ERROR_FAILURE;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> stream(cx, mReadableStream);
uint64_t size = 0;
nsresult rv = mInputStream->Available(&size);
if (NS_SUCCEEDED(rv) && size == 0) {
// In theory this should not happen. If size is 0, the stream should be
// considered closed.
rv = NS_BASE_STREAM_CLOSED;
}
// No warning for stream closed.
if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
ErrorPropagation(cx, stream, rv);
return NS_OK;
}
// This extra checking is completed. Let's wait for the next read request.
if (mState == eChecking) {
mState = eWaiting;
return NS_OK;
}
mState = eWriting;
JS::ReadableStreamUpdateDataAvailableFromSource(cx, stream, size);
// The WriteInto callback changes mState to eChecking.
MOZ_DIAGNOSTIC_ASSERT(mState == eChecking);
return NS_OK;
}
/* static */ nsresult
FetchStream::RetrieveInputStream(void* aUnderlyingReadableStreamSource,
nsIInputStream** aInputStream)
{
MOZ_ASSERT(aUnderlyingReadableStreamSource);
MOZ_ASSERT(aInputStream);
RefPtr<FetchStream> stream =
static_cast<FetchStream*>(aUnderlyingReadableStreamSource);
// if mOriginalInputStream is null, the reading already started. We don't want
// to expose the internal inputStream.
if (NS_WARN_IF(!stream->mOriginalInputStream)) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
nsCOMPtr<nsIInputStream> inputStream = stream->mOriginalInputStream;
inputStream.forget(aInputStream);
return NS_OK;
}
void
FetchStream::Close()
{
if (mState == eClosed) {
return;
}
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mGlobal))) {
ReleaseObjects();
return;
}
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> stream(cx, mReadableStream);
CloseAndReleaseObjects(cx, stream);
}
void
FetchStream::CloseAndReleaseObjects(JSContext* aCx, JS::HandleObject aStream)
{
MOZ_DIAGNOSTIC_ASSERT(mState != eClosed);
ReleaseObjects();
if (JS::ReadableStreamIsReadable(aStream)) {
JS::ReadableStreamClose(aCx, aStream);
}
}
void
FetchStream::ReleaseObjects()
{
if (mState == eClosed) {
return;
}
mState = eClosed;
if (mWorkerHolder) {
RefPtr<FetchStreamWorkerHolderShutdown> r =
new FetchStreamWorkerHolderShutdown(
static_cast<FetchStreamWorkerHolder*>(mWorkerHolder.get())->GetWorkerPrivate(),
Move(mWorkerHolder), Move(mGlobal), Move(mStreamHolder));
r->Dispatch();
} else {
RefPtr<FetchStream> self = this;
RefPtr<Runnable> r = NS_NewRunnableFunction(
[self] () {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->RemoveObserver(self, DOM_WINDOW_DESTROYED_TOPIC);
}
self->mGlobal = nullptr;
self->mStreamHolder->NullifyStream();
self->mStreamHolder = nullptr;
});
NS_DispatchToMainThread(r);
}
}
// nsIObserver
// -----------
NS_IMETHODIMP
FetchStream::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
AssertIsOnMainThread();
MOZ_ASSERT(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0);
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
if (SameCOMIdentity(aSubject, window)) {
Close();
}
return NS_OK;
}
} // dom namespace
} // mozilla namespace
+135
View File
@@ -0,0 +1,135 @@
/* -*- Mode: C; tab-width: 8; 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_FetchStream_h
#define mozilla_dom_FetchStream_h
#include "Fetch.h"
#include "jsapi.h"
#include "nsIAsyncInputStream.h"
#include "nsIObserver.h"
#include "nsISupportsImpl.h"
#include "nsWeakReference.h"
class nsIGlobalObject;
class nsIInputStream;
namespace mozilla {
namespace dom {
namespace workers {
class WorkerHolder;
}
class FetchStreamHolder;
class FetchStream final : public nsIInputStreamCallback
, public nsIObserver
, public nsSupportsWeakReference
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAMCALLBACK
NS_DECL_NSIOBSERVER
static JSObject*
Create(JSContext* aCx, FetchStreamHolder* aStreamHolder,
nsIGlobalObject* aGlobal, nsIInputStream* aInputStream,
ErrorResult& aRv);
void
Close();
static nsresult
RetrieveInputStream(void* aUnderlyingReadableStreamSource,
nsIInputStream** aInputStream);
private:
FetchStream(nsIGlobalObject* aGlobal,
FetchStreamHolder* aStreamHolder,
nsIInputStream* aInputStream);
~FetchStream();
static void
RequestDataCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags,
size_t aDesiredSize);
static void
WriteIntoReadRequestCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags,
void* aBuffer, size_t aLength,
size_t* aByteWritten);
static JS::Value
CancelCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags,
JS::HandleValue aReason);
static void
ClosedCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags);
static void
ErroredCallback(JSContext* aCx, JS::HandleObject aStream,
void* aUnderlyingSource, uint8_t aFlags,
JS::HandleValue reason);
static void
FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags);
void
ErrorPropagation(JSContext* aCx, JS::HandleObject aStream, nsresult aRv);
void
CloseAndReleaseObjects(JSContext* aCx, JS::HandleObject aStream);
void
ReleaseObjects();
// Common methods
enum State {
// RequestDataCallback has not been called yet. We haven't started to read
// data from the stream yet.
eWaiting,
// We are reading data in a separate I/O thread.
eReading,
// We are ready to write something in the JS Buffer.
eWriting,
// After a writing, we want to check if the stream is closed. After the
// check, we go back to eWaiting. If a reading request happens in the
// meantime, we move to eReading state.
eChecking,
// Operation completed.
eClosed,
};
// Touched only on the target thread.
State mState;
nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<FetchStreamHolder> mStreamHolder;
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
// This is the original inputStream received during the CTOR. It will be
// converted into an nsIAsyncInputStream and stored into mInputStream at the
// first use.
nsCOMPtr<nsIInputStream> mOriginalInputStream;
nsCOMPtr<nsIAsyncInputStream> mInputStream;
UniquePtr<workers::WorkerHolder> mWorkerHolder;
JS::Heap<JSObject*> mReadableStream;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_FetchStream_h
+18 -1
View File
@@ -29,7 +29,24 @@ using namespace workers;
NS_IMPL_CYCLE_COLLECTING_ADDREF(Request)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Request)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Request, mOwner, mHeaders)
NS_IMPL_CYCLE_COLLECTION_CLASS(Request)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Request)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Request)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Request)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+2
View File
@@ -128,6 +128,8 @@ public:
void
GetBody(nsIInputStream** aStream) { return mRequest->GetBody(aStream); }
using FetchBody::GetBody;
void
SetBody(nsIInputStream* aStream) { return mRequest->SetBody(aStream); }
+110 -11
View File
@@ -12,12 +12,14 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/FetchBinding.h"
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/URL.h"
#include "nsDOMString.h"
#include "FetchStream.h"
#include "InternalResponse.h"
#include "WorkerPrivate.h"
@@ -26,7 +28,27 @@ namespace dom {
NS_IMPL_CYCLE_COLLECTING_ADDREF(Response)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Response)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Response, mOwner, mHeaders)
NS_IMPL_CYCLE_COLLECTION_CLASS(Response)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Response)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
tmp->mReadableStreamBody = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Response)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Response)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mReadableStreamBody)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Response)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -42,10 +64,14 @@ Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse
MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
SetMimeType();
// Keep a firm grip!
mozilla::HoldJSObjects(this);
}
Response::~Response()
{
mozilla::DropJSObjects(this);
}
/* static */ already_AddRefed<Response>
@@ -105,7 +131,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
return nullptr;
}
Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>> body;
Optional<Nullable<fetch::ResponseBodyInit>> body;
ResponseInit init;
init.mStatus = aStatus;
RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
@@ -126,7 +152,7 @@ Response::Redirect(const GlobalObject& aGlobal, const nsAString& aUrl,
/*static*/ already_AddRefed<Response>
Response::Constructor(const GlobalObject& aGlobal,
const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody,
const Optional<Nullable<fetch::ResponseBodyInit>>& aBody,
const ResponseInit& aInit, ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
@@ -201,13 +227,56 @@ Response::Constructor(const GlobalObject& aGlobal,
nsCOMPtr<nsIInputStream> bodyStream;
nsCString contentType;
uint64_t bodySize = 0;
aRv = ExtractByteStreamFromBody(aBody.Value().Value(),
getter_AddRefs(bodyStream),
contentType,
bodySize);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
if (aBody.Value().IsReadableStream()) {
const ReadableStream& readableStream =
aBody.Value().GetAsReadableStream();
JS::Rooted<JSObject*> readableStreamObj(aGlobal.Context(),
readableStream.Obj());
if (JS::ReadableStreamIsDisturbed(readableStreamObj) ||
JS::ReadableStreamIsLocked(readableStreamObj) ||
!JS::ReadableStreamIsReadable(readableStreamObj)) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr;
}
r->SetReadableStreamBody(readableStreamObj);
// XXXMC: TODO
MOZ_ASSERT(JS::ReadableStreamGetMode(readableStreamObj) !=
JS::ReadableStreamMode::ExternalSource);
void* underlyingSource = nullptr;
if (!JS::ReadableStreamGetExternalUnderlyingSource(aGlobal.Context(),
readableStreamObj,
&underlyingSource)) {
aRv.StealExceptionFromJSContext(aGlobal.Context());
return nullptr;
}
bodySize = InternalResponse::UNKNOWN_BODY_SIZE;
MOZ_ASSERT(underlyingSource);
aRv = FetchStream::RetrieveInputStream(underlyingSource,
getter_AddRefs(bodyStream));
// Releasing of the external source is needed in order to avoid an extra stream lock.
JS::ReadableStreamReleaseExternalUnderlyingSource(readableStreamObj);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
} else {
aRv = ExtractByteStreamFromBody(aBody.Value().Value(),
getter_AddRefs(bodyStream),
contentType,
bodySize);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
}
internalResponse->SetBody(bodyStream, bodySize);
if (!contentType.IsVoid() &&
@@ -228,7 +297,7 @@ Response::Constructor(const GlobalObject& aGlobal,
}
already_AddRefed<Response>
Response::Clone(ErrorResult& aRv) const
Response::Clone(JSContext* aCx, ErrorResult& aRv) const
{
if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
@@ -237,11 +306,26 @@ Response::Clone(ErrorResult& aRv) const
RefPtr<InternalResponse> ir = mInternalResponse->Clone();
RefPtr<Response> response = new Response(mOwner, ir, mSignal);
JS::Rooted<JSObject*> body(aCx);
MaybeTeeReadableStreamBody(aCx, &body, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (body) {
// Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
// if this body is a native stream. In this case, the InternalResponse will
// have a clone of the native body and the ReadableStream will be created
// lazily if needed.
response->SetReadableStreamBody(body);
}
return response.forget();
}
already_AddRefed<Response>
Response::CloneUnfiltered(ErrorResult& aRv) const
Response::CloneUnfiltered(JSContext* aCx, ErrorResult& aRv) const
{
if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
@@ -251,6 +335,21 @@ Response::CloneUnfiltered(ErrorResult& aRv) const
RefPtr<InternalResponse> clone = mInternalResponse->Clone();
RefPtr<InternalResponse> ir = clone->Unfiltered();
RefPtr<Response> ref = new Response(mOwner, ir, mSignal);
JS::Rooted<JSObject*> body(aCx);
MaybeTeeReadableStreamBody(aCx, &body, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (body) {
// Maybe we have a body, but we receive null from MaybeTeeReadableStreamBody
// if this body is a native stream. In this case, the InternalResponse will
// have a clone of the native body and the ReadableStream will be created
// lazily if needed.
ref->SetReadableStreamBody(body);
}
return ref.forget();
}
+5 -3
View File
@@ -105,6 +105,8 @@ public:
void
GetBody(nsIInputStream** aStream) { return mInternalResponse->GetBody(aStream); }
using FetchBody::GetBody;
static already_AddRefed<Response>
Error(const GlobalObject& aGlobal);
@@ -113,7 +115,7 @@ public:
static already_AddRefed<Response>
Constructor(const GlobalObject& aGlobal,
const Optional<Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>>& aBody,
const Optional<Nullable<fetch::ResponseBodyInit>>& aBody,
const ResponseInit& aInit, ErrorResult& rv);
nsIGlobalObject* GetParentObject() const
@@ -122,10 +124,10 @@ public:
}
already_AddRefed<Response>
Clone(ErrorResult& aRv) const;
Clone(JSContext* aCx, ErrorResult& aRv) const;
already_AddRefed<Response>
CloneUnfiltered(ErrorResult& aRv) const;
CloneUnfiltered(JSContext* aCx, ErrorResult& aRv) const;
void
SetBody(nsIInputStream* aBody, int64_t aBodySize);
+1
View File
@@ -29,6 +29,7 @@ SOURCES += [
'ChannelInfo.cpp',
'FetchDriver.cpp',
'FetchObserver.cpp',
'FetchStream.cpp',
'FetchUtil.cpp',
'Headers.cpp',
'InternalHeaders.cpp',
+1 -1
View File
@@ -296,7 +296,7 @@ TCPSocketParent::RecvData(const SendableData& aData,
const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
NS_ENSURE_TRUE(ok, true);
RootedTypedArray<ArrayBuffer> data(autoCx);
RootedSpiderMonkeyInterface<ArrayBuffer> data(autoCx);
data.Init(&val.toObject());
Optional<uint32_t> byteLength(buffer.Length());
mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);
+3 -3
View File
@@ -58,9 +58,9 @@ ModuleScript::UnlinkModuleRecord()
{
// Remove module's back reference to this object request if present.
if (mModuleRecord) {
MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() ==
MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() ==
this);
JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue());
mModuleRecord = nullptr;
}
}
@@ -83,7 +83,7 @@ ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
// Make module's host defined field point to this module script object.
// This is cleared in the UnlinkModuleRecord().
JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this));
HoldJSObjects(this);
}
+4 -4
View File
@@ -798,13 +798,13 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aParent,
// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier)
JSObject*
HostResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
JS::Handle<JSString*> aSpecifier)
{
// Let referencing module script be referencingModule.[[HostDefined]].
auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) == aReferencingPrivate);
JS::Value value = JS::GetModuleHostDefinedField(aModule);
auto script = static_cast<ModuleScript*>(value.toPrivate());
MOZ_ASSERT(script->ModuleRecord() == aModule);
// Let url be the result of resolving a module specifier given referencing
// module script and specifier.
+2 -3
View File
@@ -630,9 +630,8 @@ private:
ModuleScript* GetFetchedModule(nsIURI* aURL) const;
friend JSObject*
HostResolveImportedModule(JSContext* aCx,
JS::Handle<JS::Value> aReferencingPrivate,
JS::Handle<JSString*> aSpecifier);
HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
JS::Handle<JSString*> aSpecifier);
nsresult CreateModuleScript(ModuleLoadRequest* aRequest);
nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest);
@@ -122,6 +122,7 @@ run-if = e10s
[test_storagePermissionsReject.html]
[test_storagePermissionsRejectForeign.html]
[test_stylesheetPI.html]
[test_vibrator.html]
[test_WebKitCSSMatrix.html]
[test_windowedhistoryframes.html]
[test_windowProperties.html]
@@ -0,0 +1,93 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Vibrator</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<!-- Although we can't test that the vibrator works properly, we can test that
navigator.vibrate throws an exception where appropriate. -->
<script class="testbody" type="text/javascript;version=1.7">
SimpleTest.waitForExplicitFinish();
var result;
function expectFailure(param) {
result = navigator.vibrate(param);
is(result, false, 'vibrate(' + param + ') should have failed.');
}
function expectSuccess(param) {
result = navigator.vibrate(param);
is(result, true, 'vibrate(' + param + ') must succeed.');
}
function tests(aEnabled) {
// Some edge cases that the bindings should handle for us.
expectSuccess(null);
expectSuccess(undefined);
// -1 will be converted to the highest unsigned long then clamped.
expectSuccess(-1);
expectSuccess('a');
// -1 will be converted to the highest unsigned long then clamped.
expectSuccess([100, -1]);
expectSuccess([100, 'a']);
var maxVibrateMs = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_ms');
var maxVibrateListLen = SpecialPowers.getIntPref('dom.vibrator.max_vibrate_list_len');
// If we pass a vibration pattern with a value higher than max_vibrate_ms or a
// pattern longer than max_vibrate_list_len, the call should succeed but the
// pattern should be modified to match the restrictions.
// Values will be clamped to dom.vibrator.max_vibrate_ms.
expectSuccess(maxVibrateMs + 1);
expectSuccess([maxVibrateMs + 1]);
var arr = [];
for (var i = 0; i < maxVibrateListLen + 1; i++) {
arr[i] = 0;
}
// The array will be truncated to have a length equal to dom.vibrator.max_vibrate_list_len.
expectSuccess(arr);
expectSuccess(0);
expectSuccess([]);
expectSuccess('1000');
expectSuccess(1000);
expectSuccess(1000.1);
expectSuccess([0, 0, 0]);
expectSuccess(['1000', 1000]);
expectSuccess([1000, 1000]);
expectSuccess([1000, 1000.1]);
// The following loop shouldn't cause us to crash. See bug 701716.
for (var i = 0; i < 10000; i++) {
navigator.vibrate([100, 100]);
}
ok(true, "Didn't crash after issuing a lot of vibrate() calls.");
if(!aEnabled)
SimpleTest.finish();
}
SpecialPowers.pushPermissions([
{type: 'vibration', allow: true, context: document}
], function() {
// Test with the vibrator pref enabled.
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', true]]}, function() {
tests(true);
SpecialPowers.pushPrefEnv({"set": [['dom.vibrator.enabled', false]]}, tests(false));
});
// Everything should be the same when the vibrator is disabled -- in
// particular, a disabled vibrator shouldn't eat failures we'd otherwise
// observe.
}
);
</script>
</body>
</html>
+1
View File
@@ -9,6 +9,7 @@
typedef object JSON;
typedef (ArrayBuffer or ArrayBufferView or Blob or FormData or USVString or URLSearchParams) BodyInit;
typedef (Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or ReadableStream or USVString) ResponseBodyInit;
[NoInterfaceObject, Exposed=(Window,Worker)]
interface Body {
+22 -3
View File
@@ -13,9 +13,9 @@
* http://www.w3.org/TR/beacon/#sec-beacon-method
* https://html.spec.whatwg.org/#navigatorconcurrenthardware
*
* © Copyright 2004-2020 Apple Computer, Inc., Mozilla Foundation,
* Opera Software ASA and Moonchild Productions. You are granted a license to use,
* reproduce and create derivative works of this document.
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
// http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
@@ -124,6 +124,14 @@ interface NavigatorGeolocation {
};
Navigator implements NavigatorGeolocation;
// http://www.w3.org/TR/vibration/#vibration-interface
partial interface Navigator {
// We don't support sequences in unions yet
//boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);
boolean vibrate(unsigned long duration);
boolean vibrate(sequence<unsigned long> pattern);
};
// http://www.w3.org/TR/pointerevents/#extensions-to-the-navigator-interface
partial interface Navigator {
[Pref="dom.w3c_pointer_events.enabled"]
@@ -132,6 +140,17 @@ partial interface Navigator {
// Mozilla-specific extensions
// Chrome-only interface for Vibration API permission handling.
partial interface Navigator {
/* Set permission state to device vibration.
* @param permitted permission state (true for allowing vibration)
* @param persistent make the permission session-persistent
*/
[ChromeOnly]
void setVibrationPermission(boolean permitted,
optional boolean persistent = true);
};
callback interface MozIdleObserver {
// Time is in seconds and is read only when idle observers are added
// and removed.
+9 -1
View File
@@ -7,7 +7,9 @@
* https://fetch.spec.whatwg.org/#response-class
*/
[Constructor(optional BodyInit? body, optional ResponseInit init),
// This should be Constructor(optional BodyInit... but BodyInit doesn't include
// ReadableStream yet because we don't want to expose the Streams API to Request.
[Constructor(optional ResponseBodyInit? body, optional ResponseInit init),
Exposed=(Window,Worker)]
interface Response {
[NewObject] static Response error();
@@ -30,6 +32,12 @@ interface Response {
};
Response implements Body;
// This should be part of Body but we don't want to expose body to request yet.
partial interface Response {
[GetterThrows, Func="nsContentUtils::StreamsEnabled"]
readonly attribute ReadableStream? body;
};
dictionary ResponseInit {
unsigned short status = 200;
ByteString statusText = "OK";
+1
View File
@@ -299,6 +299,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
.setNativeRegExp(GetWorkerPref<bool>(NS_LITERAL_CSTRING("native_regexp")))
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
.setArrayProtoValues(GetWorkerPref<bool>(
NS_LITERAL_CSTRING("array_prototype_values")));
+1
View File
@@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEn
WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
+1 -1
View File
@@ -2830,7 +2830,7 @@ XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
nsresult rv = aVariant->GetAsJSVal(&realVal);
if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull());
RootedTypedArray<ArrayBuffer> buf(rootingCx);
RootedSpiderMonkeyInterface<ArrayBuffer> buf(rootingCx);
if (buf.Init(obj)) {
RequestBody<const ArrayBuffer> body(&buf);
return SendInternal(&body);
+16 -30
View File
@@ -212,7 +212,7 @@ VerifyRGBXCorners(uint8_t* aData, const IntSize &aSize, const int32_t aStride, S
#endif
static sk_sp<SkImage>
GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
GetSkImageForSurface(SourceSurface* aSurface)
{
if (!aSurface) {
gfxDebug() << "Creating null Skia image from null SourceSurface";
@@ -220,7 +220,7 @@ GetSkImageForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>* aLock)
}
if (aSurface->GetType() == SurfaceType::SKIA) {
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage(aLock);
return static_cast<SourceSurfaceSkia*>(aSurface)->GetImage();
}
DataSourceSurface* surf = aSurface->GetDataSurface().take();
@@ -390,9 +390,9 @@ ExtractAlphaBitmap(sk_sp<SkImage> aImage, SkBitmap* aResultBitmap)
}
static sk_sp<SkImage>
ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
ExtractAlphaForSurface(SourceSurface* aSurface)
{
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &aLock);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image) {
return nullptr;
}
@@ -411,11 +411,7 @@ ExtractAlphaForSurface(SourceSurface* aSurface, Maybe<MutexAutoLock>& aLock)
}
static void
SetPaintPattern(SkPaint& aPaint,
const Pattern& aPattern,
Maybe<MutexAutoLock>& aLock,
Float aAlpha = 1.0,
Point aOffset = Point(0, 0))
SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0, Point aOffset = Point(0, 0))
{
switch (aPattern.GetType()) {
case PatternType::COLOR: {
@@ -477,7 +473,7 @@ SetPaintPattern(SkPaint& aPaint,
}
case PatternType::SURFACE: {
const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface, &aLock);
sk_sp<SkImage> image = GetSkImageForSurface(pat.mSurface);
if (!image) {
aPaint.setColor(SK_ColorTRANSPARENT);
break;
@@ -529,7 +525,7 @@ struct AutoPaintSetup {
: mNeedsRestore(false), mAlpha(1.0)
{
Init(aCanvas, aOptions, aMaskBounds, false);
SetPaintPattern(mPaint, aPattern, mLock, mAlpha, aOffset);
SetPaintPattern(mPaint, aPattern, mAlpha, aOffset);
}
AutoPaintSetup(SkCanvas *aCanvas, const DrawOptions& aOptions, const Rect* aMaskBounds = nullptr, bool aForceGroup = false)
@@ -583,7 +579,6 @@ struct AutoPaintSetup {
SkPaint mPaint;
bool mNeedsRestore;
SkCanvas* mCanvas;
Maybe<MutexAutoLock> mLock;
Float mAlpha;
};
@@ -606,8 +601,7 @@ DrawTargetSkia::DrawSurface(SourceSurface *aSurface,
MarkChanged();
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image) {
return;
}
@@ -660,8 +654,7 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface,
MarkChanged();
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image) {
return;
}
@@ -1414,9 +1407,8 @@ DrawTargetSkia::Mask(const Pattern &aSource,
MarkChanged();
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource);
Maybe<MutexAutoLock> lock;
SkPaint maskPaint;
SetPaintPattern(maskPaint, aMask, lock);
SetPaintPattern(maskPaint, aMask);
SkLayerRasterizer::Builder builder;
builder.addLayer(maskPaint);
@@ -1435,8 +1427,7 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource,
MarkChanged();
AutoPaintSetup paint(mCanvas.get(), aOptions, aSource, nullptr, -aOffset);
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask, lock);
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(aMask);
if (!alphaMask) {
gfxDebug() << *this << ": MaskSurface() failed to extract alpha for mask";
return;
@@ -1465,8 +1456,7 @@ DrawTarget::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& a
fullMat.PostTranslate(-xformBounds.x, -xformBounds.y, 0);
// Read in the source data.
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface, &lock);
sk_sp<SkImage> srcImage = GetSkImageForSurface(aSurface);
if (!srcImage) {
return true;
}
@@ -1525,8 +1515,7 @@ DrawTargetSkia::Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x
MarkChanged();
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image) {
return true;
}
@@ -1610,8 +1599,7 @@ already_AddRefed<SourceSurface>
DrawTargetSkia::OptimizeGPUSourceSurface(SourceSurface *aSurface) const
{
// Check if the underlying SkImage already has an associated GrTexture.
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image || image->isTextureBacked()) {
RefPtr<SourceSurface> surface(aSurface);
return surface.forget();
@@ -1728,8 +1716,7 @@ DrawTargetSkia::CopySurface(SourceSurface *aSurface,
{
MarkChanged();
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> image = GetSkImageForSurface(aSurface, &lock);
sk_sp<SkImage> image = GetSkImageForSurface(aSurface);
if (!image) {
return;
}
@@ -2074,8 +2061,7 @@ DrawTargetSkia::PopLayer()
paint.setColor(SK_ColorTRANSPARENT);
}
Maybe<MutexAutoLock> lock;
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask, lock);
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask);
if (!alphaMask) {
gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";
} else {
-19
View File
@@ -40,25 +40,6 @@ SourceSurfaceSkia::GetFormat() const
return mFormat;
}
sk_sp<SkImage>
SourceSurfaceSkia::GetImage(Maybe<MutexAutoLock>* aLock) {
// If we were provided a lock object, we can let the caller access
// a shared SkImage and we know it won't go away while the lock is held.
if (aLock) {
// We are locked, so now we can check mDrawTarget.
// If it's null, then we're not shared and we can unlock eagerly.
if (!mDrawTarget) {
aLock->reset();
}
} else {
// Otherwise we need to call DrawTargetWillChange to ensure we have our
// own copy of the SkImage.
DrawTargetWillChange();
}
sk_sp<SkImage> image = mImage;
return image;
}
static sk_sp<SkData>
MakeSkData(unsigned char* aData, const IntSize& aSize, int32_t aStride)
{
+1 -5
View File
@@ -10,10 +10,6 @@
#include <vector>
#include "skia/include/core/SkCanvas.h"
#include "skia/include/core/SkImage.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
typedef mozilla::MutexAutoLock MutexAutoLock;
namespace mozilla {
@@ -32,7 +28,7 @@ public:
virtual IntSize GetSize() const;
virtual SurfaceFormat GetFormat() const;
sk_sp<SkImage> GetImage(Maybe<MutexAutoLock>* aLock);
sk_sp<SkImage>& GetImage() { return mImage; }
bool InitFromData(unsigned char* aData,
const IntSize &aSize,
+79
View File
@@ -94,8 +94,87 @@ WindowIsActive(nsPIDOMWindowInner* aWindow)
return !document->Hidden();
}
StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
void InitLastIDToVibrate()
{
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
ClearOnShutdown(&gLastIDToVibrate);
}
} // namespace
void
Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window)
{
Vibrate(pattern, WindowIdentifier(window));
}
void
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
{
AssertMainThread();
// Only active windows may start vibrations. If |id| hasn't gone
// through the IPC layer -- that is, if our caller is the outside
// world, not hal_proxy -- check whether the window is active. If
// |id| has gone through IPC, don't check the window's visibility;
// only the window corresponding to the bottommost process has its
// visibility state set correctly.
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
return;
}
if (!InSandbox()) {
if (!gLastIDToVibrate) {
InitLastIDToVibrate();
}
*gLastIDToVibrate = id.AsArray();
}
// Don't forward our ID if we are not in the sandbox, because hal_impl
// doesn't need it, and we don't want it to be tempted to read it. The
// empty identifier will assert if it's used.
PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
}
void
CancelVibrate(nsPIDOMWindowInner* window)
{
CancelVibrate(WindowIdentifier(window));
}
void
CancelVibrate(const WindowIdentifier &id)
{
AssertMainThread();
// Although only active windows may start vibrations, a window may
// cancel its own vibration even if it's no longer active.
//
// After a window is marked as inactive, it sends a CancelVibrate
// request. We want this request to cancel a playing vibration
// started by that window, so we certainly don't want to reject the
// cancellation request because the window is now inactive.
//
// But it could be the case that, after this window became inactive,
// some other window came along and started a vibration. We don't
// want this window's cancellation request to cancel that window's
// actively-playing vibration!
//
// To solve this problem, we keep track of the id of the last window
// to start a vibration, and only accepts cancellation requests from
// the same window. All other cancellation requests are ignored.
if (InSandbox() || (gLastIDToVibrate && *gLastIDToVibrate == id.AsArray())) {
// Don't forward our ID if we are not in the sandbox, because hal_impl
// doesn't need it, and we don't want it to be tempted to read it. The
// empty identifier will assert if it's used.
PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id : WindowIdentifier()));
}
}
template <class InfoType>
class ObserversManager
{
+35
View File
@@ -50,6 +50,41 @@ typedef Observer<SystemTimezoneChangeInformation> SystemTimezoneChangeObserver;
namespace MOZ_HAL_NAMESPACE {
/**
* Turn the default vibrator device on/off per the pattern specified
* by |pattern|. Each element in the pattern is the number of
* milliseconds to turn the vibrator on or off. The first element in
* |pattern| is an "on" element, the next is "off", and so on.
*
* If |pattern| is empty, any in-progress vibration is canceled.
*
* Only an active window within an active tab may call Vibrate; calls
* from inactive windows and windows on inactive tabs do nothing.
*
* If you're calling hal::Vibrate from the outside world, pass an
* nsIDOMWindow* in place of the WindowIdentifier parameter.
* The method with WindowIdentifier will be called automatically.
*/
void Vibrate(const nsTArray<uint32_t>& pattern,
nsPIDOMWindowInner* aWindow);
void Vibrate(const nsTArray<uint32_t>& pattern,
const hal::WindowIdentifier &id);
/**
* Cancel a vibration started by the content window identified by
* WindowIdentifier.
*
* If the window was the last window to start a vibration, the
* cancellation request will go through even if the window is not
* active.
*
* As with hal::Vibrate(), if you're calling hal::CancelVibrate from the outside
* world, pass an nsIDOMWindow*. The method with WindowIdentifier will be called
* automatically.
*/
void CancelVibrate(nsPIDOMWindowInner* aWindow);
void CancelVibrate(const hal::WindowIdentifier &id);
/**
* Determine whether the device's screen is currently enabled.
*/
+22
View File
@@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 8; 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/. */
#include "Hal.h"
using mozilla::hal::WindowIdentifier;
namespace mozilla {
namespace hal_impl {
void
Vibrate(const nsTArray<uint32_t>& pattern, const hal::WindowIdentifier &)
{}
void
CancelVibrate(const hal::WindowIdentifier &)
{}
} // namespace hal_impl
} // namespace mozilla
+5
View File
@@ -29,6 +29,7 @@ if CONFIG['OS_TARGET'] == 'Linux':
'fallback/FallbackAlarm.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackSensor.cpp',
'fallback/FallbackVibration.cpp',
'linux/LinuxMemory.cpp',
'linux/LinuxPower.cpp',
]
@@ -38,6 +39,7 @@ elif CONFIG['OS_TARGET'] == 'WINNT':
'fallback/FallbackMemory.cpp',
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackVibration.cpp',
'windows/WindowsSensor.cpp',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
@@ -46,6 +48,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
'fallback/FallbackMemory.cpp',
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackVibration.cpp',
]
elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
UNIFIED_SOURCES += [
@@ -54,6 +57,7 @@ elif CONFIG['OS_TARGET'] in ('OpenBSD', 'NetBSD', 'FreeBSD', 'DragonFly'):
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackSensor.cpp',
'fallback/FallbackVibration.cpp',
]
else:
UNIFIED_SOURCES += [
@@ -62,6 +66,7 @@ else:
'fallback/FallbackPower.cpp',
'fallback/FallbackScreenConfiguration.cpp',
'fallback/FallbackSensor.cpp',
'fallback/FallbackVibration.cpp',
]
UNIFIED_SOURCES += [
+3
View File
@@ -69,6 +69,9 @@ child:
async NotifySystemTimezoneChange(SystemTimezoneChangeInformation aSystemTimezoneChangeInfo);
parent:
async Vibrate(uint32_t[] pattern, uint64_t[] id, PBrowser browser);
async CancelVibrate(uint64_t[] id, PBrowser browser);
async EnableNetworkNotifications();
async DisableNetworkNotifications();
sync GetCurrentNetworkInformation()
+52
View File
@@ -45,6 +45,28 @@ Hal()
return sHal;
}
void
Vibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id)
{
HAL_LOG("Vibrate: Sending to parent process.");
AutoTArray<uint32_t, 8> p(pattern);
WindowIdentifier newID(id);
newID.AppendProcessID();
Hal()->SendVibrate(p, newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
}
void
CancelVibrate(const WindowIdentifier &id)
{
HAL_LOG("CancelVibrate: Sending to parent process.");
WindowIdentifier newID(id);
newID.AppendProcessID();
Hal()->SendCancelVibrate(newID.AsArray(), TabChild::GetFrom(newID.GetWindow()));
}
void
EnableNetworkNotifications()
{
@@ -357,6 +379,36 @@ public:
hal::UnregisterSystemTimezoneChangeObserver(this);
}
virtual bool
RecvVibrate(InfallibleTArray<unsigned int>&& pattern,
InfallibleTArray<uint64_t>&& id,
PBrowserParent *browserParent) override
{
// We give all content vibration permission.
// TabParent *tabParent = TabParent::GetFrom(browserParent);
/* xxxkhuey wtf
nsCOMPtr<nsIDOMWindow> window =
do_QueryInterface(tabParent->GetBrowserDOMWindow());
*/
WindowIdentifier newID(id, nullptr);
hal::Vibrate(pattern, newID);
return true;
}
virtual bool
RecvCancelVibrate(InfallibleTArray<uint64_t> &&id,
PBrowserParent *browserParent) override
{
//TabParent *tabParent = TabParent::GetFrom(browserParent);
/* XXXkhuey wtf
nsCOMPtr<nsIDOMWindow> window =
tabParent->GetBrowserDOMWindow();
*/
WindowIdentifier newID(id, nullptr);
hal::CancelVibrate(newID);
return true;
}
virtual bool
RecvEnableNetworkNotifications() override {
// We give all content access to this network-status information.
+510
View File
@@ -0,0 +1,510 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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/. */
/*
* JSAPI functions and callbacks related to WHATWG Stream objects.
*
* Much of the API here mirrors the JS API of ReadableStream and associated
* classes, e.g. ReadableStreamDefaultReader, ReadableStreamBYOBReader,
* ReadableStreamDefaultController, ReadableByteStreamController, and
* ReadableStreamBYOBRequest.
*
* There are some crucial differences, though: Functionality that's exposed
* as methods/accessors on controllers in JS is exposed as functions taking
* ReadableStream instances instead. This is because an analysis of how
* the API would be used showed that all functions that'd take controllers
* would do so by first getting the controller from the stream instance it's
* associated with and then call the function taking it. I.e., it would purely
* add boilerplate without any gains in ease of use of the API.
*
* Some functions exposed here deal with ReadableStream instances that have an
* embedding-provided underlying source. These instances are largely similar
* to byte streams as created using |new ReadableStream({type: "bytes"})|:
* They enable users to acquire ReadableStreamBYOBReaders and only vend chunks
* that're typed array instances.
*
* When creating an "external readable stream" using
* JS::NewReadableExternalSourceStreamObject, an underlying source and a set
* of flags can be passed to be stored on the stream. The underlying source is
* treated as an opaque void* pointer by the JS engine: it's purely meant as
* a reference to be used by the embedding to identify whatever actual source
* it uses to supply data for the stream. Similarly, the flags aren't
* interpreted by the JS engine, but are passed to some of the callbacks below
* and can be retrieved using JS::ReadableStreamGetEmbeddingFlags.
*
* External readable streams are optimized to allow the embedding to interact
* with them with a minimum of overhead: chunks aren't enqueued as individual
* typed array instances; instead, the embedding only updates the amount of
* data available using ReadableStreamUpdateDataAvailableFromSource.
* When content requests data by reading from a reader,
* WriteIntoReadRequestBufferCallback is invoked, asking the embedding to
* write data directly into the buffer we're about to hand to content.
*
* Additionally, ReadableStreamGetExternalUnderlyingSource can be used to
* get the void* pointer to the underlying source. This is equivalent to
* acquiring a reader for the stream in that it locks the stream until it
* is released again using JS::ReadableStreamReleaseExternalUnderlyingSource.
*
* Embeddings are expected to detect situations where an API exposed to JS
* takes a ReadableStream to read from that has an external underlying source.
* In those situations, it might be preferable to directly perform data
* transfers from the stream's underlying source to whatever sink the
* embedding uses, assuming that such direct transfers can be performed
* more efficiently.
*/
#ifndef js_Stream_h
#define js_Stream_h
#include "jstypes.h"
#include "js/TypeDecls.h"
namespace JS {
/**
* Invoked whenever a reader desires more data from a ReadableStream's
* embedding-provided underlying source.
*
* The given |desiredSize| is the absolute size, not a delta from the previous
* desired size.
*/
typedef void
(* RequestReadableStreamDataCallback)(JSContext* cx, HandleObject stream,
void* underlyingSource, uint8_t flags, size_t desiredSize);
/**
* Invoked to cause the embedding to fill the given |buffer| with data from
* the given embedding-provided underlying source.
*
* This can only happen after the embedding has updated the amount of data
* available using JS::ReadableStreamUpdateDataAvailableFromSource. If at
* least one read request is pending when
* JS::ReadableStreamUpdateDataAvailableFromSource is called,
* the WriteIntoReadRequestBufferCallback is invoked immediately from under
* the call to JS::WriteIntoReadRequestBufferCallback. If not, it is invoked
* if and when a new read request is made.
*
* Note: This callback *must not cause GC*, because that could potentially
* invalidate the |buffer| pointer.
*/
typedef void
(* WriteIntoReadRequestBufferCallback)(JSContext* cx, HandleObject stream,
void* underlyingSource, uint8_t flags, void* buffer,
size_t length, size_t* bytesWritten);
/**
* Invoked in reaction to the ReadableStream being canceled to allow the
* embedding to free the underlying source.
*
* This is equivalent to calling |cancel| on non-external underlying sources
* provided to the ReadableStream constructor in JavaScript.
*
* The given |reason| is the JS::Value that was passed as an argument to
* ReadableStream#cancel().
*
* The returned JS::Value will be used to resolve the Promise returned by
* ReadableStream#cancel().
*/
typedef Value
(* CancelReadableStreamCallback)(JSContext* cx, HandleObject stream,
void* underlyingSource, uint8_t flags, HandleValue reason);
/**
* Invoked in reaction to a ReadableStream with an embedding-provided
* underlying source being closed.
*/
typedef void
(* ReadableStreamClosedCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
uint8_t flags);
/**
* Invoked in reaction to a ReadableStream with an embedding-provided
* underlying source being errored with the
* given reason.
*/
typedef void
(* ReadableStreamErroredCallback)(JSContext* cx, HandleObject stream, void* underlyingSource,
uint8_t flags, HandleValue reason);
/**
* Invoked in reaction to a ReadableStream with an embedding-provided
* underlying source being finalized. Only the underlying source is passed
* as an argument, while the ReadableStream itself is not to prevent the
* embedding from operating on a JSObject that might not be in a valid state
* anymore.
*
* Note: the ReadableStream might be finalized on a background thread. That
* means this callback might be invoked from an arbitrary thread, which the
* embedding must be able to handle.
*/
typedef void
(* ReadableStreamFinalizeCallback)(void* underlyingSource, uint8_t flags);
/**
* Sets runtime-wide callbacks to use for interacting with embedding-provided
* hooks for operating on ReadableStream instances.
*
* See the documentation for the individual callback types for details.
*/
extern JS_PUBLIC_API(void)
SetReadableStreamCallbacks(JSContext* cx,
RequestReadableStreamDataCallback dataRequestCallback,
WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
CancelReadableStreamCallback cancelCallback,
ReadableStreamClosedCallback closedCallback,
ReadableStreamErroredCallback erroredCallback,
ReadableStreamFinalizeCallback finalizeCallback);
extern JS_PUBLIC_API(bool)
HasReadableStreamCallbacks(JSContext* cx);
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, configured as a default stream.
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
* instead of the original value of |ReadableStream.prototype|.
*/
extern JS_PUBLIC_API(JSObject*)
NewReadableDefaultStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr,
HandleFunction size = nullptr, double highWaterMark = 1,
HandleObject proto = nullptr);
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, configured as a byte stream.
* If a |proto| is passed, that gets set as the instance's [[Prototype]]
* instead of the original value of |ReadableStream.prototype|.
*/
extern JS_PUBLIC_API(JSObject*)
NewReadableByteStreamObject(JSContext* cx, HandleObject underlyingSource = nullptr,
double highWaterMark = 0, HandleObject proto = nullptr);
/**
* Returns a new instance of the ReadableStream builtin class in the current
* compartment, with the right slot layout. If a |proto| is passed, that gets
* set as the instance's [[Prototype]] instead of the original value of
* |ReadableStream.prototype|.
*
* The instance is optimized for operating as a byte stream backed by an
* embedding-provided underlying source, using the callbacks set via
* |JS::SetReadableStreamCallbacks|.
*
* The given |flags| will be passed to all applicable callbacks and can be
* used to disambiguate between different types of stream sources the
* embedding might support.
*
* Note: the embedding is responsible for ensuring that the pointer to the
* underlying source stays valid as long as the stream can be read from.
* The underlying source can be freed if the tree is canceled or errored.
* It can also be freed if the stream is destroyed. The embedding is notified
* of that using ReadableStreamFinalizeCallback.
*/
extern JS_PUBLIC_API(JSObject*)
NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
uint8_t flags = 0, HandleObject proto = nullptr);
/**
* Returns the flags that were passed to NewReadableExternalSourceStreamObject
* when creating the given stream.
*
* Asserts that the given stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(uint8_t)
ReadableStreamGetEmbeddingFlags(const JSObject* stream);
/**
* Returns the embedding-provided underlying source of the given |stream|.
*
* Can be used to optimize operations if both the underlying source and the
* intended sink are embedding-provided. In that case it might be
* preferrable to pipe data directly from source to sink without interacting
* with the stream at all.
*
* Locks the stream until ReadableStreamReleaseExternalUnderlyingSource is
* called.
*
* Throws an exception if the stream is locked, i.e. if a reader has been
* acquired for the stream, or if ReadableStreamGetExternalUnderlyingSource
* has been used previously without releasing the external source again.
*
* Throws an exception if the stream isn't readable, i.e if it is errored or
* closed. This is different from ReadableStreamGetReader because we don't
* have a Promise to resolve/reject, which a reader provides.
*
* Asserts that the stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject stream, void** source);
/**
* Releases the embedding-provided underlying source of the given |stream|,
* returning the stream into an unlocked state.
*
* Asserts that the stream was locked through
* ReadableStreamGetExternalUnderlyingSource.
*
* Asserts that the stream has an embedding-provided underlying source.
*/
extern JS_PUBLIC_API(void)
ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream);
/**
* Update the amount of data available at the underlying source of the given
* |stream|.
*
* Can only be used for streams with an embedding-provided underlying source.
* The JS engine will use the given value to satisfy read requests for the
* stream by invoking the JS::WriteIntoReadRequestBuffer callback.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, HandleObject stream,
uint32_t availableData);
/**
* Returns true if the given object is an unwrapped ReadableStream object,
* false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStream(const JSObject* obj);
/**
* Returns true if the given object is an unwrapped
* ReadableStreamDefaultReader or ReadableStreamBYOBReader object,
* false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStreamReader(const JSObject* obj);
/**
* Returns true if the given object is an unwrapped
* ReadableStreamDefaultReader object, false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStreamDefaultReader(const JSObject* obj);
/**
* Returns true if the given object is an unwrapped
* ReadableStreamBYOBReader object, false otherwise.
*/
extern JS_PUBLIC_API(bool)
IsReadableStreamBYOBReader(const JSObject* obj);
enum class ReadableStreamMode {
Default,
Byte,
ExternalSource
};
/**
* Returns the stream's ReadableStreamMode. If the mode is |Byte| or
* |ExternalSource|, it's possible to acquire a BYOB reader for more optimized
* operations.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(ReadableStreamMode)
ReadableStreamGetMode(const JSObject* stream);
enum class ReadableStreamReaderMode {
Default,
BYOB
};
/**
* Returns true if the given ReadableStream is readable, false if not.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamIsReadable(const JSObject* stream);
/**
* Returns true if the given ReadableStream is locked, false if not.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamIsLocked(const JSObject* stream);
/**
* Returns true if the given ReadableStream is disturbed, false if not.
*
* Asserts that |stream| is an ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamIsDisturbed(const JSObject* stream);
/**
* Cancels the given ReadableStream with the given reason and returns a
* Promise resolved according to the result.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamCancel(JSContext* cx, HandleObject stream, HandleValue reason);
/**
* Creates a reader of the type specified by the mode option and locks the
* stream to the new reader.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamGetReader(JSContext* cx, HandleObject stream, ReadableStreamReaderMode mode);
/**
* Tees the given ReadableStream and stores the two resulting streams in
* outparams. Returns false if the operation fails, e.g. because the stream is
* locked.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamTee(JSContext* cx, HandleObject stream,
MutableHandleObject branch1Stream, MutableHandleObject branch2Stream);
/**
* Retrieves the desired combined size of additional chunks to fill the given
* ReadableStream's queue. Stores the result in |value| and sets |hasValue| to
* true on success, returns false on failure.
*
* If the stream is errored, the call will succeed but no value will be stored
* in |value| and |hasValue| will be set to false.
*
* Note: This is semantically equivalent to the |desiredSize| getter on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(void)
ReadableStreamGetDesiredSize(JSObject* stream, bool* hasValue, double* value);
/**
* Closes the given ReadableStream.
*
* Throws a TypeError and returns false if the closing operation fails.
*
* Note: This is semantically equivalent to the |close| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamClose(JSContext* cx, HandleObject stream);
/**
* Returns true if the given ReadableStream reader is locked, false otherwise.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
* ReadableStreamBYOBReader instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamReaderIsClosed(const JSObject* reader);
/**
* Enqueues the given chunk in the given ReadableStream.
*
* Throws a TypeError and returns false if the enqueing operation fails.
*
* Note: This is semantically equivalent to the |enqueue| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* If the ReadableStream has an underlying byte source, the given chunk must
* be a typed array or a DataView. Consider using
* ReadableByteStreamEnqueueBuffer.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamEnqueue(JSContext* cx, HandleObject stream, HandleValue chunk);
/**
* Enqueues the given buffer as a chunk in the given ReadableStream.
*
* Throws a TypeError and returns false if the enqueing operation fails.
*
* Note: This is semantically equivalent to the |enqueue| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity. Additionally, the JS version only
* takes typed arrays and ArrayBufferView instances as arguments, whereas
* this takes an ArrayBuffer, obviating the need to wrap it into a typed
* array.
*
* Asserts that |stream| is an unwrapped ReadableStream instance and |buffer|
* an unwrapped ArrayBuffer instance.
*/
extern JS_PUBLIC_API(bool)
ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject stream, HandleObject buffer);
/**
* Errors the given ReadableStream, causing all future interactions to fail
* with the given error value.
*
* Throws a TypeError and returns false if the erroring operation fails.
*
* Note: This is semantically equivalent to the |error| method on
* the stream controller's prototype in JS. We expose it with the stream
* itself as a target for simplicity.
*
* Asserts that |stream| is an unwrapped ReadableStream instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamError(JSContext* cx, HandleObject stream, HandleValue error);
/**
* Cancels the given ReadableStream reader's associated stream.
*
* Throws a TypeError and returns false if the given reader isn't active.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
* ReadableStreamBYOBReader instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason);
/**
* Cancels the given ReadableStream reader's associated stream.
*
* Throws a TypeError and returns false if the given reader has pending
* read or readInto (for default or byob readers, respectively) requests.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader or
* ReadableStreamBYOBReader instance.
*/
extern JS_PUBLIC_API(bool)
ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
/**
* Requests a read from the reader's associated ReadableStream and returns the
* resulting PromiseObject.
*
* Returns a Promise that's resolved with the read result once available or
* rejected immediately if the stream is errored or the operation failed.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject reader);
/**
* Requests a read from the reader's associated ReadableStream into the given
* ArrayBufferView and returns the resulting PromiseObject.
*
* Returns a Promise that's resolved with the read result once available or
* rejected immediately if the stream is errored or the operation failed.
*
* Asserts that |reader| is an unwrapped ReadableStreamDefaultReader and
* |view| an unwrapped typed array or DataView instance.
*/
extern JS_PUBLIC_API(JSObject*)
ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject reader, HandleObject view);
} // namespace JS
#endif // js_Realm_h
+12 -8
View File
@@ -725,12 +725,6 @@ ModuleObject::namespace_()
return &value.toObject().as<ModuleNamespaceObject>();
}
ScriptSourceObject*
ModuleObject::scriptSourceObject() const
{
return &getReservedSlot(ScriptSourceObjectSlot).toObject().as<ScriptSourceObject>();
}
FunctionDeclarationVector*
ModuleObject::functionDeclarations()
{
@@ -744,10 +738,8 @@ ModuleObject::functionDeclarations()
void
ModuleObject::init(HandleScript script)
{
MOZ_ASSERT(script);
initReservedSlot(ScriptSlot, PrivateValue(script));
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
initReservedSlot(ScriptSourceObjectSlot, ObjectValue(script->scriptSourceUnwrap()));
}
void
@@ -876,6 +868,18 @@ ModuleObject::evaluationError() const
return getReservedSlot(EvaluationErrorSlot);
}
Value
ModuleObject::hostDefinedField() const
{
return getReservedSlot(HostDefinedSlot);
}
void
ModuleObject::setHostDefinedField(const JS::Value& value)
{
setReservedSlot(HostDefinedSlot, value);
}
Scope*
ModuleObject::enclosingScope() const
{
+4 -2
View File
@@ -219,7 +219,7 @@ class ModuleObject : public NativeObject
NamespaceSlot,
StatusSlot,
EvaluationErrorSlot,
ScriptSourceObjectSlot,
HostDefinedSlot,
RequestedModulesSlot,
ImportEntriesSlot,
LocalExportEntriesSlot,
@@ -271,7 +271,7 @@ class ModuleObject : public NativeObject
ModuleStatus status() const;
bool hadEvaluationError() const;
Value evaluationError() const;
ScriptSourceObject* scriptSourceObject() const;
Value hostDefinedField() const;
ArrayObject& requestedModules() const;
ArrayObject& importEntries() const;
ArrayObject& localExportEntries() const;
@@ -284,6 +284,8 @@ class ModuleObject : public NativeObject
static bool Instantiate(JSContext* cx, HandleModuleObject self);
static bool Evaluate(JSContext* cx, HandleModuleObject self);
void setHostDefinedField(const JS::Value& value);
// For BytecodeEmitter.
bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun);
File diff suppressed because it is too large. Load diff
+170
View File
@@ -0,0 +1,170 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 builtin_Stream_h
#define builtin_Stream_h
#include "builtin/Promise.h"
#include "vm/NativeObject.h"
namespace js {
class AutoSetNewObjectMetadata;
class ReadableStream : public NativeObject
{
public:
static ReadableStream* createDefaultStream(JSContext* cx, HandleValue underlyingSource,
HandleValue size, HandleValue highWaterMark,
HandleObject proto = nullptr);
static ReadableStream* createByteStream(JSContext* cx, HandleValue underlyingSource,
HandleValue highWaterMark,
HandleObject proto = nullptr);
static ReadableStream* createExternalSourceStream(JSContext* cx, void* underlyingSource,
uint8_t flags, HandleObject proto = nullptr);
bool readable() const;
bool closed() const;
bool errored() const;
bool disturbed() const;
bool locked() const;
void desiredSize(bool* hasSize, double* size) const;
JS::ReadableStreamMode mode() const;
static MOZ_MUST_USE bool close(JSContext* cx, Handle<ReadableStream*> stream);
static MOZ_MUST_USE JSObject* cancel(JSContext* cx, Handle<ReadableStream*> stream,
HandleValue reason);
static MOZ_MUST_USE bool error(JSContext* cx, Handle<ReadableStream*> stream,
HandleValue error);
static MOZ_MUST_USE NativeObject* getReader(JSContext* cx, Handle<ReadableStream*> stream,
JS::ReadableStreamReaderMode mode);
static MOZ_MUST_USE bool tee(JSContext* cx,
Handle<ReadableStream*> stream, bool cloneForBranch2,
MutableHandle<ReadableStream*> branch1Stream,
MutableHandle<ReadableStream*> branch2Stream);
static MOZ_MUST_USE bool enqueue(JSContext* cx, Handle<ReadableStream*> stream,
HandleValue chunk);
static MOZ_MUST_USE bool enqueueBuffer(JSContext* cx, Handle<ReadableStream*> stream,
Handle<ArrayBufferObject*> chunk);
static MOZ_MUST_USE bool getExternalSource(JSContext* cx, Handle<ReadableStream*> stream,
void** source);
void releaseExternalSource();
uint8_t embeddingFlags() const;
static MOZ_MUST_USE bool updateDataAvailableFromSource(JSContext* cx,
Handle<ReadableStream*> stream,
uint32_t availableData);
enum State {
Readable = 1 << 0,
Closed = 1 << 1,
Errored = 1 << 2,
Disturbed = 1 << 3
};
private:
static MOZ_MUST_USE ReadableStream* createStream(JSContext* cx, HandleObject proto = nullptr);
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamDefaultReader : public NativeObject
{
public:
static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle<ReadableStreamDefaultReader*> reader);
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamBYOBReader : public NativeObject
{
public:
static MOZ_MUST_USE JSObject* read(JSContext* cx, Handle<ReadableStreamBYOBReader*> reader,
Handle<ArrayBufferViewObject*> view);
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
bool ReadableStreamReaderIsClosed(const JSObject* reader);
MOZ_MUST_USE bool ReadableStreamReaderCancel(JSContext* cx, HandleObject reader,
HandleValue reason);
MOZ_MUST_USE bool ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader);
class ReadableStreamDefaultController : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableByteStreamController : public NativeObject
{
public:
bool hasExternalSource();
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ReadableStreamBYOBRequest : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class ByteLengthQueuingStrategy : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
class CountQueuingStrategy : public NativeObject
{
public:
static bool constructor(JSContext* cx, unsigned argc, Value* vp);
static const ClassSpec classSpec_;
static const Class class_;
static const ClassSpec protoClassSpec_;
static const Class protoClass_;
};
} // namespace js
#endif /* builtin_Stream_h */
+12
View File
@@ -1435,6 +1435,14 @@ RejectPromise(JSContext* cx, unsigned argc, Value* vp)
return result;
}
static bool
StreamsAreEnabled(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(cx->options().streams());
return true;
}
static unsigned finalizeCount = 0;
static void
@@ -4172,6 +4180,10 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0,
"rejectPromise(promise, reason)",
" Reject a Promise by calling the JSAPI function JS::RejectPromise."),
JS_FN_HELP("streamsAreEnabled", StreamsAreEnabled, 0, 0,
"streamsAreEnabled()",
" Returns a boolean indicating whether WHATWG Streams are enabled for the current compartment."),
JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
"makeFinalizeObserver()",
" Get a special object whose finalization increases the counter returned\n"
+2 -4
View File
@@ -2,8 +2,6 @@
* 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 "TypedObjectConstants.h"
function ViewedArrayBufferIfReified(tarray) {
assert(IsTypedArray(tarray), "non-typed array asked for its buffer");
@@ -1633,7 +1631,7 @@ function ArrayBufferSlice(start, end) {
ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
// Steps 19-21.
ArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped);
ArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
// Step 22.
return new_;
@@ -1715,7 +1713,7 @@ function SharedArrayBufferSlice(start, end) {
ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen);
// Steps 16-18.
SharedArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped);
SharedArrayBufferCopyData(new_, 0, O, first | 0, newLen | 0, isWrapped);
// Step 19.
return new_;
+1
View File
@@ -23,6 +23,7 @@
*/
#include "SelfHostingDefines.h"
#include "TypedObjectConstants.h"
// Assertions and debug printing, defined here instead of in the header above
// to make `assert` invisible to C++.
+1
View File
@@ -24,6 +24,7 @@ SOURCES += [
'ReflectParse.cpp',
'RegExp.cpp',
'SIMD.cpp',
'Stream.cpp',
'SymbolObject.cpp',
'TestingFunctions.cpp',
'TypedObject.cpp',
-18
View File
@@ -28,7 +28,6 @@
#include "js/HeapAPI.h"
#include "js/RootingAPI.h"
#include "js/TracingAPI.h"
#include "js/TraceKind.h"
struct JSRuntime;
@@ -273,23 +272,6 @@ struct Cell
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
template <class T>
inline bool is() const {
return getTraceKind() == JS::MapTypeToTraceKind<T>::kind;
}
template<class T>
inline T* as() {
MOZ_ASSERT(this->is<T>());
return static_cast<T*>(this);
}
template <class T>
inline const T* as() const {
MOZ_ASSERT(this->is<T>());
return static_cast<const T*>(this);
}
#ifdef DEBUG
inline bool isAligned() const;
void dump(FILE* fp) const;
+17 -17
View File
@@ -11,7 +11,7 @@ FINAL_LIBRARY = "js"
# Includes should be relative to parent path
LOCAL_INCLUDES += ["!..", ".."]
UNIFIED_SOURCES += [
SOURCES += [
'AliasAnalysis.cpp',
'AliasAnalysisShared.cpp',
'AlignmentMaskAnalysis.cpp',
@@ -83,14 +83,14 @@ UNIFIED_SOURCES += [
]
if CONFIG['MOZ_DEBUG']:
UNIFIED_SOURCES += ['Disassembler.cpp']
SOURCES += ['Disassembler.cpp']
if not CONFIG['ENABLE_ION']:
UNIFIED_SOURCES += [
SOURCES += [
'none/Trampoline-none.cpp'
]
elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
UNIFIED_SOURCES += [
SOURCES += [
'x86-shared/Architecture-x86-shared.cpp',
'x86-shared/Assembler-x86-shared.cpp',
'x86-shared/AssemblerBuffer-x86-shared.cpp',
@@ -101,11 +101,11 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
'x86-shared/MacroAssembler-x86-shared.cpp',
'x86-shared/MoveEmitter-x86-shared.cpp',
]
UNIFIED_SOURCES += [
SOURCES += [
'x86-shared/Disassembler-x86-shared.cpp', # using namespace js::jit::X86Encoding;
]
if CONFIG['JS_CODEGEN_X64']:
UNIFIED_SOURCES += [
SOURCES += [
'x64/Assembler-x64.cpp',
'x64/Bailouts-x64.cpp',
'x64/BaselineCompiler-x64.cpp',
@@ -117,7 +117,7 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
'x64/Trampoline-x64.cpp',
]
else:
UNIFIED_SOURCES += [
SOURCES += [
'x86/Assembler-x86.cpp',
'x86/Bailouts-x86.cpp',
'x86/BaselineCompiler-x86.cpp',
@@ -129,7 +129,7 @@ elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
'x86/Trampoline-x86.cpp',
]
elif CONFIG['JS_CODEGEN_ARM']:
UNIFIED_SOURCES += [
SOURCES += [
'arm/Architecture-arm.cpp',
'arm/Assembler-arm.cpp',
'arm/Bailouts-arm.cpp',
@@ -145,16 +145,16 @@ elif CONFIG['JS_CODEGEN_ARM']:
'arm/Trampoline-arm.cpp',
]
if CONFIG['JS_SIMULATOR_ARM']:
UNIFIED_SOURCES += [
SOURCES += [
'arm/Simulator-arm.cpp'
]
elif CONFIG['OS_ARCH'] == 'Darwin':
UNIFIED_SOURCES += [
SOURCES += [
'arm/llvm-compiler-rt/arm/aeabi_idivmod.S',
'arm/llvm-compiler-rt/arm/aeabi_uidivmod.S',
]
elif CONFIG['JS_CODEGEN_ARM64']:
UNIFIED_SOURCES += [
SOURCES += [
'arm64/Architecture-arm64.cpp',
'arm64/Assembler-arm64.cpp',
'arm64/Bailouts-arm64.cpp',
@@ -177,14 +177,14 @@ elif CONFIG['JS_CODEGEN_ARM64']:
'arm64/vixl/Utils-vixl.cpp'
]
if CONFIG['JS_SIMULATOR_ARM64']:
UNIFIED_SOURCES += [
SOURCES += [
'arm64/vixl/Debugger-vixl.cpp',
'arm64/vixl/Logic-vixl.cpp',
'arm64/vixl/MozSimulator-vixl.cpp',
'arm64/vixl/Simulator-vixl.cpp'
]
elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
UNIFIED_SOURCES += [
SOURCES += [
'mips-shared/Architecture-mips-shared.cpp',
'mips-shared/Assembler-mips-shared.cpp',
'mips-shared/Bailouts-mips-shared.cpp',
@@ -196,7 +196,7 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'mips-shared/MoveEmitter-mips-shared.cpp',
]
if CONFIG['JS_CODEGEN_MIPS32']:
UNIFIED_SOURCES += [
SOURCES += [
'mips32/Architecture-mips32.cpp',
'mips32/Assembler-mips32.cpp',
'mips32/Bailouts-mips32.cpp',
@@ -210,11 +210,11 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'mips32/Trampoline-mips32.cpp',
]
if CONFIG['JS_SIMULATOR_MIPS32']:
UNIFIED_SOURCES += [
SOURCES += [
'mips32/Simulator-mips32.cpp'
]
elif CONFIG['JS_CODEGEN_MIPS64']:
UNIFIED_SOURCES += [
SOURCES += [
'mips64/Architecture-mips64.cpp',
'mips64/Assembler-mips64.cpp',
'mips64/Bailouts-mips64.cpp',
@@ -228,6 +228,6 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
'mips64/Trampoline-mips64.cpp',
]
if CONFIG['JS_SIMULATOR_MIPS64']:
UNIFIED_SOURCES += [
SOURCES += [
'mips64/Simulator-mips64.cpp'
]
-2
View File
@@ -3,8 +3,6 @@
# 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/.
FILES_PER_UNIFIED_FILE = 6
# Also set in shell/moz.build
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True
+29 -1
View File
@@ -593,7 +593,35 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i
MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
// Async Iteration
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'")
MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
// ReadableStream
MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG,0, JSEXN_RANGEERR,"'underlyingSource.type' must be \"bytes\" or undefined.")
MSG_DEF(JSMSG_READABLESTREAM_INVALID_READER_MODE, 0, JSEXN_RANGEERR,"'mode' must be \"byob\" or undefined.")
MSG_DEF(JSMSG_NUMBER_MUST_BE_FINITE_NON_NEGATIVE, 1, JSEXN_RANGEERR, "'{0}' must be a finite, non-negative number.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_BYTESWRITTEN, 0, JSEXN_RANGEERR, "'bytesWritten' exceeds remaining length.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_SIZE, 0, JSEXN_RANGEERR, "view size does not match requested data.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_INVALID_VIEW_OFFSET, 0, JSEXN_RANGEERR, "view offset does not match requested position.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_LOCKED, 1, JSEXN_TYPEERR, "'{0}' may only be called on a locked stream.")
MSG_DEF(JSMSG_READABLESTREAM_LOCKED, 0, JSEXN_TYPEERR, "A Reader may only be created for an unlocked ReadableStream.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableByteStreamController.")
MSG_DEF(JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER, 1, JSEXN_TYPEERR, "{0} requires a ReadableStreamDefaultController.")
MSG_DEF(JSMSG_READABLESTREAM_CONTROLLER_SET, 0, JSEXN_TYPEERR, "The ReadableStream already has a controller defined.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_OWNED, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may only be called on a reader owned by a stream.")
MSG_DEF(JSMSG_READABLESTREAMREADER_NOT_EMPTY, 1, JSEXN_TYPEERR, "The ReadableStream reader method '{0}' may not be called on a reader with read requests.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREADER_READ_EMPTY_VIEW, 0, JSEXN_TYPEERR, "ReadableStreamBYOBReader.read() was passed an empty TypedArrayBuffer view.")
MSG_DEF(JSMSG_READABLESTREAMREADER_RELEASED, 0, JSEXN_TYPEERR, "The ReadableStream reader was released.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_CLOSED, 1, JSEXN_TYPEERR, "'{0}' called on a stream already closing.")
MSG_DEF(JSMSG_READABLESTREAMCONTROLLER_NOT_READABLE, 1, JSEXN_TYPEERR, "'{0}' may only be called on a stream in the 'readable' state.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNKSIZE,0, JSEXN_RANGEERR, "ReadableByteStreamController requires a positive integer or undefined for 'autoAllocateChunkSize'.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK, 1, JSEXN_TYPEERR, "{0} passed a bad chunk.")
MSG_DEF(JSMSG_READABLEBYTESTREAMCONTROLLER_CLOSE_PENDING_PULL, 0, JSEXN_TYPEERR, "The ReadableByteStreamController cannot be closed while the buffer is being filled.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_NO_CONTROLLER, 1, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method '{0}' called on a request with no controller.")
MSG_DEF(JSMSG_READABLESTREAMBYOBREQUEST_RESPOND_CLOSED, 0, JSEXN_TYPEERR, "ReadableStreamBYOBRequest method 'respond' called with non-zero number of bytes with a closed controller.")
MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "ReadableStream method {0} not yet implemented")
// Other Stream-related
MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.")
+5
View File
@@ -126,6 +126,11 @@ if CONFIG['ENABLE_ION']:
'testJitRValueAlloc.cpp',
]
if CONFIG['ENABLE_STREAMS']:
UNIFIED_SOURCES += [
'testReadableStream.cpp',
]
DEFINES['EXPORT_JS_API'] = True
LOCAL_INCLUDES += [
+1
View File
@@ -29,6 +29,7 @@
#include "js/RequiredDefines.h"
#include "js/RootingAPI.h"
#include "js/SliceBudget.h"
#include "js/Stream.h"
#include "js/StructuredClone.h"
#include "js/TracingAPI.h"
#include "js/TrackedOptimizationInfo.h"
+676
View File
@@ -0,0 +1,676 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "jsapi.h"
#include "jsapi-tests/tests.h"
using namespace JS;
char test_buffer_data[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
static JSObject*
NewDefaultStream(JSContext* cx, HandleObject source = nullptr, HandleFunction size = nullptr,
double highWaterMark = 1, HandleObject proto = nullptr)
{
RootedObject stream(cx, NewReadableDefaultStreamObject(cx, source, size, highWaterMark,
proto));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
static JSObject*
NewByteStream(JSContext* cx, double highWaterMark = 0, HandleObject proto = nullptr)
{
RootedObject source(cx, JS_NewPlainObject(cx));
MOZ_ASSERT(source);
RootedObject stream(cx, NewReadableByteStreamObject(cx, source, highWaterMark, proto));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
static bool dataRequestCBCalled = false;
static void
DataRequestCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
size_t desiredSize)
{
MOZ_ASSERT(!dataRequestCBCalled, "Invalid test setup");
dataRequestCBCalled = true;
}
static bool writeIntoRequestBufferCBCalled = false;
static void
WriteIntoRequestBufferCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
void* buffer, size_t length, size_t* bytesWritten)
{
MOZ_ASSERT(!writeIntoRequestBufferCBCalled, "Invalid test setup");
MOZ_ASSERT(length <= sizeof(test_buffer_data));
memcpy(buffer, test_buffer_data, length);
writeIntoRequestBufferCBCalled = true;
*bytesWritten = length;
}
static bool cancelStreamCBCalled = false;
static Value cancelStreamReason;
static Value
CancelStreamCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
HandleValue reason)
{
MOZ_ASSERT(!cancelStreamCBCalled, "Invalid test setup");
cancelStreamCBCalled = true;
cancelStreamReason = reason;
return reason;
}
static bool streamClosedCBCalled = false;
static Value streamClosedReason;
static void
StreamClosedCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags)
{
MOZ_ASSERT(!streamClosedCBCalled, "Invalid test setup");
streamClosedCBCalled = true;
}
static bool streamErroredCBCalled = false;
static Value streamErroredReason;
static void
StreamErroredCB(JSContext* cx, HandleObject stream, void* underlyingSource, uint8_t flags,
HandleValue reason)
{
MOZ_ASSERT(!streamErroredCBCalled, "Invalid test setup");
streamErroredCBCalled = true;
streamErroredReason = reason;
}
static bool finalizeStreamCBCalled = false;
static void* finalizedStreamUnderlyingSource;
static void
FinalizeStreamCB(void* underlyingSource, uint8_t flags)
{
MOZ_ASSERT(!finalizeStreamCBCalled, "Invalid test setup");
finalizeStreamCBCalled = true;
finalizedStreamUnderlyingSource = underlyingSource;
}
static void
ResetCallbacks()
{
dataRequestCBCalled = false;
writeIntoRequestBufferCBCalled = false;
cancelStreamReason = UndefinedValue();
cancelStreamCBCalled = false;
streamClosedCBCalled = false;
streamErroredCBCalled = false;
finalizeStreamCBCalled = false;
}
static bool
GetIterResult(JSContext* cx, HandleObject promise, MutableHandleValue value, bool* done)
{
RootedObject iterResult(cx, &GetPromiseResult(promise).toObject());
bool found;
if (!JS_HasProperty(cx, iterResult, "value", &found))
return false;
MOZ_ASSERT(found);
if (!JS_HasProperty(cx, iterResult, "done", &found))
return false;
MOZ_ASSERT(found);
RootedValue doneVal(cx);
if (!JS_GetProperty(cx, iterResult, "value", value))
return false;
if (!JS_GetProperty(cx, iterResult, "done", &doneVal))
return false;
*done = doneVal.toBoolean();
MOZ_ASSERT_IF(*done, value.isUndefined());
return true;
}
static JSObject*
GetReadChunk(JSContext* cx, HandleObject readRequest)
{
MOZ_ASSERT(GetPromiseState(readRequest) == PromiseState::Fulfilled);
RootedValue resultVal(cx, GetPromiseResult(readRequest));
MOZ_ASSERT(resultVal.isObject());
RootedObject result(cx, &resultVal.toObject());
RootedValue chunkVal(cx);
JS_GetProperty(cx, result, "value", &chunkVal);
return &chunkVal.toObject();
}
BEGIN_TEST(testReadableStream_NewReadableStream)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Default);
return true;
}
END_TEST(testReadableStream_NewReadableStream)
BEGIN_TEST(testReadableStream_NewReadableByteStream)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
CHECK(ReadableStreamGetMode(stream) == ReadableStreamMode::Byte);
return true;
}
END_TEST(testReadableStream_NewReadableByteStream)
BEGIN_TEST(testReadableStream_ReadableStreamGetReaderDefault)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
CHECK(IsReadableStreamDefaultReader(reader));
CHECK(ReadableStreamIsLocked(stream));
CHECK(!ReadableStreamReaderIsClosed(reader));
return true;
}
END_TEST(testReadableStream_ReadableStreamGetReaderDefault)
BEGIN_TEST(testReadableStream_ReadableStreamGetReaderBYOB)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB));
CHECK(reader);
CHECK(IsReadableStreamBYOBReader(reader));
CHECK(ReadableStreamIsLocked(stream));
CHECK(!ReadableStreamReaderIsClosed(reader));
return true;
}
END_TEST(testReadableStream_ReadableStreamGetReaderBYOB)
BEGIN_TEST(testReadableStream_ReadableStreamTee)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject leftStream(cx);
RootedObject rightStream(cx);
CHECK(ReadableStreamTee(cx, stream, &leftStream, &rightStream));
CHECK(ReadableStreamIsLocked(stream));
CHECK(leftStream);
CHECK(IsReadableStream(leftStream));
CHECK(rightStream);
CHECK(IsReadableStream(rightStream));
return true;
}
END_TEST(testReadableStream_ReadableStreamTee)
BEGIN_TEST(testReadableStream_ReadableStreamEnqueue)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject chunk(cx, JS_NewPlainObject(cx));
CHECK(chunk);
RootedValue chunkVal(cx, ObjectValue(*chunk));
CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
return true;
}
END_TEST(testReadableStream_ReadableStreamEnqueue)
BEGIN_TEST(testReadableStream_ReadableByteStreamEnqueue)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject chunk(cx, JS_NewUint8Array(cx, 42));
CHECK(chunk);
CHECK(!ReadableByteStreamEnqueueBuffer(cx, stream, chunk));
CHECK(JS_IsExceptionPending(cx));
return true;
}
END_TEST(testReadableStream_ReadableByteStreamEnqueue)
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderRead)
{
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
RootedObject chunk(cx, JS_NewPlainObject(cx));
CHECK(chunk);
RootedValue chunkVal(cx, ObjectValue(*chunk));
CHECK(ReadableStreamEnqueue(cx, stream, chunkVal));
CHECK(GetReadChunk(cx, request) == chunk);
return true;
}
END_TEST(testReadableStream_ReadableStreamDefaultReaderRead)
BEGIN_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
size_t length = sizeof(test_buffer_data);
RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data));
CHECK(buffer);
RootedObject chunk(cx, JS_NewUint8ArrayWithBuffer(cx, buffer, 0, length));
CHECK(chunk);
bool isShared;
CHECK(!JS_IsDetachedArrayBufferObject(buffer));
CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, chunk));
CHECK(JS_IsDetachedArrayBufferObject(buffer));
RootedObject readChunk(cx, GetReadChunk(cx, request));
CHECK(JS_IsUint8Array(readChunk));
void* readBufferData;
{
JS::AutoCheckCannotGC autoNoGC(cx);
readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC);
}
CHECK(readBufferData);
CHECK(!memcmp(test_buffer_data, readBufferData, length));
return true;
}
END_TEST(testReadableStream_ReadableByteStreamDefaultReaderRead)
BEGIN_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead)
{
RootedObject stream(cx, NewByteStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::BYOB));
CHECK(reader);
size_t length = sizeof(test_buffer_data);
RootedObject targetArray(cx, JS_NewUint8Array(cx, length));
bool isShared;
RootedObject request(cx, ReadableStreamBYOBReaderRead(cx, reader, targetArray));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(JS_IsDetachedArrayBufferObject(JS_GetArrayBufferViewBuffer(cx, targetArray, &isShared)));
RootedObject buffer(cx, JS_NewArrayBufferWithExternalContents(cx, length, test_buffer_data));
CHECK(buffer);
CHECK(!JS_IsDetachedArrayBufferObject(buffer));
CHECK(ReadableByteStreamEnqueueBuffer(cx, stream, buffer));
CHECK(JS_IsDetachedArrayBufferObject(buffer));
RootedObject readChunk(cx, GetReadChunk(cx, request));
CHECK(JS_IsUint8Array(readChunk));
void* readBufferData;
{
JS::AutoCheckCannotGC autoNoGC(cx);
readBufferData = JS_GetArrayBufferViewData(readChunk, &isShared, autoNoGC);
}
CHECK(readBufferData);
CHECK(!memcmp(test_buffer_data, readBufferData, length));
// TODO: eliminate the memcpy that happens here.
// CHECK(readBufferData == test_buffer_data);
return true;
}
END_TEST(testReadableStream_ReadableByteStreamBYOBReaderRead)
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderClose)
{
SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
&CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB);
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(ReadableStreamClose(cx, stream));
bool done;
RootedValue value(cx);
CHECK(GetPromiseState(request) == PromiseState::Fulfilled);
CHECK(GetIterResult(cx, request, &value, &done));
CHECK(value.isUndefined());
CHECK(done);
// The callbacks are only invoked for external streams.
CHECK(!streamClosedCBCalled);
return true;
}
END_TEST(testReadableStream_ReadableStreamDefaultReaderClose)
BEGIN_TEST(testReadableStream_ReadableStreamDefaultReaderError)
{
ResetCallbacks();
SetReadableStreamCallbacks(cx, &DataRequestCB, &WriteIntoRequestBufferCB,
&CancelStreamCB, &StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB);
RootedObject stream(cx, NewDefaultStream(cx));
CHECK(stream);
RootedObject reader(cx, ReadableStreamGetReader(cx, stream, ReadableStreamReaderMode::Default));
CHECK(reader);
RootedObject request(cx, ReadableStreamDefaultReaderRead(cx, reader));
CHECK(request);
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(ReadableStreamIsLocked(stream));
CHECK(ReadableStreamIsReadable(stream));
RootedValue error(cx, Int32Value(42));
CHECK(ReadableStreamError(cx, stream, error));
CHECK(GetPromiseState(request) == PromiseState::Rejected);
RootedValue reason(cx, GetPromiseResult(request));
CHECK(reason.isInt32());
CHECK(reason.toInt32() == 42);
// The callbacks are only invoked for external streams.
CHECK(!streamErroredCBCalled);
return true;
}
END_TEST(testReadableStream_ReadableStreamDefaultReaderError)
static JSObject*
NewExternalSourceStream(JSContext* cx, void* underlyingSource,
RequestReadableStreamDataCallback dataRequestCallback,
WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
CancelReadableStreamCallback cancelCallback,
ReadableStreamClosedCallback closedCallback,
ReadableStreamErroredCallback erroredCallback,
ReadableStreamFinalizeCallback finalizeCallback)
{
SetReadableStreamCallbacks(cx, dataRequestCallback, writeIntoReadRequestCallback,
cancelCallback, closedCallback, erroredCallback,
finalizeCallback);
RootedObject stream(cx, NewReadableExternalSourceStreamObject(cx, underlyingSource));
MOZ_ASSERT_IF(stream, IsReadableStream(stream));
return stream;
}
BEGIN_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
CHECK(ReadableStreamGetMode(stream) == JS::ReadableStreamMode::ExternalSource);
void* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
CHECK(underlyingSource == &test_buffer_data);
CHECK(ReadableStreamIsLocked(stream));
ReadableStreamReleaseExternalUnderlyingSource(stream);
return true;
}
END_TEST(testReadableStream_CreateReadableByteStreamWithExternalSource)
BEGIN_TEST(testReadableStream_ExternalSourceCancel)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
RootedValue reason(cx, Int32Value(42));
CHECK(ReadableStreamCancel(cx, stream, reason));
CHECK(cancelStreamCBCalled);
CHECK(cancelStreamReason == reason);
return true;
}
END_TEST(testReadableStream_ExternalSourceCancel)
BEGIN_TEST(testReadableStream_ExternalSourceGetReader)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL("stream.getReader()", &rval);
CHECK(rval.isObject());
RootedObject reader(cx, &rval.toObject());
CHECK(IsReadableStreamDefaultReader(reader));
return true;
}
END_TEST(testReadableStream_ExternalSourceGetReader)
BEGIN_TEST(testReadableStream_ExternalSourceUpdateAvailableData)
{
ResetCallbacks();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
ReadableStreamUpdateDataAvailableFromSource(cx, stream, 1024);
return true;
}
END_TEST(testReadableStream_ExternalSourceUpdateAvailableData)
struct ReadFromExternalSourceFixture : public JSAPITest
{
virtual ~ReadFromExternalSourceFixture() {}
bool readWithoutDataAvailable(const char* evalSrc, const char* evalSrc2,
uint32_t writtenLength)
{
ResetCallbacks();
definePrint();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB,
&CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
js::RunJobs(cx);
void* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
CHECK(underlyingSource == &test_buffer_data);
CHECK(ReadableStreamIsLocked(stream));
ReadableStreamReleaseExternalUnderlyingSource(stream);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL(evalSrc, &rval);
CHECK(dataRequestCBCalled);
CHECK(!writeIntoRequestBufferCBCalled);
CHECK(rval.isObject());
RootedObject promise(cx, &rval.toObject());
CHECK(IsPromiseObject(promise));
CHECK(GetPromiseState(promise) == PromiseState::Pending);
size_t length = sizeof(test_buffer_data);
ReadableStreamUpdateDataAvailableFromSource(cx, stream, length);
CHECK(writeIntoRequestBufferCBCalled);
CHECK(GetPromiseState(promise) == PromiseState::Fulfilled);
RootedValue iterVal(cx);
bool done;
if (!GetIterResult(cx, promise, &iterVal, &done))
return false;
CHECK(!done);
RootedObject chunk(cx, &iterVal.toObject());
CHECK(JS_IsUint8Array(chunk));
{
JS::AutoCheckCannotGC noGC(cx);
bool dummy;
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
CHECK(!memcmp(buffer, test_buffer_data, writtenLength));
}
dataRequestCBCalled = false;
writeIntoRequestBufferCBCalled = false;
EVAL(evalSrc2, &rval);
CHECK(dataRequestCBCalled);
CHECK(!writeIntoRequestBufferCBCalled);
return true;
}
bool readWithDataAvailable(const char* evalSrc, uint32_t writtenLength) {
ResetCallbacks();
definePrint();
RootedObject stream(cx, NewExternalSourceStream(cx, &test_buffer_data, &DataRequestCB,
&WriteIntoRequestBufferCB, &CancelStreamCB,
&StreamClosedCB, &StreamErroredCB,
&FinalizeStreamCB));
CHECK(stream);
void* underlyingSource;
CHECK(ReadableStreamGetExternalUnderlyingSource(cx, stream, &underlyingSource));
CHECK(underlyingSource == &test_buffer_data);
CHECK(ReadableStreamIsLocked(stream));
ReadableStreamReleaseExternalUnderlyingSource(stream);
size_t length = sizeof(test_buffer_data);
ReadableStreamUpdateDataAvailableFromSource(cx, stream, length);
RootedValue streamVal(cx, ObjectValue(*stream));
CHECK(JS_SetProperty(cx, global, "stream", streamVal));
RootedValue rval(cx);
EVAL(evalSrc, &rval);
CHECK(writeIntoRequestBufferCBCalled);
CHECK(rval.isObject());
RootedObject promise(cx, &rval.toObject());
CHECK(IsPromiseObject(promise));
CHECK(GetPromiseState(promise) == PromiseState::Fulfilled);
RootedValue iterVal(cx);
bool done;
if (!GetIterResult(cx, promise, &iterVal, &done))
return false;
CHECK(!done);
RootedObject chunk(cx, &iterVal.toObject());
CHECK(JS_IsUint8Array(chunk));
{
JS::AutoCheckCannotGC noGC(cx);
bool dummy;
void* buffer = JS_GetArrayBufferViewData(chunk, &dummy, noGC);
CHECK(!memcmp(buffer, test_buffer_data, writtenLength));
}
return true;
}
};
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
{
return readWithoutDataAvailable("r = stream.getReader(); r.read()", "r.read()", sizeof(test_buffer_data));
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithoutDataAvailable)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceCloseWithPendingRead)
{
CHECK(readWithoutDataAvailable("r = stream.getReader(); request0 = r.read(); "
"request1 = r.read(); request0", "r.read()",
sizeof(test_buffer_data)));
RootedValue val(cx);
CHECK(JS_GetProperty(cx, global, "request1", &val));
CHECK(val.isObject());
RootedObject request(cx, &val.toObject());
CHECK(IsPromiseObject(request));
CHECK(GetPromiseState(request) == PromiseState::Pending);
CHECK(JS_GetProperty(cx, global, "stream", &val));
RootedObject stream(cx, &val.toObject());
ReadableStreamClose(cx, stream);
val = GetPromiseResult(request);
MOZ_ASSERT(val.isObject());
RootedObject result(cx, &val.toObject());
JS_GetProperty(cx, result, "done", &val);
CHECK(val.isBoolean());
CHECK(val.toBoolean() == true);
JS_GetProperty(cx, result, "value", &val);
CHECK(val.isUndefined());
return true;
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceCloseWithPendingRead)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
{
return readWithDataAvailable("r = stream.getReader(); r.read()", sizeof(test_buffer_data));
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadDefaultWithDataAvailable)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable)
{
return readWithoutDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(63))", "r.read(new Uint8Array(10))", 10);
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithoutDataAvailable)
BEGIN_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithDataAvailable)
{
return readWithDataAvailable("r = stream.getReader({mode: 'byob'}); r.read(new Uint8Array(10))", 10);
}
END_FIXTURE_TEST(ReadFromExternalSourceFixture,
testReadableStream_ExternalSourceReadBYOBWithDataAvailable)
+4
View File
@@ -81,6 +81,10 @@ JSObject* JSAPITest::createGlobal(JSPrincipals* principals)
/* Create the global object. */
JS::RootedObject newGlobal(cx);
JS::CompartmentOptions options;
#ifdef ENABLE_STREAMS
options.creationOptions().setStreamsEnabled(true);
#endif
printf("enabled\n");
options.behaviors().setVersion(JSVERSION_LATEST);
newGlobal = JS_NewGlobalObject(cx, getGlobalClass(), principals, JS::FireOnNewGlobalHook,
options);
+368 -18
View File
@@ -47,6 +47,7 @@
#include "builtin/MapObject.h"
#include "builtin/Promise.h"
#include "builtin/RegExp.h"
#include "builtin/Stream.h"
#include "builtin/SymbolObject.h"
#ifdef ENABLE_SIMD
# include "builtin/SIMD.h"
@@ -4691,27 +4692,15 @@ JS::CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
}
JS_PUBLIC_API(void)
JS::SetModulePrivate(JSObject* module, const JS::Value& value)
JS::SetModuleHostDefinedField(JSObject* module, const JS::Value& value)
{
module->as<ModuleObject>().scriptSourceObject()->setPrivate(value);
module->as<ModuleObject>().setHostDefinedField(value);
}
JS_PUBLIC_API(JS::Value)
JS::GetModulePrivate(JSObject* module)
JS::GetModuleHostDefinedField(JSObject* module)
{
return module->as<ModuleObject>().scriptSourceObject()->getPrivate();
}
JS_PUBLIC_API(void)
JS::SetScriptPrivate(JSScript* script, const JS::Value& value)
{
script->scriptSourceUnwrap().setPrivate(value);
}
JS_PUBLIC_API(JS::Value)
JS::GetScriptPrivate(JSScript* script)
{
return script->scriptSourceUnwrap().getPrivate();
return module->as<ModuleObject>().hostDefinedField();
}
JS_PUBLIC_API(bool)
@@ -5028,7 +5017,6 @@ CallOriginalPromiseThenImpl(JSContext* cx, JS::HandleObject promiseObj,
return false;
}
return true;
}
JS_PUBLIC_API(JSObject*)
@@ -5055,7 +5043,7 @@ JS::AddPromiseReactions(JSContext* cx, JS::HandleObject promiseObj,
* Unforgeable version of Promise.all for internal use.
*
* Takes a dense array of Promise objects and returns a promise that's
* resolved with an array of resolution values when all those promises ahve
* resolved with an array of resolution values when all those promises have
* been resolved, or rejected with the rejection value of the first rejected
* promise.
*
@@ -5070,6 +5058,368 @@ JS::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
return js::GetWaitForAllPromise(cx, promises);
}
JS_PUBLIC_API(JSObject*)
JS::NewReadableDefaultStreamObject(JSContext* cx,
JS::HandleObject underlyingSource /* = nullptr */,
JS::HandleFunction size /* = nullptr */,
double highWaterMark /* = 1 */,
JS::HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedObject source(cx, underlyingSource);
if (!source) {
source = NewBuiltinClassInstance<PlainObject>(cx);
if (!source)
return nullptr;
}
RootedValue sourceVal(cx, ObjectValue(*source));
RootedValue sizeVal(cx, size ? ObjectValue(*size) : UndefinedValue());
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
return ReadableStream::createDefaultStream(cx, sourceVal, sizeVal, highWaterMarkVal, proto);
}
JS_PUBLIC_API(JSObject*)
JS::NewReadableByteStreamObject(JSContext* cx,
JS::HandleObject underlyingSource /* = nullptr */,
double highWaterMark /* = 1 */,
JS::HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
RootedObject source(cx, underlyingSource);
if (!source) {
source = NewBuiltinClassInstance<PlainObject>(cx);
if (!source)
return nullptr;
}
RootedValue sourceVal(cx, ObjectValue(*source));
RootedValue highWaterMarkVal(cx, NumberValue(highWaterMark));
return ReadableStream::createByteStream(cx, sourceVal, highWaterMarkVal, proto);
}
extern JS_PUBLIC_API(void)
JS::SetReadableStreamCallbacks(JSContext* cx,
JS::RequestReadableStreamDataCallback dataRequestCallback,
JS::WriteIntoReadRequestBufferCallback writeIntoReadRequestCallback,
JS::CancelReadableStreamCallback cancelCallback,
JS::ReadableStreamClosedCallback closedCallback,
JS::ReadableStreamErroredCallback erroredCallback,
JS::ReadableStreamFinalizeCallback finalizeCallback)
{
MOZ_ASSERT(dataRequestCallback);
MOZ_ASSERT(writeIntoReadRequestCallback);
MOZ_ASSERT(cancelCallback);
MOZ_ASSERT(closedCallback);
MOZ_ASSERT(erroredCallback);
MOZ_ASSERT(finalizeCallback);
JSRuntime* rt = cx->runtime();
MOZ_ASSERT(!rt->readableStreamDataRequestCallback);
MOZ_ASSERT(!rt->readableStreamWriteIntoReadRequestCallback);
MOZ_ASSERT(!rt->readableStreamCancelCallback);
MOZ_ASSERT(!rt->readableStreamClosedCallback);
MOZ_ASSERT(!rt->readableStreamErroredCallback);
MOZ_ASSERT(!rt->readableStreamFinalizeCallback);
rt->readableStreamDataRequestCallback = dataRequestCallback;
rt->readableStreamWriteIntoReadRequestCallback = writeIntoReadRequestCallback;
rt->readableStreamCancelCallback = cancelCallback;
rt->readableStreamClosedCallback = closedCallback;
rt->readableStreamErroredCallback = erroredCallback;
rt->readableStreamFinalizeCallback = finalizeCallback;
}
JS_PUBLIC_API(bool)
JS::HasReadableStreamCallbacks(JSContext* cx)
{
return cx->runtime()->readableStreamDataRequestCallback;
}
JS_PUBLIC_API(JSObject*)
JS::NewReadableExternalSourceStreamObject(JSContext* cx, void* underlyingSource,
uint8_t flags /* = 0 */,
HandleObject proto /* = nullptr */)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
#ifdef DEBUG
JSRuntime* rt = cx->runtime();
MOZ_ASSERT(rt->readableStreamDataRequestCallback);
MOZ_ASSERT(rt->readableStreamWriteIntoReadRequestCallback);
MOZ_ASSERT(rt->readableStreamCancelCallback);
MOZ_ASSERT(rt->readableStreamClosedCallback);
MOZ_ASSERT(rt->readableStreamErroredCallback);
MOZ_ASSERT(rt->readableStreamFinalizeCallback);
#endif // DEBUG
return ReadableStream::createExternalSourceStream(cx, underlyingSource, flags, proto);
}
JS_PUBLIC_API(uint8_t)
JS::ReadableStreamGetEmbeddingFlags(const JSObject* stream)
{
return stream->as<ReadableStream>().embeddingFlags();
}
JS_PUBLIC_API(bool)
JS::IsReadableStream(const JSObject* obj)
{
return obj->is<ReadableStream>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamReader(const JSObject* obj)
{
return obj->is<ReadableStreamDefaultReader>() || obj->is<ReadableStreamBYOBReader>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamDefaultReader(const JSObject* obj)
{
return obj->is<ReadableStreamDefaultReader>();
}
JS_PUBLIC_API(bool)
JS::IsReadableStreamBYOBReader(const JSObject* obj)
{
return obj->is<ReadableStreamBYOBReader>();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamIsReadable(const JSObject* stream)
{
return stream->as<ReadableStream>().readable();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamIsLocked(const JSObject* stream)
{
return stream->as<ReadableStream>().locked();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamIsDisturbed(const JSObject* stream)
{
return stream->as<ReadableStream>().disturbed();
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamCancel(JSContext* cx, HandleObject streamObj, HandleValue reason)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, reason);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::cancel(cx, stream, reason);
}
JS_PUBLIC_API(JS::ReadableStreamMode)
JS::ReadableStreamGetMode(const JSObject* stream)
{
return stream->as<ReadableStream>().mode();
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamGetReader(JSContext* cx, HandleObject streamObj, ReadableStreamReaderMode mode)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::getReader(cx, stream, mode);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamGetExternalUnderlyingSource(JSContext* cx, HandleObject streamObj, void** source)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::getExternalSource(cx, stream, source);
}
JS_PUBLIC_API(void)
JS::ReadableStreamReleaseExternalUnderlyingSource(JSObject* stream)
{
stream->as<ReadableStream>().releaseExternalSource();
}
JS_PUBLIC_API(bool)
JS::ReadableStreamUpdateDataAvailableFromSource(JSContext* cx, JS::HandleObject streamObj,
uint32_t availableData)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::updateDataAvailableFromSource(cx, stream, availableData);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamTee(JSContext* cx, HandleObject streamObj,
MutableHandleObject branch1Obj, MutableHandleObject branch2Obj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
Rooted<ReadableStream*> branch1Stream(cx);
Rooted<ReadableStream*> branch2Stream(cx);
if (!ReadableStream::tee(cx, stream, false, &branch1Stream, &branch2Stream))
return false;
branch1Obj.set(branch1Stream);
branch2Obj.set(branch2Stream);
return true;
}
JS_PUBLIC_API(void)
JS::ReadableStreamGetDesiredSize(JSObject* streamObj, bool* hasValue, double* value)
{
streamObj->as<ReadableStream>().desiredSize(hasValue, value);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamClose(JSContext* cx, HandleObject streamObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return ReadableStream::close(cx, stream);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamEnqueue(JSContext* cx, HandleObject streamObj, HandleValue chunk)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, chunk);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
if (stream->mode() != JS::ReadableStreamMode::Default) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAM_NOT_DEFAULT_CONTROLLER,
"JS::ReadableStreamEnqueue");
return false;
}
return ReadableStream::enqueue(cx, stream, chunk);
}
JS_PUBLIC_API(bool)
JS::ReadableByteStreamEnqueueBuffer(JSContext* cx, HandleObject streamObj, HandleObject chunkObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, chunkObj);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
if (stream->mode() != JS::ReadableStreamMode::Byte) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLESTREAM_NOT_BYTE_STREAM_CONTROLLER,
"JS::ReadableByteStreamEnqueueBuffer");
return false;
}
Rooted<ArrayBufferObject*> buffer(cx);
if (chunkObj->is<ArrayBufferViewObject>()) {
bool dummy;
buffer = &JS_GetArrayBufferViewBuffer(cx, chunkObj, &dummy)->as<ArrayBufferObject>();
} else if (chunkObj->is<ArrayBufferObject>()) {
buffer = &chunkObj->as<ArrayBufferObject>();
} else {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_READABLEBYTESTREAMCONTROLLER_BAD_CHUNK,
"JS::ReadableByteStreamEnqueueBuffer");
return false;
}
return ReadableStream::enqueueBuffer(cx, stream, buffer);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamError(JSContext* cx, HandleObject streamObj, HandleValue error)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, streamObj);
assertSameCompartment(cx, error);
Rooted<ReadableStream*> stream(cx, &streamObj->as<ReadableStream>());
return js::ReadableStream::error(cx, stream, error);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReaderIsClosed(const JSObject* reader)
{
return js::ReadableStreamReaderIsClosed(reader);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReaderCancel(JSContext* cx, HandleObject reader, HandleValue reason)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, reader);
assertSameCompartment(cx, reason);
return js::ReadableStreamReaderCancel(cx, reader, reason);
}
JS_PUBLIC_API(bool)
JS::ReadableStreamReaderReleaseLock(JSContext* cx, HandleObject reader)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, reader);
return js::ReadableStreamReaderReleaseLock(cx, reader);
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamDefaultReaderRead(JSContext* cx, HandleObject readerObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, readerObj);
Rooted<ReadableStreamDefaultReader*> reader(cx, &readerObj->as<ReadableStreamDefaultReader>());
return js::ReadableStreamDefaultReader::read(cx, reader);
}
JS_PUBLIC_API(JSObject*)
JS::ReadableStreamBYOBReaderRead(JSContext* cx, HandleObject readerObj, HandleObject viewObj)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, readerObj);
assertSameCompartment(cx, viewObj);
Rooted<ReadableStreamBYOBReader*> reader(cx, &readerObj->as<ReadableStreamBYOBReader>());
Rooted<ArrayBufferViewObject*> view(cx, &viewObj->as<ArrayBufferViewObject>());
return js::ReadableStreamBYOBReader::read(cx, reader, view);
}
JS_PUBLIC_API(void)
JS::SetAsyncTaskCallbacks(JSContext* cx, JS::StartAsyncTaskCallback start,
JS::FinishAsyncTaskCallback finish)
+18 -19
View File
@@ -35,6 +35,7 @@
#include "js/Principals.h"
#include "js/Realm.h"
#include "js/RootingAPI.h"
#include "js/Stream.h"
#include "js/TracingAPI.h"
#include "js/UniquePtr.h"
#include "js/Utility.h"
@@ -1169,6 +1170,16 @@ class JS_PUBLIC_API(ContextOptions) {
return *this;
}
bool streams() const { return streams_; }
ContextOptions& setStreams(bool flag) {
streams_ = flag;
return *this;
}
ContextOptions& toggleStreams() {
streams_ = !streams_;
return *this;
}
bool nativeRegExp() const { return nativeRegExp_; }
ContextOptions& setNativeRegExp(bool flag) {
nativeRegExp_ = flag;
@@ -1251,6 +1262,7 @@ class JS_PUBLIC_API(ContextOptions) {
bool strictMode_ : 1;
bool extraWarnings_ : 1;
bool arrayProtoValues_ : 1;
bool streams_ : 1;
};
JS_PUBLIC_API(ContextOptions&)
@@ -4324,7 +4336,7 @@ extern JS_PUBLIC_API(bool)
Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
const char* filename, JS::MutableHandleValue rval);
using ModuleResolveHook = JSObject* (*)(JSContext*, HandleValue, HandleString);
using ModuleResolveHook = JSObject* (*)(JSContext*, HandleObject, HandleString);
/**
* Get the HostResolveImportedModule hook for the runtime.
@@ -4347,30 +4359,17 @@ CompileModule(JSContext* cx, const ReadOnlyCompileOptions& options,
SourceBufferHolder& srcBuf, JS::MutableHandleObject moduleRecord);
/**
* Set a private value associated with a source text module record.
* Set the [[HostDefined]] field of a source text module record to the given
* value.
*/
extern JS_PUBLIC_API(void)
SetModulePrivate(JSObject* module, const JS::Value& value);
SetModuleHostDefinedField(JSObject* module, const JS::Value& value);
/**
* Get the private value associated with a source text module record.
* Get the [[HostDefined]] field of a source text module record.
*/
extern JS_PUBLIC_API(JS::Value)
GetModulePrivate(JSObject* module);
/**
* Set a private value associated with a script. Note that this value is shared
* by all nested scripts compiled from a single source file.
*/
extern JS_PUBLIC_API(void)
SetScriptPrivate(JSScript* script, const JS::Value& value);
/**
* Get the private value associated with a script. Note that this value is
* shared by all nested scripts compiled from a single source file.
*/
extern JS_PUBLIC_API(JS::Value)
GetScriptPrivate(JSScript* script);
GetModuleHostDefinedField(JSObject* module);
/*
* Perform the ModuleInstantiate operation on the given source text module
+9
View File
@@ -1778,6 +1778,9 @@ UnwrapArrayBufferView(JSObject* obj);
extern JS_FRIEND_API(JSObject*)
UnwrapSharedArrayBuffer(JSObject* obj);
extern JS_FRIEND_API(JSObject*)
UnwrapReadableStream(JSObject* obj);
namespace detail {
@@ -1994,6 +1997,12 @@ JS_IsArrayBufferViewObject(JSObject* obj);
extern JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteLength(JSObject* obj);
/**
* More generic name for JS_GetTypedArrayByteOffset to cover DataViews as well
*/
extern JS_FRIEND_API(uint32_t)
JS_GetArrayBufferViewByteOffset(JSObject* obj);
/*
* Return a pointer to the start of the data referenced by a typed array. The
* data is still owned by the typed array, and should not be modified on
+8 -3
View File
@@ -947,6 +947,13 @@ static const JSFunctionSpec number_methods[] = {
JS_FS_END
};
bool
js::IsInteger(const Value& val)
{
return val.isInt32() ||
(mozilla::IsFinite(val.toDouble()) && JS::ToInteger(val.toDouble()) == val.toDouble());
}
// ES6 draft ES6 15.7.3.12
static bool
Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
@@ -957,9 +964,7 @@ Number_isInteger(JSContext* cx, unsigned argc, Value* vp)
return true;
}
Value val = args[0];
args.rval().setBoolean(val.isInt32() ||
(mozilla::IsFinite(val.toDouble()) &&
JS::ToInteger(val.toDouble()) == val.toDouble()));
args.rval().setBoolean(js::IsInteger(val));
return true;
}
+4
View File
@@ -72,6 +72,10 @@ Int32ToString(ExclusiveContext* cx, int32_t i);
extern JSAtom*
Int32ToAtom(ExclusiveContext* cx, int32_t si);
// ES6 15.7.3.12
extern bool
IsInteger(const Value& val);
/*
* Convert an integer or double (contained in the given value) to a string and
* append to the given buffer.
+17 -6
View File
@@ -106,12 +106,23 @@ IF_SIMD(real,imaginary)(SIMD, 44, InitSimdClass, OCLASP(Si
real(TypedArray, 46, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
IF_SAB(real,imaginary)(Atomics, 47, InitAtomicsClass, OCLASP(Atomics)) \
real(SavedFrame, 48, InitViaClassSpec, &js::SavedFrame::class_) \
real(WebAssembly, 49, InitWebAssemblyClass, CLASP(WebAssembly)) \
imaginary(WasmModule, 50, dummy, dummy) \
imaginary(WasmInstance, 51, dummy, dummy) \
imaginary(WasmMemory, 52, dummy, dummy) \
imaginary(WasmTable, 53, dummy, dummy) \
real(Promise, 54, InitViaClassSpec, OCLASP(Promise)) \
real(Promise, 49, InitViaClassSpec, OCLASP(Promise)) \
real(ReadableStream, 50, InitViaClassSpec, &js::ReadableStream::class_) \
real(ReadableStreamDefaultReader, 51, InitViaClassSpec, &js::ReadableStreamDefaultReader::class_) \
real(ReadableStreamBYOBReader, 52, InitViaClassSpec, &js::ReadableStreamBYOBReader::class_) \
real(ReadableStreamDefaultController, 53, InitViaClassSpec, &js::ReadableStreamDefaultController::class_) \
real(ReadableByteStreamController, 54, InitViaClassSpec, &js::ReadableByteStreamController::class_) \
real(ReadableStreamBYOBRequest, 55, InitViaClassSpec, &js::ReadableStreamBYOBRequest::class_) \
imaginary(WritableStream, 56, dummy, dummy) \
imaginary(WritableStreamDefaultWriter, 57, dummy, dummy) \
imaginary(WritableStreamDefaultController, 58, dummy, dummy) \
real(ByteLengthQueuingStrategy, 59, InitViaClassSpec, &js::ByteLengthQueuingStrategy::class_) \
real(CountQueuingStrategy, 60, InitViaClassSpec, &js::CountQueuingStrategy::class_) \
real(WebAssembly, 61, InitWebAssemblyClass, CLASP(WebAssembly)) \
imaginary(WasmModule, 62, dummy, dummy) \
imaginary(WasmInstance, 63, dummy, dummy) \
imaginary(WasmMemory, 64, dummy, dummy) \
imaginary(WasmTable, 65, dummy, dummy)
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
+5 -15
View File
@@ -638,22 +638,12 @@ class ScriptSourceObject : public NativeObject
return static_cast<JSScript*>(untyped);
}
void setPrivate(const Value& value) {
setReservedSlot(PRIVATE_SLOT, value);
}
Value getPrivate() const {
return getReservedSlot(PRIVATE_SLOT);
}
private:
enum {
SOURCE_SLOT = 0,
ELEMENT_SLOT,
ELEMENT_PROPERTY_SLOT,
INTRODUCTION_SCRIPT_SLOT,
PRIVATE_SLOT,
RESERVED_SLOTS
};
static const uint32_t SOURCE_SLOT = 0;
static const uint32_t ELEMENT_SLOT = 1;
static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
static const uint32_t RESERVED_SLOTS = 4;
};
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
+10 -18
View File
@@ -83,6 +83,7 @@ EXPORTS.js += [
'../public/RequiredDefines.h',
'../public/RootingAPI.h',
'../public/SliceBudget.h',
'../public/Stream.h',
'../public/StructuredClone.h',
'../public/SweepingAPI.h',
'../public/TraceKind.h',
@@ -131,18 +132,22 @@ if not CONFIG['JS_DISABLE_SHELL']:
if CONFIG['MOZ_VTUNE']:
DIRS += ['vtune']
UNIFIED_SOURCES += [
SOURCES += [
'jsalloc.cpp',
'jsapi.cpp',
'jsarray.cpp',
'jsatom.cpp',
'jsbool.cpp',
'jscntxt.cpp',
'jscompartment.cpp',
'jsdate.cpp',
'jsdtoa.cpp',
'jsexn.cpp',
'jsfriendapi.cpp',
'jsfun.cpp',
'jsgc.cpp',
'jsiter.cpp',
'jsmath.cpp',
'jsnativestack.cpp',
'jsnum.cpp',
'jsobj.cpp',
@@ -152,16 +157,8 @@ UNIFIED_SOURCES += [
'jspropertytree.cpp',
'jsscript.cpp',
'jsstr.cpp',
'jsweakmap.cpp',
]
# Can't be built unified
SOURCES += [
'jsarray.cpp',
'jsatom.cpp',
'jsdtoa.cpp',
'jsmath.cpp',
'jsutil.cpp',
'jsweakmap.cpp',
]
# Suppress warnings in third-party code.
@@ -169,7 +166,7 @@ SOURCES += [
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough']
UNIFIED_SOURCES += [
SOURCES += [
'vm/ArgumentsObject.cpp',
'vm/ArrayBufferObject.cpp',
'vm/AsyncFunction.cpp',
@@ -189,6 +186,7 @@ UNIFIED_SOURCES += [
'vm/GlobalObject.cpp',
'vm/HelperThreads.cpp',
'vm/Id.cpp',
'vm/Initialization.cpp',
'vm/Interpreter.cpp',
'vm/JSONParser.cpp',
'vm/MemoryMetrics.cpp',
@@ -230,18 +228,12 @@ UNIFIED_SOURCES += [
'vm/Xdr.cpp',
]
# Can't be built unified
SOURCES += [
'vm/Initialization.cpp',
]
if CONFIG['JS_POSIX_NSPR']:
UNIFIED_SOURCES += [
SOURCES += [
'vm/PosixNSPR.cpp',
]
if CONFIG['ENABLE_TRACE_LOGGING']:
# Can't be built unified
SOURCES += [
'vm/TraceLogging.cpp',
'vm/TraceLoggingGraph.cpp',
-51
View File
@@ -1,51 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file forward-defines Irregexp classes that need to be visible
// to the rest of Spidermonkey and re-exports them into js::irregexp.
#ifndef regexp_RegExpTypes_h
#define regexp_RegExpTypes_h
namespace js {
class MatchPairs;
}
namespace v8 {
namespace internal {
struct InputOutputData {
const void* inputStart;
const void* inputEnd;
// Index into inputStart (in chars) at which to begin matching.
size_t startIndex;
js::MatchPairs* matches;
template <typename CharT>
InputOutputData(const CharT* inputStart, const CharT* inputEnd,
size_t startIndex, js::MatchPairs* matches)
: inputStart(inputStart),
inputEnd(inputEnd),
startIndex(startIndex),
matches(matches)
{}
};
} // namespace internal
} // namespace v8
namespace js {
namespace irregexp {
using InputOutputData = v8::internal::InputOutputData;
} // namespace irregexp
} // namespace js
#endif // regexp_RegExpTypes_h
+3 -2
View File
@@ -1,2 +1,3 @@
Imported using import-irregexp.py from:
https://github.com/v8/v8/tree/560f2d8bb3f3a72d78e1a7d7654235d53fdcc83c/src/regexp
This code was most recently imported from the following version of V8:
https://github.com/v8/v8/tree/2599d3cc208a3a4873be517285220abd8416c3d7/src/regexp
+55 -96
View File
@@ -1,4 +1,4 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -7,19 +7,18 @@
#include <iostream>
#include <sstream>
#include "regexp/special-case.h"
#include "unicode/uchar.h"
#include "unicode/uniset.h"
namespace v8 {
namespace internal {
static const uc32 kSurrogateStart = 0xd800;
static const uc32 kSurrogateEnd = 0xdfff;
static const uc32 kNonBmpStart = 0x10000;
// The following code generates "src/regexp/special-case.cc".
void PrintSet(std::ofstream& out, const char* name,
// The following code generates BuildSpecialAddSet() and BuildIgnoreSet()
// functions into "src/regexp/special-case.cc".
// See more details in http://shorturl.at/adfO5
void PrintSet(std::ofstream& out, const char* func_name,
const icu::UnicodeSet& set) {
out << "icu::UnicodeSet Build" << name << "() {\n"
out << "icu::UnicodeSet " << func_name << "() {\n"
<< " icu::UnicodeSet set;\n";
for (int32_t i = 0; i < set.getRangeCount(); i++) {
if (set.getRangeStart(i) == set.getRangeEnd(i)) {
@@ -31,113 +30,73 @@ void PrintSet(std::ofstream& out, const char* name,
}
out << " set.freeze();\n"
<< " return set;\n"
<< "}\n\n";
out << "struct " << name << "Data {\n"
<< " " << name << "Data() : set(Build" << name << "()) {}\n"
<< " const icu::UnicodeSet set;\n"
<< "};\n\n";
out << "//static\n"
<< "const icu::UnicodeSet& RegExpCaseFolding::" << name << "() {\n"
<< " static base::LazyInstance<" << name << "Data>::type set =\n"
<< " LAZY_INSTANCE_INITIALIZER;\n"
<< " return set.Pointer()->set;\n"
<< "}\n\n";
<< "}\n";
}
void PrintSpecial(std::ofstream& out) {
icu::UnicodeSet current;
icu::UnicodeSet processed(0xd800, 0xdbff); // Ignore surrogate range.
icu::UnicodeSet special_add;
icu::UnicodeSet ignore;
UErrorCode status = U_ZERO_ERROR;
icu::UnicodeSet upper("[\\p{Lu}]", status);
CHECK(U_SUCCESS(status));
// Iterate through all chars in BMP except surrogates.
for (UChar32 i = 0; i < kNonBmpStart; i++) {
if (i >= kSurrogateStart && i <= kSurrogateEnd) {
continue; // Ignore surrogate range
}
current.set(i, i);
current.closeOver(USET_CASE_INSENSITIVE);
// Check to see if all characters in the case-folding equivalence
// class as defined by UnicodeSet::closeOver all map to the same
// canonical value.
UChar32 canonical = RegExpCaseFolding::Canonicalize(i);
bool class_has_matching_canonical_char = false;
bool class_has_non_matching_canonical_char = false;
for (int32_t j = 0; j < current.getRangeCount(); j++) {
for (UChar32 c = current.getRangeStart(j); c <= current.getRangeEnd(j);
c++) {
if (c == i) {
continue;
}
UChar32 other_canonical = RegExpCaseFolding::Canonicalize(c);
if (canonical == other_canonical) {
class_has_matching_canonical_char = true;
} else {
class_has_non_matching_canonical_char = true;
}
}
}
// If any other character in i's equivalence class has a
// different canonical value, then i needs special handling. If
// no other character shares a canonical value with i, we can
// ignore i when adding alternatives for case-independent
// comparison. If at least one other character shares a
// canonical value, then i needs special handling.
if (class_has_non_matching_canonical_char) {
if (class_has_matching_canonical_char) {
special_add.add(i);
} else {
ignore.add(i);
}
}
}
// Verify that no Unicode equivalence class contains two non-trivial
// JS equivalence classes. Every character in SpecialAddSet has the
// same canonical value as every other non-IgnoreSet character in
// its Unicode equivalence class. Therefore, if we call closeOver on
// a set containing no IgnoreSet characters, the only characters
// that must be removed from the result are in IgnoreSet. This fact
// is used in CharacterRange::AddCaseEquivalents.
for (int32_t i = 0; i < special_add.getRangeCount(); i++) {
for (UChar32 c = special_add.getRangeStart(i);
c <= special_add.getRangeEnd(i); c++) {
UChar32 canonical = RegExpCaseFolding::Canonicalize(c);
current.set(c, c);
// Iterate through all chars in BMP except ASCII and Surrogate.
for (UChar32 i = 0x80; i < 0x010000; i++) {
// Ignore those characters which is already processed.
if (!processed.contains(i)) {
current.set(i, i);
current.closeOver(USET_CASE_INSENSITIVE);
current.removeAll(ignore);
for (int32_t j = 0; j < current.getRangeCount(); j++) {
for (UChar32 c2 = current.getRangeStart(j);
c2 <= current.getRangeEnd(j); c2++) {
CHECK_EQ(canonical, RegExpCaseFolding::Canonicalize(c2));
}
// Remember we already processed current.
processed.addAll(current);
// All uppercase characters in current.
icu::UnicodeSet keep_upper(current);
keep_upper.retainAll(upper);
// Check if we have more than one uppercase character in current.
// If there are more than one uppercase character, then it is a special
// set which need to be added into either "Special Add" set or "Ignore"
// set.
int32_t number_of_upper = 0;
for (int32_t i = 0; i < keep_upper.getRangeCount() && i <= 1; i++) {
number_of_upper +=
keep_upper.getRangeEnd(i) - keep_upper.getRangeStart(i) + 1;
}
if (number_of_upper > 1) {
// Add all non uppercase characters (could be Ll or Mn) to special add
// set.
current.removeAll(upper);
special_add.addAll(current);
// Add the uppercase characters of non uppercase character to
// special add set.
CHECK_GT(current.getRangeCount(), 0);
UChar32 main_upper = u_toupper(current.getRangeStart(0));
special_add.add(main_upper);
// Add all uppercase except the main upper to ignore set.
keep_upper.remove(main_upper);
ignore.addAll(keep_upper);
}
}
}
PrintSet(out, "IgnoreSet", ignore);
PrintSet(out, "SpecialAddSet", special_add);
// Remove any ASCII
special_add.remove(0x0000, 0x007f);
PrintSet(out, "BuildIgnoreSet", ignore);
PrintSet(out, "BuildSpecialAddSet", special_add);
}
void WriteHeader(const char* header_filename) {
std::ofstream out(header_filename);
out << std::hex << std::setfill('0') << std::setw(4);
out << "// Copyright 2020 the V8 project authors. All rights reserved.\n"
<< "// Use of this source code is governed by a BSD-style license that\n"
<< "// can be found in the LICENSE file.\n\n"
<< "// Automatically generated by regexp/gen-regexp-special-case.cc\n\n"
<< "// The following functions are used to build UnicodeSets\n"
<< "// for special cases where the case-folding algorithm used by\n"
<< "// UnicodeSet::closeOver(USET_CASE_INSENSITIVE) does not match\n"
<< "// the algorithm defined in ECMAScript 2020 21.2.2.8.2 (Runtime\n"
<< "// Semantics: Canonicalize) step 3.\n\n"
out << "// Automatically generated by regexp/gen-regexp-special-case.cc\n"
<< "// The following functions are used to build icu::UnicodeSet\n"
<< "// for specical cases different between Unicode and ECMA262.\n"
<< "#ifdef V8_INTL_SUPPORT\n"
<< "#include \"src/base/lazy-instance.h\"\n\n"
<< "#include \"src/regexp/special-case.h\"\n\n"
<< "#include \"unicode/uniset.h\"\n"
<< "namespace v8 {\n"
-143
View File
@@ -1,143 +0,0 @@
#!/usr/bin/env python3
# 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/.
# This script handles all the mechanical steps of importing irregexp from v8:
#
# 1. Acquire the source: either from github, or optionally from a local copy of v8.
# 2. Copy the contents of v8/src/regexp into js/src/regexp
# - Exclude files that we have chosen not to import.
# 3. While doing so, update #includes:
# - Change "src/regexp/*" to "regexp/*".
# - Remove other v8-specific headers completely.
# 4. Add '#include "regexp/regexp-shim.h" in the necessary places.
# 5. Update the VERSION file to include the correct git hash.
#
# Usage:
# cd path/to/js/src/regexp
# ./import-irregexp.py --path path/to/v8/src/regexp
#
# Alternatively, without the --path argument, import-irregexp.py will
# clone v8 from github into a temporary directory.
#
# After running this script, changes to the shim code may be necessary
# to account for changes in upstream irregexp.
import os
import re
import subprocess
import sys
from pathlib import Path
def get_hash(path):
# Get the hash for the current git revision
cwd = os.getcwd()
os.chdir(path)
command = ['git', 'rev-parse', 'HEAD']
result = subprocess.check_output(command, encoding='utf-8')
os.chdir(cwd)
return result.rstrip()
def copy_and_update_includes(src_path, dst_path):
# List of header files that need to include the shim header
need_shim = ['property-sequences.h',
'regexp-ast.h',
'regexp-bytecode-peephole.h',
'regexp-bytecodes.h',
'regexp-dotprinter.h',
'regexp.h',
'regexp-macro-assembler.h',
'regexp-stack.h',
'special-case.h']
src = open(str(src_path), 'r')
dst = open(str(dst_path), 'w')
# 1. Rewrite includes of V8 regexp headers:
regexp_include = re.compile('#include "src/regexp')
regexp_include_new = '#include "regexp'
# 2. Remove includes of other V8 headers
other_include = re.compile('#include "src/')
# 3. If needed, add '#include "regexp/regexp-shim.h"'.
# Note: We get a little fancy to ensure that header files are
# in alphabetic order. `need_to_add_shim` is true if we still
# have to add the shim header in this file. `adding_shim_now`
# is true if we have found a '#include "src/*' and we are just
# waiting to find something alphabetically smaller (or an empty
# line) so that we can insert the shim header in the right place.
need_to_add_shim = src_path.name in need_shim
adding_shim_now = False
for line in src:
if adding_shim_now:
if (line == '\n' or line > '#include "src/regexp/regexp-shim.h"'):
dst.write('#include "regexp/regexp-shim.h"\n')
need_to_add_shim = False
adding_shim_now = False
if regexp_include.search(line):
dst.write(re.sub(regexp_include, regexp_include_new, line))
elif other_include.search(line):
if need_to_add_shim:
adding_shim_now = True
else:
dst.write(line)
def import_from(srcdir, dstdir):
excluded = ['OWNERS',
'regexp.cc',
'regexp-utils.cc',
'regexp-utils.h',
'regexp-macro-assembler-arch.h']
for file in srcdir.iterdir():
if file.is_dir():
continue
if str(file.name) in excluded:
continue
copy_and_update_includes(file, dstdir / file.name)
# Update VERSION file
hash = get_hash(srcdir)
version_file = open(str(dstdir / 'VERSION'), 'w')
version_file.write('Imported using import-irregexp.py from:\n')
version_file.write('https://github.com/v8/v8/tree/%s/src/regexp\n' % hash)
if __name__ == '__main__':
import argparse
import tempfile
# This script should be run from js/src/regexp to work correctly.
current_path = Path(os.getcwd())
expected_path = 'js/src/regexp'
if not current_path.match(expected_path):
raise RuntimeError('%s must be run from %s' % (sys.argv[0],
expected_path))
parser = argparse.ArgumentParser(description='Import irregexp from v8')
parser.add_argument('-p', '--path', help='path to v8/src/regexp')
args = parser.parse_args()
if args.path:
src_path = Path(args.path)
if not (src_path / 'regexp.h').exists():
print('Usage:\n import-irregexp.py --path <path/to/v8/src/regexp>')
sys.exit(1)
import_from(src_path, current_path)
sys.exit(0)
with tempfile.TemporaryDirectory() as tempdir:
v8_git = 'https://github.com/v8/v8.git'
clone = 'git clone --depth 1 %s %s' % (v8_git, tempdir)
os.system(clone)
src_path = Path(tempdir) / 'src/regexp'
import_from(src_path, current_path)
+1 -7
View File
@@ -22,16 +22,10 @@ SOURCES += [
'regexp-interpreter.cc',
'regexp-macro-assembler-tracer.cc',
'regexp-macro-assembler.cc',
'regexp-native-macro-assembler.cc',
'regexp-parser.cc',
'regexp-shim.cc',
'regexp-stack.cc',
'util/unicode.cc'
]
if CONFIG['ENABLE_INTL_API']:
CXXFLAGS += ['-DV8_INTL_SUPPORT']
SOURCES += [
'property-sequences.cc',
'special-case.cc'
]
SOURCES += ['property-sequences.cc']
-1
View File
@@ -7,7 +7,6 @@
#ifdef V8_INTL_SUPPORT
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
+7 -23
View File
@@ -5,7 +5,6 @@
#ifndef V8_REGEXP_REGEXP_AST_H_
#define V8_REGEXP_REGEXP_AST_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
@@ -458,11 +457,7 @@ class RegExpQuantifier final : public RegExpTree {
class RegExpCapture final : public RegExpTree {
public:
explicit RegExpCapture(int index)
: body_(nullptr),
index_(index),
min_match_(0),
max_match_(0),
name_(nullptr) {}
: body_(nullptr), index_(index), name_(nullptr) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler, RegExpNode* on_success) override;
static RegExpNode* ToNode(RegExpTree* body, int index,
@@ -472,14 +467,10 @@ class RegExpCapture final : public RegExpTree {
bool IsAnchoredAtEnd() override;
Interval CaptureRegisters() override;
bool IsCapture() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
int min_match() override { return body_->min_match(); }
int max_match() override { return body_->max_match(); }
RegExpTree* body() { return body_; }
void set_body(RegExpTree* body) {
body_ = body;
min_match_ = body->min_match();
max_match_ = body->max_match();
}
void set_body(RegExpTree* body) { body_ = body; }
int index() const { return index_; }
const ZoneVector<uc16>* name() const { return name_; }
void set_name(const ZoneVector<uc16>* name) { name_ = name; }
@@ -489,17 +480,12 @@ class RegExpCapture final : public RegExpTree {
private:
RegExpTree* body_;
int index_;
int min_match_;
int max_match_;
const ZoneVector<uc16>* name_;
};
class RegExpGroup final : public RegExpTree {
public:
explicit RegExpGroup(RegExpTree* body)
: body_(body),
min_match_(body->min_match()),
max_match_(body->max_match()) {}
explicit RegExpGroup(RegExpTree* body) : body_(body) {}
void* Accept(RegExpVisitor* visitor, void* data) override;
RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success) override {
@@ -509,15 +495,13 @@ class RegExpGroup final : public RegExpTree {
bool IsAnchoredAtStart() override { return body_->IsAnchoredAtStart(); }
bool IsAnchoredAtEnd() override { return body_->IsAnchoredAtEnd(); }
bool IsGroup() override;
int min_match() override { return min_match_; }
int max_match() override { return max_match_; }
int min_match() override { return body_->min_match(); }
int max_match() override { return body_->max_match(); }
Interval CaptureRegisters() override { return body_->CaptureRegisters(); }
RegExpTree* body() { return body_; }
private:
RegExpTree* body_;
int min_match_;
int max_match_;
};
class RegExpLookaround final : public RegExpTree {
+5 -3
View File
@@ -327,11 +327,13 @@ void RegExpBytecodeGenerator::CheckNotBackReference(int start_reg,
}
void RegExpBytecodeGenerator::CheckNotBackReferenceIgnoreCase(
int start_reg, bool read_backward, Label* on_not_equal) {
int start_reg, bool read_backward, bool unicode, Label* on_not_equal) {
DCHECK_LE(0, start_reg);
DCHECK_GE(kMaxRegister, start_reg);
Emit(read_backward ? BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD
: BC_CHECK_NOT_BACK_REF_NO_CASE,
Emit(read_backward ? (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD
: BC_CHECK_NOT_BACK_REF_NO_CASE_BACKWARD)
: (unicode ? BC_CHECK_NOT_BACK_REF_NO_CASE_UNICODE
: BC_CHECK_NOT_BACK_REF_NO_CASE),
start_reg);
EmitOrLink(on_not_equal);
}
+1 -1
View File
@@ -69,7 +69,7 @@ class V8_EXPORT_PRIVATE RegExpBytecodeGenerator : public RegExpMacroAssembler {
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match);
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward,
bool read_backward, bool unicode,
Label* on_no_match);
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
@@ -428,6 +428,7 @@ BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
size_t index) const {
DCHECK(IsSequence());
DCHECK(argument_mapping_ != nullptr);
DCHECK_GE(index, 0);
DCHECK_LT(index, argument_mapping_->size());
return argument_mapping_->at(index);
-1
View File
@@ -5,7 +5,6 @@
#ifndef V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
#define V8_REGEXP_REGEXP_BYTECODE_PEEPHOLE_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
+4 -5
View File
@@ -5,7 +5,6 @@
#ifndef V8_REGEXP_REGEXP_BYTECODES_H_
#define V8_REGEXP_REGEXP_BYTECODES_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
@@ -100,12 +99,12 @@ STATIC_ASSERT(1 << BYTECODE_SHIFT > BYTECODE_MASK);
V(CHECK_BIT_IN_TABLE, 34, 24) /* bc8 pad24 addr32 bits128 */ \
V(CHECK_LT, 35, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_GT, 36, 8) /* bc8 pad8 uc16 addr32 */ \
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) /* UNUSED */ \
V(CHECK_NOT_BACK_REF, 37, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE, 38, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE, 39, 8) \
V(CHECK_NOT_BACK_REF_BACKWARD, 40, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD, 41, 8) /* bc8 reg_idx24 addr32 */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) /* UNUSED */ \
V(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD, 42, 8) \
V(CHECK_NOT_REGS_EQUAL, 43, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
V(CHECK_REGISTER_LT, 44, 12) /* bc8 reg_idx24 value32 addr32 */ \
V(CHECK_REGISTER_GE, 45, 12) /* bc8 reg_idx24 value32 addr32 */ \
+100 -14
View File
@@ -1137,6 +1137,39 @@ Vector<const int> CharacterRange::GetWordBounds() {
return Vector<const int>(kWordRanges, kWordRangeCount - 1);
}
#ifdef V8_INTL_SUPPORT
struct IgnoreSet {
IgnoreSet() : set(BuildIgnoreSet()) {}
const icu::UnicodeSet set;
};
struct SpecialAddSet {
SpecialAddSet() : set(BuildSpecialAddSet()) {}
const icu::UnicodeSet set;
};
icu::UnicodeSet BuildAsciiAToZSet() {
icu::UnicodeSet set('a', 'z');
set.add('A', 'Z');
set.freeze();
return set;
}
struct AsciiAToZSet {
AsciiAToZSet() : set(BuildAsciiAToZSet()) {}
const icu::UnicodeSet set;
};
static base::LazyInstance<IgnoreSet>::type ignore_set =
LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<SpecialAddSet>::type special_add_set =
LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<AsciiAToZSet>::type ascii_a_to_z_set =
LAZY_INSTANCE_INITIALIZER;
#endif // V8_INTL_SUPPORT
// static
void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
ZoneList<CharacterRange>* ranges,
@@ -1159,22 +1192,75 @@ void CharacterRange::AddCaseEquivalents(Isolate* isolate, Zone* zone,
others.add(from, to);
}
// Compute the set of additional characters that should be added,
// using UnicodeSet::closeOver. ECMA 262 defines slightly different
// case-folding rules than Unicode, so some characters that are
// added by closeOver do not match anything other than themselves in
// JS. For example, 'ſ' (U+017F LATIN SMALL LETTER LONG S) is the
// same case-insensitive character as 's' or 'S' according to
// Unicode, but does not match any other character in JS. To handle
// this case, we add such characters to the IgnoreSet and filter
// them out. We filter twice: once before calling closeOver (to
// prevent 'ſ' from adding 's'), and once after calling closeOver
// (to prevent 's' from adding 'ſ'). See regexp/special-case.h for
// more information.
// Set of characters already added to ranges that do not need to be added
// again.
icu::UnicodeSet already_added(others);
others.removeAll(RegExpCaseFolding::IgnoreSet());
// Set of characters in ranges that are in the 52 ASCII characters [a-zA-Z].
icu::UnicodeSet in_ascii_a_to_z(others);
in_ascii_a_to_z.retainAll(ascii_a_to_z_set.Pointer()->set);
// Remove all chars in [a-zA-Z] from others.
others.removeAll(in_ascii_a_to_z);
// Set of characters in ranges that are overlapping with special add set.
icu::UnicodeSet in_special_add(others);
in_special_add.retainAll(special_add_set.Pointer()->set);
others.removeAll(in_special_add);
// Ignore all chars in ignore set.
others.removeAll(ignore_set.Pointer()->set);
// For most of the chars in ranges that is still in others, find the case
// equivlant set by calling closeOver(USET_CASE_INSENSITIVE).
others.closeOver(USET_CASE_INSENSITIVE);
others.removeAll(RegExpCaseFolding::IgnoreSet());
// Because closeOver(USET_CASE_INSENSITIVE) may add ASCII [a-zA-Z] to others,
// but ECMA262 "i" mode won't consider that, remove them from others.
// Ex: U+017F add 'S' and 's' to others.
others.removeAll(ascii_a_to_z_set.Pointer()->set);
// Special handling for in_ascii_a_to_z.
for (int32_t i = 0; i < in_ascii_a_to_z.getRangeCount(); i++) {
UChar32 start = in_ascii_a_to_z.getRangeStart(i);
UChar32 end = in_ascii_a_to_z.getRangeEnd(i);
// Check if it is uppercase A-Z by checking bit 6.
if (start & 0x0020) {
// Add the lowercases
others.add(start & 0x005F, end & 0x005F);
} else {
// Add the uppercases
others.add(start | 0x0020, end | 0x0020);
}
}
// Special handling for chars in "Special Add" set.
for (int32_t i = 0; i < in_special_add.getRangeCount(); i++) {
UChar32 end = in_special_add.getRangeEnd(i);
for (UChar32 ch = in_special_add.getRangeStart(i); ch <= end; ch++) {
// Add the uppercase of this character if itself is not an uppercase
// character.
// Note: The if condiction cannot be u_islower(ch) because ch could be
// neither uppercase nor lowercase but Mn.
if (!u_isupper(ch)) {
others.add(u_toupper(ch));
}
icu::UnicodeSet candidates(ch, ch);
candidates.closeOver(USET_CASE_INSENSITIVE);
for (int32_t j = 0; j < candidates.getRangeCount(); j++) {
UChar32 end2 = candidates.getRangeEnd(j);
for (UChar32 ch2 = candidates.getRangeStart(j); ch2 <= end2; ch2++) {
// Add character that is not uppercase to others.
if (!u_isupper(ch2)) {
others.add(ch2);
}
}
}
}
}
// Remove all characters which already in the ranges.
others.removeAll(already_added);
// Add others to the ranges
+59 -56
View File
@@ -5,9 +5,7 @@
#include "regexp/regexp-compiler.h"
#include "regexp/regexp-macro-assembler-arch.h"
#ifdef V8_INTL_SUPPORT
#include "regexp/special-case.h"
#endif // V8_INTL_SUPPORT
#include "regexp/regexp-macro-assembler-tracer.h"
#ifdef V8_INTL_SUPPORT
#include "unicode/locid.h"
@@ -239,15 +237,20 @@ RegExpCompiler::RegExpCompiler(Isolate* isolate, Zone* zone, int capture_count,
RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
Isolate* isolate, RegExpMacroAssembler* macro_assembler, RegExpNode* start,
int capture_count, Handle<String> pattern) {
macro_assembler_ = macro_assembler;
#ifdef DEBUG
if (FLAG_trace_regexp_assembler)
macro_assembler_ = new RegExpMacroAssemblerTracer(isolate, macro_assembler);
else
#endif
macro_assembler_ = macro_assembler;
ZoneVector<RegExpNode*> work_list(zone());
std::vector<RegExpNode*> work_list;
work_list_ = &work_list;
Label fail;
macro_assembler_->PushBacktrack(&fail);
Trace new_trace;
start->Emit(this, &new_trace);
macro_assembler_->BindJumpTarget(&fail);
macro_assembler_->Bind(&fail);
macro_assembler_->Fail();
while (!work_list.empty()) {
RegExpNode* node = work_list.back();
@@ -261,9 +264,14 @@ RegExpCompiler::CompilationResult RegExpCompiler::Assemble(
}
Handle<HeapObject> code = macro_assembler_->GetCode(pattern);
isolate->IncreaseTotalRegexpCodeGenerated(code);
isolate->IncreaseTotalRegexpCodeGenerated(code->Size());
work_list_ = nullptr;
#ifdef DEBUG
if (FLAG_trace_regexp_assembler) {
delete macro_assembler_;
}
#endif
return {*code, next_register_};
}
@@ -549,7 +557,7 @@ void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
}
// On backtrack we need to restore state.
assembler->BindJumpTarget(&undo);
assembler->Bind(&undo);
RestoreAffectedRegisters(assembler, max_register, registers_to_pop,
registers_to_clear);
if (backtrack() == nullptr) {
@@ -712,34 +720,32 @@ static int GetCaseIndependentLetters(Isolate* isolate, uc16 character,
unibrow::uchar* letters,
int letter_length) {
#ifdef V8_INTL_SUPPORT
if (RegExpCaseFolding::IgnoreSet().contains(character)) {
// Special case for U+017F which has upper case in ASCII range.
if (character == 0x017f) {
letters[0] = character;
return 1;
}
bool in_special_add_set =
RegExpCaseFolding::SpecialAddSet().contains(character);
icu::UnicodeSet set;
set.add(character);
set = set.closeOver(USET_CASE_INSENSITIVE);
UChar32 canon = 0;
if (in_special_add_set) {
canon = RegExpCaseFolding::Canonicalize(character);
}
int32_t range_count = set.getRangeCount();
int items = 0;
for (int32_t i = 0; i < range_count; i++) {
UChar32 start = set.getRangeStart(i);
UChar32 end = set.getRangeEnd(i);
CHECK(end - start + items <= letter_length);
for (UChar32 cu = start; cu <= end; cu++) {
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
if (in_special_add_set && RegExpCaseFolding::Canonicalize(cu) != canon) {
continue;
// Only add to the output if character is not in ASCII range
// or the case equivalent character is in ASCII range.
// #sec-runtime-semantics-canonicalize-ch
// 3.g If the numeric value of ch ≥ 128 and the numeric value of cu < 128,
// return ch.
if (!((start >= 128) && (character < 128))) {
// No range have start and end span across code point 128.
DCHECK((start >= 128) == (end >= 128));
for (UChar32 cu = start; cu <= end; cu++) {
if (one_byte_subject && cu > String::kMaxOneByteCharCode) break;
letters[items++] = (unibrow::uchar)(cu);
}
letters[items++] = (unibrow::uchar)(cu);
}
}
return items;
@@ -846,6 +852,10 @@ static bool ShortCutEmitCharacterPair(RegExpMacroAssembler* macro_assembler,
return false;
}
using EmitCharacterFunction = bool(Isolate* isolate, RegExpCompiler* compiler,
uc16 c, Label* on_failure, int cp_offset,
bool check, bool preloaded);
// Only emits letters (things that have case). Only used for case independent
// matches.
static inline bool EmitAtomLetter(Isolate* isolate, RegExpCompiler* compiler,
@@ -1833,13 +1843,13 @@ RegExpNode* TextNode::FilterOneByte(int depth) {
if (elm.text_type() == TextElement::ATOM) {
Vector<const uc16> quarks = elm.atom()->data();
for (int j = 0; j < quarks.length(); j++) {
uc16 c = quarks[j];
uint16_t c = quarks[j];
if (elm.atom()->ignore_case()) {
c = unibrow::Latin1::TryConvertToLatin1(c);
}
if (c > unibrow::Latin1::kMaxChar) return set_replacement(nullptr);
// Replace quark in case we converted to Latin-1.
uc16* writable_quarks = const_cast<uc16*>(quarks.begin());
uint16_t* writable_quarks = const_cast<uint16_t*>(quarks.begin());
writable_quarks[j] = c;
}
} else {
@@ -2294,6 +2304,7 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
for (int j = preloaded ? 0 : quarks.length() - 1; j >= 0; j--) {
if (first_element_checked && i == 0 && j == 0) continue;
if (DeterminedAlready(quick_check, elm.cp_offset() + j)) continue;
EmitCharacterFunction* emit_function = nullptr;
uc16 quark = quarks[j];
if (elm.atom()->ignore_case()) {
// Everywhere else we assume that a non-Latin-1 character cannot match
@@ -2301,9 +2312,6 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
// invalid by using the Latin1 equivalent instead.
quark = unibrow::Latin1::TryConvertToLatin1(quark);
}
bool needs_bounds_check =
*checked_up_to < cp_offset + j || read_backward();
bool bounds_checked = false;
switch (pass) {
case NON_LATIN1_MATCH:
DCHECK(one_byte);
@@ -2313,24 +2321,24 @@ void TextNode::TextEmitPass(RegExpCompiler* compiler, TextEmitPassType pass,
}
break;
case NON_LETTER_CHARACTER_MATCH:
bounds_checked =
EmitAtomNonLetter(isolate, compiler, quark, backtrack,
cp_offset + j, needs_bounds_check, preloaded);
emit_function = &EmitAtomNonLetter;
break;
case SIMPLE_CHARACTER_MATCH:
bounds_checked = EmitSimpleCharacter(isolate, compiler, quark,
backtrack, cp_offset + j,
needs_bounds_check, preloaded);
emit_function = &EmitSimpleCharacter;
break;
case CASE_CHARACTER_MATCH:
bounds_checked =
EmitAtomLetter(isolate, compiler, quark, backtrack,
cp_offset + j, needs_bounds_check, preloaded);
emit_function = &EmitAtomLetter;
break;
default:
break;
}
if (bounds_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
if (emit_function != nullptr) {
bool bounds_check = *checked_up_to < cp_offset + j || read_backward();
bool bound_checked =
emit_function(isolate, compiler, quark, backtrack, cp_offset + j,
bounds_check, preloaded);
if (bound_checked) UpdateBoundsCheck(cp_offset + j, checked_up_to);
}
}
} else {
DCHECK_EQ(TextElement::CHAR_CLASS, elm.text_type());
@@ -3416,8 +3424,8 @@ void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
DCHECK_EQ(start_reg_ + 1, end_reg_);
if (IgnoreCase(flags_)) {
assembler->CheckNotBackReferenceIgnoreCase(start_reg_, read_backward(),
trace->backtrack());
assembler->CheckNotBackReferenceIgnoreCase(
start_reg_, read_backward(), IsUnicode(flags_), trace->backtrack());
} else {
assembler->CheckNotBackReference(start_reg_, read_backward(),
trace->backtrack());
@@ -3589,17 +3597,12 @@ template <typename... Propagators>
class Analysis : public NodeVisitor {
public:
Analysis(Isolate* isolate, bool is_one_byte)
: isolate_(isolate),
is_one_byte_(is_one_byte),
error_(RegExpError::kNone) {}
: isolate_(isolate), is_one_byte_(is_one_byte), error_message_(nullptr) {}
void EnsureAnalyzed(RegExpNode* that) {
StackLimitCheck check(isolate());
if (check.HasOverflowed()) {
if (FLAG_correctness_fuzzer_suppressions) {
FATAL("Analysis: Aborting on stack overflow");
}
fail(RegExpError::kAnalysisStackOverflow);
fail("Stack overflow");
return;
}
if (that->info()->been_analyzed || that->info()->being_analyzed) return;
@@ -3609,12 +3612,12 @@ class Analysis : public NodeVisitor {
that->info()->been_analyzed = true;
}
bool has_failed() { return error_ != RegExpError::kNone; }
RegExpError error() {
DCHECK(error_ != RegExpError::kNone);
return error_;
bool has_failed() { return error_message_ != nullptr; }
const char* error_message() {
DCHECK(error_message_ != nullptr);
return error_message_;
}
void fail(RegExpError error) { error_ = error; }
void fail(const char* error_message) { error_message_ = error_message; }
Isolate* isolate() const { return isolate_; }
@@ -3699,19 +3702,19 @@ class Analysis : public NodeVisitor {
private:
Isolate* isolate_;
bool is_one_byte_;
RegExpError error_;
const char* error_message_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
};
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte,
RegExpNode* node) {
Analysis<AssertionPropagator, EatsAtLeastPropagator> analysis(isolate,
is_one_byte);
DCHECK_EQ(node->info()->been_analyzed, false);
analysis.EnsureAnalyzed(node);
DCHECK_IMPLIES(analysis.has_failed(), analysis.error() != RegExpError::kNone);
return analysis.has_failed() ? analysis.error() : RegExpError::kNone;
DCHECK_IMPLIES(analysis.has_failed(), analysis.error_message() != nullptr);
return analysis.has_failed() ? analysis.error_message() : nullptr;
}
void BackReferenceNode::FillInBMInfo(Isolate* isolate, int offset, int budget,
+10 -6
View File
@@ -422,7 +422,10 @@ struct PreloadState {
// Analysis performs assertion propagation and computes eats_at_least_ values.
// See the comments on AssertionPropagator and EatsAtLeastPropagator for more
// details.
RegExpError AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
//
// This method returns nullptr on success or a null-terminated failure message
// on failure.
const char* AnalyzeRegExp(Isolate* isolate, bool is_one_byte, RegExpNode* node);
class FrequencyCollator {
public:
@@ -499,17 +502,18 @@ class RegExpCompiler {
}
struct CompilationResult final {
explicit CompilationResult(RegExpError err) : error(err) {}
explicit CompilationResult(const char* error_message)
: error_message(error_message) {}
CompilationResult(Object code, int registers)
: code(code), num_registers(registers) {}
static CompilationResult RegExpTooBig() {
return CompilationResult(RegExpError::kTooLarge);
return CompilationResult("RegExp too big");
}
bool Succeeded() const { return error == RegExpError::kNone; }
bool Succeeded() const { return error_message == nullptr; }
const RegExpError error = RegExpError::kNone;
const char* const error_message = nullptr;
Object code;
int num_registers = 0;
};
@@ -571,7 +575,7 @@ class RegExpCompiler {
int next_register_;
int unicode_lookaround_stack_register_;
int unicode_lookaround_position_register_;
ZoneVector<RegExpNode*>* work_list_;
std::vector<RegExpNode*>* work_list_;
int recursion_depth_;
RegExpMacroAssembler* macro_assembler_;
bool one_byte_;
-1
View File
@@ -5,7 +5,6 @@
#ifndef V8_REGEXP_REGEXP_DOTPRINTER_H_
#define V8_REGEXP_REGEXP_DOTPRINTER_H_
#include "regexp/regexp-shim.h"
namespace v8 {
namespace internal {
-22
View File
@@ -1,22 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "regexp/regexp-error.h"
namespace v8 {
namespace internal {
const char* kRegExpErrorStrings[] = {
#define TEMPLATE(NAME, STRING) STRING,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
};
const char* RegExpErrorString(RegExpError error) {
DCHECK_LT(error, RegExpError::NumErrors);
return kRegExpErrorStrings[static_cast<int>(error)];
}
} // namespace internal
} // namespace v8
-57
View File
@@ -1,57 +0,0 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_REGEXP_REGEXP_ERROR_H_
#define V8_REGEXP_REGEXP_ERROR_H_
#include "regexp-shim.h"
namespace v8 {
namespace internal {
#define REGEXP_ERROR_MESSAGES(T) \
T(None, "") \
T(StackOverflow, "Maximum call stack size exceeded") \
T(AnalysisStackOverflow, "Stack overflow") \
T(TooLarge, "Regular expression too large") \
T(UnterminatedGroup, "Unterminated group") \
T(UnmatchedParen, "Unmatched ')'") \
T(EscapeAtEndOfPattern, "\\ at end of pattern") \
T(InvalidPropertyName, "Invalid property name") \
T(InvalidEscape, "Invalid escape") \
T(InvalidDecimalEscape, "Invalid decimal escape") \
T(InvalidUnicodeEscape, "Invalid Unicode escape") \
T(NothingToRepeat, "Nothing to repeat") \
T(LoneQuantifierBrackets, "Lone quantifier brackets") \
T(RangeOutOfOrder, "numbers out of order in {} quantifier") \
T(IncompleteQuantifier, "Incomplete quantifier") \
T(InvalidQuantifier, "Invalid quantifier") \
T(InvalidGroup, "Invalid group") \
T(MultipleFlagDashes, "Multiple dashes in flag group") \
T(RepeatedFlag, "Repeated flag in flag group") \
T(InvalidFlagGroup, "Invalid flag group") \
T(TooManyCaptures, "Too many captures") \
T(InvalidCaptureGroupName, "Invalid capture group name") \
T(DuplicateCaptureGroupName, "Duplicate capture group name") \
T(InvalidNamedReference, "Invalid named reference") \
T(InvalidNamedCaptureReference, "Invalid named capture referenced") \
T(InvalidClassEscape, "Invalid class escape") \
T(InvalidClassPropertyName, "Invalid property name in character class") \
T(InvalidCharacterClass, "Invalid character class") \
T(UnterminatedCharacterClass, "Unterminated character class") \
T(OutOfOrderCharacterClass, "Range out of order in character class")
enum class RegExpError : uint32_t {
#define TEMPLATE(NAME, STRING) k##NAME,
REGEXP_ERROR_MESSAGES(TEMPLATE)
#undef TEMPLATE
NumErrors
};
V8_EXPORT_PRIVATE const char* RegExpErrorString(RegExpError error);
} // namespace internal
} // namespace v8
#endif // V8_REGEXP_REGEXP_ERROR_H_
+40 -25
View File
@@ -28,18 +28,18 @@ namespace internal {
namespace {
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
Vector<const uc16> subject) {
Vector<const uc16> subject, bool unicode) {
Address offset_a =
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(from)));
Address offset_b =
reinterpret_cast<Address>(const_cast<uc16*>(&subject.at(current)));
size_t length = len * kUC16Size;
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(offset_a, offset_b,
length, isolate) == 1;
return RegExpMacroAssembler::CaseInsensitiveCompareUC16(
offset_a, offset_b, length, unicode ? nullptr : isolate) == 1;
}
bool BackRefMatchesNoCase(Isolate* isolate, int from, int current, int len,
Vector<const uint8_t> subject) {
Vector<const uint8_t> subject, bool unicode) {
// For Latin1 characters the unicode flag makes no difference.
for (int i = 0; i < len; i++) {
unsigned int old_char = subject[from++];
@@ -82,17 +82,11 @@ int32_t Load32Aligned(const byte* pc) {
return *reinterpret_cast<const int32_t*>(pc);
}
// TODO(jgruber): Rename to Load16AlignedUnsigned.
uint32_t Load16Aligned(const byte* pc) {
int32_t Load16Aligned(const byte* pc) {
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
return *reinterpret_cast<const uint16_t*>(pc);
}
int32_t Load16AlignedSigned(const byte* pc) {
DCHECK_EQ(0, reinterpret_cast<intptr_t>(pc) & 1);
return *reinterpret_cast<const int16_t*>(pc);
}
// A simple abstraction over the backtracking stack used by the interpreter.
//
// Despite the name 'backtracking' stack, it's actually used as a generic stack
@@ -740,14 +734,26 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
DISPATCH();
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE) {
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current + len > subject.length() ||
!BackRefMatchesNoCase(isolate, from, current, len, subject, true)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
current += len;
}
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE);
DISPATCH();
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current + len > subject.length() ||
!BackRefMatchesNoCase(isolate, from, current, len, subject)) {
!BackRefMatchesNoCase(isolate, from, current, len, subject,
false)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
@@ -757,14 +763,27 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
DISPATCH();
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD) {
UNREACHABLE(); // TODO(jgruber): Remove this unused bytecode.
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current - len < 0 ||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
true)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
current -= len;
}
ADVANCE(CHECK_NOT_BACK_REF_NO_CASE_UNICODE_BACKWARD);
DISPATCH();
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE_BACKWARD) {
int from = registers[insn >> BYTECODE_SHIFT];
int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from >= 0 && len > 0) {
if (current - len < 0 ||
!BackRefMatchesNoCase(isolate, from, current - len, len, subject)) {
!BackRefMatchesNoCase(isolate, from, current - len, len, subject,
false)) {
SET_PC_FROM_OFFSET(Load32Aligned(pc + 4));
DISPATCH();
}
@@ -809,7 +828,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR) {
int load_offset = (insn >> BYTECODE_SHIFT);
int32_t advance = Load16AlignedSigned(pc + 4);
uint32_t advance = Load16Aligned(pc + 4);
uint32_t c = Load16Aligned(pc + 6);
while (static_cast<uintptr_t>(current + load_offset) <
static_cast<uintptr_t>(subject.length())) {
@@ -825,7 +844,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR_AND) {
int load_offset = (insn >> BYTECODE_SHIFT);
int32_t advance = Load16AlignedSigned(pc + 4);
uint16_t advance = Load16Aligned(pc + 4);
uint16_t c = Load16Aligned(pc + 6);
uint32_t mask = Load32Aligned(pc + 8);
int32_t maximum_offset = Load32Aligned(pc + 12);
@@ -843,7 +862,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR_POS_CHECKED) {
int load_offset = (insn >> BYTECODE_SHIFT);
int32_t advance = Load16AlignedSigned(pc + 4);
uint16_t advance = Load16Aligned(pc + 4);
uint16_t c = Load16Aligned(pc + 6);
int32_t maximum_offset = Load32Aligned(pc + 8);
while (static_cast<uintptr_t>(current + maximum_offset) <=
@@ -860,7 +879,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_BIT_IN_TABLE) {
int load_offset = (insn >> BYTECODE_SHIFT);
int32_t advance = Load16AlignedSigned(pc + 4);
uint32_t advance = Load16Aligned(pc + 4);
const byte* table = pc + 8;
while (static_cast<uintptr_t>(current + load_offset) <
static_cast<uintptr_t>(subject.length())) {
@@ -876,7 +895,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE) {
int load_offset = (insn >> BYTECODE_SHIFT);
int32_t advance = Load16AlignedSigned(pc + 4);
uint16_t advance = Load16Aligned(pc + 4);
uint16_t limit = Load16Aligned(pc + 6);
const byte* table = pc + 8;
while (static_cast<uintptr_t>(current + load_offset) <
@@ -897,7 +916,7 @@ IrregexpInterpreter::Result RawMatch(Isolate* isolate, ByteArray code_array,
}
BYTECODE(SKIP_UNTIL_CHAR_OR_CHAR) {
int load_offset = (insn >> BYTECODE_SHIFT);
int32_t advance = Load32Aligned(pc + 4);
uint32_t advance = Load32Aligned(pc + 4);
uint16_t c = Load16Aligned(pc + 8);
uint16_t c2 = Load16Aligned(pc + 10);
while (static_cast<uintptr_t>(current + load_offset) <
@@ -997,8 +1016,6 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchInternal(
}
}
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// This method is called through an external reference from RegExpExecInternal
// builtin.
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
@@ -1025,8 +1042,6 @@ IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromJs(
start_position, call_origin);
}
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
IrregexpInterpreter::Result IrregexpInterpreter::MatchForCallFromRuntime(
Isolate* isolate, Handle<JSRegExp> regexp, Handle<String> subject_string,
int* registers, int registers_length, int start_position) {
-291
View File
@@ -1,291 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements the NativeRegExpMacroAssembler interface for
// SpiderMonkey. It provides the same interface as each of V8's
// architecture-specific implementations.
#ifndef RegexpMacroAssemblerArch_h
#define RegexpMacroAssemblerArch_h
#include "jit/MacroAssembler.h"
#include "regexp/regexp-macro-assembler.h"
namespace v8 {
namespace internal {
struct FrameData {
// Character position at the start of the input, stored as a
// negative offset from the end of the string (input_end_pointer_).
size_t inputStart;
// The backtrack_stack_pointer_ register points to the top of the stack.
// This points to the bottom of the backtrack stack.
void* backtrackStackBase;
// Copy of the input MatchPairs.
int32_t* matches; // pointer to capture array
int32_t numMatches; // size of capture array
};
class SMRegExpMacroAssembler final : public NativeRegExpMacroAssembler {
public:
SMRegExpMacroAssembler(JSContext* cx, Isolate* isolate,
js::jit::StackMacroAssembler& masm, Zone* zone,
Mode mode, uint32_t num_capture_registers);
virtual ~SMRegExpMacroAssembler() {} // Nothing to do here
virtual int stack_limit_slack();
virtual IrregexpImplementation Implementation();
virtual bool Succeed();
virtual void Fail();
virtual void AdvanceCurrentPosition(int by);
virtual void PopCurrentPosition();
virtual void PushCurrentPosition();
virtual void SetCurrentPositionFromEnd(int by);
virtual void Backtrack();
virtual void Bind(Label* label);
virtual void GoTo(Label* label);
virtual void PushBacktrack(Label* label);
virtual void CheckCharacter(uint32_t c, Label* on_equal);
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
virtual void CheckCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_equal);
virtual void CheckNotCharacterAfterAnd(uint32_t c, uint32_t mask,
Label* on_not_equal);
virtual void CheckNotCharacterAfterMinusAnd(uc16 c, uc16 minus, uc16 mask,
Label* on_not_equal);
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
virtual void CheckCharacterInRange(uc16 from, uc16 to, Label* on_in_range);
virtual void CheckCharacterNotInRange(uc16 from, uc16 to,
Label* on_not_in_range);
virtual void CheckAtStart(int cp_offset, Label* on_at_start);
virtual void CheckNotAtStart(int cp_offset, Label* on_not_at_start);
virtual void CheckPosition(int cp_offset, Label* on_outside_input);
virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
virtual bool CheckSpecialCharacterClass(uc16 type, Label* on_no_match);
virtual void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match);
virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
bool read_backward,
Label* on_no_match);
virtual void LoadCurrentCharacterImpl(int cp_offset, Label* on_end_of_input,
bool check_bounds, int characters,
int eats_at_least);
virtual void AdvanceRegister(int reg, int by);
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
virtual void IfRegisterEqPos(int reg, Label* if_eq);
virtual void PopRegister(int register_index);
virtual void PushRegister(int register_index,
StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
virtual void ReadStackPointerFromRegister(int reg);
virtual void WriteStackPointerToRegister(int reg);
virtual void SetRegister(int register_index, int to);
virtual void ClearRegisters(int reg_from, int reg_to);
virtual Handle<HeapObject> GetCode(Handle<String> source);
private:
size_t frameSize_ = 0;
void createStackFrame();
void initFrameAndRegs();
void successHandler();
void exitHandler();
void backtrackHandler();
void stackOverflowHandler();
// Push a register on the backtrack stack.
void Push(js::jit::Register value);
// Pop a value from the backtrack stack.
void Pop(js::jit::Register target);
void CheckAtStartImpl(int cp_offset, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckCharacterImpl(js::jit::Imm32 c, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckCharacterAfterAndImpl(uint32_t c, uint32_t and_with, Label* on_cond,
bool negate);
void CheckCharacterInRangeImpl(uc16 from, uc16 to, Label* on_cond,
js::jit::Assembler::Condition cond);
void CheckNotBackReferenceImpl(int start_reg, bool read_backward,
Label* on_no_match, bool ignore_case);
void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
void JumpOrBacktrack(Label* to);
// MacroAssembler methods that take a Label can be called with a
// null label, which means that we should backtrack if we would jump
// to that label. This is a helper to avoid writing out the same
// logic a dozen times.
inline js::jit::Label* LabelOrBacktrack(Label* to) {
return to ? to->inner() : &backtrack_label_;
}
void CheckBacktrackStackLimit();
static bool GrowBacktrackStack(RegExpStack* regexp_stack);
static uint32_t CaseInsensitiveCompareStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
static uint32_t CaseInsensitiveCompareUCStrings(const char16_t* substring1,
const char16_t* substring2,
size_t byteLength);
inline int char_size() { return static_cast<int>(mode_); }
inline js::jit::Scale factor() {
return mode_ == UC16 ? js::jit::TimesTwo : js::jit::TimesOne;
}
js::jit::Address inputStart() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, inputStart));
}
js::jit::Address backtrackStackBase() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, backtrackStackBase));
}
js::jit::Address matches() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, matches));
}
js::jit::Address numMatches() {
return js::jit::Address(masm_.getStackPointer(),
offsetof(FrameData, numMatches));
}
// The stack-pointer-relative location of a regexp register.
js::jit::Address register_location(int register_index) {
return js::jit::Address(masm_.getStackPointer(),
register_offset(register_index));
}
int32_t register_offset(int register_index) {
MOZ_ASSERT(register_index >= 0 && register_index <= kMaxRegister);
if (num_registers_ <= register_index) {
num_registers_ = register_index + 1;
}
static_assert(alignof(uintptr_t) <= alignof(FrameData));
return sizeof(FrameData) + register_index * sizeof(uintptr_t*);
}
JSContext* cx_;
js::jit::StackMacroAssembler& masm_;
/*
* This assembler uses the following registers:
*
* - current_character_:
* Contains the character (or characters) currently being examined.
* Must be loaded using LoadCurrentCharacter before using any of the
* dispatch methods. After a matching pass for a global regexp,
* temporarily stores the index of capture start.
* - current_position_:
* Current position in input *as negative byte offset from end of string*.
* - input_end_pointer_:
* Points to byte after last character in the input. current_position_ is
* relative to this.
* - backtrack_stack_pointer_:
* Points to tip of the (heap-allocated) backtrack stack. The stack grows
* downward (like the native stack).
* - temp0_, temp1_, temp2_:
* Scratch registers.
*
* The native stack pointer is used to access arguments (InputOutputData),
* local variables (FrameData), and irregexp's internal virtual registers
* (see register_location).
*/
js::jit::Register current_character_;
js::jit::Register current_position_;
js::jit::Register input_end_pointer_;
js::jit::Register backtrack_stack_pointer_;
js::jit::Register temp0_, temp1_, temp2_;
js::jit::Label entry_label_;
js::jit::Label start_label_;
js::jit::Label backtrack_label_;
js::jit::Label success_label_;
js::jit::Label exit_label_;
js::jit::Label stack_overflow_label_;
js::jit::Label exit_with_exception_label_;
// When we generate the code to push a backtrack label's address
// onto the backtrack stack, we don't know its final address. We
// have to patch it after linking. This is slightly delicate, as the
// Label itself (which is allocated on the stack) may not exist by
// the time we link. The approach is as follows:
//
// 1. When we push a label on the backtrack stack (PushBacktrack),
// we bind the label's patchOffset_ field to the offset within
// the code that should be overwritten. This works because each
// label is only pushed by a single instruction.
//
// 2. When we bind a label (Bind), we check to see if it has a
// bound patchOffset_. If it does, we create a LabelPatch mapping
// its patch offset to the offset of the label itself.
//
// 3. While linking the code, we walk the list of label patches
// and patch the code accordingly.
class LabelPatch {
public:
LabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset)
: patchOffset_(patchOffset), labelOffset_(labelOffset) {}
js::jit::CodeOffset patchOffset_;
size_t labelOffset_ = 0;
};
js::Vector<LabelPatch, 4, js::SystemAllocPolicy> labelPatches_;
void AddLabelPatch(js::jit::CodeOffset patchOffset, size_t labelOffset) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!labelPatches_.emplaceBack(patchOffset, labelOffset)) {
oomUnsafe.crash("Irregexp label patch");
}
}
Mode mode_;
int num_registers_;
int num_capture_registers_;
js::jit::LiveGeneralRegisterSet savedRegisters_;
public:
using TableVector =
js::Vector<PseudoHandle<ByteArrayData>, 4, js::SystemAllocPolicy>;
TableVector& tables() { return tables_; }
private:
TableVector tables_;
void AddTable(PseudoHandle<ByteArrayData> table) {
js::AutoEnterOOMUnsafeRegion oomUnsafe;
if (!tables_.append(std::move(table))) {
oomUnsafe.crash("Irregexp table append");
}
}
};
} // namespace internal
} // namespace v8
#endif // RegexpMacroAssemblerArch_h
@@ -349,15 +349,17 @@ void RegExpMacroAssemblerTracer::CheckNotBackReference(int start_reg,
assembler_->CheckNotBackReference(start_reg, read_backward, on_no_match);
}
void RegExpMacroAssemblerTracer::CheckNotBackReferenceIgnoreCase(
int start_reg, bool read_backward, Label* on_no_match) {
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s, label[%08x]);\n",
int start_reg, bool read_backward, bool unicode, Label* on_no_match) {
PrintF(" CheckNotBackReferenceIgnoreCase(register=%d, %s %s, label[%08x]);\n",
start_reg, read_backward ? "backward" : "forward",
LabelToInt(on_no_match));
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward,
unicode ? "unicode" : "non-unicode", LabelToInt(on_no_match));
assembler_->CheckNotBackReferenceIgnoreCase(start_reg, read_backward, unicode,
on_no_match);
}
void RegExpMacroAssemblerTracer::CheckPosition(int cp_offset,
Label* on_outside_input) {
PrintF(" CheckPosition(cp_offset=%d, label[%08x]);\n", cp_offset,
@@ -33,6 +33,7 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler {
void CheckNotBackReference(int start_reg, bool read_backward,
Label* on_no_match) override;
void CheckNotBackReferenceIgnoreCase(int start_reg, bool read_backward,
bool unicode,
Label* on_no_match) override;
void CheckNotCharacter(unsigned c, Label* on_not_equal) override;
void CheckNotCharacterAfterAnd(unsigned c, unsigned and_with,
+35 -12
View File
@@ -110,7 +110,34 @@ bool NativeRegExpMacroAssembler::CanReadUnaligned() {
return FLAG_enable_regexp_unaligned_accesses && !slow_safe();
}
#ifndef COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
const byte* NativeRegExpMacroAssembler::StringCharacterPosition(
String subject, int start_index, const DisallowHeapAllocation& no_gc) {
if (subject.IsConsString()) {
subject = ConsString::cast(subject).first();
} else if (subject.IsSlicedString()) {
start_index += SlicedString::cast(subject).offset();
subject = SlicedString::cast(subject).parent();
}
if (subject.IsThinString()) {
subject = ThinString::cast(subject).actual();
}
DCHECK_LE(0, start_index);
DCHECK_LE(start_index, subject.length());
if (subject.IsSeqOneByteString()) {
return reinterpret_cast<const byte*>(
SeqOneByteString::cast(subject).GetChars(no_gc) + start_index);
} else if (subject.IsSeqTwoByteString()) {
return reinterpret_cast<const byte*>(
SeqTwoByteString::cast(subject).GetChars(no_gc) + start_index);
} else if (subject.IsExternalOneByteString()) {
return reinterpret_cast<const byte*>(
ExternalOneByteString::cast(subject).GetChars() + start_index);
} else {
DCHECK(subject.IsExternalTwoByteString());
return reinterpret_cast<const byte*>(
ExternalTwoByteString::cast(subject).GetChars() + start_index);
}
}
// This method may only be called after an interrupt.
int NativeRegExpMacroAssembler::CheckStackGuardState(
@@ -118,10 +145,9 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
Address* return_address, Code re_code, Address* subject,
const byte** input_start, const byte** input_end) {
DisallowHeapAllocation no_gc;
Address old_pc = PointerAuthentication::AuthenticatePC(return_address, 0);
DCHECK_LE(re_code.raw_instruction_start(), old_pc);
DCHECK_LE(old_pc, re_code.raw_instruction_end());
DCHECK(re_code.raw_instruction_start() <= *return_address);
DCHECK(*return_address <= re_code.raw_instruction_end());
StackLimitCheck check(isolate);
bool js_has_overflowed = check.JsHasOverflowed();
@@ -163,11 +189,9 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
}
if (*code_handle != re_code) { // Return address no longer valid
// Overwrite the return address on the stack.
intptr_t delta = code_handle->address() - re_code.address();
Address new_pc = old_pc + delta;
// TODO(v8:10026): avoid replacing a signed pointer.
PointerAuthentication::ReplacePC(return_address, new_pc, 0);
// Overwrite the return address on the stack.
*return_address += delta;
}
// If we continue, we need to update the subject string addresses.
@@ -182,7 +206,8 @@ int NativeRegExpMacroAssembler::CheckStackGuardState(
} else {
*subject = subject_handle->ptr();
intptr_t byte_length = *input_end - *input_start;
*input_start = subject_handle->AddressOfCharacterAt(start_index, no_gc);
*input_start =
StringCharacterPosition(*subject_handle, start_index, no_gc);
*input_end = *input_start + byte_length;
}
}
@@ -230,7 +255,7 @@ int NativeRegExpMacroAssembler::Match(Handle<JSRegExp> regexp,
DisallowHeapAllocation no_gc;
const byte* input_start =
subject_ptr.AddressOfCharacterAt(start_offset + slice_offset, no_gc);
StringCharacterPosition(subject_ptr, start_offset + slice_offset, no_gc);
int byte_length = char_length << char_size_shift;
const byte* input_end = input_start + byte_length;
return Execute(*subject, start_offset, input_start, input_end, offsets_vector,
@@ -276,8 +301,6 @@ int NativeRegExpMacroAssembler::Execute(
return result;
}
#endif // !COMPILING_IRREGEXP_FOR_EXTERNAL_EMBEDDER
// clang-format off
const byte NativeRegExpMacroAssembler::word_character_map[] = {
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
Loaded 100 of 247 files, more files were not shown because too many files have changed in this diff. Show more