Merge m-c to autoland, a=merge

MozReview-Commit-ID: 5c1Pgm4aVKe
This commit is contained in:
Wes Kocher 2017-08-29 16:17:50 -07:00
commit de530c1ea6
103 changed files with 2476 additions and 1006 deletions

View File

@ -73,15 +73,6 @@ var inputTests = [
},
// 7
{
input: "Date.prototype",
output: /Object \{.*\}/,
printOutput: "Invalid Date",
inspectable: true,
variablesViewLabel: "Object",
},
// 8
{
input: "new Number(43)",
output: "Number { 43 }",
@ -90,7 +81,7 @@ var inputTests = [
variablesViewLabel: "Number { 43 }"
},
// 9
// 8
{
input: "new String('hello')",
output: /String { "hello", 6 more.* }/,
@ -99,7 +90,7 @@ var inputTests = [
variablesViewLabel: "String"
},
// 10
// 9
{
input: "(function () { var s = new String('hello'); s.whatever = 23; " +
" return s;})()",
@ -109,7 +100,7 @@ var inputTests = [
variablesViewLabel: "String"
},
// 11
// 10
{
input: "(function () { var s = new String('hello'); s[8] = 'x'; " +
" return s;})()",
@ -119,7 +110,7 @@ var inputTests = [
variablesViewLabel: "String"
},
// 12
// 11
{
// XXX: Can't test fulfilled and rejected promises, because promises get
// settled on the next tick of the event loop.
@ -130,7 +121,7 @@ var inputTests = [
variablesViewLabel: "Promise"
},
// 13
// 12
{
input: "(function () { var p = new Promise(function () {}); " +
"p.foo = 1; return p; }())",
@ -140,7 +131,7 @@ var inputTests = [
variablesViewLabel: "Promise"
},
// 14
// 13
{
input: "new Object({1: 'this\\nis\\nsupposed\\nto\\nbe\\na\\nvery" +
"\\nlong\\nstring\\n,shown\\non\\na\\nsingle\\nline', " +
@ -152,7 +143,7 @@ var inputTests = [
variablesViewLabel: "Object[4]"
},
// 15
// 14
{
input: "new Proxy({a:1},[1,2,3])",
output: 'Proxy { <target>: Object, <handler>: Array[3] }',

View File

@ -887,8 +887,7 @@ ReadWasmModule(JSContext* aCx,
MOZ_ASSERT(aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessSameThread ||
aHolder->CloneScope() == StructuredCloneHolder::StructuredCloneScope::SameProcessDifferentThread);
RefPtr<JS::WasmModule> wasmModule = aHolder->WasmModules()[aIndex];
return wasmModule->createObject(aCx);
return aHolder->WasmModules()[aIndex]->createObject(aCx);
}
bool

View File

@ -35,6 +35,7 @@ EXPORTS.mozilla.dom += [
'MultipartBlobImpl.h',
'MutableBlobStorage.h',
'MutableBlobStreamListener.h',
'StreamBlobImpl.h',
]
UNIFIED_SOURCES += [

View File

@ -35,6 +35,7 @@
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/IDBObjectStoreBinding.h"
#include "mozilla/dom/MemoryBlobImpl.h"
#include "mozilla/dom/StreamBlobImpl.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
@ -42,6 +43,8 @@
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsCOMPtr.h"
#include "nsQueryObject.h"
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "ProfilerHelpers.h"
#include "ReportInternalError.h"
#include "WorkerPrivate.h"
@ -189,6 +192,288 @@ GenerateRequest(JSContext* aCx, IDBObjectStore* aObjectStore)
return request.forget();
}
// Blob internal code clones this stream before it's passed to IPC, so we
// serialize module's optimized code only when AsyncWait() is called.
class WasmCompiledModuleStream final
: public nsIAsyncInputStream
, public nsICloneableInputStream
, private JS::WasmModuleListener
{
nsCOMPtr<nsISerialEventTarget> mOwningThread;
// Initially and while waiting for compilation to complete, the mModule field
// is non-null, keeping alive the module whose code needs to be serialized.
RefPtr<JS::WasmModule> mModule;
// A module stream can have up to one input stream callback.
nsCOMPtr<nsIInputStreamCallback> mCallback;
// When a module's optimized code is ready to be serialized, it will be
// serialized into mStream and the mModule will be released. At this point,
// stream reads will succeed.
nsCOMPtr<nsIInputStream> mStream;
// When the stream is finished reading or closed, mStatus will be changed from
// NS_OK to NS_BASE_STREAM_CLOSED or whatever status was passed to
// CloseWithStatus.
nsresult mStatus;
public:
explicit WasmCompiledModuleStream(JS::WasmModule* aModule)
: mOwningThread(GetCurrentThreadSerialEventTarget())
, mModule(aModule)
, mStatus(NS_OK)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aModule);
}
bool
IsOnOwningThread() const
{
MOZ_ASSERT(mOwningThread);
bool current;
return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)) && current;
}
void
AssertIsOnOwningThread() const
{
MOZ_ASSERT(IsOnOwningThread());
}
private:
// Copy constructor for cloning.
explicit WasmCompiledModuleStream(const WasmCompiledModuleStream& aOther)
: mOwningThread(aOther.mOwningThread)
, mModule(aOther.mModule)
, mStatus(aOther.mStatus)
{
AssertIsOnOwningThread();
if (aOther.mStream) {
nsCOMPtr<nsICloneableInputStream> cloneableStream =
do_QueryInterface(aOther.mStream);
MOZ_ASSERT(cloneableStream);
MOZ_ALWAYS_SUCCEEDS(cloneableStream->Clone(getter_AddRefs(mStream)));
}
}
~WasmCompiledModuleStream() override
{
AssertIsOnOwningThread();
MOZ_ALWAYS_SUCCEEDS(Close());
}
void
CallCallback()
{
AssertIsOnOwningThread();
MOZ_ASSERT(mCallback);
nsCOMPtr<nsIInputStreamCallback> callback;
callback.swap(mCallback);
callback->OnInputStreamReady(this);
}
NS_DECL_THREADSAFE_ISUPPORTS
// nsIInputStream:
NS_IMETHOD
Close() override
{
AssertIsOnOwningThread();
return CloseWithStatus(NS_BASE_STREAM_CLOSED);
}
NS_IMETHOD
Available(uint64_t* _retval) override
{
AssertIsOnOwningThread();
if (NS_FAILED(mStatus)) {
return mStatus;
}
if (!mStream) {
*_retval = 0;
return NS_OK;
}
return mStream->Available(_retval);
}
NS_IMETHOD
Read(char* aBuf, uint32_t aCount, uint32_t* _retval) override
{
AssertIsOnOwningThread();
return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
}
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter,
void* aClosure,
uint32_t aCount,
uint32_t* _retval) override
{
AssertIsOnOwningThread();
if (NS_FAILED(mStatus)) {
*_retval = 0;
return NS_OK;
}
if (!mStream) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
return mStream->ReadSegments(aWriter, aClosure, aCount, _retval);
}
NS_IMETHOD
IsNonBlocking(bool* _retval) override
{
AssertIsOnOwningThread();
*_retval = true;
return NS_OK;
}
// nsIAsyncInputStream:
NS_IMETHOD
CloseWithStatus(nsresult aStatus) override
{
AssertIsOnOwningThread();
if (NS_FAILED(mStatus)) {
return NS_OK;
}
mModule = nullptr;
if (mStream) {
MOZ_ALWAYS_SUCCEEDS(mStream->Close());
mStream = nullptr;
}
mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
if (mCallback) {
CallCallback();
}
return NS_OK;
}
NS_IMETHOD
AsyncWait(nsIInputStreamCallback* aCallback,
uint32_t aFlags,
uint32_t aRequestedCount,
nsIEventTarget* aEventTarget) override
{
AssertIsOnOwningThread();
MOZ_ASSERT_IF(mCallback, !aCallback);
if (aFlags) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (!aCallback) {
mCallback = nullptr;
return NS_OK;
}
if (aEventTarget) {
mCallback =
NS_NewInputStreamReadyEvent("WasmCompiledModuleStream::AsyncWait",
aCallback,
aEventTarget);
} else {
mCallback = aCallback;
}
if (NS_FAILED(mStatus) || mStream) {
CallCallback();
return NS_OK;
}
MOZ_ASSERT(mModule);
mModule->notifyWhenCompilationComplete(this);
return NS_OK;
}
// nsICloneableInputStream:
NS_IMETHOD
GetCloneable(bool* aCloneable) override
{
AssertIsOnOwningThread();
*aCloneable = true;
return NS_OK;
}
NS_IMETHOD
Clone(nsIInputStream** _retval) override
{
AssertIsOnOwningThread();
nsCOMPtr<nsIInputStream> clone = new WasmCompiledModuleStream(*this);
clone.forget(_retval);
return NS_OK;
}
// JS::WasmModuleListener:
void
onCompilationComplete() override
{
if (!IsOnOwningThread()) {
MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(NewCancelableRunnableMethod(
"WasmCompiledModuleStream::onCompilationComplete",
this,
&WasmCompiledModuleStream::onCompilationComplete)));
return;
}
if (NS_FAILED(mStatus) || !mCallback) {
return;
}
MOZ_ASSERT(mModule);
size_t compiledSize = mModule->compiledSerializedSize();
nsCString compiled;
compiled.SetLength(compiledSize);
mModule->compiledSerialize(
reinterpret_cast<uint8_t*>(compiled.BeginWriting()), compiledSize);
MOZ_ALWAYS_SUCCEEDS(NS_NewCStringInputStream(getter_AddRefs(mStream),
compiled));
mModule = nullptr;
CallCallback();
}
};
NS_IMPL_ISUPPORTS(WasmCompiledModuleStream,
nsIInputStream,
nsIAsyncInputStream,
nsICloneableInputStream)
bool
StructuredCloneWriteCallback(JSContext* aCx,
JSStructuredCloneWriter* aWriter,
@ -351,20 +636,26 @@ StructuredCloneWriteCallback(JSContext* aCx,
size_t bytecodeSize = module->bytecodeSerializedSize();
UniquePtr<uint8_t[]> bytecode(new uint8_t[bytecodeSize]);
MOZ_ASSERT(bytecode);
module->bytecodeSerialize(bytecode.get(), bytecodeSize);
RefPtr<BlobImpl> blobImpl =
new MemoryBlobImpl(bytecode.release(), bytecodeSize, EmptyString());
RefPtr<Blob> bytecodeBlob = Blob::Create(nullptr, blobImpl);
size_t compiledSize = module->compiledSerializedSize();
UniquePtr<uint8_t[]> compiled(new uint8_t[compiledSize]);
MOZ_ASSERT(compiled);
module->compiledSerialize(compiled.get(), compiledSize);
if (module->compilationComplete()) {
size_t compiledSize = module->compiledSerializedSize();
UniquePtr<uint8_t[]> compiled(new uint8_t[compiledSize]);
module->compiledSerialize(compiled.get(), compiledSize);
blobImpl =
new MemoryBlobImpl(compiled.release(), compiledSize, EmptyString());
} else {
nsCOMPtr<nsIInputStream> stream(new WasmCompiledModuleStream(module));
blobImpl = StreamBlobImpl::Create(stream, EmptyString(), UINT64_MAX);
}
blobImpl =
new MemoryBlobImpl(compiled.release(), compiledSize, EmptyString());
RefPtr<Blob> compiledBlob = Blob::Create(nullptr, blobImpl);
if (cloneWriteInfo->mFiles.Length() + 1 > size_t(UINT32_MAX)) {

View File

@ -77,6 +77,7 @@ function* testHarnessSteps() {
"set": [
["dom.indexedDB.testing", true],
["dom.indexedDB.experimental", true],
["javascript.options.wasm_baselinejit", true] // This can be removed when on by default
]
},
nextTestHarnessStep

View File

@ -115,6 +115,7 @@ support-files =
unit/test_wasm_index_getAllObjects.js
unit/test_wasm_indexes.js
unit/test_wasm_put_get_values.js
unit/test_wasm_serialize_tiering.js
unit/test_writer_starvation.js
[test_abort_deleted_index.html]
@ -266,3 +267,4 @@ scheme=https
[test_wasm_index_getAllObjects.html]
[test_wasm_indexes.html]
[test_wasm_put_get_values.html]
[test_wasm_serialize_tiering.html]

View File

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript" src="unit/test_wasm_serialize_tiering.js"></script>
<script type="text/javascript" src="file.js"></script>
<script type="text/javascript" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

View File

@ -0,0 +1,106 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
var testGenerator = testSteps();
function* testSteps()
{
const name =
this.window ? window.location.pathname : "test_wasm_serialize_tiering.js";
const objectStoreName = "Wasm";
if (!isWasmSupported()) {
finishTest();
return;
}
// Make a module big enough so that tiering is significant.
const N = 50;
let bigFunc = `(func (result f64)\n`;
for (let i = 0; i < N; i++) {
bigFunc += ` f64.const 1.0\n`;
}
for (let i = 0; i < N - 1; i++) {
bigFunc += ` f64.add\n`;
}
bigFunc += `)`;
let bigModule = `(module \n`;
for (let i = 0; i < 100; i++) {
bigModule += bigFunc;
}
bigModule += ` (export "run" (func 10))\n`;
bigModule += `)`;
getWasmBinary(bigModule);
let bigBinary = yield undefined;
info("Opening database");
let request = indexedDB.open(name);
request.onerror = errorHandler;
request.onupgradeneeded = continueToNextStepSync;
request.onsuccess = unexpectedSuccessHandler;
yield undefined;
// upgradeneeded
request.onupgradeneeded = unexpectedSuccessHandler;
request.onsuccess = continueToNextStepSync;
info("Creating objectStore");
request.result.createObjectStore(objectStoreName);
yield undefined;
// success
let db = request.result;
db.onerror = errorHandler;
info("Storing wasm modules");
let objectStore = db.transaction([objectStoreName], "readwrite")
.objectStore(objectStoreName);
const NumModules = 5;
const NumCopies = 5;
let finishedAdds = 0;
for (let moduleIndex = 0; moduleIndex < NumModules; moduleIndex++) {
let module = new WebAssembly.Module(bigBinary);
for (let copyIndex = 0; copyIndex < NumCopies; copyIndex++) {
let key = String(moduleIndex) + " " + String(copyIndex);
let request = objectStore.add(module, key);
request.onsuccess = function() {
is(request.result, key, "Got correct key");
if (++finishedAdds === NumModules * NumCopies) {
continueToNextStepSync();
}
}
}
}
yield undefined;
info("Getting wasm");
let finishedGets = 0;
for (let moduleIndex = 0; moduleIndex < NumModules; moduleIndex++) {
for (let copyIndex = 0; copyIndex < NumCopies; copyIndex++) {
let key = String(moduleIndex) + " " + String(copyIndex);
let request = objectStore.get(key);
request.onsuccess = function() {
let module = request.result;
let instance = new WebAssembly.Instance(module);
is(instance.exports.run(), N, "Got correct run() result");
if (++finishedGets === NumModules * NumCopies) {
continueToNextStepSync();
}
}
}
}
yield undefined;
finishTest();
}

View File

@ -681,3 +681,7 @@ var SpecialPowers = {
}
},
};
// This can be removed soon when on by default.
if (SpecialPowers.isMainProcess())
SpecialPowers.setBoolPref("javascript.options.wasm_baselinejit", true);

View File

@ -74,3 +74,5 @@ skip-if = coverage # bug 1336727
skip-if = coverage # bug 1336727
[test_wasm_recompile.js]
skip-if = coverage # bug 1336727
[test_wasm_serialize_tiering.js]
skip-if = coverage # bug 1336727

View File

@ -817,7 +817,7 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
return rv;
}
URIParams uriToLoad;
OptionalURIParams uriToLoad;
SerializeURI(aURI, uriToLoad);
Unused << SendCreateWindowInDifferentProcess(aTabOpener,
aChromeFlags,

View File

@ -4829,7 +4829,7 @@ ContentParent::RecvCreateWindowInDifferentProcess(
const bool& aCalledFromJS,
const bool& aPositionSpecified,
const bool& aSizeSpecified,
const URIParams& aURIToLoad,
const OptionalURIParams& aURIToLoad,
const nsCString& aFeatures,
const nsCString& aBaseURI,
const float& aFullZoom,

View File

@ -541,7 +541,7 @@ public:
const bool& aCalledFromJS,
const bool& aPositionSpecified,
const bool& aSizeSpecified,
const URIParams& aURIToLoad,
const OptionalURIParams& aURIToLoad,
const nsCString& aFeatures,
const nsCString& aBaseURI,
const float& aFullZoom,

View File

@ -1031,7 +1031,7 @@ parent:
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified,
URIParams aURIToLoad,
OptionalURIParams aURIToLoad,
nsCString aFeatures,
nsCString aBaseURI,
float aFullZoom,

View File

@ -77,3 +77,4 @@ skip-if = true || !e10s # Prerendering requires e10s, turned off for e10s-multi
support-files =
test_noopener_source.html
test_noopener_target.html
[browser_noopener_null_uri.js]

View File

@ -0,0 +1,12 @@
add_task(async function browserNoopenerNullUri() {
await BrowserTestUtils.withNewTab({gBrowser}, async function(aBrowser) {
let waitFor = BrowserTestUtils.waitForNewWindow();
await ContentTask.spawn(aBrowser, null, async () => {
ok(!content.window.open(undefined, undefined, 'noopener'),
"window.open should return null");
});
let win = await waitFor;
ok(win, "We successfully opened a new window in the content process!");
await BrowserTestUtils.closeWindow(win);
});
});

View File

@ -69,7 +69,7 @@ public:
}
void
SendData(const nsCString& aBuffer) override
SendData(const wr::ByteBuffer& aBuffer) override
{
Unused << SendBuffer(aBuffer);
}
@ -159,7 +159,7 @@ private:
}
IPCResult
RecvBuffer(const nsCString& aBuffer) override
RecvBuffer(const wr::ByteBuffer& aBuffer) override
{
BufferReceived(aBuffer);
return IPC_OK();

View File

@ -349,14 +349,16 @@ IPCStreamDestination::ActorDestroyed()
}
void
IPCStreamDestination::BufferReceived(const nsCString& aBuffer)
IPCStreamDestination::BufferReceived(const wr::ByteBuffer& aBuffer)
{
MOZ_ASSERT(mWriter);
uint32_t numWritten = 0;
// This should only fail if we hit an OOM condition.
nsresult rv = mWriter->Write(aBuffer.get(), aBuffer.Length(), &numWritten);
nsresult rv = mWriter->Write(reinterpret_cast<char*>(aBuffer.mData),
aBuffer.mLength,
&numWritten);
if (NS_WARN_IF(NS_FAILED(rv))) {
RequestClose(rv);
}

View File

@ -14,6 +14,11 @@ class nsIAsyncInputStream;
class nsIAsyncOutputStream;
namespace mozilla {
namespace wr {
struct ByteBuffer;
} // wr namespace
namespace ipc {
class PChildToParentStreamParent;
@ -55,7 +60,7 @@ protected:
ActorDestroyed();
void
BufferReceived(const nsCString& aBuffer);
BufferReceived(const wr::ByteBuffer& aBuffer);
void
CloseReceived(nsresult aRv);

View File

@ -67,7 +67,7 @@ public:
}
void
SendData(const nsCString& aBuffer) override
SendData(const wr::ByteBuffer& aBuffer) override
{
Unused << SendBuffer(aBuffer);
}
@ -159,7 +159,7 @@ private:
}
IPCResult
RecvBuffer(const nsCString& aBuffer) override
RecvBuffer(const wr::ByteBuffer& aBuffer) override
{
BufferReceived(aBuffer);
return IPC_OK();

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IPCStreamSource.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "nsIAsyncInputStream.h"
#include "nsICancelableRunnable.h"
#include "nsIRunnable.h"
@ -16,6 +17,7 @@ using mozilla::dom::workers::Canceling;
using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
using mozilla::dom::workers::Status;
using mozilla::dom::workers::WorkerPrivate;
using mozilla::wr::ByteBuffer;
namespace mozilla {
namespace ipc {
@ -190,7 +192,7 @@ void
IPCStreamSource::Start()
{
NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
DoRead(ReadReason::Starting);
DoRead();
}
void
@ -201,7 +203,7 @@ IPCStreamSource::StartDestroy()
}
void
IPCStreamSource::DoRead(ReadReason aReadReason)
IPCStreamSource::DoRead()
{
NS_ASSERT_OWNINGTHREAD(IPCStreamSource);
MOZ_ASSERT(mState == eActorConstructed);
@ -215,70 +217,44 @@ IPCStreamSource::DoRead(ReadReason aReadReason)
static_assert(kMaxBytesPerMessage <= static_cast<uint64_t>(UINT32_MAX),
"kMaxBytesPerMessage must cleanly cast to uint32_t");
// Per the nsIInputStream API, having 0 bytes available in a stream is
// ambiguous. It could mean "no data yet, but some coming later" or it could
// mean "EOF" (end of stream).
//
// If we're just starting up, we can't distinguish between those two options.
// But if we're being notified that an async stream is ready, then the
// first Available() call returning 0 should really mean end of stream.
// Subsequent Available() calls, after we read some data, could mean anything.
bool noDataMeansEOF = (aReadReason == ReadReason::Notified);
char buffer[kMaxBytesPerMessage];
while (true) {
// It should not be possible to transition to closed state without
// this loop terminating via a return.
MOZ_ASSERT(mState == eActorConstructed);
// Use non-auto here as we're unlikely to hit stack storage with the
// sizes we are sending. Also, it would be nice to avoid another copy
// to the IPC layer which we avoid if we use COW strings. Unfortunately
// IPC does not seem to support passing dependent storage types.
nsCString buffer;
uint64_t available = 0;
nsresult rv = mStream->Available(&available);
// See if the stream is closed by checking the return of Available.
uint64_t dummy;
nsresult rv = mStream->Available(&dummy);
if (NS_FAILED(rv)) {
OnEnd(rv);
return;
}
if (available == 0) {
if (noDataMeansEOF) {
OnEnd(rv);
} else {
Wait();
}
return;
}
uint32_t expectedBytes =
static_cast<uint32_t>(std::min(available, kMaxBytesPerMessage));
buffer.SetLength(expectedBytes);
uint32_t bytesRead = 0;
rv = mStream->Read(buffer.BeginWriting(), buffer.Length(), &bytesRead);
MOZ_ASSERT_IF(NS_FAILED(rv), bytesRead == 0);
buffer.SetLength(bytesRead);
// After this point, having no data tells us nothing about EOF.
noDataMeansEOF = false;
// If we read any data from the stream, send it across.
if (!buffer.IsEmpty()) {
SendData(buffer);
}
rv = mStream->Read(buffer, kMaxBytesPerMessage, &bytesRead);
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
MOZ_ASSERT(bytesRead == 0);
Wait();
return;
}
// Any other error or zero-byte read indicates end-of-stream
if (NS_FAILED(rv) || buffer.IsEmpty()) {
if (NS_FAILED(rv)) {
MOZ_ASSERT(bytesRead == 0);
OnEnd(rv);
return;
}
// Zero-byte read indicates end-of-stream.
if (bytesRead == 0) {
OnEnd(NS_BASE_STREAM_CLOSED);
return;
}
// We read some data from the stream, send it across.
SendData(ByteBuffer(bytesRead, reinterpret_cast<uint8_t*>(buffer)));
}
}
@ -307,7 +283,7 @@ IPCStreamSource::OnStreamReady(Callback* aCallback)
MOZ_ASSERT(aCallback == mCallback);
mCallback->ClearSource();
mCallback = nullptr;
DoRead(ReadReason::Notified);
DoRead();
}
void

View File

@ -20,6 +20,10 @@ class nsIContentChild;
class nsIContentParent;
} // dom namespace
namespace wr {
struct ByteBuffer;
} // wr namespace
namespace ipc {
class PBackgroundChild;
@ -109,7 +113,7 @@ protected:
Close(nsresult aRv) = 0;
virtual void
SendData(const nsCString& aBuffer) = 0;
SendData(const wr::ByteBuffer& aBuffer) = 0;
void
ActorConstructed();
@ -121,11 +125,7 @@ private:
virtual bool
Notify(dom::workers::Status aStatus) override;
enum class ReadReason {
Starting, // We're trying to read because we just started off.
Notified // We're trying to read because the streams said it's ready.
};
void DoRead(ReadReason aReadReason);
void DoRead();
void Wait();

View File

@ -6,6 +6,10 @@ include protocol PBackground;
include protocol PContent;
include protocol PContentBridge;
include "mozilla/layers/WebRenderMessageUtils.h";
using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
namespace mozilla {
namespace ipc {
@ -16,7 +20,7 @@ protocol PChildToParentStream
manager PBackground or PContent or PContentBridge;
parent:
async Buffer(nsCString aBuffer);
async Buffer(ByteBuffer aBuffer);
async Close(nsresult aRv);
child:

View File

@ -6,6 +6,10 @@ include protocol PBackground;
include protocol PContent;
include protocol PContentBridge;
include "mozilla/layers/WebRenderMessageUtils.h";
using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
namespace mozilla {
namespace ipc {
@ -16,7 +20,7 @@ protocol PParentToChildStream
manager PBackground or PContent or PContentBridge;
child:
async Buffer(nsCString aBuffer);
async Buffer(ByteBuffer aBuffer);
async Close(nsresult aRv);
parent:

View File

@ -93,6 +93,17 @@ ActivationContext::~ActivationContext()
Release();
}
/* static */ Result<uintptr_t,HRESULT>
ActivationContext::GetCurrent()
{
HANDLE actCtx;
if (!::GetCurrentActCtx(&actCtx)) {
return Result<uintptr_t,HRESULT>(HRESULT_FROM_WIN32(::GetLastError()));
}
return reinterpret_cast<uintptr_t>(actCtx);
}
ActivationContextRegion::ActivationContextRegion(const ActivationContext& aActCtx)
: mActCtx(aActCtx)
, mActCookie(0)

View File

@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/Move.h"
#include "mozilla/Result.h"
#include <windows.h>
@ -34,6 +35,8 @@ public:
return mActCtx != INVALID_HANDLE_VALUE;
}
static Result<uintptr_t,HRESULT> GetCurrent();
private:
void Init(ACTCTX& aActCtx);
void AddRef();

View File

@ -13,6 +13,9 @@
#include "mozilla/Assertions.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
#include "nsExceptionHandler.h"
#endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
#include "nsWindowsHelpers.h"
#include "nsXULAppAPI.h"
@ -47,6 +50,23 @@ MainThreadRuntime::MainThreadRuntime()
, mActCtxRgn(a11y::Compatibility::GetActCtxResourceId())
#endif // defined(ACCESSIBILITY)
{
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
GeckoProcessType procType = XRE_GetProcessType();
if (procType == GeckoProcessType_Default ||
procType == GeckoProcessType_Content) {
auto actctx = ActivationContext::GetCurrent();
nsAutoCString strActCtx;
if (actctx.isOk()) {
strActCtx.AppendPrintf("0x%p", actctx.unwrap());
} else {
strActCtx.AppendPrintf("HRESULT 0x%08X", actctx.unwrapErr());
}
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AssemblyManifestCtx"),
strActCtx);
}
#endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
// We must be the outermost COM initialization on this thread. The COM runtime
// cannot be configured once we start manipulating objects
MOZ_ASSERT(mStaRegion.IsValidOutermost());

View File

@ -333,7 +333,6 @@ GetOBJREFSize(NotNull<IStream*> aStream)
return 0;
}
MOZ_ASSERT(clsid == CLSID_StdMarshal);
if (clsid != CLSID_StdMarshal) {
// We can only calulate the size if the payload is a standard OBJREF as
// identified by clsid == CLSID_StdMarshal.

View File

@ -5,14 +5,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Move.h"
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
#include "HandlerData.h"
#include "mozilla/a11y/Platform.h"
#include "mozilla/mscom/ActivationContext.h"
#endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/mscom/ProxyStream.h"
#include "mozilla/mscom/Utils.h"
#if defined(MOZ_CRASHREPORTER)
#include "InterfaceRegistrationAnnotator.h"
#include "mozilla/mscom/Objref.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
#include "RegistrationAnnotator.h"
#endif
#include <windows.h>
@ -73,19 +79,30 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
return;
}
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
const uint32_t expectedStreamLen = GetOBJREFSize(WrapNotNull(mStream));
nsAutoCString strActCtx;
#endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
HRESULT unmarshalResult = S_OK;
// We need to convert to an interface here otherwise we mess up const
// correctness with IPDL. We'll request an IUnknown and then QI the
// actual interface later.
auto marshalFn = [&]() -> void
auto marshalFn = [this, &strActCtx, &unmarshalResult, &aIID]() -> void
{
// OK to forget mStream when calling into this function because the stream
// gets released even if the unmarshaling part fails.
#if defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
auto curActCtx = ActivationContext::GetCurrent();
if (curActCtx.isOk()) {
strActCtx.AppendPrintf("0x%p", curActCtx.unwrap());
} else {
strActCtx.AppendPrintf("HRESULT 0x%08X", curActCtx.unwrapErr());
}
#endif // defined(ACCESSIBILITY) && defined(MOZ_CRASHREPORTER)
unmarshalResult =
::CoGetInterfaceAndReleaseStream(mStream.forget().take(), aIID,
getter_AddRefs(mUnmarshaledProxy));
::CoUnmarshalInterface(mStream, aIID, getter_AddRefs(mUnmarshaledProxy));
MOZ_ASSERT(SUCCEEDED(unmarshalResult));
};
@ -98,12 +115,33 @@ ProxyStream::ProxyStream(REFIID aIID, const BYTE* aInitBuf,
EnsureMTA mta(marshalFn);
}
mStream = nullptr;
#if defined(MOZ_CRASHREPORTER)
if (FAILED(unmarshalResult)) {
if (FAILED(unmarshalResult) || !mUnmarshaledProxy) {
nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("CoGetInterfaceAndReleaseStreamFailure"), hrAsStr);
NS_LITERAL_CSTRING("CoUnmarshalInterfaceResult"), hrAsStr);
AnnotateInterfaceRegistration(aIID);
#if defined(ACCESSIBILITY)
AnnotateClassRegistration(CLSID_AccessibleHandler);
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("UnmarshalActCtx"),
strActCtx);
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("A11yHandlerRegistered"),
a11y::IsHandlerRegistered() ?
NS_LITERAL_CSTRING("true") :
NS_LITERAL_CSTRING("false"));
nsAutoCString strExpectedStreamLen;
strExpectedStreamLen.AppendInt(expectedStreamLen);
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ExpectedStreamLen"),
strExpectedStreamLen);
nsAutoCString actualStreamLen;
actualStreamLen.AppendInt(aInitBufSize);
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ActualStreamLen"),
actualStreamLen);
#endif // defined(ACCESSIBILITY)
}
#endif // defined(MOZ_CRASHREPORTER)
}

