mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 20:05:49 +00:00
Merge m-c to autoland, a=merge
MozReview-Commit-ID: 5c1Pgm4aVKe
This commit is contained in:
commit
de530c1ea6
@ -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] }',
|
||||
|
@ -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
|
||||
|
@ -35,6 +35,7 @@ EXPORTS.mozilla.dom += [
|
||||
'MultipartBlobImpl.h',
|
||||
'MutableBlobStorage.h',
|
||||
'MutableBlobStreamListener.h',
|
||||
'StreamBlobImpl.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -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(¤t)) && 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)) {
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
20
dom/indexedDB/test/test_wasm_serialize_tiering.html
Normal file
20
dom/indexedDB/test/test_wasm_serialize_tiering.html
Normal 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>
|
106
dom/indexedDB/test/unit/test_wasm_serialize_tiering.js
Normal file
106
dom/indexedDB/test/unit/test_wasm_serialize_tiering.js
Normal 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();
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -817,7 +817,7 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
||||
return rv;
|
||||
}
|
||||
|
||||
URIParams uriToLoad;
|
||||
OptionalURIParams uriToLoad;
|
||||
SerializeURI(aURI, uriToLoad);
|
||||
Unused << SendCreateWindowInDifferentProcess(aTabOpener,
|
||||
aChromeFlags,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -1031,7 +1031,7 @@ parent:
|
||||
bool aCalledFromJS,
|
||||
bool aPositionSpecified,
|
||||
bool aSizeSpecified,
|
||||
URIParams aURIToLoad,
|
||||
OptionalURIParams aURIToLoad,
|
||||
nsCString aFeatures,
|
||||
nsCString aBaseURI,
|
||||
float aFullZoom,
|
||||
|
@ -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]
|
||||
|
12
dom/tests/browser/browser_noopener_null_uri.js
Normal file
12
dom/tests/browser/browser_noopener_null_uri.js
Normal 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);
|
||||
});
|
||||
});
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
@ -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
|
@ -39,7 +39,7 @@ UNIFIED_SOURCES += [
|
||||
|
||||
if CONFIG['MOZ_CRASHREPORTER']:
|
||||
UNIFIED_SOURCES += [
|
||||
'InterfaceRegistrationAnnotator.cpp',
|
||||
'RegistrationAnnotator.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ACCESSIBILITY']:
|
||||
|
@ -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()); }
|
||||
|
@ -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({})); })();
|
||||
|
@ -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";
|
||||
|
24
js/src/jit-test/tests/wasm/utf8.js
Normal file
24
js/src/jit-test/tests/wasm/utf8.js
Normal 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);
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 };
|
||||
|
@ -396,7 +396,7 @@ CodeGeneratorMIPS::visitDivOrModI64(LDivOrModI64* lir)
|
||||
masm.bind(¬min);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
_(ProcessExecutableRegion, 500) \
|
||||
_(WasmCodeProfilingLabels, 500) \
|
||||
_(OffThreadPromiseState, 500) \
|
||||
_(WasmTier2GeneratorComplete, 500) \
|
||||
_(WasmModuleTieringLock, 500) \
|
||||
\
|
||||
_(TraceLoggerGraphState, 600) \
|
||||
_(VTuneLock, 600)
|
||||
|
@ -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]] **********************************************************************************/
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
5
mobile/android/app/src/main/res/anim/slide_in_left.xml
Normal file
5
mobile/android/app/src/main/res/anim/slide_in_left.xml
Normal 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>
|
5
mobile/android/app/src/main/res/anim/slide_in_right.xml
Normal file
5
mobile/android/app/src/main/res/anim/slide_in_right.xml
Normal 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>
|
5
mobile/android/app/src/main/res/anim/slide_out_left.xml
Normal file
5
mobile/android/app/src/main/res/anim/slide_out_left.xml
Normal 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>
|
5
mobile/android/app/src/main/res/anim/slide_out_right.xml
Normal file
5
mobile/android/app/src/main/res/anim/slide_out_right.xml
Normal 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>
|
@ -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!");
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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',
|
||||
|
@ -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);
|
||||
|
@ -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
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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>
|
@ -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
Loading…
Reference in New Issue
Block a user