View File

@ -4,7 +4,7 @@
* 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 "InterfaceRegistrationAnnotator.h"
#include "RegistrationAnnotator.h"
#include "mozilla/JSONWriter.h"
#include "mozilla/mscom/Utils.h"
@ -363,6 +363,45 @@ AnnotateInterfaceRegistration(REFIID aIid)
} else {
annotationKey.AppendLiteral("Child");
}
CrashReporter::AnnotateCrashReport(annotationKey,
static_cast<CStringWriter*>(json.WriteFunc())->Get());
}
void
AnnotateClassRegistration(REFCLSID aClsid)
{
#if defined(DEBUG)
const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
#else
const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
#endif
nsAutoString strClsid;
GUIDToString(aClsid, strClsid);
JSONWriter json(MakeUnique<CStringWriter>());
json.Start(style);
json.StartObjectProperty("HKLM", style);
AnnotateClsidRegistrationForHive(json, HKEY_LOCAL_MACHINE, strClsid, style);
json.EndObject();
json.StartObjectProperty("HKCU", style);
AnnotateClsidRegistrationForHive(json, HKEY_CURRENT_USER, strClsid, style);
json.EndObject();
json.End();
nsAutoCString annotationKey;
annotationKey.AppendLiteral("ClassRegistrationInfo");
if (XRE_IsParentProcess()) {
annotationKey.AppendLiteral("Parent");
} else {
annotationKey.AppendLiteral("Child");
}
CrashReporter::AnnotateCrashReport(annotationKey,
static_cast<CStringWriter*>(json.WriteFunc())->Get());
}

View File

@ -4,8 +4,8 @@
* 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_mscom_InterfaceRegistrationAnnotator_h
#define mozilla_mscom_InterfaceRegistrationAnnotator_h
#ifndef mozilla_mscom_RegistrationAnnotator_h
#define mozilla_mscom_RegistrationAnnotator_h
#if !defined(MOZ_CRASHREPORTER)
#error "This header requires crash reporting to be enabled"
@ -15,8 +15,9 @@ namespace mozilla {
namespace mscom {
void AnnotateInterfaceRegistration(REFIID aIid);
void AnnotateClassRegistration(REFCLSID aClsid);
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_InterfaceRegistrationAnnotator_h
#endif // mozilla_mscom_RegistrationAnnotator_h

View File

@ -39,7 +39,7 @@ UNIFIED_SOURCES += [
if CONFIG['MOZ_CRASHREPORTER']:
UNIFIED_SOURCES += [
'InterfaceRegistrationAnnotator.cpp',
'RegistrationAnnotator.cpp',
]
if CONFIG['ACCESSIBILITY']:

View File

@ -326,6 +326,13 @@ LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* out
extern JS_PUBLIC_API(bool)
StringIsASCII(const char* s);
/*
* Returns true if the given length-delimited string is a valid UTF-8 string,
* false otherwise.
*/
extern JS_PUBLIC_API(bool)
StringIsUTF8(const uint8_t* s, uint32_t length);
} // namespace JS
inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); }

View File

@ -1,4 +1,4 @@
// |jit-test| error: InternalError
// |jit-test| error: TypeError
var Date_toString = newGlobal().Date.prototype.toString;
(function f(){ f(Date_toString.call({})); })();

View File

@ -152,7 +152,7 @@ test("new Date()", d => justDontThrow(Date.prototype.toLocaleFormat.call(d)));
test("new Date()", d => justDontThrow(Date.prototype.toTimeString.call(d)));
test("new Date()", d => justDontThrow(Date.prototype.toDateString.call(d)));
test("new Date()", d => justDontThrow(Date.prototype.toSource.call(d)));
test("new Date()", d => justDontThrow(Date.prototype.toString.call(d)), true);
test("new Date()", d => justDontThrow(Date.prototype.toString.call(d)));
test("new Date()", d => justDontThrow(Date.prototype.valueOf.call(d)));
throw "done";

View File

@ -0,0 +1,24 @@
/*
(module
(func (param i32) (result i32)
(i32.add (get_local 0) (get_local 0)))
(export "hello" (func 0)))
*/
var txt =
`00 61 73 6d 01 00 00 00 01 06 01 60 01 7f 01 7f
03 02 01 00 07 09 01 05 68 65 6c 6c 6f 00 00 0a
09 01 07 00 20 00 20 00 6a 0b`
var bin = new Uint8Array(txt.split(" ").map((x) => parseInt(x, 16)));
var mod = new WebAssembly.Module(bin);
var ins = new WebAssembly.Instance(mod);
assertEq(ins.exports.hello(37), 2*37);
assertEq(bin[25], 0x65); // the 'e' in 'hello'
bin[25] = 0x85; // bad UTF-8
assertEq(WebAssembly.validate(bin), false);
assertThrowsInstanceOf(() => new WebAssembly.Module(bin), WebAssembly.CompileError);

View File

@ -2106,7 +2106,7 @@ CacheIRCompiler::emitArrayJoinResult()
// And only if elem0 is a string.
Address elementAddr(scratch, 0);
masm.branchTestString(Assembler::NonZero, elementAddr, failure->label());
masm.branchTestString(Assembler::NotEqual, elementAddr, failure->label());
// Store the value.
masm.loadValue(elementAddr, output.valueReg());

View File

@ -1458,12 +1458,12 @@ class MacroAssembler : public MacroAssemblerSpecific
DEFINED_ON(arm);
// wasm specific methods, used in both the wasm baseline compiler and ion.
void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm, mips32, mips64);
void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm, mips_shared);
void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm, mips32, mips64);
void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm, mips_shared);
void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);

View File

@ -505,15 +505,20 @@ SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValu
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult result;
if (MOZ_LIKELY(!obj->getOpsSetProperty())) {
if (!NativeSetProperty(
cx, obj.as<NativeObject>(), id, value, receiver,
(op == JSOP_SETNAME || op == JSOP_STRICTSETNAME ||
op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME)
? Unqualified
: Qualified,
result))
if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME ||
op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME)
{
return false;
if (!NativeSetProperty<Unqualified>(cx, obj.as<NativeObject>(), id, value, receiver,
result))
{
return false;
}
} else {
if (!NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, value, receiver,
result))
{
return false;
}
}
} else {
if (!SetProperty(cx, obj, id, value, receiver, result))

View File

@ -865,7 +865,8 @@ template <class Iter>
static void
TraceOneDataRelocation(JSTracer* trc, Iter* iter)
{
Instruction* ins = iter->cur();
Iter iterCopy(*iter);
Register dest;
Assembler::RelocStyle rs;
const void* prior = Assembler::GetPtr32Target(iter, &dest, &rs);
@ -876,12 +877,12 @@ TraceOneDataRelocation(JSTracer* trc, Iter* iter)
"ion-masm-ptr");
if (ptr != prior) {
MacroAssemblerARM::ma_mov_patch(Imm32(int32_t(ptr)), dest, Assembler::Always, rs, ins);
MacroAssemblerARM::ma_mov_patch(Imm32(int32_t(ptr)), dest, Assembler::Always, rs, iterCopy);
// L_LDR won't cause any instructions to be updated.
if (rs != Assembler::L_LDR) {
AutoFlushICache::flush(uintptr_t(ins), 4);
AutoFlushICache::flush(uintptr_t(ins->next()), 4);
AutoFlushICache::flush(uintptr_t(iter->cur()), 4);
AutoFlushICache::flush(uintptr_t(iter->next()), 4);
}
}
}
@ -901,7 +902,7 @@ TraceDataRelocations(JSTracer* trc, ARMBuffer* buffer, CompactBufferReader& read
{
while (reader.more()) {
BufferOffset offset(reader.readUnsigned());
ARMBuffer::AssemblerBufferInstIterator iter(offset, buffer);
BufferInstructionIterator iter(offset, buffer);
TraceOneDataRelocation(trc, &iter);
}
}
@ -3119,14 +3120,15 @@ Assembler::PatchDataWithValueCheck(CodeLocationLabel label, PatchedImmPtr newVal
PatchedImmPtr expectedValue)
{
Instruction* ptr = reinterpret_cast<Instruction*>(label.raw());
InstructionIterator iter(ptr);
Register dest;
Assembler::RelocStyle rs;
InstructionIterator iter(ptr);
DebugOnly<const uint32_t*> val = GetPtr32Target(&iter, &dest, &rs);
MOZ_ASSERT(uint32_t((const uint32_t*)val) == uint32_t(expectedValue.value));
MacroAssembler::ma_mov_patch(Imm32(int32_t(newValue.value)), dest, Always, rs, ptr);
iter = InstructionIterator(ptr);
MacroAssembler::ma_mov_patch(Imm32(int32_t(newValue.value)), dest, Always, rs, iter);
// L_LDR won't cause any instructions to be updated.
if (rs != L_LDR) {
@ -3179,6 +3181,20 @@ InstIsGuard(Instruction* inst, const PoolHeader** ph)
return *ph != nullptr;
}
static bool
InstIsGuard(BufferInstructionIterator& iter, const PoolHeader** ph)
{
Instruction* inst = iter.cur();
Assembler::Condition c = inst->extractCond();
if (c != Assembler::Always)
return false;
if (!(inst->is<InstBXReg>() || inst->is<InstBImm>()))
return false;
// See if the next instruction is a pool header.
*ph = iter.peek()->as<const PoolHeader>();
return *ph != nullptr;
}
static bool
InstIsBNop(Instruction* inst)
{
@ -3224,6 +3240,28 @@ Instruction::skipPool()
return this;
}
void
BufferInstructionIterator::skipPool()
{
// If this is a guard, and the next instruction is a header, always work
// around the pool. If it isn't a guard, then start looking ahead.
const PoolHeader* ph;
if (InstIsGuard(*this, &ph)) {
// Don't skip a natural guard.
if (ph->isNatural())
return;
advance(sizeof(Instruction) * (1 + ph->size()));
skipPool();
return;
}
if (InstIsBNop(cur())) {
next();
skipPool();
}
}
// Cases to be handled:
// 1) no pools or branches in sight => return this+1
// 2) branch to next instruction => return this+2, because a nop needed to be inserted into the stream.
@ -3367,13 +3405,6 @@ Assembler::BailoutTableStart(uint8_t* code)
return (uint8_t*) inst;
}
InstructionIterator::InstructionIterator(Instruction* i_)
: i(i_)
{
// Work around pools with an artificial pool guard and around nop-fill.
i = i->skipPool();
}
uint32_t Assembler::NopFill = 0;
uint32_t

View File

@ -2252,24 +2252,35 @@ class InstMOV : public InstALU
static InstMOV* AsTHIS (const Instruction& i);
};
class InstructionIterator
{
private:
Instruction* i;
Instruction* inst_;
public:
explicit InstructionIterator(Instruction* i_);
explicit InstructionIterator(Instruction* inst) : inst_(inst) {
skipPool();
}
void skipPool() {
inst_ = inst_->skipPool();
}
Instruction* next() {
i = i->next();
inst_ = inst_->next();
return cur();
}
Instruction* cur() const {
return i;
return inst_;
}
};
class BufferInstructionIterator : public ARMBuffer::AssemblerBufferInstIterator
{
public:
BufferInstructionIterator(BufferOffset bo, ARMBuffer* buffer)
: ARMBuffer::AssemblerBufferInstIterator(bo, buffer)
{}
void skipPool();
};
static const uint32_t NumIntArgRegs = 4;
// There are 16 *float* registers available for arguments

View File

@ -323,8 +323,8 @@ void
MacroAssembler::patchAdd32ToPtr(CodeOffset offset, Imm32 imm)
{
ScratchRegisterScope scratch(*this);
ma_mov_patch(imm, scratch, Always,
HasMOVWT() ? L_MOVWT : L_LDR, offsetToInstruction(offset));
BufferInstructionIterator iter(BufferOffset(offset.offset()), &m_buffer);
ma_mov_patch(imm, scratch, Always, HasMOVWT() ? L_MOVWT : L_LDR, iter);
}
void

View File

@ -346,41 +346,36 @@ MacroAssemblerARM::ma_movPatchable(ImmPtr imm, Register dest, Assembler::Conditi
ma_movPatchable(Imm32(int32_t(imm.value)), dest, c);
}
/* static */ void
MacroAssemblerARM::ma_mov_patch(Imm32 imm_, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i)
/* static */
template<class Iter>
void
MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, Assembler::Condition c,
RelocStyle rs, Iter iter)
{
MOZ_ASSERT(i);
int32_t imm = imm_.value;
MOZ_ASSERT(iter.cur());
// Make sure the current instruction is not an artificial guard inserted
// by the assembler buffer.
i = i->skipPool();
iter.skipPool();
int32_t imm = imm32.value;
switch(rs) {
case L_MOVWT:
Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, i);
i = i->next();
Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, i);
Assembler::as_movw_patch(dest, Imm16(imm & 0xffff), c, iter.cur());
Assembler::as_movt_patch(dest, Imm16(imm >> 16 & 0xffff), c, iter.next());
break;
case L_LDR:
Assembler::WritePoolEntry(i, c, imm);
Assembler::WritePoolEntry(iter.cur(), c, imm);
break;
}
}
/* static */ void
MacroAssemblerARM::ma_mov_patch(ImmPtr imm, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i)
{
ma_mov_patch(Imm32(int32_t(imm.value)), dest, c, rs, i);
}
Instruction*
MacroAssemblerARM::offsetToInstruction(CodeOffset offs)
{
return editSrc(BufferOffset(offs.offset()));
}
template void
MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, Assembler::Condition c,
RelocStyle rs, InstructionIterator iter);
template void
MacroAssemblerARM::ma_mov_patch(Imm32 imm32, Register dest, Assembler::Condition c,
RelocStyle rs, BufferInstructionIterator iter);
void
MacroAssemblerARM::ma_mov(Register src, Register dest, SBit s, Assembler::Condition c)

View File

@ -125,12 +125,10 @@ class MacroAssemblerARM : public Assembler
void ma_movPatchable(Imm32 imm, Register dest, Assembler::Condition c);
void ma_movPatchable(ImmPtr imm, Register dest, Assembler::Condition c);
// To be used with Iter := InstructionIterator or BufferInstructionIterator.
template<class Iter>
static void ma_mov_patch(Imm32 imm, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i);
static void ma_mov_patch(ImmPtr imm, Register dest, Assembler::Condition c,
RelocStyle rs, Instruction* i);
Instruction* offsetToInstruction(CodeOffset offs);
RelocStyle rs, Iter iter);
// ALU based ops
// mov

View File

@ -182,6 +182,7 @@ class Registers
(1 << Registers::s5) |
(1 << Registers::s6) |
(1 << Registers::s7) |
(1 << Registers::fp) |
(1 << Registers::ra);
static const SetType WrapperMask =
@ -198,7 +199,6 @@ class Registers
(1 << Registers::k1) |
(1 << Registers::gp) |
(1 << Registers::sp) |
(1 << Registers::fp) |
(1 << Registers::ra);
// Registers that can be allocated without being saved, generally.

View File

@ -103,7 +103,7 @@ static constexpr Register InvalidReg { Registers::invalid_reg };
static constexpr FloatRegister InvalidFloatReg;
static constexpr Register StackPointer = sp;
static constexpr Register FramePointer = InvalidReg;
static constexpr Register FramePointer = fp;
static constexpr Register ReturnReg = v0;
static constexpr FloatRegister ReturnSimd128Reg = InvalidFloatReg;
static constexpr FloatRegister ScratchSimd128Reg = InvalidFloatReg;

View File

@ -536,9 +536,6 @@ CodeGeneratorMIPSShared::visitMulI64(LMulI64* lir)
case 1:
// nop
return;
case 2:
masm.add64(ToRegister64(lhs), ToRegister64(lhs));
return;
default:
if (constant > 0) {
// Use shift if constant is power of 2.
@ -1012,12 +1009,18 @@ CodeGeneratorMIPSShared::visitRotateI64(LRotateI64* lir)
Register64 output = ToOutRegister64(lir);
Register temp = ToTempRegisterOrInvalid(lir->temp());
#ifdef JS_CODEGEN_MIPS64
MOZ_ASSERT(input == output);
#endif
if (count->isConstant()) {
int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
if (!c)
if (!c) {
#ifdef JS_CODEGEN_MIPS32
masm.move64(input, output);
#endif
return;
}
if (mir->isLeftRotate())
masm.rotateLeft64(Imm32(c), input, output, temp);
else
@ -1459,46 +1462,30 @@ CodeGeneratorMIPSShared::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir)
MWasmTruncateToInt32* mir = lir->mir();
MIRType fromType = mir->input()->type();
MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32);
auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input);
addOutOfLineCode(ool, mir);
Label* oolEntry = ool->entry();
if (mir->isUnsigned()) {
// When the input value is Infinity, NaN, or rounds to an integer outside the
// range [INT64_MIN; INT64_MAX + 1[, the Invalid Operation flag is set in the FCSR.
if (fromType == MIRType::Double)
masm.as_truncld(ScratchDoubleReg, input);
masm.wasmTruncateDoubleToUInt32(input, output, oolEntry);
else if (fromType == MIRType::Float32)
masm.as_truncls(ScratchDoubleReg, input);
masm.wasmTruncateFloat32ToUInt32(input, output, oolEntry);
else
MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
// Check that the result is in the uint32_t range.
masm.moveFromDoubleHi(ScratchDoubleReg, output);
masm.as_cfc1(ScratchRegister, Assembler::FCSR);
masm.as_ext(ScratchRegister, ScratchRegister, 16, 1);
masm.ma_or(output, ScratchRegister);
masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual);
masm.moveFromFloat32(ScratchDoubleReg, output);
MOZ_CRASH("unexpected type");
return;
}
// When the input value is Infinity, NaN, or rounds to an integer outside the
// range [INT32_MIN; INT32_MAX + 1[, the Invalid Operation flag is set in the FCSR.
if (fromType == MIRType::Double)
masm.as_truncwd(ScratchFloat32Reg, input);
masm.wasmTruncateDoubleToInt32(input, output, oolEntry);
else if (fromType == MIRType::Float32)
masm.as_truncws(ScratchFloat32Reg, input);
masm.wasmTruncateFloat32ToInt32(input, output, oolEntry);
else
MOZ_CRASH("unexpected type in visitWasmTruncateToInt32");
// Check that the result is in the int32_t range.
masm.as_cfc1(output, Assembler::FCSR);
masm.as_ext(output, output, 16, 1);
masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual);
MOZ_CRASH("unexpected type");
masm.bind(ool->rejoin());
masm.moveFromFloat32(ScratchFloat32Reg, output);
}
void
@ -1507,7 +1494,7 @@ CodeGeneratorMIPSShared::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCh
FloatRegister input = ool->input();
MIRType fromType = ool->fromType();
MIRType toType = ool->toType();
bool isUnsigned = ool->isUnsigned();
// Eagerly take care of NaNs.
Label inputIsNaN;
if (fromType == MIRType::Double)
@ -1517,49 +1504,60 @@ CodeGeneratorMIPSShared::visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCh
else
MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
Label fail;
// Handle special values (not needed for unsigned values).
if (!ool->isUnsigned()) {
if (toType == MIRType::Int32) {
// MWasmTruncateToInt32
if (fromType == MIRType::Double) {
// we've used truncwd. the only valid double values that can
// truncate to INT32_MIN are in ]INT32_MIN - 1; INT32_MIN].
masm.loadConstantDouble(double(INT32_MIN) - 1.0, ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &fail);
masm.loadConstantDouble(double(INT32_MIN), ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleGreaterThan, input, ScratchDoubleReg, &fail);
masm.as_truncwd(ScratchFloat32Reg, ScratchDoubleReg);
masm.jump(ool->rejoin());
}
} else if (toType == MIRType::Int64) {
if (fromType == MIRType::Double) {
masm.loadConstantDouble(double(INT64_MIN), ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleLessThan, input, ScratchDoubleReg, &fail);
masm.loadConstantDouble(double(INT64_MAX) + 1.0, ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input,
ScratchDoubleReg, &fail);
masm.jump(ool->rejoin());
}
// By default test for the following inputs and bail:
// signed: ] -Inf, INTXX_MIN - 1.0 ] and [ INTXX_MAX + 1.0 : +Inf [
// unsigned: ] -Inf, -1.0 ] and [ UINTXX_MAX + 1.0 : +Inf [
// Note: we cannot always represent those exact values. As a result
// this changes the actual comparison a bit.
double minValue, maxValue;
Assembler::DoubleCondition minCond = Assembler::DoubleLessThanOrEqual;
Assembler::DoubleCondition maxCond = Assembler::DoubleGreaterThanOrEqual;
if (toType == MIRType::Int64) {
if (isUnsigned) {
minValue = -1;
maxValue = double(UINT64_MAX) + 1.0;
} else {
// In the float32/double range there exists no value between
// INT64_MIN and INT64_MIN - 1.0. Making INT64_MIN the lower-bound.
minValue = double(INT64_MIN);
minCond = Assembler::DoubleLessThan;
maxValue = double(INT64_MAX) + 1.0;
}
} else {
if (toType == MIRType::Int64) {
if (fromType == MIRType::Double) {
masm.loadConstantDouble(double(-1), ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, &fail);
masm.loadConstantDouble(double(UINT64_MAX) + 1.0, ScratchDoubleReg);
masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input,
ScratchDoubleReg, &fail);
masm.jump(ool->rejoin());
if (isUnsigned) {
minValue = -1;
maxValue = double(UINT32_MAX) + 1.0;
} else {
if (fromType == MIRType::Float32) {
// In the float32 range there exists no value between
// INT32_MIN and INT32_MIN - 1.0. Making INT32_MIN the lower-bound.
minValue = double(INT32_MIN);
minCond = Assembler::DoubleLessThan;
} else {
minValue = double(INT32_MIN) - 1.0;
}
}
maxValue = double(INT32_MAX) + 1.0;
}
}
Label fail;
if (fromType == MIRType::Double) {
masm.loadConstantDouble(minValue, ScratchDoubleReg);
masm.branchDouble(minCond, input, ScratchDoubleReg, &fail);
masm.loadConstantDouble(maxValue, ScratchDoubleReg);
masm.branchDouble(maxCond, input, ScratchDoubleReg, &fail);
} else {
masm.loadConstantFloat32(float(minValue), ScratchFloat32Reg);
masm.branchFloat(minCond, input, ScratchFloat32Reg, &fail);
masm.loadConstantFloat32(float(maxValue), ScratchFloat32Reg);
masm.branchFloat(maxCond, input, ScratchFloat32Reg, &fail);
}
masm.jump(ool->rejoin());
// Handle errors.
masm.bind(&fail);
masm.jump(trap(ool, wasm::Trap::IntegerOverflow));
@ -2041,6 +2039,7 @@ CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
const MAsmJSLoadHeap* mir = ins->mir();
const LAllocation* ptr = ins->ptr();
const LDefinition* out = ins->output();
const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
bool isSigned;
int size;
@ -2091,7 +2090,8 @@ CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
}
Label done, outOfRange;
masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump);
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptrReg, ToRegister(boundsCheckLimit),
&outOfRange);
// Offset is ok, let's load value.
if (isFloat) {
if (size == 32)
@ -2114,8 +2114,6 @@ CodeGeneratorMIPSShared::visitAsmJSLoadHeap(LAsmJSLoadHeap* ins)
masm.move32(Imm32(0), ToRegister(out));
}
masm.bind(&done);
MOZ_CRASH("NYI - patching is no longer an option");
}
void
@ -2124,6 +2122,7 @@ CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
const MAsmJSStoreHeap* mir = ins->mir();
const LAllocation* value = ins->value();
const LAllocation* ptr = ins->ptr();
const LAllocation* boundsCheckLimit = ins->boundsCheckLimit();
bool isSigned;
int size;
@ -2178,7 +2177,8 @@ CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
}
Label outOfRange;
masm.ma_b(ptrReg, ScratchRegister, &outOfRange, Assembler::AboveOrEqual, ShortJump);
masm.wasmBoundsCheck(Assembler::AboveOrEqual, ptrReg, ToRegister(boundsCheckLimit),
&outOfRange);
// Offset is ok, let's store value.
if (isFloat) {
@ -2192,7 +2192,6 @@ CodeGeneratorMIPSShared::visitAsmJSStoreHeap(LAsmJSStoreHeap* ins)
}
masm.bind(&outOfRange);
MOZ_CRASH("NYI - patching is no longer an option");
}
void
@ -2301,9 +2300,12 @@ CodeGeneratorMIPSShared::visitWasmStackArg(LWasmStackArg* ins)
} else {
if (ins->arg()->isGeneralReg()) {
masm.storePtr(ToRegister(ins->arg()), Address(StackPointer, mir->spOffset()));
} else {
} else if (mir->input()->type() == MIRType::Double) {
masm.storeDouble(ToFloatRegister(ins->arg()).doubleOverlay(),
Address(StackPointer, mir->spOffset()));
} else {
masm.storeFloat32(ToFloatRegister(ins->arg()),
Address(StackPointer, mir->spOffset()));
}
}
}

View File

@ -284,20 +284,6 @@ class LUDivOrMod : public LBinaryMath<0>
}
};
class LInt64ToFloatingPoint : public LInstructionHelper<1, INT64_PIECES, 0>
{
public:
LIR_HEADER(Int64ToFloatingPoint);
explicit LInt64ToFloatingPoint(const LInt64Allocation& in) {
setInt64Operand(0, in);
}
MInt64ToFloatingPoint* mir() const {
return mir_->toInt64ToFloatingPoint();
}
};
namespace details {
// Base class for the int64 and non-int64 variants.

View File

@ -74,9 +74,11 @@ void
LIRGeneratorMIPSShared::lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* lhs, MDefinition* rhs)
{
bool needsTemp = false;
bool cannotAliasRhs = false;
#ifdef JS_CODEGEN_MIPS32
needsTemp = true;
cannotAliasRhs = true;
if (rhs->isConstant()) {
int64_t constant = rhs->toConstant()->toInt64();
int32_t shift = mozilla::FloorLog2(constant);
@ -87,10 +89,10 @@ LIRGeneratorMIPSShared::lowerForMulInt64(LMulI64* ins, MMul* mir, MDefinition* l
needsTemp = false;
}
#endif
ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
ins->setInt64Operand(INT64_PIECES,
lhs != rhs ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
(lhs != rhs || cannotAliasRhs) ? useInt64OrConstant(rhs) : useInt64OrConstantAtStart(rhs));
if (needsTemp)
ins->setTemp(0, temp());
@ -102,10 +104,16 @@ void
LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT64_PIECES + 1, Temps>* ins,
MDefinition* mir, MDefinition* lhs, MDefinition* rhs)
{
#ifdef JS_CODEGEN_MIPS32
if (mir->isRotate()) {
if (!rhs->isConstant())
ins->setTemp(0, temp());
ins->setInt64Operand(0, useInt64Register(lhs));
} else {
ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
}
#else
ins->setInt64Operand(0, useInt64RegisterAtStart(lhs));
#if defined(JS_NUNBOX32)
if (mir->isRotate())
ins->setTemp(0, temp());
#endif
static_assert(LShiftI64::Rhs == INT64_PIECES, "Assume Rhs is located at INT64_PIECES.");
@ -113,7 +121,14 @@ LIRGeneratorMIPSShared::lowerForShiftInt64(LInstructionHelper<INT64_PIECES, INT6
ins->setOperand(INT64_PIECES, useRegisterOrConstant(rhs));
#ifdef JS_CODEGEN_MIPS32
if (mir->isRotate())
defineInt64(ins, mir);
else
defineInt64ReuseInput(ins, mir, 0);
#else
defineInt64ReuseInput(ins, mir, 0);
#endif
}
template void LIRGeneratorMIPSShared::lowerForShiftInt64(
@ -322,7 +337,13 @@ LIRGeneratorMIPSShared::visitWasmLoad(MWasmLoad* ins)
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation ptr = useRegisterAtStart(base);
LAllocation ptr;
#ifdef JS_CODEGEN_MIPS32
if (ins->type() == MIRType::Int64)
ptr = useRegister(base);
else
#endif
ptr = useRegisterAtStart(base);
if (IsUnaligned(ins->access())) {
if (ins->type() == MIRType::Int64) {
@ -368,7 +389,7 @@ LIRGeneratorMIPSShared::visitWasmStore(MWasmStore* ins)
LAllocation baseAlloc = useRegisterAtStart(base);
if (IsUnaligned(ins->access())) {
if (ins->type() == MIRType::Int64) {
if (ins->access().type() == Scalar::Int64) {
LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
auto* lir = new(alloc()) LWasmUnalignedStoreI64(baseAlloc, valueAlloc, temp());
if (ins->access().offset())
@ -387,7 +408,7 @@ LIRGeneratorMIPSShared::visitWasmStore(MWasmStore* ins)
return;
}
if (ins->type() == MIRType::Int64) {
if (ins->access().type() == Scalar::Int64) {
LInt64Allocation valueAlloc = useInt64RegisterAtStart(value);
auto* lir = new(alloc()) LWasmStoreI64(baseAlloc, valueAlloc);
if (ins->access().offset())
@ -478,17 +499,23 @@ LIRGeneratorMIPSShared::visitAsmJSLoadHeap(MAsmJSLoadHeap* ins)
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation baseAlloc;
LAllocation limitAlloc;
// For MIPS it is best to keep the 'base' in a register if a bounds check
// is needed.
if (base->isConstant() && !ins->needsBoundsCheck()) {
// A bounds check is only skipped for a positive index.
MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
baseAlloc = LAllocation(base->toConstant());
} else
} else {
baseAlloc = useRegisterAtStart(base);
if (ins->needsBoundsCheck()) {
MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
limitAlloc = useRegisterAtStart(boundsCheckLimit);
}
}
define(new(alloc()) LAsmJSLoadHeap(baseAlloc), ins);
define(new(alloc()) LAsmJSLoadHeap(baseAlloc, limitAlloc), ins);
}
void
@ -499,14 +526,20 @@ LIRGeneratorMIPSShared::visitAsmJSStoreHeap(MAsmJSStoreHeap* ins)
MDefinition* base = ins->base();
MOZ_ASSERT(base->type() == MIRType::Int32);
LAllocation baseAlloc;
LAllocation limitAlloc;
if (base->isConstant() && !ins->needsBoundsCheck()) {
MOZ_ASSERT(base->toConstant()->toInt32() >= 0);
baseAlloc = LAllocation(base->toConstant());
} else
} else{
baseAlloc = useRegisterAtStart(base);
if (ins->needsBoundsCheck()) {
MDefinition* boundsCheckLimit = ins->boundsCheckLimit();
MOZ_ASSERT(boundsCheckLimit->type() == MIRType::Int32);
limitAlloc = useRegisterAtStart(boundsCheckLimit);
}
}
add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value())), ins);
add(new(alloc()) LAsmJSStoreHeap(baseAlloc, useRegisterAtStart(ins->value()), limitAlloc), ins);
}
void
@ -703,24 +736,6 @@ LIRGeneratorMIPSShared::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayEleme
define(lir, ins);
}
void
LIRGeneratorMIPSShared::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
defineInt64(new(alloc()) LWasmTruncateToInt64(useRegister(opd)), ins);
}
void
LIRGeneratorMIPSShared::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Int64);
MOZ_ASSERT(IsFloatingPointType(ins->type()));
define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd)), ins);
}
void
LIRGeneratorMIPSShared::visitCopySign(MCopySign* ins)
@ -741,7 +756,7 @@ LIRGeneratorMIPSShared::visitCopySign(MCopySign* ins)
lir->setTemp(0, temp());
lir->setTemp(1, temp());
lir->setOperand(0, useRegister(lhs));
lir->setOperand(0, useRegisterAtStart(lhs));
lir->setOperand(1, useRegister(rhs));
defineReuseInput(lir, ins, 0);
}
@ -755,5 +770,5 @@ LIRGeneratorMIPSShared::visitExtendInt32ToInt64(MExtendInt32ToInt64* ins)
void
LIRGeneratorMIPSShared::visitSignExtendInt64(MSignExtendInt64* ins)
{
MOZ_CRASH("NYI : SignExtendInt64");
defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
}

View File

@ -95,8 +95,6 @@ class LIRGeneratorMIPSShared : public LIRGeneratorShared
void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
void visitSubstr(MSubstr* ins);
void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
void visitCopySign(MCopySign* ins);
void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
void visitSignExtendInt64(MSignExtendInt64* ins);

View File

@ -1551,7 +1551,8 @@ MacroAssembler::Pop(const ValueOperand& val)
void
MacroAssembler::PopStackPtr()
{
MOZ_CRASH("NYI");
asMasm().ma_load(StackPointer, Address(StackPointer, 0), SizeWord);
framePushed_ -= sizeof(intptr_t);
}
@ -1761,4 +1762,26 @@ MacroAssembler::comment(const char* msg)
Assembler::comment(msg);
}
void
MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
{
as_truncwd(ScratchFloat32Reg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
as_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void
MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry)
{
as_truncws(ScratchFloat32Reg, input);
as_cfc1(ScratchRegister, Assembler::FCSR);
moveFromFloat32(ScratchFloat32Reg, output);
as_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
//}}} check_macroassembler_style

View File

@ -65,7 +65,7 @@ static constexpr Register WasmTableCallIndexReg = ABINonArgReg2;
static constexpr Register JSReturnReg_Type = a3;
static constexpr Register JSReturnReg_Data = a2;
static constexpr Register64 ReturnReg64(InvalidReg, InvalidReg);
static constexpr Register64 ReturnReg64(v1, v0);
static constexpr FloatRegister ReturnFloat32Reg = { FloatRegisters::f0, FloatRegister::Single };
static constexpr FloatRegister ReturnDoubleReg = { FloatRegisters::f0, FloatRegister::Double };
static constexpr FloatRegister ScratchFloat32Reg = { FloatRegisters::f18, FloatRegister::Single };

View File

@ -396,7 +396,7 @@ CodeGeneratorMIPS::visitDivOrModI64(LDivOrModI64* lir)
masm.bind(&notmin);
}
masm.setupUnalignedABICall(temp);
masm.setupWasmABICall();
masm.passABIArg(lhs.high);
masm.passABIArg(lhs.low);
masm.passABIArg(rhs.high);
@ -435,7 +435,7 @@ CodeGeneratorMIPS::visitUDivOrModI64(LUDivOrModI64* lir)
if (lir->canBeDivideByZero())
masm.branchTest64(Assembler::Zero, rhs, rhs, temp, trap(lir, wasm::Trap::IntegerDivideByZero));
masm.setupUnalignedABICall(temp);
masm.setupWasmABICall();
masm.passABIArg(lhs.high);
masm.passABIArg(lhs.low);
masm.passABIArg(rhs.high);
@ -497,6 +497,7 @@ CodeGeneratorMIPS::emitWasmLoadI64(T* lir)
masm.ma_sra(output.high, output.low, Imm32(31));
} else {
ScratchRegisterScope scratch(masm);
MOZ_ASSERT(output.low != ptr);
masm.ma_load_unaligned(output.low, BaseIndex(HeapReg, ptr, TimesOne),
temp, SizeWord, isSigned ? SignExtend : ZeroExtend);
masm.ma_addu(scratch, ptr, Imm32(INT64HIGH_OFFSET));
@ -515,6 +516,7 @@ CodeGeneratorMIPS::emitWasmLoadI64(T* lir)
masm.ma_sra(output.high, output.low, Imm32(31));
} else {
ScratchRegisterScope scratch(masm);
MOZ_ASSERT(output.low != ptr);
masm.ma_load(output.low, BaseIndex(HeapReg, ptr, TimesOne), SizeWord);
masm.ma_addu(scratch, ptr, Imm32(INT64HIGH_OFFSET));
masm.ma_load(output.high, BaseIndex(HeapReg, scratch, TimesOne), SizeWord);
@ -688,6 +690,25 @@ CodeGeneratorMIPS::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
masm.move32(ToRegister(input.high()), output);
}
void
CodeGeneratorMIPS::visitSignExtendInt64(LSignExtendInt64* lir)
{
Register64 input = ToRegister64(lir->getInt64Operand(0));
Register64 output = ToOutRegister64(lir);
switch (lir->mode()) {
case MSignExtendInt64::Byte:
masm.move8SignExtend(input.low, output.low);
break;
case MSignExtendInt64::Half:
masm.move16SignExtend(input.low, output.low);
break;
case MSignExtendInt64::Word:
masm.move32(input.low, output.low);
break;
}
masm.ma_sra(output.high, output.low, Imm32(31));
}
void
CodeGeneratorMIPS::visitClzI64(LClzI64* lir)
{
@ -738,12 +759,17 @@ CodeGeneratorMIPS::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
MOZ_CRASH("unexpected type in visitOutOfLineWasmTruncateCheck");
}
masm.setupUnalignedABICall(output.high);
masm.Push(input);
masm.setupWasmABICall();
masm.passABIArg(scratch, MoveOp::DOUBLE);
if (lir->mir()->isUnsigned())
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToUint64);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::TruncateDoubleToInt64);
masm.Pop(input);
masm.ma_b(output.high, Imm32(0x80000000), ool->rejoin(), Assembler::NotEqual);
masm.ma_b(output.low, Imm32(0x00000000), ool->rejoin(), Assembler::NotEqual);
masm.ma_b(ool->entry());
@ -767,20 +793,23 @@ CodeGeneratorMIPS::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir)
regs.take(input.high);
Register temp = regs.takeAny();
masm.setupUnalignedABICall(temp);
masm.setupWasmABICall();
masm.passABIArg(input.high);
masm.passABIArg(input.low);
if (lir->mir()->isUnsigned())
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Uint64ToDouble, MoveOp::DOUBLE);
if (toType == MIRType::Double)
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Uint64ToDouble, MoveOp::DOUBLE);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Uint64ToFloat32, MoveOp::FLOAT32);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Int64ToDouble, MoveOp::DOUBLE);
if (toType == MIRType::Double)
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Int64ToDouble, MoveOp::DOUBLE);
else
masm.callWithABI(mir->bytecodeOffset(), wasm::SymbolicAddress::Int64ToFloat32, MoveOp::FLOAT32);
MOZ_ASSERT_IF(toType == MIRType::Double, output == ReturnDoubleReg);
if (toType == MIRType::Float32) {
MOZ_ASSERT(output == ReturnFloat32Reg);
masm.convertDoubleToFloat32(ReturnDoubleReg, output);
}
MOZ_ASSERT_IF(toType == MIRType::Float32, output == ReturnFloat32Reg);
}
void
@ -799,4 +828,4 @@ CodeGeneratorMIPS::setReturnDoubleRegs(LiveRegisterSet* regs)
regs->add(ReturnFloat32Reg);
regs->add(ReturnDoubleReg.singleOverlay(1));
regs->add(ReturnDoubleReg);
}
}

View File

@ -56,6 +56,7 @@ class CodeGeneratorMIPS : public CodeGeneratorMIPSShared
void visitWasmReinterpretToI64(LWasmReinterpretToI64* lir);
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
void visitSignExtendInt64(LSignExtendInt64* ins);
void visitClzI64(LClzI64* ins);
void visitCtzI64(LCtzI64* ins);
void visitNotI64(LNotI64* ins);

View File

@ -163,6 +163,21 @@ class LWasmTruncateToInt64 : public LCallInstructionHelper<INT64_PIECES, 1, 0>
}
};
class LInt64ToFloatingPoint : public LCallInstructionHelper<1, INT64_PIECES, 0>
{
public:
LIR_HEADER(Int64ToFloatingPoint);
explicit LInt64ToFloatingPoint(const LInt64Allocation& in) {
setInt64Operand(0, in);
}
MInt64ToFloatingPoint* mir() const {
return mir_->toInt64ToFloatingPoint();
}
};
} // namespace jit
} // namespace js

View File

@ -256,3 +256,23 @@ LIRGeneratorMIPS::visitRandom(MRandom* ins)
temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
}
void
LIRGeneratorMIPS::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
defineReturn(new(alloc()) LWasmTruncateToInt64(useRegisterAtStart(opd)), ins);
}
void
LIRGeneratorMIPS::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Int64);
MOZ_ASSERT(IsFloatingPointType(ins->type()));
defineReturn(new(alloc()) LInt64ToFloatingPoint(useInt64RegisterAtStart(opd)), ins);
}

View File

@ -47,6 +47,8 @@ class LIRGeneratorMIPS : public LIRGeneratorMIPSShared
void visitUnbox(MUnbox* unbox);
void visitReturn(MReturn* ret);
void visitRandom(MRandom* ins);
void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
};
typedef LIRGeneratorMIPS LIRGeneratorSpecific;

View File

@ -141,6 +141,8 @@ MacroAssembler::addPtr(ImmWord imm, Register dest)
void
MacroAssembler::add64(Register64 src, Register64 dest)
{
MOZ_ASSERT(dest.low != src.low);
as_addu(dest.low, dest.low, src.low);
as_sltu(ScratchRegister, dest.low, src.low);
as_addu(dest.high, dest.high, src.high);
@ -190,6 +192,10 @@ MacroAssembler::subPtr(Imm32 imm, Register dest)
void
MacroAssembler::sub64(Register64 src, Register64 dest)
{
MOZ_ASSERT(dest.low != src.high);
MOZ_ASSERT(dest.high != src.low);
MOZ_ASSERT(dest.high != src.high);
as_sltu(ScratchRegister, dest.low, src.low);
as_subu(dest.high, dest.high, ScratchRegister);
as_subu(dest.low, dest.low, src.low);
@ -435,6 +441,8 @@ MacroAssembler::rshift64(Register unmaskedShift, Register64 dest)
ScratchRegisterScope shift(*this);
ma_and(shift, unmaskedShift, Imm32(0x3f));
ma_b(shift, Imm32(0), &done, Equal);
ma_srl(dest.low, dest.low, shift);
ma_subu(shift, shift, Imm32(32));
ma_b(shift, Imm32(0), &less, LessThan);
@ -481,6 +489,7 @@ MacroAssembler::rshift64Arithmetic(Register unmaskedShift, Register64 dest)
ScratchRegisterScope shift(*this);
ma_and(shift, unmaskedShift, Imm32(0x3f));
ma_b(shift, Imm32(0), &done, Equal);
ma_srl(dest.low, dest.low, shift);
ma_subu(shift, shift, Imm32(32));
@ -542,12 +551,13 @@ MacroAssembler::rotateLeft64(Register shift, Register64 src, Register64 dest, Re
MOZ_ASSERT(temp != src.low && temp != src.high);
MOZ_ASSERT(shift != src.low && shift != src.high);
MOZ_ASSERT(temp != InvalidReg);
MOZ_ASSERT(src != dest);
ScratchRegisterScope shift_value(*this);
Label high, done, zero;
Label high, swap, done, zero;
ma_and(temp, shift, Imm32(0x3f));
ma_b(temp, Imm32(32), &high, GreaterThanOrEqual);
ma_b(temp, Imm32(32), &swap, Equal);
ma_b(temp, Imm32(32), &high, GreaterThan);
// high = high << shift | low >> 32 - shift
// low = low << shift | high >> 32 - shift
@ -568,7 +578,11 @@ MacroAssembler::rotateLeft64(Register shift, Register64 src, Register64 dest, Re
ma_move(dest.low, src.low);
ma_move(dest.high, src.high);
ma_b(&done);
bind(&swap);
ma_move(SecondScratchReg, src.low);
ma_move(dest.low, src.high);
ma_move(dest.high, SecondScratchReg);
ma_b(&done);
// A 32 - 64 shift is a 0 - 32 shift in the other direction.
bind(&high);
ma_and(shift, shift, Imm32(0x3f));
@ -625,12 +639,14 @@ MacroAssembler::rotateRight64(Register shift, Register64 src, Register64 dest, R
MOZ_ASSERT(temp != src.low && temp != src.high);
MOZ_ASSERT(shift != src.low && shift != src.high);
MOZ_ASSERT(temp != InvalidReg);
MOZ_ASSERT(src != dest);
ScratchRegisterScope shift_value(*this);
Label high, done, zero;
Label high, swap, done, zero;
ma_and(temp, shift, Imm32(0x3f));
ma_b(temp, Imm32(32), &high, GreaterThanOrEqual);
ma_b(temp, Imm32(32), &swap, Equal);
ma_b(temp, Imm32(32), &high, GreaterThan);
// high = high >> shift | low << 32 - shift
// low = low >> shift | high << 32 - shift
@ -655,7 +671,11 @@ MacroAssembler::rotateRight64(Register shift, Register64 src, Register64 dest, R
ma_move(dest.low, src.low);
ma_move(dest.high, src.high);
ma_b(&done);
bind(&swap);
ma_move(SecondScratchReg, src.low);
ma_move(dest.low, src.high);
ma_move(dest.high, SecondScratchReg);
ma_b(&done);
// A 32 - 64 shift is a 0 - 32 shift in the other direction.
bind(&high);
ma_and(shift, shift, Imm32(0x3f));
@ -1067,18 +1087,16 @@ template <class L>
void
MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Register boundsCheckLimit, L label)
{
MOZ_CRASH("NYI - patching is no longer available");
// BufferOffset bo = ma_BoundsCheck(ScratchRegister);
// append(wasm::BoundsCheck(bo.getOffset()));
// ma_b(index, ScratchRegister, label, cond);
ma_b(index, boundsCheckLimit, label, cond);
}
template <class L>
void
MacroAssembler::wasmBoundsCheck(Condition cond, Register index, Address boundsCheckLimit, L label)
{
MOZ_CRASH("NYI - patching is no longer available");
SecondScratchRegisterScope scratch2(*this);
load32(boundsCheckLimit,SecondScratchReg);
ma_b(index, SecondScratchReg, label, cond);
}
//}}} check_macroassembler_style

View File

@ -2427,4 +2427,52 @@ template void
MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
const BaseIndex& dest, MIRType slotType);
void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
{
loadConstantDouble(double(-1.0), ScratchDoubleReg);
branchDouble(Assembler::DoubleLessThanOrEqual, input, ScratchDoubleReg, oolEntry);
loadConstantDouble(double(UINT32_MAX) + 1.0, ScratchDoubleReg);
branchDouble(Assembler::DoubleGreaterThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
Label done, simple;
loadConstantDouble(double(0x80000000UL), ScratchDoubleReg);
branchDouble(Assembler::DoubleLessThan, input, ScratchDoubleReg, &simple);
as_subd(ScratchDoubleReg, input, ScratchDoubleReg);
as_truncwd(ScratchDoubleReg, ScratchDoubleReg);
moveFromFloat32(ScratchDoubleReg, output);
ma_li(ScratchRegister, Imm32(0x80000000UL));
ma_or(output, ScratchRegister);
ma_b(&done);
bind(&simple);
as_truncwd(ScratchDoubleReg, input);
moveFromFloat32(ScratchDoubleReg, output);
bind(&done);
}
void
MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry)
{
loadConstantFloat32(double(-1.0), ScratchDoubleReg);
branchFloat(Assembler::DoubleLessThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
loadConstantFloat32(double(UINT32_MAX) + 1.0, ScratchDoubleReg);
branchFloat(Assembler::DoubleGreaterThanOrEqualOrUnordered, input, ScratchDoubleReg, oolEntry);
Label done, simple;
loadConstantFloat32(double(0x80000000UL), ScratchDoubleReg);
branchFloat(Assembler::DoubleLessThan, input, ScratchDoubleReg, &simple);
as_subs(ScratchDoubleReg, input, ScratchDoubleReg);
as_truncws(ScratchDoubleReg, ScratchDoubleReg);
moveFromFloat32(ScratchDoubleReg, output);
ma_li(ScratchRegister, Imm32(0x80000000UL));
ma_or(output, ScratchRegister);
ma_b(&done);
bind(&simple);
as_truncws(ScratchDoubleReg, input);
moveFromFloat32(ScratchDoubleReg, output);
bind(&done);
}
//}}} check_macroassembler_style

View File

@ -36,11 +36,9 @@ struct EnterJITRegs
double f22;
double f20;
// empty slot for alignment
uintptr_t align;
// non-volatile registers.
uintptr_t ra;
uintptr_t fp;
uintptr_t s7;
uintptr_t s6;
uintptr_t s5;
@ -80,6 +78,7 @@ GenerateReturn(MacroAssembler& masm, int returnCode)
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s5)), s5);
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s6)), s6);
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, s7)), s7);
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, fp)), fp);
masm.loadPtr(Address(StackPointer, offsetof(EnterJITRegs, ra)), ra);
// Restore non-volatile floating point registers
@ -110,6 +109,7 @@ GeneratePrologue(MacroAssembler& masm)
masm.storePtr(s5, Address(StackPointer, offsetof(EnterJITRegs, s5)));
masm.storePtr(s6, Address(StackPointer, offsetof(EnterJITRegs, s6)));
masm.storePtr(s7, Address(StackPointer, offsetof(EnterJITRegs, s7)));
masm.storePtr(fp, Address(StackPointer, offsetof(EnterJITRegs, fp)));
masm.storePtr(ra, Address(StackPointer, offsetof(EnterJITRegs, ra)));
masm.as_sd(f20, StackPointer, offsetof(EnterJITRegs, f20));

View File

@ -605,6 +605,24 @@ CodeGeneratorMIPS64::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir)
}
}
void
CodeGeneratorMIPS::visitSignExtendInt64(LSignExtendInt64* lir)
{
Register64 input = ToRegister64(lir->getInt64Operand(0));
Register64 output = ToOutRegister64(lir);
switch (lir->mode()) {
case MSignExtendInt64::Byte:
masm.move8SignExtend(input.reg, output.reg);
break;
case MSignExtendInt64::Half:
masm.move16SignExtend(input.reg, output.reg);
break;
case MSignExtendInt64::Word:
masm.ma_sll(output.reg, input.reg, Imm32(0));
break;
}
}
void
CodeGeneratorMIPS64::visitClzI64(LClzI64* lir)
{
@ -664,7 +682,8 @@ CodeGeneratorMIPS64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
// Check that the result is in the uint64_t range.
masm.moveFromDouble(ScratchDoubleReg, output);
masm.as_cfc1(ScratchRegister, Assembler::FCSR);
masm.as_ext(ScratchRegister, ScratchRegister, 16, 1);
// extract invalid operation flag (bit 6) from FCSR
masm.as_ext(ScratchRegister, ScratchRegister, 6, 1);
masm.ma_dsrl(SecondScratchReg, output, Imm32(63));
masm.ma_or(SecondScratchReg, ScratchRegister);
masm.ma_b(SecondScratchReg, Imm32(0), ool->entry(), Assembler::NotEqual);
@ -684,7 +703,7 @@ CodeGeneratorMIPS64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
// Check that the result is in the uint64_t range.
masm.moveFromDouble(ScratchDoubleReg, output);
masm.as_cfc1(ScratchRegister, Assembler::FCSR);
masm.as_ext(ScratchRegister, ScratchRegister, 16, 1);
masm.as_ext(ScratchRegister, ScratchRegister, 6, 1);
masm.ma_dsrl(SecondScratchReg, output, Imm32(63));
masm.ma_or(SecondScratchReg, ScratchRegister);
masm.ma_b(SecondScratchReg, Imm32(0), ool->entry(), Assembler::NotEqual);
@ -705,7 +724,7 @@ CodeGeneratorMIPS64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir)
// Check that the result is in the int64_t range.
masm.as_cfc1(output, Assembler::FCSR);
masm.as_ext(output, output, 16, 1);
masm.as_ext(output, output, 6, 1);
masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual);
masm.bind(ool->rejoin());

View File

@ -62,6 +62,7 @@ class CodeGeneratorMIPS64 : public CodeGeneratorMIPSShared
void visitWasmReinterpretToI64(LWasmReinterpretToI64* lir);
void visitExtendInt32ToInt64(LExtendInt32ToInt64* lir);
void visitWrapInt64ToInt32(LWrapInt64ToInt32* lir);
void visitSignExtendInt64(LSignExtendInt64* ins);
void visitClzI64(LClzI64* lir);
void visitCtzI64(LCtzI64* lir);
void visitNotI64(LNotI64* lir);

View File

@ -134,6 +134,20 @@ class LWasmTruncateToInt64 : public LInstructionHelper<1, 1, 0>
}
};
class LInt64ToFloatingPoint : public LInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(Int64ToFloatingPoint);
explicit LInt64ToFloatingPoint(const LInt64Allocation& in) {
setInt64Operand(0, in);
}
MInt64ToFloatingPoint* mir() const {
return mir_->toInt64ToFloatingPoint();
}
};
} // namespace jit
} // namespace js

View File

@ -182,3 +182,23 @@ LIRGeneratorMIPS64::visitRandom(MRandom* ins)
LRandom *lir = new(alloc()) LRandom(temp(), temp(), temp());
defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
}
void
LIRGeneratorMIPS64::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32);
defineInt64(new(alloc()) LWasmTruncateToInt64(useRegister(opd)), ins);
}
void
LIRGeneratorMIPS64::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins)
{
MDefinition* opd = ins->input();
MOZ_ASSERT(opd->type() == MIRType::Int64);
MOZ_ASSERT(IsFloatingPointType(ins->type()));
define(new(alloc()) LInt64ToFloatingPoint(useInt64Register(opd)), ins);
}

View File

@ -47,6 +47,8 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared
void visitUnbox(MUnbox* unbox);
void visitReturn(MReturn* ret);
void visitRandom(MRandom* ins);
void visitWasmTruncateToInt64(MWasmTruncateToInt64* ins);
void visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins);
};
typedef LIRGeneratorMIPS64 LIRGeneratorSpecific;

View File

@ -2571,4 +2571,32 @@ template void
MacroAssembler::storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType,
const BaseIndex& dest, MIRType slotType);
void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
{
as_truncld(ScratchDoubleReg, input);
moveFromDoubleHi(ScratchDoubleReg, output);
as_cfc1(ScratchRegister, Assembler::FCSR);
as_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_or(ScratchRegister, output);
moveFromFloat32(ScratchDoubleReg, output);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
void
MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry)
{
as_truncls(ScratchDoubleReg, input);
moveFromDoubleHi(ScratchDoubleReg, output);
as_cfc1(ScratchRegister, Assembler::FCSR);
as_ext(ScratchRegister, ScratchRegister, 6, 1);
ma_or(ScratchRegister, output);
moveFromFloat32(ScratchDoubleReg, output);
ma_b(ScratchRegister, Imm32(0), oolEntry, Assembler::NotEqual);
}
//}}} check_macroassembler_style

View File

@ -151,7 +151,6 @@ class AssemblerBuffer
{
protected:
typedef BufferSlice<SliceSize> Slice;
typedef AssemblerBuffer<SliceSize, Inst> AssemblerBuffer_;
// Doubly-linked list of BufferSlices, with the most recent in tail position.
Slice* head;
@ -409,24 +408,29 @@ class AssemblerBuffer
return BufferOffset(bufferSize);
}
typedef AssemblerBuffer<SliceSize, Inst> ThisClass;
class AssemblerBufferInstIterator
{
BufferOffset bo;
AssemblerBuffer_* m_buffer;
BufferOffset bo_;
ThisClass* buffer_;
public:
explicit AssemblerBufferInstIterator(BufferOffset off, AssemblerBuffer_* buffer)
: bo(off), m_buffer(buffer)
explicit AssemblerBufferInstIterator(BufferOffset bo, ThisClass* buffer)
: bo_(bo), buffer_(buffer)
{ }
void advance(int offset) {
bo_ = BufferOffset(bo_.getOffset() + offset);
}
Inst* next() {
Inst* i = m_buffer->getInst(bo);
bo = BufferOffset(bo.getOffset() + i->size());
advance(cur()->size());
return cur();
}
Inst* cur() {
return m_buffer->getInst(bo);
Inst* peek() {
return buffer_->getInst(BufferOffset(bo_.getOffset() + cur()->size()));
}
Inst* cur() const {
return buffer_->getInst(bo_);
}
};
};

View File

@ -6509,8 +6509,7 @@ SetBuildIdOp(JSContext* cx, BuildIdOp buildIdOp);
/**
* The WasmModule interface allows the embedding to hold a reference to the
* underying C++ implementation of a JS WebAssembly.Module object for purposes
* of efficient postMessage() of WebAssembly.Module and (de)serialization off
* the object's JSRuntime's thread for IndexedDB.
* of efficient postMessage() and (de)serialization from a random thread.
*
* For postMessage() sharing:
*
@ -6531,8 +6530,10 @@ SetBuildIdOp(JSContext* cx, BuildIdOp buildIdOp);
* compiled code are written into separate files: a bytecode file that always
* allows successful deserialization and a compiled-code file keyed on cpu- and
* build-id that may become invalid if either of these change between
* serialization and deserialization. After serialization, a reference is
* dropped from a separate thread so the virtual destructor must be thread-safe.
* serialization and deserialization. Due to tiering, the serialization must
* asynchronously wait for compilation to complete before requesting the
* module's compiled code. After serialization, a reference is dropped from a
* separate thread so the virtual destructor must be thread-safe.
*
* - Deserialization starts when the structured clone algorithm encounters a
* serialized WebAssembly.Module. On a background thread, the compiled-code file
@ -6540,11 +6541,26 @@ SetBuildIdOp(JSContext* cx, BuildIdOp buildIdOp);
* still valid (as described above). DeserializeWasmModule is then called to
* construct a JS::WasmModule (also on the background thread), passing the
* bytecode file descriptor and, if valid, the compiled-code file descriptor.
* The JS::WasmObject is then transported to the JSRuntime thread (which
* originated the request) and the wrapping WebAssembly.Module object is created
* by calling createObject().
* The JS::WasmObject is then transported to a JSContext thread and the wrapping
* WebAssembly.Module object is created by calling createObject().
*/
class WasmModuleListener
{
protected:
virtual ~WasmModuleListener() {}
public:
// These method signatures are chosen to exactly match nsISupports so that a
// plain nsISupports-implementing class can trivially implement this
// interface too. We can't simply #include "nsISupports.h" so we use MFBT
// equivalents for all the platform-dependent types.
virtual MozExternalRefCountType MOZ_XPCOM_ABI AddRef() = 0;
virtual MozExternalRefCountType MOZ_XPCOM_ABI Release() = 0;
virtual void onCompilationComplete() = 0;
};
struct WasmModule : js::AtomicRefCounted<WasmModule>
{
virtual ~WasmModule() {}
@ -6552,6 +6568,12 @@ struct WasmModule : js::AtomicRefCounted<WasmModule>
virtual size_t bytecodeSerializedSize() const = 0;
virtual void bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const = 0;
// Compilation must complete before the serialized code is requested. If
// compilation is not complete, the embedding must wait until notified by
// implementing WasmModuleListener. SpiderMonkey will hold a RefPtr to
// 'listener' until onCompilationComplete() is called.
virtual bool compilationComplete() const = 0;
virtual bool notifyWhenCompilationComplete(WasmModuleListener* listener) = 0;
virtual size_t compiledSerializedSize() const = 0;
virtual void compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const = 0;

View File

@ -2940,47 +2940,20 @@ date_toSource(JSContext* cx, unsigned argc, Value* vp)
}
#endif
MOZ_ALWAYS_INLINE bool
IsObject(HandleValue v)
{
return v.isObject();
}
// ES6 20.3.4.41.
MOZ_ALWAYS_INLINE bool
date_toString_impl(JSContext* cx, const CallArgs& args)
{
// Step 1.
RootedObject obj(cx, &args.thisv().toObject());
// Step 2.
ESClass cls;
if (!GetBuiltinClass(cx, obj, &cls))
return false;
double tv;
if (cls != ESClass::Date) {
// Step 2.
tv = GenericNaN();
} else {
// Step 3.
RootedValue unboxed(cx);
if (!Unbox(cx, obj, &unboxed))
return false;
tv = unboxed.toNumber();
}
// Step 4.
return FormatDate(cx, tv, FormatSpec::DateTime, args.rval());
// Steps 1-2.
return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
FormatSpec::DateTime, args.rval());
}
bool
date_toString(JSContext* cx, unsigned argc, Value* vp)
{
// Step 1.
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsObject, date_toString_impl>(cx, args);
return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args);
}
MOZ_ALWAYS_INLINE bool

View File

@ -1,10 +1,12 @@
var BUGNUMBER = 861219;
var summary = 'Date.prototype.toString is a generic function';
// Revised in ECMA 2018, Date.prototype.toString is no longer generic (bug 1381433).
print(BUGNUMBER + ": " + summary);
for (var thisValue of [{}, [], /foo/, Date.prototype, new Proxy(new Date(), {})])
assertEq(Date.prototype.toString.call(thisValue), "Invalid Date");
assertThrowsInstanceOf(() => Date.prototype.toString.call(thisValue), TypeError);
for (var prim of [null, undefined, 0, 1.2, true, false, "foo", Symbol.iterator])
assertThrowsInstanceOf(() => Date.prototype.toString.call(prim), TypeError);

View File

@ -496,3 +496,30 @@ JS::StringIsASCII(const char* s)
}
return true;
}
bool
JS::StringIsUTF8(const uint8_t* s, uint32_t length)
{
const uint8_t* limit = s + length;
while (s < limit) {
uint32_t len;
if ((*s & 0x80) == 0)
len = 1;
else if ((*s & 0xE0) == 0xC0)
len = 2;
else if ((*s & 0xF0) == 0xE0)
len = 3;
else if ((*s & 0xF8) == 0xF0)
len = 4;
else
return false;
if (s + len > limit)
return false;
for (uint32_t i = 1; i < len; i++) {
if ((s[i] & 0xC0) != 0x80)
return false;
}
s += len;
}
return true;
}

View File

@ -577,7 +577,7 @@ ModuleEnvironmentObject::setProperty(JSContext* cx, HandleObject obj, HandleId i
if (self->importBindings().has(id))
return result.failReadOnly();
return NativeSetProperty(cx, self, id, v, receiver, Qualified, result);
return NativeSetProperty<Qualified>(cx, self, id, v, receiver, result);
}
/* static */ bool

View File

@ -351,7 +351,7 @@ SetNameOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleObject e
else
varobj = &env->as<NativeObject>();
MOZ_ASSERT(!varobj->getOpsSetProperty());
ok = NativeSetProperty(cx, varobj, id, val, receiver, Unqualified, result);
ok = NativeSetProperty<Unqualified>(cx, varobj, id, val, receiver, result);
} else {
ok = SetProperty(cx, env, id, val, receiver, result);
}

View File

@ -43,7 +43,7 @@
_(ProcessExecutableRegion, 500) \
_(WasmCodeProfilingLabels, 500) \
_(OffThreadPromiseState, 500) \
_(WasmTier2GeneratorComplete, 500) \
_(WasmModuleTieringLock, 500) \
\
_(TraceLoggerGraphState, 600) \
_(VTuneLock, 600)

View File

@ -1259,6 +1259,29 @@ UpdateShapeTypeAndValue(JSContext* cx, NativeObject* obj, Shape* shape, jsid id,
MarkTypePropertyNonWritable(cx, obj, id);
}
// Version of UpdateShapeTypeAndValue optimized for plain data properties.
static MOZ_ALWAYS_INLINE void
UpdateShapeTypeAndValueForWritableDataProp(JSContext* cx, NativeObject* obj, Shape* shape,
jsid id, const Value& value)
{
MOZ_ASSERT(id == shape->propid());
MOZ_ASSERT(shape->hasSlot());
MOZ_ASSERT(shape->hasDefaultGetter());
MOZ_ASSERT(shape->hasDefaultSetter());
MOZ_ASSERT(shape->writable());
obj->setSlotWithType(cx, shape, value, /* overwriting = */ false);
// Per the acquired properties analysis, when the shape of a partially
// initialized object is changed to its fully initialized shape, its
// group can be updated as well.
if (TypeNewScript* newScript = obj->groupRaw()->newScript()) {
if (newScript->initializedShape() == shape)
obj->setGroup(newScript->initializedGroup());
}
}
void
js::AddPropertyTypesAfterProtoChange(JSContext* cx, NativeObject* obj, ObjectGroup* oldGroup)
{
@ -1440,6 +1463,26 @@ AddOrChangeProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
return CallAddPropertyHook(cx, obj, id, desc.value());
}
// Version of AddOrChangeProperty optimized for adding a plain data property.
// This function doesn't handle integer ids as we may have to store them in
// dense elements.
static MOZ_ALWAYS_INLINE bool
AddDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v)
{
MOZ_ASSERT(!JSID_IS_INT(id));
if (!PurgeEnvironmentChain(cx, obj, id))
return false;
Shape* shape = NativeObject::addEnumerableDataProperty(cx, obj, id);
if (!shape)
return false;
UpdateShapeTypeAndValueForWritableDataProp(cx, obj, shape, id, v);
return CallAddPropertyHook(cx, obj, id, v);
}
static bool IsConfigurable(unsigned attrs) { return (attrs & JSPROP_PERMANENT) == 0; }
static bool IsEnumerable(unsigned attrs) { return (attrs & JSPROP_ENUMERATE) != 0; }
static bool IsWritable(unsigned attrs) { return (attrs & JSPROP_READONLY) == 0; }
@ -1872,10 +1915,8 @@ js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* na
static bool
DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
Handle<PropertyDescriptor> desc, ObjectOpResult& result)
HandleValue v, ObjectOpResult& result)
{
desc.assertComplete();
// Optimized NativeDefineProperty() version for known absent properties.
// Dispense with custom behavior of exotic native objects first.
@ -1928,8 +1969,20 @@ DefineNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
if (!obj->nonProxyIsExtensible())
return result.fail(JSMSG_CANT_DEFINE_PROP_OBJECT_NOT_EXTENSIBLE);
if (!AddOrChangeProperty<IsAddOrChange::Add>(cx, obj, id, desc))
return false;
if (JSID_IS_INT(id)) {
// This might be a dense element. Use AddOrChangeProperty as it knows
// how to deal with that.
Rooted<PropertyDescriptor> desc(cx);
desc.setDataDescriptor(v, JSPROP_ENUMERATE);
if (!AddOrChangeProperty<IsAddOrChange::Add>(cx, obj, id, desc))
return false;
} else {
if (!AddDataProperty(cx, obj, id, v))
return false;
}
return result.succeed();
}
@ -2334,7 +2387,7 @@ NativeGetPropertyInline(JSContext* cx,
// being resolved.
// What they all have in common is we do not want to keep walking
// the prototype chain.
RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
JSObject* proto = done ? nullptr : pobj->staticPrototype();
// Step 4.c. The spec algorithm simply returns undefined if proto is
// null, but see the comment on GetNonexistentProperty.
@ -2346,8 +2399,10 @@ NativeGetPropertyInline(JSContext* cx,
// plumbing of JSObject::getGeneric; the top of the loop is where
// we're going to end up anyway. But if pobj is non-native,
// that optimization would be incorrect.
if (proto->getOpsGetProperty())
return GeneralizedGetProperty(cx, proto, id, receiver, nameLookup, vp);
if (proto->getOpsGetProperty()) {
RootedObject protoRoot(cx, proto);
return GeneralizedGetProperty(cx, protoRoot, id, receiver, nameLookup, vp);
}
pobj = &proto->as<NativeObject>();
}
@ -2539,11 +2594,12 @@ js::SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue
* FIXME: This should be updated to follow ES6 draft rev 28, section 9.1.9,
* steps 4.d.i and 5.
*/
template <QualifiedBool IsQualified>
static bool
SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result)
HandleValue receiver, ObjectOpResult& result)
{
if (!qualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) {
if (!IsQualified && receiver.isObject() && receiver.toObject().isUnqualifiedVarObj()) {
RootedString idStr(cx, JSID_TO_STRING(id));
if (!MaybeReportUndeclaredVarAssignment(cx, idStr))
return false;
@ -2551,7 +2607,7 @@ SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handl
// Pure optimization for the common case. There's no point performing the
// lookup in step 5.c again, as our caller just did it for us.
if (qualified && receiver.isObject() && obj == &receiver.toObject()) {
if (IsQualified && receiver.isObject() && obj == &receiver.toObject()) {
// Ensure that a custom GetOwnPropertyOp, if present, doesn't
// introduce additional properties which weren't previously found by
// LookupOwnProperty.
@ -2567,19 +2623,19 @@ SetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handl
// Step 5.e. Define the new data property.
Rooted<PropertyDescriptor> desc(cx);
desc.initFields(nullptr, v, JSPROP_ENUMERATE, nullptr, nullptr);
if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
// Purge the property cache of now-shadowed id in receiver's environment chain.
if (!PurgeEnvironmentChain(cx, obj, id))
return false;
Rooted<PropertyDescriptor> desc(cx);
desc.initFields(nullptr, v, JSPROP_ENUMERATE, nullptr, nullptr);
MOZ_ASSERT(!cx->helperThread());
return op(cx, obj, id, desc, result);
}
return DefineNonexistentProperty(cx, obj, id, desc, result);
return DefineNonexistentProperty(cx, obj, id, v, result);
}
return SetPropertyByDefining(cx, id, v, receiver, result);
@ -2696,9 +2752,10 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa
return result.succeed();
}
template <QualifiedBool IsQualified>
bool
js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result)
HandleValue receiver, ObjectOpResult& result)
{
// Fire watchpoints, if any.
RootedValue v(cx, value);
@ -2736,10 +2793,10 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handle
// being resolved.
// What they all have in common is we do not want to keep walking
// the prototype chain.
RootedObject proto(cx, done ? nullptr : pobj->staticPrototype());
JSObject* proto = done ? nullptr : pobj->staticPrototype();
if (!proto) {
// Step 4.d.i (and step 5).
return SetNonexistentProperty(cx, obj, id, v, receiver, qualified, result);
return SetNonexistentProperty<IsQualified>(cx, obj, id, v, receiver, result);
}
// Step 4.c.i. If the prototype is also native, this step is a
@ -2751,20 +2808,31 @@ js::NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, Handle
// Unqualified assignments are not specified to go through [[Set]]
// at all, but they do go through this function. So check for
// unqualified assignment to a nonexistent global (a strict error).
if (!qualified) {
RootedObject protoRoot(cx, proto);
if (!IsQualified) {
bool found;
if (!HasProperty(cx, proto, id, &found))
if (!HasProperty(cx, protoRoot, id, &found))
return false;
if (!found)
return SetNonexistentProperty(cx, obj, id, v, receiver, qualified, result);
return SetNonexistentProperty<IsQualified>(cx, obj, id, v, receiver, result);
}
return SetProperty(cx, proto, id, v, receiver, result);
return SetProperty(cx, protoRoot, id, v, receiver, result);
}
pobj = &proto->as<NativeObject>();
}
}
template bool
js::NativeSetProperty<Qualified>(JSContext* cx, HandleNativeObject obj, HandleId id,
HandleValue value, HandleValue receiver,
ObjectOpResult& result);
template bool
js::NativeSetProperty<Unqualified>(JSContext* cx, HandleNativeObject obj, HandleId id,
HandleValue value, HandleValue receiver,
ObjectOpResult& result);
bool
js::NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
HandleValue receiver, ObjectOpResult& result)
@ -2772,7 +2840,7 @@ js::NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, Hand
RootedId id(cx);
if (!IndexToId(cx, index, &id))
return false;
return NativeSetProperty(cx, obj, id, v, receiver, Qualified, result);
return NativeSetProperty<Qualified>(cx, obj, id, v, receiver, result);
}
/*** [[Delete]] **********************************************************************************/

View File

@ -805,6 +805,7 @@ class NativeObject : public ShapedObject
JSGetterOp getter, JSSetterOp setter,
uint32_t slot, unsigned attrs, unsigned flags,
bool allowDictionary = true);
static Shape* addEnumerableDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id);
/* Add a data property whose id is not yet in this scope. */
static Shape* addDataProperty(JSContext* cx, HandleNativeObject obj,
@ -1456,9 +1457,10 @@ enum QualifiedBool {
Qualified = 1
};
template <QualifiedBool Qualified>
extern bool
NativeSetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue v,
HandleValue receiver, QualifiedBool qualified, ObjectOpResult& result);
HandleValue receiver, ObjectOpResult& result);
extern bool
NativeSetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue v,
@ -1550,7 +1552,7 @@ js::SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
{
if (obj->getOpsSetProperty())
return JSObject::nonNativeSetProperty(cx, obj, id, v, receiver, result);
return NativeSetProperty(cx, obj.as<NativeObject>(), id, v, receiver, Qualified, result);
return NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, v, receiver, result);
}
inline bool

View File

@ -528,6 +528,97 @@ NativeObject::addPropertyInternal(JSContext* cx,
return nullptr;
}
/* static */ Shape*
NativeObject::addEnumerableDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id)
{
// Like addProperty(Internal), but optimized for the common case of adding a
// new enumerable data property.
AutoKeepShapeTables keep(cx);
ShapeTable* table = nullptr;
ShapeTable::Entry* entry = nullptr;
if (!obj->inDictionaryMode()) {
if (MOZ_UNLIKELY(ShouldConvertToDictionary(obj))) {
if (!toDictionaryMode(cx, obj))
return nullptr;
table = obj->lastProperty()->maybeTable(keep);
entry = &table->search<MaybeAdding::Adding>(id, keep);
}
} else {
table = obj->lastProperty()->ensureTableForDictionary(cx, keep);
if (!table)
return nullptr;
if (table->needsToGrow()) {
if (!table->grow(cx))
return nullptr;
}
entry = &table->search<MaybeAdding::Adding>(id, keep);
MOZ_ASSERT(!entry->shape());
}
MOZ_ASSERT(!!table == !!entry);
/* Find or create a property tree node labeled by our arguments. */
RootedShape last(cx, obj->lastProperty());
UnownedBaseShape* nbase = GetBaseShapeForNewShape(cx, last, id);
if (!nbase)
return nullptr;
Shape* shape;
if (obj->inDictionaryMode()) {
uint32_t slot;
if (!allocDictionarySlot(cx, obj, &slot))
return nullptr;
Rooted<StackShape> child(cx, StackShape(nbase, id, slot, JSPROP_ENUMERATE, 0));
MOZ_ASSERT(last == obj->lastProperty());
shape = Allocate<Shape>(cx);
if (!shape)
return nullptr;
if (slot >= obj->lastProperty()->base()->slotSpan()) {
if (MOZ_UNLIKELY(!obj->setSlotSpan(cx, slot + 1))) {
new (shape) Shape(obj->lastProperty()->base()->unowned(), 0);
return nullptr;
}
}
shape->initDictionaryShape(child, obj->numFixedSlots(), &obj->shape_);
} else {
uint32_t slot = obj->slotSpan();
MOZ_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
// Objects with many properties are converted to dictionary
// mode, so we can't overflow SHAPE_MAXIMUM_SLOT here.
MOZ_ASSERT(slot < JSSLOT_FREE(obj->getClass()) + PropertyTree::MAX_HEIGHT);
MOZ_ASSERT(slot < SHAPE_MAXIMUM_SLOT);
Rooted<StackShape> child(cx, StackShape(nbase, id, slot, JSPROP_ENUMERATE, 0));
shape = cx->zone()->propertyTree().inlinedGetChild(cx, last, child);
if (!shape)
return nullptr;
if (!obj->setLastProperty(cx, shape)) {
obj->checkShapeConsistency();
return nullptr;
}
}
MOZ_ASSERT(shape == obj->lastProperty());
if (table) {
/* Store the tree node pointer in the table entry for id. */
entry->setPreservingCollision(shape);
table->incEntryCount();
/* Pass the table along to the new last property, namely shape. */
MOZ_ASSERT(shape->parent->maybeTable(keep) == table);
shape->parent->handoffTableTo(shape);
}
obj->checkShapeConsistency();
return shape;
}
Shape*
js::ReshapeForAllocKind(JSContext* cx, Shape* shape, TaggedProto proto,
gc::AllocKind allocKind)

View File

@ -1287,11 +1287,11 @@ OpIter<Policy>::readCurrentMemory()
if (!env_.usesMemory())
return fail("can't touch memory without memory");
uint32_t flags;
if (!readVarU32(&flags))
uint8_t flags;
if (!readFixedU8(&flags))
return false;
if (flags != uint32_t(MemoryTableFlags::Default))
if (flags != uint8_t(MemoryTableFlags::Default))
return fail("unexpected flags");
return push(ValType::I32);
@ -1306,11 +1306,11 @@ OpIter<Policy>::readGrowMemory(Value* input)
if (!env_.usesMemory())
return fail("can't touch memory without memory");
uint32_t flags;
if (!readVarU32(&flags))
uint8_t flags;
if (!readFixedU8(&flags))
return false;
if (flags != uint32_t(MemoryTableFlags::Default))
if (flags != uint8_t(MemoryTableFlags::Default))
return fail("unexpected flags");
if (!popWithType(ValType::I32, input))
@ -1604,11 +1604,11 @@ OpIter<Policy>::readCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector*
if (*sigIndex >= env_.numSigs())
return fail("signature index out of range");
uint32_t flags;
if (!readVarU32(&flags))
uint8_t flags;
if (!readFixedU8(&flags))
return false;
if (flags != uint32_t(MemoryTableFlags::Default))
if (flags != uint8_t(MemoryTableFlags::Default))
return fail("unexpected flags");
if (!popWithType(ValType::I32, callee))

View File

@ -271,13 +271,13 @@ static const unsigned PoppedFP = 0;
static const unsigned PoppedExitReason = 0;
#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
static const unsigned BeforePushRetAddr = 0;
static const unsigned PushedRetAddr = 4;
static const unsigned PushedTLS = 8;
static const unsigned PushedExitReason = 12;
static const unsigned PushedFP = 16;
static const unsigned SetFP = 20;
static const unsigned PoppedFP = 8;
static const unsigned PoppedExitReason = 4;
static const unsigned PushedRetAddr = 8;
static const unsigned PushedTLS = 16;
static const unsigned PushedExitReason = 28;
static const unsigned PushedFP = 36;
static const unsigned SetFP = 40;
static const unsigned PoppedFP = 16;
static const unsigned PoppedExitReason = 8;
#elif defined(JS_CODEGEN_NONE)
static const unsigned PushedRetAddr = 0;
static const unsigned PushedTLS = 1;

View File

@ -18,6 +18,9 @@
#include "wasm/WasmModule.h"
#include <chrono>
#include <thread>
#include "jsnspr.h"
#include "jit/JitOptions.h"
@ -247,7 +250,7 @@ class Module::Tier2GeneratorTaskImpl : public Tier2GeneratorTask
~Tier2GeneratorTaskImpl() override {
if (!finished_)
module_->unblockOnTier2GeneratorFinished(CompileMode::Once);
module_->notifyCompilationListeners();
}
void cancel() override {
@ -263,7 +266,7 @@ class Module::Tier2GeneratorTaskImpl : public Tier2GeneratorTask
void
Module::startTier2(const CompileArgs& args)
{
MOZ_ASSERT(mode_ == CompileMode::Once);
MOZ_ASSERT(!tiering_.lock()->active);
// If a Module initiates tier-2 compilation, we must ensure that eventually
// unblockOnTier2GeneratorFinished() is called. Since we must ensure
@ -275,14 +278,31 @@ Module::startTier2(const CompileArgs& args)
if (!task)
return;
{
LockGuard<Mutex> l(tier2Lock_);
mode_ = CompileMode::Tier1;
}
tiering_.lock()->active = true;
StartOffThreadWasmTier2Generator(Move(task));
}
void
Module::notifyCompilationListeners()
{
// Notify listeners without holding the lock to avoid deadlocks if the
// listener takes their own lock or reenters this Module.
Tiering::ListenerVector listeners;
{
auto tiering = tiering_.lock();
MOZ_ASSERT(tiering->active);
tiering->active = false;
Swap(listeners, tiering->listeners);
}
for (RefPtr<JS::WasmModuleListener>& listener : listeners)
listener->onCompilationComplete();
}
void
Module::finishTier2(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
UniqueConstCodeSegment code2, UniqueModuleEnvironment env2)
@ -299,7 +319,7 @@ Module::finishTier2(UniqueLinkDataTier linkData2, UniqueMetadataTier metadata2,
// unblock anyone waiting on it.
metadata().commitTier2();
unblockOnTier2GeneratorFinished(CompileMode::Tier2);
notifyCompilationListeners();
// And we update the jump vector.
@ -338,16 +358,45 @@ Module::bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const
MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
}
/* virtual */ bool
Module::compilationComplete() const
{
// For the purposes of serialization, if there is not an active tier-2
// compilation in progress, compilation is "complete" in that
// compiledSerialize() can be called. Now, tier-2 compilation may have
// failed or never started in the first place, but in such cases, a
// zero-byte compilation is serialized, triggering recompilation on upon
// deserialization. Basically, we only want serialization to wait if waiting
// would eventually produce tier-2 code.
return !tiering_.lock()->active;
}
/* virtual */ bool
Module::notifyWhenCompilationComplete(JS::WasmModuleListener* listener)
{
{
auto tiering = tiering_.lock();
if (tiering->active)
return tiering->listeners.append(listener);
}
// Notify the listener without holding the lock to avoid deadlocks if the
// listener takes their own lock or reenters this Module.
listener->onCompilationComplete();
return true;
}
/* virtual */ size_t
Module::compiledSerializedSize() const
{
MOZ_ASSERT(!tiering_.lock()->active);
// The compiled debug code must not be saved, set compiled size to 0,
// so Module::assumptionsMatch will return false during assumptions
// deserialization.
if (metadata().debugEnabled)
return 0;
blockOnIonCompileFinished();
if (!code_->hasTier(Tier::Serialized))
return 0;
@ -363,12 +412,13 @@ Module::compiledSerializedSize() const
/* virtual */ void
Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
{
MOZ_ASSERT(!tiering_.lock()->active);
if (metadata().debugEnabled) {
MOZ_RELEASE_ASSERT(compiledSize == 0);
return;
}
blockOnIonCompileFinished();
if (!code_->hasTier(Tier::Serialized)) {
MOZ_RELEASE_ASSERT(compiledSize == 0);
return;
@ -385,22 +435,6 @@ Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
}
void
Module::blockOnIonCompileFinished() const
{
LockGuard<Mutex> l(tier2Lock_);
while (mode_ == CompileMode::Tier1 && !metadata().hasTier2())
tier2Cond_.wait(l);
}
void
Module::unblockOnTier2GeneratorFinished(CompileMode newMode) const
{
LockGuard<Mutex> l(tier2Lock_);
mode_ = newMode;
tier2Cond_.notify_all();
}
/* static */ bool
Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
{
@ -611,8 +645,10 @@ Module::extractCode(JSContext* cx, Tier tier, MutableHandleValue vp) const
if (!result)
return false;
if (tier == Tier::Ion)
blockOnIonCompileFinished();
// This function is only used for testing purposes so we can simply
// busy-wait on tiered compilation to complete.
while (!compilationComplete())
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (!code_->hasTier(tier)) {
vp.setNull();

View File

@ -109,6 +109,21 @@ class LinkData
WASM_DECLARE_SERIALIZABLE(LinkData)
};
// Contains the locked tiering state of a Module: whether there is an active
// background tier-2 compilation in progress and, if so, the list of listeners
// waiting for the tier-2 compilation to complete.
struct Tiering
{
typedef Vector<RefPtr<JS::WasmModuleListener>, 0, SystemAllocPolicy> ListenerVector;
Tiering() : active(false) {}
~Tiering() { MOZ_ASSERT(listeners.empty()); MOZ_ASSERT(!active); }
ListenerVector listeners;
bool active;
};
// Module represents a compiled wasm module and primarily provides two
// operations: instantiation and serialization. A Module can be instantiated any
// number of times to produce new Instance objects. A Module can be serialized
@ -133,6 +148,7 @@ class Module : public JS::WasmModule
const DataSegmentVector dataSegments_;
const ElemSegmentVector elemSegments_;
const SharedBytes bytecode_;
ExclusiveData<Tiering> tiering_;
// `codeIsBusy_` is set to false initially and then to true when `code_` is
// already being used for an instance and can't be shared because it may be
@ -141,20 +157,6 @@ class Module : public JS::WasmModule
mutable Atomic<bool> codeIsBusy_;
// The lock guards the mode_ member, and the lock/cond pair are used to
// allow threads to wait for the availability of Ion code and signal the
// completion of tier-2 compilation; see blockOnIonCompileFinished and
// unblockOnTier2GeneratorFinished, below.
mutable Mutex tier2Lock_;
mutable ConditionVariable tier2Cond_;
// Access mode_ only under the lock. It will be changed from Tier1 to Tier2
// once Tier2 compilation is finished, and from Tier1 to Once if Tier2
// compilation is disabled (in testing modes) or cancelled.
mutable CompileMode mode_;
bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
bool instantiateTable(JSContext* cx,
@ -167,6 +169,7 @@ class Module : public JS::WasmModule
const ValVector& globalImports) const;
class Tier2GeneratorTaskImpl;
void notifyCompilationListeners();
public:
Module(Assumptions&& assumptions,
@ -187,9 +190,8 @@ class Module : public JS::WasmModule
dataSegments_(Move(dataSegments)),
elemSegments_(Move(elemSegments)),
bytecode_(&bytecode),
codeIsBusy_(false),
tier2Lock_(js::mutexid::WasmTier2GeneratorComplete),
mode_(CompileMode::Once)
tiering_(mutexid::WasmModuleTieringLock),
codeIsBusy_(false)
{
MOZ_ASSERT_IF(metadata().debugEnabled, unlinkedCodeForDebugging_);
}
@ -240,10 +242,12 @@ class Module : public JS::WasmModule
void unblockOnTier2GeneratorFinished(CompileMode newMode) const;
// Structured clone support:
// JS API and JS::WasmModule implementation:
size_t bytecodeSerializedSize() const override;
void bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const override;
bool compilationComplete() const override;
bool notifyWhenCompilationComplete(JS::WasmModuleListener* listener) override;
size_t compiledSerializedSize() const override;
void compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const override;

View File

@ -1134,10 +1134,7 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
// Reserve space to store resumePC and HeapReg.
masm.subFromStackPtr(Imm32(2 * sizeof(intptr_t)));
// set to zero so we can use masm.framePushed() below.
masm.push(Imm32(0)); // space used as return address, updated below
masm.setFramePushed(0); // set to 0 now so that framePushed is offset of return address
masm.PushRegsInMask(AllRegsExceptSP); // save all GP/FP registers (except PC and SP)
masm.setFramePushed(0);
static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too.");
// save all registers,except sp. After this stack is alligned.
masm.PushRegsInMask(AllRegsExceptSP);
@ -1166,6 +1163,10 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel)
// This will restore stack to the address before the call.
masm.moveToStackPtr(s0);
// Store resumePC into the reserved space.
masm.storePtr(ReturnReg, Address(s0, masm.framePushed()));
masm.PopRegsInMask(AllRegsExceptSP);
// Pop resumePC into PC. Clobber HeapReg to make the jump and restore it

View File

@ -74,12 +74,12 @@ Decoder::startSection(SectionId id, ModuleEnvironment* env, uint32_t* sectionSta
// Only start a section with 'id', skipping any custom sections before it.
uint32_t idValue;
if (!readVarU32(&idValue))
uint8_t idValue;
if (!readFixedU8(&idValue))
goto rewind;
while (idValue != uint32_t(id)) {
if (idValue != uint32_t(SectionId::Custom))
while (idValue != uint8_t(id)) {
if (idValue != uint8_t(SectionId::Custom))
goto rewind;
// Rewind to the beginning of the current section since this is what
@ -91,7 +91,7 @@ Decoder::startSection(SectionId id, ModuleEnvironment* env, uint32_t* sectionSta
// Having successfully skipped a custom section, consider the next
// section.
currentSectionStart = cur_;
if (!readVarU32(&idValue))
if (!readFixedU8(&idValue))
goto rewind;
}
@ -207,8 +207,8 @@ Decoder::startNameSubsection(NameType nameType, uint32_t* endOffset)
{
const uint8_t* initialPosition = cur_;
uint32_t nameTypeValue;
if (!readVarU32(&nameTypeValue))
uint8_t nameTypeValue;
if (!readFixedU8(&nameTypeValue))
return false;
if (nameTypeValue != uint8_t(nameType)) {
@ -760,8 +760,8 @@ DecodeTypeSection(Decoder& d, ModuleEnvironment* env)
return false;
for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
uint32_t form;
if (!d.readVarU32(&form) || form != uint32_t(TypeCode::Func))
uint8_t form;
if (!d.readFixedU8(&form) || form != uint8_t(TypeCode::Func))
return d.fail("expected function form");
uint32_t numArgs;
@ -820,6 +820,9 @@ DecodeName(Decoder& d)
if (!d.readBytes(numBytes, &bytes))
return nullptr;
if (!JS::StringIsUTF8(bytes, numBytes))
return nullptr;
UniqueChars name(js_pod_malloc<char>(numBytes + 1));
if (!name)
return nullptr;
@ -845,12 +848,12 @@ DecodeSignatureIndex(Decoder& d, const SigWithIdVector& sigs, uint32_t* sigIndex
static bool
DecodeLimits(Decoder& d, Limits* limits)
{
uint32_t flags;
if (!d.readVarU32(&flags))
uint8_t flags;
if (!d.readFixedU8(&flags))
return d.fail("expected flags");
if (flags & ~uint32_t(0x1))
return d.failf("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1)));
if (flags & ~uint8_t(0x1))
return d.failf("unexpected bits set in flags: %" PRIu32, (flags & ~uint8_t(0x1)));
if (!d.readVarU32(&limits->initial))
return d.fail("expected initial length");
@ -875,11 +878,11 @@ DecodeLimits(Decoder& d, Limits* limits)
static bool
DecodeTableLimits(Decoder& d, TableDescVector* tables)
{
uint32_t elementType;
if (!d.readVarU32(&elementType))
uint8_t elementType;
if (!d.readFixedU8(&elementType))
return d.fail("expected table element type");
if (elementType != uint32_t(TypeCode::AnyFunc))
if (elementType != uint8_t(TypeCode::AnyFunc))
return d.fail("expected 'anyfunc' element type");
Limits limits;
@ -923,14 +926,14 @@ DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
if (!DecodeValType(d, ModuleKind::Wasm, type))
return false;
uint32_t flags;
if (!d.readVarU32(&flags))
uint8_t flags;
if (!d.readFixedU8(&flags))
return d.fail("expected global flags");
if (flags & ~uint32_t(GlobalTypeImmediate::AllowedMask))
if (flags & ~uint8_t(GlobalTypeImmediate::AllowedMask))
return d.fail("unexpected bits set in global flags");
*isMutable = flags & uint32_t(GlobalTypeImmediate::IsMutable);
*isMutable = flags & uint8_t(GlobalTypeImmediate::IsMutable);
return true;
}
@ -981,8 +984,8 @@ DecodeImport(Decoder& d, ModuleEnvironment* env)
if (!funcName)
return d.fail("expected valid import func name");
uint32_t rawImportKind;
if (!d.readVarU32(&rawImportKind))
uint8_t rawImportKind;
if (!d.readFixedU8(&rawImportKind))
return d.fail("failed to read import kind");
DefinitionKind importKind = DefinitionKind(rawImportKind);
@ -1281,8 +1284,8 @@ DecodeExport(Decoder& d, ModuleEnvironment* env, CStringSet* dupSet)
if (!fieldName)
return false;
uint32_t exportKind;
if (!d.readVarU32(&exportKind))
uint8_t exportKind;
if (!d.readFixedU8(&exportKind))
return d.fail("failed to read export kind");
switch (DefinitionKind(exportKind)) {

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-100%p" android:toXDelta="0"
android:duration="@android:integer/config_shortAnimTime"/>
</set>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0"
android:duration="@android:integer/config_shortAnimTime"/>
</set>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-100%p"
android:duration="@android:integer/config_shortAnimTime"/>
</set>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="100%p"
android:duration="@android:integer/config_shortAnimTime"/>
</set>

View File

@ -2066,12 +2066,13 @@ public class BrowserApp extends GeckoApp
final String name = message.getString("name");
final String startUrl = message.getString("start_url");
final String manifestPath = message.getString("manifest_path");
final String manifestUrl = message.getString("manifest_url");
final LoadFaviconResult loadIconResult = FaviconDecoder
.decodeDataURI(this, message.getString("icon"));
if (loadIconResult != null) {
final Bitmap icon = loadIconResult
.getBestBitmap(GeckoAppShell.getPreferredIconSize());
GeckoApplication.createAppShortcut(name, startUrl, manifestPath, icon);
GeckoApplication.createAppShortcut(name, startUrl, manifestPath, manifestUrl, icon);
} else {
Log.e(LOGTAG, "Failed to load icon!");
}

View File

@ -522,11 +522,13 @@ public class GeckoApplication extends Application {
}
public static void createAppShortcut(final String aTitle, final String aURI,
final String manifestPath, final Bitmap aIcon) {
final String manifestPath, final String manifestUrl,
final Bitmap aIcon) {
final Intent shortcutIntent = new Intent();
shortcutIntent.setAction(GeckoApp.ACTION_WEBAPP);
shortcutIntent.setData(Uri.parse(aURI));
shortcutIntent.putExtra("MANIFEST_PATH", manifestPath);
shortcutIntent.putExtra("MANIFEST_URL", manifestUrl);
shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME,
LauncherActivity.class.getName());
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION,

View File

@ -17,41 +17,35 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import org.json.JSONObject;
import org.json.JSONException;
import org.mozilla.gecko.ActivityHandlerHelper;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.customtabs.CustomTabsActivity;
import org.mozilla.gecko.DoorHangerPopup;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoScreenOrientation;
import org.mozilla.gecko.GeckoView;
import org.mozilla.gecko.GeckoViewSettings;
import org.mozilla.gecko.icons.decoders.FaviconDecoder;
import org.mozilla.gecko.icons.decoders.LoadFaviconResult;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.prompts.PromptService;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.ColorUtil;
import org.mozilla.gecko.util.FileUtils;
public class WebAppActivity extends AppCompatActivity
implements GeckoView.NavigationListener {
private static final String LOGTAG = "WebAppActivity";
public static final String MANIFEST_PATH = "MANIFEST_PATH";
public static final String MANIFEST_URL = "MANIFEST_URL";
private static final String SAVED_INTENT = "savedIntent";
private GeckoView mGeckoView;
@ -61,8 +55,13 @@ public class WebAppActivity extends AppCompatActivity
private boolean mIsFullScreenMode;
private boolean mIsFullScreenContent;
private boolean mCanGoBack;
private Uri mManifestUrl;
private Uri mStartUrl;
private Uri mScope;
private WebAppManifest mManifest;
@Override
public void onCreate(Bundle savedInstanceState) {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 &&
@ -97,14 +96,14 @@ public class WebAppActivity extends AppCompatActivity
final GeckoViewSettings settings = mGeckoView.getSettings();
settings.setBoolean(GeckoViewSettings.USE_MULTIPROCESS, false);
final Uri u = getIntent().getData();
if (u != null) {
mGeckoView.loadUri(u.toString());
}
mManifest = WebAppManifest.fromFile(getIntent().getStringExtra(MANIFEST_URL),
getIntent().getStringExtra(MANIFEST_PATH));
updateFromManifest();
mGeckoView.loadUri(mManifest.getStartUri().toString());
setContentView(mGeckoView);
loadManifest(getIntent().getStringExtra(MANIFEST_PATH));
}
@Override
@ -153,40 +152,27 @@ public class WebAppActivity extends AppCompatActivity
}
}
private void loadManifest(String manifestPath) {
if (TextUtils.isEmpty(manifestPath)) {
Log.e(LOGTAG, "Missing manifest");
return;
private void updateFromManifest() {
if (AppConstants.Versions.feature21Plus) {
updateTaskAndStatusBar();
}
try {
final File manifestFile = new File(manifestPath);
final JSONObject manifest = FileUtils.readJSONObjectFromFile(manifestFile);
final JSONObject manifestField = manifest.getJSONObject("manifest");
if (AppConstants.Versions.feature21Plus) {
loadManifestV21(manifest, manifestField);
}
updateScreenOrientation(manifestField);
updateDisplayMode(manifestField);
} catch (IOException | JSONException e) {
Log.e(LOGTAG, "Failed to read manifest", e);
}
updateScreenOrientation();
updateDisplayMode();
}
// The customisations defined in the manifest only work on Android API 21+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void loadManifestV21(JSONObject manifest, JSONObject manifestField) {
final Integer color = readColorFromManifest(manifestField);
final String name = readNameFromManifest(manifestField);
final Bitmap icon = readIconFromManifest(manifest);
mScope = readScopeFromManifest(manifest);
final ActivityManager.TaskDescription taskDescription = (color == null)
? new ActivityManager.TaskDescription(name, icon)
: new ActivityManager.TaskDescription(name, icon, color);
private void updateTaskAndStatusBar() {
final Integer themeColor = mManifest.getThemeColor();
final String name = mManifest.getName();
final Bitmap icon = mManifest.getIcon();
updateStatusBarColorV21(color);
final ActivityManager.TaskDescription taskDescription = (themeColor == null)
? new ActivityManager.TaskDescription(name, icon)
: new ActivityManager.TaskDescription(name, icon, themeColor);
updateStatusBarColorV21(themeColor);
setTaskDescription(taskDescription);
}
@ -199,21 +185,22 @@ public class WebAppActivity extends AppCompatActivity
}
}
private void updateScreenOrientation(JSONObject manifest) {
String orientString = manifest.optString("orientation", null);
private void updateScreenOrientation() {
final String orientString = mManifest.getOrientation();
if (orientString == null) {
return;
}
GeckoScreenOrientation.ScreenOrientation orientation =
final GeckoScreenOrientation.ScreenOrientation orientation =
GeckoScreenOrientation.screenOrientationFromString(orientString);
int activityOrientation = GeckoScreenOrientation.screenOrientationToAndroidOrientation(orientation);
final int activityOrientation =
GeckoScreenOrientation.screenOrientationToAndroidOrientation(orientation);
setRequestedOrientation(activityOrientation);
}
private void updateDisplayMode(JSONObject manifest) {
String displayMode = manifest.optString("display");
private void updateDisplayMode() {
final String displayMode = mManifest.getDisplayMode();
updateFullScreenMode(displayMode.equals("fullscreen"));
@ -237,84 +224,6 @@ public class WebAppActivity extends AppCompatActivity
mGeckoView.getSettings().setInt(GeckoViewSettings.DISPLAY_MODE, mode.value());
}
private Integer readColorFromManifest(JSONObject manifest) {
final String colorStr = manifest.optString("theme_color", null);
if (colorStr != null) {
return ColorUtil.parseStringColor(colorStr);
}
return null;
}
private String readNameFromManifest(JSONObject manifest) {
String name = manifest.optString("name", null);
if (name == null) {
name = manifest.optString("short_name", null);
}
if (name == null) {
name = manifest.optString("start_url", null);
}
return name;
}
private Bitmap readIconFromManifest(JSONObject manifest) {
final String iconStr = manifest.optString("cached_icon", null);
if (iconStr == null) {
return null;
}
final LoadFaviconResult loadIconResult = FaviconDecoder
.decodeDataURI(this, iconStr);
if (loadIconResult == null) {
return null;
}
return loadIconResult.getBestBitmap(GeckoAppShell.getPreferredIconSize());
}
private Uri readScopeFromManifest(JSONObject manifest) {
final String scopeStr = manifest.optString("scope", null);
if (scopeStr == null) {
return null;
}
Uri res = Uri.parse(scopeStr);
if (res.isRelative()) {
// TODO: Handle this more correctly.
return null;
}
return res;
}
private boolean isInScope(String url) {
if (mScope == null) {
return true;
}
final Uri uri = Uri.parse(url);
if (!uri.getScheme().equals(mScope.getScheme())) {
return false;
}
if (!uri.getHost().equals(mScope.getHost())) {
return false;
}
final List<String> scopeSegments = mScope.getPathSegments();
final List<String> urlSegments = uri.getPathSegments();
if (scopeSegments.size() > urlSegments.size()) {
return false;
}
for (int i = 0; i < scopeSegments.size(); i++) {
if (!scopeSegments.get(i).equals(urlSegments.get(i))) {
return false;
}
}
return true;
}
/* GeckoView.NavigationListener */
@Override
public void onLocationChange(GeckoView view, String url) {
@ -330,17 +239,30 @@ public class WebAppActivity extends AppCompatActivity
}
@Override
public boolean onLoadUri(final GeckoView view, final String uri,
public boolean onLoadUri(final GeckoView view, final String urlStr,
final TargetWindow where) {
if (isInScope(uri)) {
view.loadUri(uri);
} else {
final Intent intent = new Intent(getIntent());
intent.setClassName(getApplicationContext(),
CustomTabsActivity.class.getName());
intent.setData(Uri.parse(uri));
startActivity(intent);
final Uri url = Uri.parse(urlStr);
if (url == null) {
// We can't really handle this, so deny it?
Log.w(LOGTAG, "Failed to parse URL for navigation: " + urlStr);
return true;
}
if (mManifest.isInScope(url) && where != TargetWindow.NEW) {
// This is in scope and wants to load in the same frame, so
// let Gecko handle it.
return false;
}
CustomTabsIntent tab = new CustomTabsIntent.Builder()
.addDefaultShareMenuItem()
.setToolbarColor(mManifest.getThemeColor())
.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left)
.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right)
.build();
tab.intent.setClass(this, CustomTabsActivity.class);
tab.launchUrl(this, url);
return true;
}

View File

@ -0,0 +1,226 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* 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/. */
package org.mozilla.gecko.webapps;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONObject;
import org.json.JSONException;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.icons.decoders.FaviconDecoder;
import org.mozilla.gecko.icons.decoders.LoadFaviconResult;
import org.mozilla.gecko.util.ColorUtil;
import org.mozilla.gecko.util.FileUtils;
public class WebAppManifest {
private static final String LOGTAG = "WebAppManifest";
private Uri mManifestUri;
private Integer mThemeColor;
private String mName;
private Bitmap mIcon;
private Uri mScope;
private Uri mStartUri;
private String mDisplayMode;
private String mOrientation;
public static WebAppManifest fromFile(final String url, final String path) {
if (url == null || TextUtils.isEmpty(url)) {
throw new IllegalArgumentException("Must pass a non-empty manifest URL");
}
if (path == null || TextUtils.isEmpty(path)) {
throw new IllegalArgumentException("Must pass a non-empty manifest path");
}
final Uri manifestUri = Uri.parse(url);
if (manifestUri == null) {
throw new IllegalArgumentException("Must pass a valid manifest URL");
}
try {
final File manifestFile = new File(path);
// Gecko adds some add some additional data, such as cached_icon, in
// the toplevel object. The actual webapp manifest is in the "manifest" field.
final JSONObject manifest = FileUtils.readJSONObjectFromFile(manifestFile);
final JSONObject manifestField = manifest.getJSONObject("manifest");
return new WebAppManifest(manifestUri, manifest, manifestField);
} catch (Exception e) {
Log.e(LOGTAG, "Failed to read webapp manifest", e);
return null;
}
}
private WebAppManifest(final Uri uri, final JSONObject manifest, final JSONObject manifestField) {
mManifestUri = uri;
readManifest(manifest, manifestField);
}
public Integer getThemeColor() {
return mThemeColor;
}
public String getName() {
return mName;
}
public Bitmap getIcon() {
return mIcon;
}
public Uri getScope() {
return mScope;
}
public Uri getStartUri() {
return mStartUri;
}
public String getDisplayMode() {
return mDisplayMode;
}
public String getOrientation() {
return mOrientation;
}
private void readManifest(final JSONObject manifest, final JSONObject manifestField) {
mThemeColor = readThemeColor(manifestField);
mName = readName(manifestField);
mIcon = readIcon(manifest);
mScope = readScope(manifestField);
mStartUri = readStartUrl(manifestField);
mDisplayMode = manifestField.optString("display", null);
mOrientation = manifestField.optString("orientation", null);
}
private Integer readThemeColor(final JSONObject manifest) {
final String colorStr = manifest.optString("theme_color", null);
if (colorStr != null) {
return ColorUtil.parseStringColor(colorStr);
}
return null;
}
private Uri buildRelativeUrl(final Uri base, final Uri relative) {
Uri.Builder builder = new Uri.Builder()
.scheme(base.getScheme())
.authority(base.getAuthority());
try {
// This is pretty gross, but seems to be the easiest way to get
// a normalized path without java.nio.Path[s], which is
// only in SDK 26.
File file = new File(base.getPath() + "/" + relative.getPath());
builder.path(file.getCanonicalPath());
} catch (java.io.IOException e) {
return null;
}
return builder.query(relative.getQuery())
.fragment(relative.getFragment())
.build();
}
private Uri readStartUrl(final JSONObject manifest) {
Uri startUrl = Uri.parse(manifest.optString("start_url", "/"));
if (startUrl.isRelative()) {
startUrl = buildRelativeUrl(stripLastPathSegment(mManifestUri), startUrl);
}
return startUrl;
}
private String readName(final JSONObject manifest) {
String name = manifest.optString("name", null);
if (name == null) {
name = manifest.optString("short_name", null);
}
if (name == null) {
name = manifest.optString("start_url", null);
}
return name;
}
private Bitmap readIcon(final JSONObject manifest) {
final String iconStr = manifest.optString("cached_icon", null);
if (iconStr == null) {
return null;
}
final LoadFaviconResult loadIconResult = FaviconDecoder
.decodeDataURI(GeckoAppShell.getApplicationContext(), iconStr);
if (loadIconResult == null) {
return null;
}
return loadIconResult.getBestBitmap(GeckoAppShell.getPreferredIconSize());
}
private static Uri stripLastPathSegment(final Uri uri) {
final Uri.Builder builder = new Uri.Builder()
.scheme(uri.getScheme())
.authority(uri.getAuthority());
final List<String> segments = uri.getPathSegments();
for (int i = 0; i < (segments.size() - 1); i++) {
builder.appendPath(segments.get(i));
}
return builder.build();
}
private Uri readScope(final JSONObject manifest) {
final String scopeStr = manifest.optString("scope", null);
if (scopeStr == null) {
return null;
}
Uri scope = Uri.parse(scopeStr);
if (scope == null) {
return null;
}
if (scope.isRelative()) {
scope = buildRelativeUrl(stripLastPathSegment(mManifestUri), scope);
}
return scope;
}
public boolean isInScope(final Uri uri) {
if (mScope == null) {
return true;
}
if (!uri.getScheme().equals(mScope.getScheme())) {
return false;
}
if (!uri.getHost().equals(mScope.getHost())) {
return false;
}
if (!uri.getPath().startsWith(mScope.getPath())) {
return false;
}
return true;
}
}

View File

@ -962,6 +962,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'util/WindowUtil.java',
'webapps/WebAppActivity.java',
'webapps/WebAppIndexer.java',
'webapps/WebAppManifest.java',
'webapps/WebApps.java',
'widget/ActionModePresenter.java',
'widget/ActivityChooserModel.java',

View File

@ -2240,7 +2240,8 @@ async function installManifest(browser, data) {
icon,
name: manifest.name,
start_url: manifest.start_url,
manifest_path: manifest.path
manifest_path: manifest.path,
manifest_url: manifest.url
});
} catch (err) {
Cu.reportError("Failed to install: " + err.message);

View File

@ -131,7 +131,9 @@ class NativePanZoomController extends JNIObject implements PanZoomController {
final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
event.getPointerCoords(0, coords);
final float x = coords.x;
final float y = coords.y;
// Mouse events are not adjusted by the AndroidDyanmicToolbarAnimator so adjust the offset
// here.
final float y = coords.y - mView.getCurrentToolbarHeight();
return handleMouseEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y, event.getButtonState());
}

File diff suppressed because it is too large Load Diff

View File

@ -94,6 +94,9 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags,
mSentClientCert(false),
mNotedTimeUntilReady(false),
mFailedVerification(false),
mIsShortWritePending(false),
mShortWritePendingByte(0),
mShortWriteOriginalAmount(-1),
mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
mKEAKeyBits(0),
mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
@ -1479,9 +1482,69 @@ PSMSend(PRFileDesc* fd, const void* buf, int32_t amount, int flags,
DEBUG_DUMP_BUFFER((unsigned char*) buf, amount);
#endif
if (socketInfo->IsShortWritePending() && amount > 0) {
// We got "SSL short write" last time, try to flush the pending byte.
#ifdef DEBUG
socketInfo->CheckShortWrittenBuffer(static_cast<const unsigned char*>(buf), amount);
#endif
buf = socketInfo->GetShortWritePendingByteRef();
amount = 1;
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
("[%p] pushing 1 byte after SSL short write", fd));
}
int32_t bytesWritten = fd->lower->methods->send(fd->lower, buf, amount,
flags, timeout);
// NSS indicates that it can't write all requested data (due to network
// congestion, for example) by returning either one less than the amount
// of data requested or 16383, if the requested amount is greater than
// 16384. We refer to this as a "short write". If we simply returned
// the amount that NSS did write, the layer above us would then call
// PSMSend with a very small amount of data (often 1). This is inefficient
// and can lead to alternating between sending large packets and very small
// packets. To prevent this, we alert the layer calling us that the operation
// would block and that it should be retried later, with the same data.
// When it does, we tell NSS to write the remaining byte it didn't write
// in the previous call. We then return the total number of bytes written,
// which is the number that caused the short write plus the additional byte
// we just wrote out.
// The 16384 value is based on libssl's maximum buffer size:
// MAX_FRAGMENT_LENGTH - 1
//
// It's in a private header, though, filed bug 1394822 to expose it.
static const int32_t kShortWrite16k = 16383;
if ((amount > 1 && bytesWritten == (amount - 1)) ||
(amount > kShortWrite16k && bytesWritten == kShortWrite16k)) {
// This is indication of an "SSL short write", block to force retry.
socketInfo->SetShortWritePending(
bytesWritten + 1, // The amount to return after the flush
*(static_cast<const unsigned char*>(buf) + bytesWritten));
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
("[%p] indicated SSL short write for %d bytes (written just %d bytes)",
fd, amount, bytesWritten));
bytesWritten = -1;
PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
#ifdef DEBUG
socketInfo->RememberShortWrittenBuffer(static_cast<const unsigned char*>(buf));
#endif
} else if (socketInfo->IsShortWritePending() && bytesWritten == 1) {
// We have now flushed all pending data in the SSL socket
// after the indicated short write. Tell the upper layer
// it has sent all its data now.
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose, ("[%p] finished SSL short write", fd));
bytesWritten = socketInfo->ResetShortWritePending();
}
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
("[%p] wrote %d bytes\n", fd, bytesWritten));

View File

@ -10,6 +10,7 @@
#include "TransportSecurityInfo.h"
#include "mozilla/Assertions.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
#include "nsDataHashtable.h"
#include "nsIClientAuthDialogs.h"
@ -120,6 +121,49 @@ public:
void SetMACAlgorithmUsed(int16_t mac) { mMACAlgorithmUsed = mac; }
void SetShortWritePending(int32_t amount, unsigned char data)
{
mIsShortWritePending = true;
mShortWriteOriginalAmount = amount;
mShortWritePendingByte = data;
}
bool IsShortWritePending()
{
return mIsShortWritePending;
}
unsigned char const* GetShortWritePendingByteRef()
{
return &mShortWritePendingByte;
}
int32_t ResetShortWritePending()
{
mIsShortWritePending = false;
return mShortWriteOriginalAmount;
}
#ifdef DEBUG
// These helpers assert that the caller does try to send the same data
// as it was previously when we hit the short-write. This is a measure
// to make sure we communicate correctly to the consumer.
void RememberShortWrittenBuffer(const unsigned char *data)
{
mShortWriteBufferCheck = mozilla::MakeUnique<char[]>(mShortWriteOriginalAmount);
memcpy(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount);
}
void CheckShortWrittenBuffer(const unsigned char *data, int32_t amount)
{
if (!mShortWriteBufferCheck) return;
MOZ_ASSERT(amount >= mShortWriteOriginalAmount,
"unexpected amount length after short write");
MOZ_ASSERT(!memcmp(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount),
"unexpected buffer content after short write");
mShortWriteBufferCheck = nullptr;
}
#endif
protected:
virtual ~nsNSSSocketInfo();
@ -149,6 +193,24 @@ private:
bool mNotedTimeUntilReady;
bool mFailedVerification;
// True when SSL layer has indicated an "SSL short write", i.e. need
// to call on send one or more times to push all pending data to write.
bool mIsShortWritePending;
// These are only valid if mIsShortWritePending is true.
//
// Value of the last byte pending from the SSL short write that needs
// to be passed to subsequent calls to send to perform the flush.
unsigned char mShortWritePendingByte;
// Original amount of data the upper layer has requested to write to
// return after the successful flush.
int32_t mShortWriteOriginalAmount;
#ifdef DEBUG
mozilla::UniquePtr<char[]> mShortWriteBufferCheck;
#endif
// mKEA* are used in false start and http/2 detetermination
// Values are from nsISSLSocketControl
int16_t mKEAUsed;

View File

@ -316269,6 +316269,11 @@
{}
]
],
"webstorage/resources/storage_session_window_noopener_second.html": [
[
{}
]
],
"webstorage/resources/storage_session_window_open_second.html": [
[
{}
@ -383962,6 +383967,12 @@
}
]
],
"webstorage/storage_session_window_noopener.html": [
[
"/webstorage/storage_session_window_noopener.html",
{}
]
],
"webstorage/storage_session_window_open.html": [
[
"/webstorage/storage_session_window_open.html",
@ -637661,6 +637672,10 @@
"56b3c331a13ef832f9aa1721839516bf96e0eda8",
"support"
],
"webstorage/resources/storage_session_window_noopener_second.html": [
"56743e7c12f266463dface58439ab5f2a644a802",
"support"
],
"webstorage/resources/storage_session_window_open_second.html": [
"828dee748950bef2094afc43b0f9db8c3a655acb",
"support"
@ -637729,6 +637744,10 @@
"16420690823c36d08a83656746a0f05324602036",
"testharness"
],
"webstorage/storage_session_window_noopener.html": [
"386af726f84fac19f96d64420c53927cd548f88b",
"testharness"
],
"webstorage/storage_session_window_open.html": [
"86f5002f1c0e22234928673465cc4317b6d599f5",
"testharness"

View File

@ -0,0 +1,34 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebStorage Test: sessionStorage - second page</title>
</head>
<body>
<script>
var storage = window.sessionStorage;
var assertions = [];
assertions.push({
actual: storage.getItem("FOO"),
expected: null,
message: "storage.getItem('FOO')"
});
storage.setItem("FOO", "BAR-NEWWINDOW");
assertions.push({
actual: storage.getItem("FOO"),
expected: "BAR-NEWWINDOW",
message: "value for FOO after changing"
});
let channel = new BroadcastChannel('storage_session_window_noopener');
channel.postMessage(assertions, '*');
window.close();
</script>
</body>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebStorage Test: sessionStorage - open a new window with noopener</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
async_test(function(t) {
var storage = window.sessionStorage;
storage.clear();
storage.setItem("FOO", "BAR");
let channel = new BroadcastChannel("storage_session_window_noopener");
channel.addEventListener("message", t.step_func(function(e) {
e.data.forEach(t.step_func(function(assertion) {
assert_equals(assertion.actual, assertion.expected, assertion.message);
}));
assert_equals(storage.getItem("FOO"), "BAR", "value for FOO in original window");
t.done();
}));
var win = window.open("resources/storage_session_window_noopener_second.html",
"_blank",
"noopener");
}, "A new noopener window to make sure there is a not copy of the previous window's sessionStorage");
</script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More