mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
69d7e4d6b5
@ -2,6 +2,7 @@
|
||||
|
||||
[test_bindings.xhtml]
|
||||
[test_embeds.xul]
|
||||
skip-if = (os == "linux" && (debug || asan)) # Bug 845176
|
||||
[test_general.html]
|
||||
[test_general.xul]
|
||||
[test_tabbrowser.xul]
|
||||
|
@ -209,14 +209,18 @@ def write_cpp(eventname, iface, fd):
|
||||
for a in attributes:
|
||||
writeAttributeGetter(fd, classname, a)
|
||||
|
||||
def gen_header_file(fd, conf_file):
|
||||
def get_conf(conf_file):
|
||||
conf = Configuration(conf_file)
|
||||
inc_dir = mozpath.join(buildconfig.topobjdir, 'dist', 'idl')
|
||||
inc_dir = [
|
||||
mozpath.join(buildconfig.topsrcdir, 'accessible', 'interfaces'),
|
||||
mozpath.join(buildconfig.topsrcdir, 'xpcom', 'base'),
|
||||
]
|
||||
return conf, inc_dir
|
||||
|
||||
return print_header_file(fd, conf, [inc_dir])
|
||||
def gen_header_file(fd, conf_file):
|
||||
conf, inc_dir = get_conf(conf_file)
|
||||
return print_header_file(fd, conf, inc_dir)
|
||||
|
||||
def gen_cpp_file(fd, conf_file):
|
||||
conf = Configuration(conf_file)
|
||||
inc_dir = mozpath.join(buildconfig.topobjdir, 'dist', 'idl')
|
||||
|
||||
return print_cpp_file(fd, conf, [inc_dir])
|
||||
conf, inc_dir = get_conf(conf_file)
|
||||
return print_cpp_file(fd, conf, inc_dir)
|
||||
|
@ -10,6 +10,7 @@ const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
|
||||
Cu.importGlobalProperties(['FileReader']);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
|
||||
@ -198,9 +199,7 @@ var LogCapture = {
|
||||
try {
|
||||
this.ensureLoaded();
|
||||
|
||||
let fr = Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
|
||||
let fr = new FileReader();
|
||||
fr.onload = function(evt) {
|
||||
deferred.resolve(new Uint8Array(evt.target.result));
|
||||
};
|
||||
|
@ -1279,6 +1279,8 @@ pref("services.sync.prefs.sync.lightweightThemes.selectedThemeID", true);
|
||||
pref("services.sync.prefs.sync.lightweightThemes.usedThemes", true);
|
||||
pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true);
|
||||
pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true);
|
||||
pref("services.sync.prefs.sync.network.cookie.lifetime.days", true);
|
||||
pref("services.sync.prefs.sync.network.cookie.thirdparty.sessionOnly", true);
|
||||
pref("services.sync.prefs.sync.permissions.default.image", true);
|
||||
pref("services.sync.prefs.sync.pref.advanced.images.disable_button.view_image", true);
|
||||
pref("services.sync.prefs.sync.pref.advanced.javascript.disable_button.advanced", true);
|
||||
|
@ -16,6 +16,8 @@ Cu.import("resource:///modules/MigrationUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/LoginHelper.jsm");
|
||||
|
||||
Cu.importGlobalProperties(['FileReader']);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
|
||||
@ -545,8 +547,7 @@ Cookies.prototype = {
|
||||
},
|
||||
|
||||
_readCookieFile(aFile, aCallback) {
|
||||
let fileReader = Cc["@mozilla.org/files/filereader;1"].
|
||||
createInstance(Ci.nsIDOMFileReader);
|
||||
let fileReader = new FileReader();
|
||||
let onLoadEnd = () => {
|
||||
fileReader.removeEventListener("loadend", onLoadEnd, false);
|
||||
|
||||
|
@ -12,5 +12,6 @@ fi
|
||||
|
||||
ac_add_options --with-branding=browser/branding/nightly
|
||||
|
||||
. "$topsrcdir/build/macosx/mozconfig.rust"
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
@ -53,6 +53,7 @@ whitelist['nightly']['macosx-universal'] += [
|
||||
'ac_add_options --disable-install-strip',
|
||||
'ac_add_options --enable-instruments',
|
||||
'ac_add_options --enable-dtrace',
|
||||
'. "$topsrcdir/build/macosx/mozconfig.rust"',
|
||||
]
|
||||
|
||||
whitelist['nightly']['win32'] += [
|
||||
|
@ -10,17 +10,17 @@
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 128301120,
|
||||
"digest": "d2d71103a6cec84b150b8f08bfef2682aa713c2d6eadce584f79836ef27ba4380ffb444165d999b79605f18ad165641a7a8cc0e04a201675ad5f655a59adbda9",
|
||||
"algorithm": "sha512",
|
||||
"filename": "rustc.tar.bz2",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 167175,
|
||||
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
|
||||
"algorithm": "sha512",
|
||||
"filename": "sccache.tar.bz2",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"algorithm": "sha512",
|
||||
"filename": "rustc.tar.bz2",
|
||||
"unpack": true,
|
||||
"digest": "28e0d27846cfa6fac5be2df4debb469b6f488d45357d21f2bec14c7c2b6abe5713965497961e7ac10120d3687751ccced95b9498cc54c529cbdfcd59d51a67ec",
|
||||
"size": 84116661
|
||||
}
|
||||
]
|
||||
|
@ -2,6 +2,5 @@
|
||||
|
||||
if test `uname -s` != Linux; then
|
||||
RUSTC="$topsrcdir/rustc/bin/rustc"
|
||||
mk_add_options "export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$topsrcdir/rustc/lib"
|
||||
ac_add_options --enable-rust
|
||||
fi
|
||||
|
@ -41,8 +41,6 @@ endif
|
||||
endif
|
||||
endif # WINNT
|
||||
|
||||
include_deps = $(eval $(if $(2),,-)include $(1))
|
||||
|
||||
ifndef INCLUDED_AUTOCONF_MK
|
||||
default::
|
||||
else
|
||||
|
@ -1296,7 +1296,7 @@ ifneq (,$(filter-out all chrome default export realchrome clean clobber clobber_
|
||||
MDDEPEND_FILES := $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(addsuffix .pp,$(notdir $(sort $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS)))))))
|
||||
|
||||
ifneq (,$(MDDEPEND_FILES))
|
||||
$(call include_deps,$(MDDEPEND_FILES))
|
||||
-include $(MDDEPEND_FILES)
|
||||
endif
|
||||
|
||||
endif
|
||||
@ -1304,7 +1304,7 @@ endif
|
||||
MDDEPEND_FILES := $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(EXTRA_MDDEPEND_FILES))))
|
||||
|
||||
ifneq (,$(MDDEPEND_FILES))
|
||||
$(call include_deps,$(MDDEPEND_FILES))
|
||||
-include $(MDDEPEND_FILES)
|
||||
endif
|
||||
|
||||
#############################################################################
|
||||
@ -1483,7 +1483,7 @@ $(foreach file,$(PP_TARGETS_ALL_RESULTS), \
|
||||
MDDEPEND_FILES := $(strip $(wildcard $(addprefix $(MDDEPDIR)/,$(addsuffix .pp,$(notdir $(PP_TARGETS_ALL_RESULTS))))))
|
||||
|
||||
ifneq (,$(MDDEPEND_FILES))
|
||||
$(call include_deps,$(MDDEPEND_FILES))
|
||||
-include $(MDDEPEND_FILES)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
83
configure.in
83
configure.in
@ -446,44 +446,6 @@ esac
|
||||
|
||||
AC_SUBST(MACOSX_DEPLOYMENT_TARGET)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Special MacOS X checks
|
||||
dnl ========================================================
|
||||
|
||||
if test -n "$MACOSX_DEPLOYMENT_TARGET" -a -n "$MOZ_RUST"; then
|
||||
AC_MSG_CHECKING([rustc compatibility with MacOS X])
|
||||
# Stock rustc doesn't support MacOS X 10.6 or earlier.
|
||||
# https://github.com/rust-lang/rust/issues/25342
|
||||
_MACOSX_TARGET_MINOR=`echo "$MACOSX_DEPLOYMENT_TARGET" | cut -d. -f2`
|
||||
if test "$_MACOSX_TARGET_MINOR" -lt 7; then
|
||||
dnl Test C linkage against rust code to see if the rust
|
||||
dnl toolchain output is compatible.
|
||||
cat > conftest.rs <<EOF
|
||||
[#[no_mangle]]
|
||||
pub extern fn rusty_answer() -> u8 { 42 }
|
||||
EOF
|
||||
ac_try="$RUSTC --crate-type staticlib -o conftest.a conftest.rs >/dev/null"
|
||||
AC_TRY_EVAL(ac_try)
|
||||
save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS conftest.a -lpthread -lm"
|
||||
AC_TRY_LINK_FUNC([rusty_answer], [
|
||||
AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok with this rustc])
|
||||
], [
|
||||
AC_MSG_RESULT([cannot link on $MACOSX_DEPLOYMENT_TARGET])
|
||||
MOZ_RUST=
|
||||
])
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
rm -rf conftest*
|
||||
else
|
||||
AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok])
|
||||
fi
|
||||
if test -z "$MOZ_RUST"; then
|
||||
AC_MSG_ERROR([rustc does not support MacOS X $MACOSX_DEPLOYMENT_TARGET
|
||||
Add 'ac_add_options --enable-macos-target=10.7' (or later)
|
||||
to mozconfig, disable Rust support, or use an alternate toolchain.])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXXCPP
|
||||
|
||||
@ -1681,6 +1643,51 @@ AC_SUBST(HAVE_64BIT_BUILD)
|
||||
AC_LANG_RESTORE
|
||||
fi # COMPILE_ENVIRONMENT
|
||||
|
||||
dnl ========================================================
|
||||
dnl Special rust checks
|
||||
dnl ========================================================
|
||||
|
||||
if test -n "$MACOSX_DEPLOYMENT_TARGET" -a -n "$MOZ_RUST"; then
|
||||
AC_MSG_CHECKING([if we're targeting 32-bit])
|
||||
if test -z "$HAVE_64BIT_BUILD"; then
|
||||
RUSTC="$RUSTC --target=i686-apple-darwin"
|
||||
AC_MSG_RESULT([using $RUSTC])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
fi
|
||||
AC_MSG_CHECKING([rustc compatibility with MacOS X])
|
||||
# Stock rustc doesn't support MacOS X 10.6 or earlier.
|
||||
# https://github.com/rust-lang/rust/issues/25342
|
||||
_MACOSX_TARGET_MINOR=`echo "$MACOSX_DEPLOYMENT_TARGET" | cut -d. -f2`
|
||||
if test "$_MACOSX_TARGET_MINOR" -lt 7; then
|
||||
dnl Test C linkage against rust code to see if the rust
|
||||
dnl toolchain output is compatible.
|
||||
cat > conftest.rs <<EOF
|
||||
[#[no_mangle]]
|
||||
pub extern fn rusty_answer() -> u8 { 42 }
|
||||
EOF
|
||||
ac_try="$RUSTC --crate-type staticlib -o conftest.a conftest.rs >/dev/null"
|
||||
AC_TRY_EVAL(ac_try)
|
||||
save_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS conftest.a -lpthread -lm"
|
||||
AC_TRY_LINK_FUNC([rusty_answer], [
|
||||
AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok with this rustc])
|
||||
], [
|
||||
AC_MSG_RESULT([cannot link for $MACOSX_DEPLOYMENT_TARGET])
|
||||
MOZ_RUST=
|
||||
])
|
||||
LDFLAGS=$save_LDFLAGS
|
||||
rm -rf conftest*
|
||||
else
|
||||
AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok])
|
||||
fi
|
||||
if test -z "$MOZ_RUST"; then
|
||||
AC_MSG_ERROR([rustc does not support MacOS X $MACOSX_DEPLOYMENT_TARGET
|
||||
Add 'ac_add_options --enable-macos-target=10.7' (or later)
|
||||
to mozconfig, disable Rust support, or use an alternate toolchain.])
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Use profiling compile flags
|
||||
dnl ========================================================
|
||||
|
@ -11,6 +11,7 @@ const {LongStringActor} = require("devtools/server/actors/string");
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
const {getSystemInfo, getSetting} = require("devtools/shared/system");
|
||||
|
||||
Cu.importGlobalProperties(["FileReader"]);
|
||||
Cu.import("resource://gre/modules/PermissionsTable.jsm")
|
||||
|
||||
var DeviceActor = exports.DeviceActor = protocol.ActorClass({
|
||||
@ -25,7 +26,6 @@ var DeviceActor = exports.DeviceActor = protocol.ActorClass({
|
||||
getWallpaper: method(function() {
|
||||
let deferred = promise.defer();
|
||||
getSetting("wallpaper.image").then((blob) => {
|
||||
let FileReader = CC("@mozilla.org/files/filereader;1");
|
||||
let reader = new FileReader();
|
||||
let conn = this.conn;
|
||||
reader.addEventListener("load", function() {
|
||||
|
@ -11,6 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/UserCustomizations.jsm");
|
||||
Cu.importGlobalProperties(["FileReader"]);
|
||||
|
||||
var promise = require("promise");
|
||||
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
@ -851,8 +852,7 @@ WebappsActor.prototype = {
|
||||
}
|
||||
|
||||
// Convert the blog to a base64 encoded data URI
|
||||
let reader = Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
let reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
deferred.resolve({
|
||||
url: reader.result
|
||||
|
@ -17,11 +17,28 @@ using mozilla::dom::KeyframeEffectReadOnly;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* static */ nsTArray<RefPtr<dom::Animation>>
|
||||
EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty)
|
||||
// Helper function to factor out the common logic from
|
||||
// GetAnimationsForCompositor and HasAnimationsForCompositor.
|
||||
//
|
||||
// Takes an optional array to fill with eligible animations.
|
||||
//
|
||||
// Returns true if there are eligible animations, false otherwise.
|
||||
bool
|
||||
FindAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty,
|
||||
nsTArray<RefPtr<dom::Animation>>* aMatches /*out*/)
|
||||
{
|
||||
nsTArray<RefPtr<dom::Animation>> result;
|
||||
MOZ_ASSERT(!aMatches || aMatches->IsEmpty(),
|
||||
"Matches array, if provided, should be empty");
|
||||
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
|
||||
if (!effects || effects->IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aFrame->RefusedAsyncAnimation()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
|
||||
if (nsLayoutUtils::IsAnimationLoggingEnabled()) {
|
||||
@ -30,18 +47,10 @@ EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
"disabled");
|
||||
AnimationUtils::LogAsyncAnimationFailure(message);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (aFrame->RefusedAsyncAnimation()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
EffectSet* effects = EffectSet::GetEffectSet(aFrame);
|
||||
if (!effects) {
|
||||
return result;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundSome = false;
|
||||
for (KeyframeEffectReadOnly* effect : *effects) {
|
||||
MOZ_ASSERT(effect && effect->GetAnimation());
|
||||
Animation* animation = effect->GetAnimation();
|
||||
@ -51,16 +60,46 @@ EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
}
|
||||
|
||||
if (effect->ShouldBlockCompositorAnimations(aFrame)) {
|
||||
result.Clear();
|
||||
return result;
|
||||
if (aMatches) {
|
||||
aMatches->Clear();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!effect->HasAnimationOfProperty(aProperty)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.AppendElement(animation);
|
||||
if (aMatches) {
|
||||
aMatches->AppendElement(animation);
|
||||
}
|
||||
foundSome = true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!foundSome || !aMatches || !aMatches->IsEmpty(),
|
||||
"If return value is true, matches array should be non-empty");
|
||||
return foundSome;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
EffectCompositor::HasAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty)
|
||||
{
|
||||
return FindAnimationsForCompositor(aFrame, aProperty, nullptr);
|
||||
}
|
||||
|
||||
/* static */ nsTArray<RefPtr<dom::Animation>>
|
||||
EffectCompositor::GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty)
|
||||
{
|
||||
nsTArray<RefPtr<dom::Animation>> result;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool foundSome =
|
||||
#endif
|
||||
FindAnimationsForCompositor(aFrame, aProperty, &result);
|
||||
MOZ_ASSERT(!foundSome || !result.IsEmpty(),
|
||||
"If return value is true, matches array should be non-empty");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -19,6 +19,9 @@ class Animation;
|
||||
class EffectCompositor
|
||||
{
|
||||
public:
|
||||
static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty);
|
||||
|
||||
static nsTArray<RefPtr<dom::Animation>>
|
||||
GetAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
nsCSSProperty aProperty);
|
||||
|
@ -118,6 +118,7 @@ public:
|
||||
{
|
||||
return Iterator::EndIterator(mEffects.Iter());
|
||||
}
|
||||
bool IsEmpty() const { return mEffects.IsEmpty(); }
|
||||
|
||||
static nsIAtom** GetEffectSetPropertyAtoms();
|
||||
|
||||
|
@ -1353,6 +1353,8 @@ BuildSegmentsFromValueEntries(nsTArray<KeyframeValueEntry>& aEntries,
|
||||
lastProperty = aEntries[i].mProperty;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(animationProperty, "animationProperty should be valid pointer.");
|
||||
|
||||
// Now generate the segment.
|
||||
AnimationPropertySegment* segment =
|
||||
animationProperty->mSegments.AppendElement();
|
||||
|
@ -15,7 +15,7 @@ Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Webapps.jsm");
|
||||
Cu.import("resource://gre/modules/MessageBroadcaster.jsm");
|
||||
|
||||
Cu.importGlobalProperties(['File']);
|
||||
Cu.importGlobalProperties(['File', 'FileReader']);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
@ -262,8 +262,7 @@ this.ImportExport = {
|
||||
debug("_writeBlobToTempFile");
|
||||
let path;
|
||||
return new Promise((aResolve, aReject) => {
|
||||
let reader = Cc['@mozilla.org/files/filereader;1']
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
let reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
path = OS.Path.join(OS.Constants.Path.tmpDir, "app-blob.zip");
|
||||
debug("onloadend path=" + path);
|
||||
|
@ -1,246 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FileIOObject.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/ProgressEvent.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
|
||||
#define ERROR_STR "error"
|
||||
#define ABORT_STR "abort"
|
||||
#define PROGRESS_STR "progress"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
const uint64_t kUnknownSize = uint64_t(-1);
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(FileIOObject, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(FileIOObject, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileIOObject)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_EVENT_HANDLER(FileIOObject, abort)
|
||||
NS_IMPL_EVENT_HANDLER(FileIOObject, error)
|
||||
NS_IMPL_EVENT_HANDLER(FileIOObject, progress)
|
||||
|
||||
FileIOObject::FileIOObject()
|
||||
: mProgressEventWasDelayed(false),
|
||||
mTimerIsActive(false),
|
||||
mReadyState(0),
|
||||
mTotal(0), mTransferred(0)
|
||||
{}
|
||||
|
||||
FileIOObject::~FileIOObject()
|
||||
{}
|
||||
|
||||
void
|
||||
FileIOObject::StartProgressEventTimer()
|
||||
{
|
||||
if (!mProgressNotifier) {
|
||||
mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
}
|
||||
if (mProgressNotifier) {
|
||||
mProgressEventWasDelayed = false;
|
||||
mTimerIsActive = true;
|
||||
mProgressNotifier->Cancel();
|
||||
mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileIOObject::ClearProgressEventTimer()
|
||||
{
|
||||
mProgressEventWasDelayed = false;
|
||||
mTimerIsActive = false;
|
||||
if (mProgressNotifier) {
|
||||
mProgressNotifier->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileIOObject::DispatchError(nsresult rv, nsAString& finalEvent)
|
||||
{
|
||||
// Set the status attribute, and dispatch the error event
|
||||
switch (rv) {
|
||||
case NS_ERROR_FILE_NOT_FOUND:
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError"));
|
||||
break;
|
||||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError"));
|
||||
break;
|
||||
default:
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Dispatch error event to signify load failure
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR));
|
||||
DispatchProgressEvent(finalEvent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileIOObject::DispatchProgressEvent(const nsAString& aType)
|
||||
{
|
||||
ProgressEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
init.mLoaded = mTransferred;
|
||||
|
||||
if (mTotal != kUnknownSize) {
|
||||
init.mLengthComputable = true;
|
||||
init.mTotal = mTotal;
|
||||
} else {
|
||||
init.mLengthComputable = false;
|
||||
init.mTotal = 0;
|
||||
}
|
||||
RefPtr<ProgressEvent> event =
|
||||
ProgressEvent::Constructor(this, aType, init);
|
||||
event->SetTrusted(true);
|
||||
|
||||
return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// nsITimerCallback
|
||||
NS_IMETHODIMP
|
||||
FileIOObject::Notify(nsITimer* aTimer)
|
||||
{
|
||||
nsresult rv;
|
||||
mTimerIsActive = false;
|
||||
|
||||
if (mProgressEventWasDelayed) {
|
||||
rv = DispatchProgressEvent(NS_LITERAL_STRING("progress"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
StartProgressEventTimer();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// InputStreamCallback
|
||||
NS_IMETHODIMP
|
||||
FileIOObject::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
if (mReadyState != 1 || aStream != mAsyncStream) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t aCount;
|
||||
nsresult rv = aStream->Available(&aCount);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && aCount) {
|
||||
rv = DoReadData(aStream, aCount);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = DoAsyncWait(aStream);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || !aCount) {
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
rv = NS_OK;
|
||||
}
|
||||
return OnLoadEnd(rv);
|
||||
}
|
||||
|
||||
mTransferred += aCount;
|
||||
|
||||
//Notify the timer is the appropriate timeframe has passed
|
||||
if (mTimerIsActive) {
|
||||
mProgressEventWasDelayed = true;
|
||||
} else {
|
||||
rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
StartProgressEventTimer();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileIOObject::OnLoadEnd(nsresult aStatus)
|
||||
{
|
||||
// Cancel the progress event timer
|
||||
ClearProgressEventTimer();
|
||||
|
||||
// FileIOObject must be in DONE stage after an operation
|
||||
mReadyState = 2;
|
||||
|
||||
nsString successEvent, termEvent;
|
||||
nsresult rv = DoOnLoadEnd(aStatus, successEvent, termEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the status field as appropriate
|
||||
if (NS_FAILED(aStatus)) {
|
||||
DispatchError(aStatus, termEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Dispatch event to signify end of a successful operation
|
||||
DispatchProgressEvent(successEvent);
|
||||
DispatchProgressEvent(termEvent);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileIOObject::DoAsyncWait(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
return aStream->AsyncWait(this,
|
||||
/* aFlags*/ 0,
|
||||
/* aRequestedCount */ 0,
|
||||
NS_GetCurrentThread());
|
||||
}
|
||||
|
||||
void
|
||||
FileIOObject::Abort(ErrorResult& aRv)
|
||||
{
|
||||
if (mReadyState != 1) {
|
||||
// XXX The spec doesn't say this
|
||||
aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearProgressEventTimer();
|
||||
|
||||
mReadyState = 2; // There are DONE constants on multiple interfaces,
|
||||
// but they all have value 2.
|
||||
// XXX The spec doesn't say this
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError"));
|
||||
|
||||
nsString finalEvent;
|
||||
DoAbort(finalEvent);
|
||||
|
||||
// Dispatch the events
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
|
||||
DispatchProgressEvent(finalEvent);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
@ -1,103 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef FileIOObject_h__
|
||||
#define FileIOObject_h__
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
|
||||
#define NS_PROGRESS_EVENT_INTERVAL 50
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
extern const uint64_t kUnknownSize;
|
||||
|
||||
// A common base class for FileReader and FileSaver
|
||||
|
||||
class FileIOObject : public DOMEventTargetHelper,
|
||||
public nsIInputStreamCallback,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
FileIOObject();
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// Common methods
|
||||
void Abort(ErrorResult& aRv);
|
||||
uint16_t ReadyState() const
|
||||
{
|
||||
return mReadyState;
|
||||
}
|
||||
DOMError* GetError() const
|
||||
{
|
||||
return mError;
|
||||
}
|
||||
|
||||
NS_METHOD GetOnabort(JSContext* aCx, JS::MutableHandle<JS::Value> aValue);
|
||||
NS_METHOD SetOnabort(JSContext* aCx, JS::Handle<JS::Value> aValue);
|
||||
NS_METHOD GetOnerror(JSContext* aCx, JS::MutableHandle<JS::Value> aValue);
|
||||
NS_METHOD SetOnerror(JSContext* aCx, JS::Handle<JS::Value> aValue);
|
||||
NS_METHOD GetOnprogress(JSContext* aCx, JS::MutableHandle<JS::Value> aValue);
|
||||
NS_METHOD SetOnprogress(JSContext* aCx, JS::Handle<JS::Value> aValue);
|
||||
|
||||
IMPL_EVENT_HANDLER(abort)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
IMPL_EVENT_HANDLER(progress)
|
||||
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileIOObject, DOMEventTargetHelper)
|
||||
|
||||
protected:
|
||||
virtual ~FileIOObject();
|
||||
|
||||
// Implemented by the derived class to do whatever it needs to do for abort
|
||||
virtual void DoAbort(nsAString& aEvent) = 0;
|
||||
|
||||
virtual nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) = 0;
|
||||
|
||||
virtual nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
|
||||
nsAString& aTerminationEvent) = 0;
|
||||
|
||||
nsresult OnLoadEnd(nsresult aStatus);
|
||||
nsresult DoAsyncWait(nsIAsyncInputStream* aStream);
|
||||
|
||||
void StartProgressEventTimer();
|
||||
void ClearProgressEventTimer();
|
||||
void DispatchError(nsresult rv, nsAString& finalEvent);
|
||||
nsresult DispatchProgressEvent(const nsAString& aType);
|
||||
|
||||
nsCOMPtr<nsITimer> mProgressNotifier;
|
||||
bool mProgressEventWasDelayed;
|
||||
bool mTimerIsActive;
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
|
||||
|
||||
RefPtr<DOMError> mError;
|
||||
|
||||
uint16_t mReadyState;
|
||||
|
||||
uint64_t mTotal;
|
||||
uint64_t mTransferred;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -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 "nsDOMFileReader.h"
|
||||
#include "FileReader.h"
|
||||
|
||||
#include "nsContentCID.h"
|
||||
#include "nsContentUtils.h"
|
||||
@ -22,6 +22,7 @@
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileReaderBinding.h"
|
||||
#include "mozilla/dom/ProgressEvent.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
|
||||
@ -30,226 +31,137 @@
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#define ABORT_STR "abort"
|
||||
#define LOAD_STR "load"
|
||||
#define LOADSTART_STR "loadstart"
|
||||
#define LOADEND_STR "loadend"
|
||||
#define ERROR_STR "error"
|
||||
#define PROGRESS_STR "progress"
|
||||
|
||||
const uint64_t kUnknownSize = uint64_t(-1);
|
||||
|
||||
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileReader)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(FileReader)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader,
|
||||
FileIOObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileReader,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBlob)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader,
|
||||
FileIOObject)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mResultArrayBuffer = nullptr;
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader,
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(FileReader,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileReader)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject)
|
||||
NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject)
|
||||
|
||||
NS_IMPL_EVENT_HANDLER(nsDOMFileReader, load)
|
||||
NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadend)
|
||||
NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadstart)
|
||||
NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, abort, FileIOObject)
|
||||
NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, progress, FileIOObject)
|
||||
NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, error, FileIOObject)
|
||||
NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper)
|
||||
|
||||
void
|
||||
nsDOMFileReader::RootResultArrayBuffer()
|
||||
FileReader::RootResultArrayBuffer()
|
||||
{
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
//nsDOMFileReader constructors/initializers
|
||||
//FileReader constructors/initializers
|
||||
|
||||
nsDOMFileReader::nsDOMFileReader()
|
||||
: mFileData(nullptr),
|
||||
mDataLen(0), mDataFormat(FILE_AS_BINARY),
|
||||
mResultArrayBuffer(nullptr)
|
||||
FileReader::FileReader(nsPIDOMWindow* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mFileData(nullptr)
|
||||
, mDataLen(0)
|
||||
, mDataFormat(FILE_AS_BINARY)
|
||||
, mResultArrayBuffer(nullptr)
|
||||
, mProgressEventWasDelayed(false)
|
||||
, mTimerIsActive(false)
|
||||
, mReadyState(EMPTY)
|
||||
, mTotal(0)
|
||||
, mTransferred(0)
|
||||
{
|
||||
SetDOMStringToNull(mResult);
|
||||
}
|
||||
|
||||
nsDOMFileReader::~nsDOMFileReader()
|
||||
FileReader::~FileReader()
|
||||
{
|
||||
FreeFileData();
|
||||
mResultArrayBuffer = nullptr;
|
||||
mozilla::DropJSObjects(this);
|
||||
DropJSObjects(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This Init method is called from the factory constructor.
|
||||
*/
|
||||
nsresult
|
||||
nsDOMFileReader::Init()
|
||||
/* static */ already_AddRefed<FileReader>
|
||||
FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
// The owner can be null when this object is used by chrome code.
|
||||
nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<FileReader> fileReader = new FileReader(owner);
|
||||
|
||||
if (!owner && nsContentUtils::IsCallerChrome()) {
|
||||
// Instead of grabbing some random global from the context stack,
|
||||
// let's use the default one (junk scope) for now.
|
||||
// We should move away from this Init...
|
||||
BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsDOMFileReader>
|
||||
nsDOMFileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<nsDOMFileReader> fileReader = new nsDOMFileReader();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!owner) {
|
||||
NS_WARNING("Unexpected owner");
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
fileReader->BindToOwner(xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
|
||||
}
|
||||
|
||||
fileReader->BindToOwner(owner);
|
||||
return fileReader.forget();
|
||||
}
|
||||
|
||||
// nsIInterfaceRequestor
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::GetInterface(const nsIID & aIID, void **aResult)
|
||||
FileReader::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
// nsIDOMFileReader
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::GetReadyState(uint16_t *aReadyState)
|
||||
{
|
||||
*aReadyState = ReadyState();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
||||
FileReader::GetResult(JSContext* aCx,
|
||||
JS::MutableHandle<JS::Value> aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
aRv = GetResult(aCx, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
|
||||
{
|
||||
JS::Rooted<JS::Value> result(aCx);
|
||||
|
||||
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
|
||||
if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
|
||||
if (mReadyState == DONE && mResultArrayBuffer) {
|
||||
result.setObject(*mResultArrayBuffer);
|
||||
} else {
|
||||
result.setNull();
|
||||
}
|
||||
|
||||
if (!JS_WrapValue(aCx, &result)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
aResult.set(result);
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsString tmpResult = mResult;
|
||||
if (!xpc::StringToJsval(aCx, tmpResult, aResult)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::GetError(nsISupports** aError)
|
||||
{
|
||||
NS_IF_ADDREF(*aError = GetError());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::ReadAsArrayBuffer(nsIDOMBlob* aBlob, JSContext* aCx)
|
||||
{
|
||||
NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER);
|
||||
ErrorResult rv;
|
||||
RefPtr<Blob> blob = static_cast<Blob*>(aBlob);
|
||||
ReadAsArrayBuffer(aCx, *blob, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::ReadAsBinaryString(nsIDOMBlob* aBlob)
|
||||
{
|
||||
NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER);
|
||||
ErrorResult rv;
|
||||
RefPtr<Blob> blob = static_cast<Blob*>(aBlob);
|
||||
ReadAsBinaryString(*blob, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::ReadAsText(nsIDOMBlob* aBlob,
|
||||
const nsAString &aCharset)
|
||||
{
|
||||
NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER);
|
||||
ErrorResult rv;
|
||||
RefPtr<Blob> blob = static_cast<Blob*>(aBlob);
|
||||
ReadAsText(*blob, aCharset, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::ReadAsDataURL(nsIDOMBlob* aBlob)
|
||||
{
|
||||
NS_ENSURE_TRUE(aBlob, NS_ERROR_NULL_POINTER);
|
||||
ErrorResult rv;
|
||||
RefPtr<Blob> blob = static_cast<Blob*>(aBlob);
|
||||
ReadAsDataURL(*blob, rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFileReader::Abort()
|
||||
{
|
||||
ErrorResult rv;
|
||||
FileIOObject::Abort(rv);
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
/* virtual */ void
|
||||
nsDOMFileReader::DoAbort(nsAString& aEvent)
|
||||
{
|
||||
// Revert status and result attributes
|
||||
SetDOMStringToNull(mResult);
|
||||
mResultArrayBuffer = nullptr;
|
||||
|
||||
mAsyncStream = nullptr;
|
||||
mBlob = nullptr;
|
||||
|
||||
//Clean up memory buffer
|
||||
FreeFileData();
|
||||
|
||||
// Tell the base class which event to dispatch
|
||||
aEvent = NS_LITERAL_STRING(LOADEND_STR);
|
||||
}
|
||||
|
||||
static
|
||||
NS_METHOD
|
||||
static NS_IMETHODIMP
|
||||
ReadFuncBinaryString(nsIInputStream* in,
|
||||
void* closure,
|
||||
const char* fromRawSegment,
|
||||
@ -271,7 +183,7 @@ ReadFuncBinaryString(nsIInputStream* in,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
|
||||
FileReader::DoOnLoadEnd(nsresult aStatus,
|
||||
nsAString& aSuccessEvent,
|
||||
nsAString& aTerminationEvent)
|
||||
{
|
||||
@ -303,7 +215,7 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
|
||||
switch (mDataFormat) {
|
||||
case FILE_AS_ARRAYBUFFER: {
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(mozilla::DOMEventTargetHelper::GetParentObject()))) {
|
||||
if (NS_WARN_IF(!jsapi.Init(DOMEventTargetHelper::GetParentObject()))) {
|
||||
FreeFileData();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -344,7 +256,7 @@ nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
|
||||
FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
|
||||
{
|
||||
MOZ_ASSERT(aStream);
|
||||
|
||||
@ -386,18 +298,21 @@ nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
|
||||
// Helper methods
|
||||
|
||||
void
|
||||
nsDOMFileReader::ReadFileContent(Blob& aBlob,
|
||||
FileReader::ReadFileContent(Blob& aBlob,
|
||||
const nsAString &aCharset,
|
||||
eDataFormat aDataFormat,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
//Implicit abort to clear any other activity going on
|
||||
Abort();
|
||||
ErrorResult error;
|
||||
Abort(error);
|
||||
error.SuppressException();
|
||||
|
||||
mError = nullptr;
|
||||
SetDOMStringToNull(mResult);
|
||||
mTransferred = 0;
|
||||
mTotal = 0;
|
||||
mReadyState = nsIDOMFileReader::EMPTY;
|
||||
mReadyState = EMPTY;
|
||||
FreeFileData();
|
||||
|
||||
mBlob = &aBlob;
|
||||
@ -405,7 +320,6 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob,
|
||||
CopyUTF16toUTF8(aCharset, mCharset);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIStreamTransportService> sts =
|
||||
do_GetService(kStreamTransportServiceCID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
@ -420,23 +334,21 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob,
|
||||
}
|
||||
|
||||
nsCOMPtr<nsITransport> transport;
|
||||
rv = sts->CreateInputTransport(stream,
|
||||
aRv = sts->CreateInputTransport(stream,
|
||||
/* aStartOffset */ 0,
|
||||
/* aReadLimit */ -1,
|
||||
/* aCloseWhenDone */ true,
|
||||
getter_AddRefs(transport));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> wrapper;
|
||||
rv = transport->OpenInputStream(/* aFlags */ 0,
|
||||
aRv = transport->OpenInputStream(/* aFlags */ 0,
|
||||
/* aSegmentSize */ 0,
|
||||
/* aSegmentCount */ 0,
|
||||
getter_AddRefs(wrapper));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -449,14 +361,16 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob,
|
||||
return;
|
||||
}
|
||||
|
||||
rv = DoAsyncWait(mAsyncStream);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
aRv = mAsyncStream->AsyncWait(this,
|
||||
/* aFlags*/ 0,
|
||||
/* aRequestedCount */ 0,
|
||||
NS_GetCurrentThread());
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
//FileReader should be in loading state here
|
||||
mReadyState = nsIDOMFileReader::LOADING;
|
||||
mReadyState = LOADING;
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
|
||||
|
||||
if (mDataFormat == FILE_AS_ARRAYBUFFER) {
|
||||
@ -469,7 +383,7 @@ nsDOMFileReader::ReadFileContent(Blob& aBlob,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMFileReader::GetAsText(Blob *aBlob,
|
||||
FileReader::GetAsText(Blob *aBlob,
|
||||
const nsACString &aCharset,
|
||||
const char *aFileData,
|
||||
uint32_t aDataLen,
|
||||
@ -509,14 +423,14 @@ nsDOMFileReader::GetAsText(Blob *aBlob,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMFileReader::GetAsDataURL(Blob *aBlob,
|
||||
FileReader::GetAsDataURL(Blob *aBlob,
|
||||
const char *aFileData,
|
||||
uint32_t aDataLen,
|
||||
nsAString& aResult)
|
||||
{
|
||||
aResult.AssignLiteral("data:");
|
||||
|
||||
nsString contentType;
|
||||
nsAutoString contentType;
|
||||
aBlob->GetType(contentType);
|
||||
if (!contentType.IsEmpty()) {
|
||||
aResult.Append(contentType);
|
||||
@ -537,7 +451,197 @@ nsDOMFileReader::GetAsDataURL(Blob *aBlob,
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
nsDOMFileReader::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
FileReader::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return FileReaderBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
FileReader::StartProgressEventTimer()
|
||||
{
|
||||
if (!mProgressNotifier) {
|
||||
mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
}
|
||||
|
||||
if (mProgressNotifier) {
|
||||
mProgressEventWasDelayed = false;
|
||||
mTimerIsActive = true;
|
||||
mProgressNotifier->Cancel();
|
||||
mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileReader::ClearProgressEventTimer()
|
||||
{
|
||||
mProgressEventWasDelayed = false;
|
||||
mTimerIsActive = false;
|
||||
if (mProgressNotifier) {
|
||||
mProgressNotifier->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileReader::DispatchError(nsresult rv, nsAString& finalEvent)
|
||||
{
|
||||
// Set the status attribute, and dispatch the error event
|
||||
switch (rv) {
|
||||
case NS_ERROR_FILE_NOT_FOUND:
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotFoundError"));
|
||||
break;
|
||||
case NS_ERROR_FILE_ACCESS_DENIED:
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("SecurityError"));
|
||||
break;
|
||||
default:
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("NotReadableError"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Dispatch error event to signify load failure
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(ERROR_STR));
|
||||
DispatchProgressEvent(finalEvent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileReader::DispatchProgressEvent(const nsAString& aType)
|
||||
{
|
||||
ProgressEventInit init;
|
||||
init.mBubbles = false;
|
||||
init.mCancelable = false;
|
||||
init.mLoaded = mTransferred;
|
||||
|
||||
if (mTotal != kUnknownSize) {
|
||||
init.mLengthComputable = true;
|
||||
init.mTotal = mTotal;
|
||||
} else {
|
||||
init.mLengthComputable = false;
|
||||
init.mTotal = 0;
|
||||
}
|
||||
RefPtr<ProgressEvent> event =
|
||||
ProgressEvent::Constructor(this, aType, init);
|
||||
event->SetTrusted(true);
|
||||
|
||||
return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// nsITimerCallback
|
||||
NS_IMETHODIMP
|
||||
FileReader::Notify(nsITimer* aTimer)
|
||||
{
|
||||
nsresult rv;
|
||||
mTimerIsActive = false;
|
||||
|
||||
if (mProgressEventWasDelayed) {
|
||||
rv = DispatchProgressEvent(NS_LITERAL_STRING("progress"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
StartProgressEventTimer();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// InputStreamCallback
|
||||
NS_IMETHODIMP
|
||||
FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
{
|
||||
if (mReadyState != LOADING || aStream != mAsyncStream) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint64_t aCount;
|
||||
nsresult rv = aStream->Available(&aCount);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && aCount) {
|
||||
rv = DoReadData(aStream, aCount);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aStream->AsyncWait(this,
|
||||
/* aFlags*/ 0,
|
||||
/* aRequestedCount */ 0,
|
||||
NS_GetCurrentThread());
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || !aCount) {
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
rv = NS_OK;
|
||||
}
|
||||
return OnLoadEnd(rv);
|
||||
}
|
||||
|
||||
mTransferred += aCount;
|
||||
|
||||
//Notify the timer is the appropriate timeframe has passed
|
||||
if (mTimerIsActive) {
|
||||
mProgressEventWasDelayed = true;
|
||||
} else {
|
||||
rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
StartProgressEventTimer();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileReader::OnLoadEnd(nsresult aStatus)
|
||||
{
|
||||
// Cancel the progress event timer
|
||||
ClearProgressEventTimer();
|
||||
|
||||
// FileReader must be in DONE stage after an operation
|
||||
mReadyState = DONE;
|
||||
|
||||
nsAutoString successEvent, termEvent;
|
||||
nsresult rv = DoOnLoadEnd(aStatus, successEvent, termEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the status field as appropriate
|
||||
if (NS_FAILED(aStatus)) {
|
||||
DispatchError(aStatus, termEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Dispatch event to signify end of a successful operation
|
||||
DispatchProgressEvent(successEvent);
|
||||
DispatchProgressEvent(termEvent);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FileReader::Abort(ErrorResult& aRv)
|
||||
{
|
||||
if (mReadyState != LOADING) {
|
||||
// XXX The spec doesn't say this
|
||||
aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
ClearProgressEventTimer();
|
||||
|
||||
mReadyState = DONE;
|
||||
|
||||
// XXX The spec doesn't say this
|
||||
mError = new DOMError(GetOwner(), NS_LITERAL_STRING("AbortError"));
|
||||
|
||||
// Revert status and result attributes
|
||||
SetDOMStringToNull(mResult);
|
||||
mResultArrayBuffer = nullptr;
|
||||
|
||||
mAsyncStream = nullptr;
|
||||
mBlob = nullptr;
|
||||
|
||||
//Clean up memory buffer
|
||||
FreeFileData();
|
||||
|
||||
// Dispatch the events
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(ABORT_STR));
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
@ -4,70 +4,55 @@
|
||||
* 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 nsDOMFileReader_h__
|
||||
#define nsDOMFileReader_h__
|
||||
#ifndef mozilla_dom_FileReader_h
|
||||
#define mozilla_dom_FileReader_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "prtime.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
|
||||
#include "nsIDOMFileReader.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "prtime.h"
|
||||
|
||||
#include "FileIOObject.h"
|
||||
#define NS_PROGRESS_EVENT_INTERVAL 50
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Blob;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
class nsDOMFileReader final : public mozilla::dom::FileIOObject,
|
||||
public nsIDOMFileReader,
|
||||
extern const uint64_t kUnknownSize;
|
||||
|
||||
class FileReader final : public DOMEventTargetHelper,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsSupportsWeakReference
|
||||
public nsSupportsWeakReference,
|
||||
public nsIInputStreamCallback,
|
||||
public nsITimerCallback
|
||||
{
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
typedef mozilla::dom::GlobalObject GlobalObject;
|
||||
typedef mozilla::dom::Blob Blob;
|
||||
|
||||
public:
|
||||
nsDOMFileReader();
|
||||
explicit FileReader(nsPIDOMWindow* aWindow);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIDOMFILEREADER
|
||||
|
||||
NS_REALLY_FORWARD_NSIDOMEVENTTARGET(mozilla::DOMEventTargetHelper)
|
||||
|
||||
// nsIInterfaceRequestor
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
// FileIOObject overrides
|
||||
virtual void DoAbort(nsAString& aEvent) override;
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader, DOMEventTargetHelper)
|
||||
|
||||
virtual nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) override;
|
||||
|
||||
virtual nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
|
||||
nsAString& aTerminationEvent) override;
|
||||
|
||||
nsPIDOMWindow* GetParentObject() const
|
||||
{
|
||||
return GetOwner();
|
||||
}
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// WebIDL
|
||||
static already_AddRefed<nsDOMFileReader>
|
||||
static already_AddRefed<FileReader>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
void ReadAsArrayBuffer(JSContext* aCx, Blob& aBlob, ErrorResult& aRv)
|
||||
{
|
||||
@ -84,23 +69,26 @@ public:
|
||||
ReadFileContent(aBlob, EmptyString(), FILE_AS_DATAURL, aRv);
|
||||
}
|
||||
|
||||
using FileIOObject::Abort;
|
||||
void Abort(ErrorResult& aRv);
|
||||
|
||||
// Inherited ReadyState().
|
||||
uint16_t ReadyState() const
|
||||
{
|
||||
return static_cast<uint16_t>(mReadyState);
|
||||
}
|
||||
|
||||
DOMError* GetError() const
|
||||
{
|
||||
return mError;
|
||||
}
|
||||
|
||||
void GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
|
||||
ErrorResult& aRv);
|
||||
|
||||
using FileIOObject::GetError;
|
||||
|
||||
IMPL_EVENT_HANDLER(loadstart)
|
||||
using FileIOObject::GetOnprogress;
|
||||
using FileIOObject::SetOnprogress;
|
||||
IMPL_EVENT_HANDLER(progress)
|
||||
IMPL_EVENT_HANDLER(load)
|
||||
using FileIOObject::GetOnabort;
|
||||
using FileIOObject::SetOnabort;
|
||||
using FileIOObject::GetOnerror;
|
||||
using FileIOObject::SetOnerror;
|
||||
IMPL_EVENT_HANDLER(abort)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
IMPL_EVENT_HANDLER(loadend)
|
||||
|
||||
void ReadAsBinaryString(Blob& aBlob, ErrorResult& aRv)
|
||||
@ -108,15 +96,15 @@ public:
|
||||
ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~FileReader();
|
||||
|
||||
nsresult Init();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMFileReader,
|
||||
FileIOObject)
|
||||
void RootResultArrayBuffer();
|
||||
|
||||
protected:
|
||||
virtual ~nsDOMFileReader();
|
||||
// This must be in sync with dom/webidl/FileReader.webidl
|
||||
enum eReadyState {
|
||||
EMPTY = 0,
|
||||
LOADING = 1,
|
||||
DONE = 2
|
||||
};
|
||||
|
||||
enum eDataFormat {
|
||||
FILE_AS_ARRAYBUFFER,
|
||||
@ -125,6 +113,8 @@ protected:
|
||||
FILE_AS_DATAURL
|
||||
};
|
||||
|
||||
void RootResultArrayBuffer();
|
||||
|
||||
void ReadFileContent(Blob& aBlob,
|
||||
const nsAString &aCharset, eDataFormat aDataFormat,
|
||||
ErrorResult& aRv);
|
||||
@ -134,7 +124,20 @@ protected:
|
||||
nsresult GetAsDataURL(Blob *aBlob, const char *aFileData,
|
||||
uint32_t aDataLen, nsAString &aResult);
|
||||
|
||||
void FreeFileData() {
|
||||
nsresult OnLoadEnd(nsresult aStatus);
|
||||
|
||||
void StartProgressEventTimer();
|
||||
void ClearProgressEventTimer();
|
||||
void DispatchError(nsresult rv, nsAString& finalEvent);
|
||||
nsresult DispatchProgressEvent(const nsAString& aType);
|
||||
|
||||
nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount);
|
||||
|
||||
nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
|
||||
nsAString& aTerminationEvent);
|
||||
|
||||
void FreeFileData()
|
||||
{
|
||||
free(mFileData);
|
||||
mFileData = nullptr;
|
||||
mDataLen = 0;
|
||||
@ -150,6 +153,22 @@ protected:
|
||||
nsString mResult;
|
||||
|
||||
JS::Heap<JSObject*> mResultArrayBuffer;
|
||||
|
||||
nsCOMPtr<nsITimer> mProgressNotifier;
|
||||
bool mProgressEventWasDelayed;
|
||||
bool mTimerIsActive;
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
|
||||
|
||||
RefPtr<DOMError> mError;
|
||||
|
||||
eReadyState mReadyState;
|
||||
|
||||
uint64_t mTotal;
|
||||
uint64_t mTransferred;
|
||||
};
|
||||
|
||||
#endif
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_FileReader_h
|
@ -9,6 +9,8 @@
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "mozilla/Hal.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
/**
|
||||
* This class is used by nsGlobalWindow to implement window.orientation
|
||||
* and window.onorientationchange. This class is defined in its own file
|
||||
|
@ -16,7 +16,6 @@ XPIDL_SOURCES += [
|
||||
'nsIDOMDOMCursor.idl',
|
||||
'nsIDOMDOMRequest.idl',
|
||||
'nsIDOMFileList.idl',
|
||||
'nsIDOMFileReader.idl',
|
||||
'nsIDOMFormData.idl',
|
||||
'nsIDOMParser.idl',
|
||||
'nsIDOMSerializer.idl',
|
||||
@ -180,6 +179,7 @@ EXPORTS.mozilla.dom += [
|
||||
'EventSource.h',
|
||||
'File.h',
|
||||
'FileList.h',
|
||||
'FileReader.h',
|
||||
'FragmentOrElement.h',
|
||||
'FromParser.h',
|
||||
'ImageEncoder.h',
|
||||
@ -244,8 +244,8 @@ UNIFIED_SOURCES += [
|
||||
'Element.cpp',
|
||||
'EventSource.cpp',
|
||||
'File.cpp',
|
||||
'FileIOObject.cpp',
|
||||
'FileList.cpp',
|
||||
'FileReader.cpp',
|
||||
'FragmentOrElement.cpp',
|
||||
'ImageEncoder.cpp',
|
||||
'ImportManager.cpp',
|
||||
@ -272,7 +272,6 @@ UNIFIED_SOURCES += [
|
||||
'nsDOMAttributeMap.cpp',
|
||||
'nsDOMCaretPosition.cpp',
|
||||
'nsDOMClassInfo.cpp',
|
||||
'nsDOMFileReader.cpp',
|
||||
'nsDOMMutationObserver.cpp',
|
||||
'nsDOMNavigationTiming.cpp',
|
||||
'nsDOMScriptObjectFactory.cpp',
|
||||
|
@ -2275,8 +2275,7 @@ struct InterfaceShimEntry {
|
||||
// interface that has interface constants that sites might be getting off
|
||||
// of Ci.
|
||||
const InterfaceShimEntry kInterfaceShimMap[] =
|
||||
{ { "nsIDOMFileReader", "FileReader" },
|
||||
{ "nsIXMLHttpRequest", "XMLHttpRequest" },
|
||||
{ { "nsIXMLHttpRequest", "XMLHttpRequest" },
|
||||
{ "nsIDOMDOMException", "DOMException" },
|
||||
{ "nsIDOMNode", "Node" },
|
||||
{ "nsIDOMCSSPrimitiveValue", "CSSPrimitiveValue" },
|
||||
|
@ -1,47 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIDOMEventTarget.idl"
|
||||
|
||||
interface nsIDOMEventListener;
|
||||
interface nsIDOMBlob;
|
||||
|
||||
[builtinclass, uuid(2f34c719-bc14-4546-9fb9-2bab75e56e45)]
|
||||
interface nsIDOMFileReader : nsIDOMEventTarget
|
||||
{
|
||||
[implicit_jscontext]
|
||||
void readAsArrayBuffer(in nsIDOMBlob filedata);
|
||||
void readAsBinaryString(in nsIDOMBlob filedata);
|
||||
void readAsText(in nsIDOMBlob filedata, [optional] in DOMString encoding);
|
||||
void readAsDataURL(in nsIDOMBlob file);
|
||||
|
||||
void abort();
|
||||
|
||||
const unsigned short EMPTY = 0;
|
||||
const unsigned short LOADING = 1;
|
||||
const unsigned short DONE = 2;
|
||||
readonly attribute unsigned short readyState;
|
||||
|
||||
[implicit_jscontext]
|
||||
readonly attribute jsval result;
|
||||
|
||||
// This is a DOMError
|
||||
readonly attribute nsISupports error;
|
||||
|
||||
[implicit_jscontext] attribute jsval onloadstart;
|
||||
[implicit_jscontext] attribute jsval onprogress;
|
||||
[implicit_jscontext] attribute jsval onload;
|
||||
[implicit_jscontext] attribute jsval onabort;
|
||||
[implicit_jscontext] attribute jsval onerror;
|
||||
[implicit_jscontext] attribute jsval onloadend;
|
||||
};
|
||||
|
||||
%{ C++
|
||||
#define NS_FILEREADER_CID \
|
||||
{0x06aa7c21, 0xfe05, 0x4cf2, \
|
||||
{0xb1, 0xc4, 0x0c, 0x71, 0x26, 0xa4, 0xf7, 0x13}}
|
||||
#define NS_FILEREADER_CONTRACTID \
|
||||
"@mozilla.org/files/filereader;1"
|
||||
%}
|
@ -26,9 +26,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
let reader =
|
||||
Components.classes["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMFileReader);
|
||||
let reader = new FileReader();
|
||||
reader.addEventListener("load", function() {
|
||||
let response = reader.result == "this is a great success!" ?
|
||||
message.json :
|
||||
|
@ -513,7 +513,6 @@ DOMInterfaces = {
|
||||
},
|
||||
|
||||
'FileReader': {
|
||||
'nativeType': 'nsDOMFileReader',
|
||||
'implicitJSContext': [ 'readAsArrayBuffer' ],
|
||||
},
|
||||
|
||||
|
@ -2269,22 +2269,6 @@ class MethodDefiner(PropertyDefiner):
|
||||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
# Output an @@iterator for generated iterator interfaces. This should
|
||||
# not be necessary, but
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1091945 means that
|
||||
# %IteratorPrototype%[@@iterator] is a broken puppy.
|
||||
if (not static and
|
||||
not unforgeable and
|
||||
descriptor.interface.isIteratorInterface()):
|
||||
self.regular.append({
|
||||
"name": "@@iterator",
|
||||
"methodInfo": False,
|
||||
"selfHostedName": "IteratorIdentity",
|
||||
"length": 0,
|
||||
"flags": "0",
|
||||
"condition": MemberCondition()
|
||||
})
|
||||
|
||||
# Generate the maplike/setlike iterator, if one wasn't already
|
||||
# generated by a method. If we already have an @@iterator symbol, fail.
|
||||
if descriptor.interface.maplikeOrSetlikeOrIterable:
|
||||
|
@ -45,8 +45,7 @@ codegen_dependencies := \
|
||||
$(GLOBAL_DEPS) \
|
||||
$(NULL)
|
||||
|
||||
# The 1 is to make codegen.pp not optional.
|
||||
$(call include_deps,codegen.pp,1)
|
||||
include codegen.pp
|
||||
|
||||
codegen.pp: $(codegen_dependencies)
|
||||
$(call py_action,webidl,$(srcdir))
|
||||
|
@ -50,11 +50,6 @@ function childFrameScript() {
|
||||
sendAsyncMessage(mmName, { op: "done", result: result });
|
||||
}
|
||||
|
||||
function createFileReader() {
|
||||
return Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
}
|
||||
|
||||
function grabAndContinue(arg) {
|
||||
testGenerator.send(arg);
|
||||
}
|
||||
@ -78,7 +73,7 @@ function childFrameScript() {
|
||||
|
||||
info("Reading blob");
|
||||
|
||||
let reader = createFileReader();
|
||||
let reader = new FileReader();
|
||||
reader.addEventListener("load", grabAndContinue);
|
||||
reader.readAsText(blob);
|
||||
|
||||
@ -94,7 +89,7 @@ function childFrameScript() {
|
||||
|
||||
info("Reading slice");
|
||||
|
||||
reader = createFileReader();
|
||||
reader = new FileReader();
|
||||
reader.addEventListener("load", grabAndContinue);
|
||||
reader.readAsText(slice);
|
||||
|
||||
@ -155,7 +150,7 @@ function childFrameScript() {
|
||||
|
||||
info("Reading blob");
|
||||
|
||||
reader = createFileReader();
|
||||
reader = new FileReader();
|
||||
reader.addEventListener("load", grabAndContinue);
|
||||
reader.readAsText(blob);
|
||||
|
||||
@ -190,7 +185,7 @@ function childFrameScript() {
|
||||
|
||||
info("Reading Slice");
|
||||
|
||||
reader = createFileReader();
|
||||
reader = new FileReader();
|
||||
reader.addEventListener("load", grabAndContinue);
|
||||
reader.readAsText(slice);
|
||||
|
||||
|
@ -7,11 +7,6 @@ var disableWorkerTest = "This test uses SpecialPowers";
|
||||
|
||||
var testGenerator = testSteps();
|
||||
|
||||
function createFileReader() {
|
||||
return SpecialPowers.Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(SpecialPowers.Ci.nsIDOMFileReader);
|
||||
}
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
const fileIOFlags = 0x02 | // PR_WRONLY
|
||||
@ -48,7 +43,7 @@ function testSteps()
|
||||
is(file.size, fileData.length, "Correct size");
|
||||
is(file.type, fileType, "Correct type");
|
||||
|
||||
let fileReader = createFileReader();
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = grabEventAndContinueHandler;
|
||||
fileReader.readAsText(file);
|
||||
|
||||
@ -85,7 +80,7 @@ function testSteps()
|
||||
is(file.size, fileData.length, "Correct size");
|
||||
is(file.type, fileType, "Correct type");
|
||||
|
||||
fileReader = createFileReader();
|
||||
fileReader = new FileReader();
|
||||
fileReader.onload = grabEventAndContinueHandler;
|
||||
fileReader.readAsText(file);
|
||||
|
||||
|
@ -52,7 +52,7 @@ if (!this.runTest) {
|
||||
enableExperimental();
|
||||
}
|
||||
|
||||
Cu.importGlobalProperties(["indexedDB", "Blob", "File"]);
|
||||
Cu.importGlobalProperties(["indexedDB", "Blob", "File", "FileReader"]);
|
||||
|
||||
do_test_pending();
|
||||
testGenerator.next();
|
||||
@ -339,12 +339,6 @@ function getFile(name, type, str)
|
||||
return new File([str], name, {type: type});
|
||||
}
|
||||
|
||||
function getFileReader()
|
||||
{
|
||||
return SpecialPowers.Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(SpecialPowers.Ci.nsIDOMFileReader);
|
||||
}
|
||||
|
||||
function compareBuffers(buffer1, buffer2)
|
||||
{
|
||||
if (buffer1.byteLength != buffer2.byteLength) {
|
||||
@ -388,7 +382,7 @@ function verifyBlob(blob1, blob2)
|
||||
}
|
||||
|
||||
if (!buffer2) {
|
||||
let reader = getFileReader();
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob2);
|
||||
reader.onload = function(event) {
|
||||
buffer2 = event.target.result;
|
||||
@ -400,7 +394,7 @@ function verifyBlob(blob1, blob2)
|
||||
}
|
||||
}
|
||||
|
||||
let reader = getFileReader();
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob1);
|
||||
reader.onload = function(event) {
|
||||
buffer1 = event.target.result;
|
||||
|
@ -44,11 +44,6 @@ function childFrameScript() {
|
||||
sendAsyncMessage(messageName, { op: "done", result: result });
|
||||
}
|
||||
|
||||
function createFileReader() {
|
||||
return Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
}
|
||||
|
||||
function grabAndContinue(arg) {
|
||||
testGenerator.send(arg);
|
||||
}
|
||||
@ -65,7 +60,7 @@ function childFrameScript() {
|
||||
|
||||
info("Reading blob");
|
||||
|
||||
let reader = createFileReader();
|
||||
let reader = new FileReader();
|
||||
reader.addEventListener("load", grabAndContinue);
|
||||
reader.readAsText(blob);
|
||||
|
||||
@ -84,7 +79,7 @@ function childFrameScript() {
|
||||
|
||||
info("Reading slice");
|
||||
|
||||
reader = createFileReader();
|
||||
reader = new FileReader();
|
||||
reader.addEventListener("load", grabAndContinue);
|
||||
reader.readAsText(slice);
|
||||
|
||||
|
@ -52,6 +52,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
|
||||
[test_HaveMetadataUnbufferedSeek.html]
|
||||
[test_HaveMetadataUnbufferedSeek_mp4.html]
|
||||
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
|
||||
[test_LoadedDataFired_mp4.html]
|
||||
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
|
||||
[test_LoadedMetadataFired.html]
|
||||
[test_LoadedMetadataFired_mp4.html]
|
||||
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
|
||||
|
68
dom/media/mediasource/test/test_LoadedDataFired_mp4.html
Normal file
68
dom/media/mediasource/test/test_LoadedDataFired_mp4.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>MSE: Check that playback only starts once we have data at time = 0</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="mediasource.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
runWithMSE(function(ms, el) {
|
||||
el.controls = true;
|
||||
el.addEventListener("loadeddata", function() {
|
||||
ok(el.buffered.length > 0, "data is buffered");
|
||||
is(el.buffered.start(0), 0, "must fire loadeddata when data has been loaded");
|
||||
is(el.currentTime, 0, "must fire loadeddata at start");
|
||||
});
|
||||
el.addEventListener("playing", function() {
|
||||
ok(el.buffered.length > 0, "data is buffered");
|
||||
is(el.buffered.start(0), 0, "must fire playing when data has been loaded");
|
||||
is(el.currentTime, 0, "must fire playing at start");
|
||||
});
|
||||
once(ms, 'sourceopen').then(function() {
|
||||
ok(true, "Receive a sourceopen event");
|
||||
var videosb = ms.addSourceBuffer("video/mp4");
|
||||
is(el.readyState, el.HAVE_NOTHING, "readyState is HAVE_NOTHING");
|
||||
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
|
||||
.then(once.bind(null, el, "loadedmetadata"))
|
||||
.then(function() {
|
||||
videosb.appendWindowStart = 2;
|
||||
videosb.appendWindowEnd = 4;
|
||||
is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA");
|
||||
// Load [2.4, 3.968344). 2.4 as it's the first keyframe after 2s and
|
||||
// 3.968344 as the last frame ends after 4s.
|
||||
return fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 8), '.m4s');
|
||||
})
|
||||
.then(function() {
|
||||
is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA");
|
||||
// test that appendWindowEnd did its job.
|
||||
ok(el.buffered.start(0) >= 2, "no data can be found prior appendWindowStart");
|
||||
ok(el.buffered.end(el.buffered.length-1) <= 4, "no data can be found beyond appendWindowEnd");
|
||||
el.play();
|
||||
return once(el, "play");
|
||||
})
|
||||
.then(function() {
|
||||
videosb.appendWindowStart = 0;
|
||||
var promises = [];
|
||||
// Load [0, 3.971666).
|
||||
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 8), '.m4s'));
|
||||
// playback can only start now.
|
||||
promises.push(once(el, "playing"));
|
||||
return Promise.all(promises);
|
||||
})
|
||||
.then(function() {
|
||||
ok(true, "playing");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -41,6 +41,7 @@ struct AudioTimelineEvent final
|
||||
double aDuration = 0.0, const float* aCurve = nullptr,
|
||||
uint32_t aCurveLength = 0)
|
||||
: mType(aType)
|
||||
, mCurve(nullptr)
|
||||
, mTimeConstant(aTimeConstant)
|
||||
, mDuration(aDuration)
|
||||
#ifdef DEBUG
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.importGlobalProperties(['Blob']);
|
||||
Cu.importGlobalProperties(['Blob', 'FileReader']);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
|
||||
@ -1303,8 +1303,7 @@ SendTransaction.prototype = Object.create(CancellableTransaction.prototype, {
|
||||
return;
|
||||
}
|
||||
|
||||
let fileReader = Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
let fileReader = new FileReader();
|
||||
fileReader.addEventListener("loadend", (aEvent) => {
|
||||
let arrayBuffer = aEvent.target.result;
|
||||
aPart.content = new Uint8Array(arrayBuffer);
|
||||
|
@ -1254,12 +1254,22 @@ _retainobject(NPObject* npobj)
|
||||
void
|
||||
_releaseobject(NPObject* npobj)
|
||||
{
|
||||
// If nothing is passed, just return, even if we're on the wrong thread.
|
||||
if (!npobj) {
|
||||
return;
|
||||
}
|
||||
|
||||
// THIS IS A KNOWN LEAK. SEE BUG 1221448.
|
||||
// If releaseobject is called off the main thread and we have a valid pointer,
|
||||
// we at least know it was created on the main thread (see _createobject
|
||||
// implementation). However, forwarding the deletion back to the main thread
|
||||
// without careful checking could cause bad memory management races. So, for
|
||||
// now, we leak by warning and then just returning early. But it should fix
|
||||
// java 7 crashes.
|
||||
if (!NS_IsMainThread()) {
|
||||
NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_releaseobject called from the wrong thread\n"));
|
||||
MOZ_CRASH("NPN_releaseobject called from the wrong thread");
|
||||
}
|
||||
if (!npobj)
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t refCnt = PR_ATOMIC_DECREMENT((int32_t*)&npobj->referenceCount);
|
||||
NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
|
||||
|
@ -10,7 +10,8 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
[Constructor]
|
||||
[Constructor,
|
||||
Exposed=(Window,System)]
|
||||
interface FileReader : EventTarget {
|
||||
// async read methods
|
||||
[Throws]
|
||||
|
@ -626,6 +626,12 @@ private:
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aIndex < mLoadInfos.Length());
|
||||
|
||||
// If one load info cancels or hits an error, it can race with the start
|
||||
// callback coming from another load info.
|
||||
if (mCanceledMainThread || !mCacheCreator) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
||||
|
@ -81,7 +81,7 @@ skip-if = toolkit == 'android'
|
||||
skip-if = toolkit == 'android'
|
||||
[test_bug596506.html]
|
||||
[test_bug597331.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
skip-if = buildapp == 'mulet' || toolkit == 'android' || asan # Bug 1211213
|
||||
[test_bug597784.html]
|
||||
[test_bug599322.html]
|
||||
skip-if = toolkit == 'android'
|
||||
|
@ -368,6 +368,17 @@ public:
|
||||
return sNoClipRect;
|
||||
}
|
||||
|
||||
float GetPresShellResolution() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
|
||||
if (AtTopLayer() && mLayer->AsContainerLayer()) {
|
||||
return mLayer->AsContainerLayer()->GetPresShellResolution();
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
EventRegionsOverride GetEventRegionsOverride() const
|
||||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
|
@ -226,26 +226,6 @@ AppendToString(std::stringstream& aStream, const Matrix& m,
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const Matrix4x4& m,
|
||||
const char* pfx, const char* sfx)
|
||||
{
|
||||
if (m.Is2D()) {
|
||||
Matrix matrix = m.As2D();
|
||||
AppendToString(aStream, matrix, pfx, sfx);
|
||||
return;
|
||||
}
|
||||
|
||||
aStream << pfx;
|
||||
aStream << nsPrintfCString(
|
||||
"[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]",
|
||||
m._11, m._12, m._13, m._14,
|
||||
m._21, m._22, m._23, m._24,
|
||||
m._31, m._32, m._33, m._34,
|
||||
m._41, m._42, m._43, m._44).get();
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const Matrix5x4& m,
|
||||
const char* pfx, const char* sfx)
|
||||
|
@ -7,7 +7,7 @@
|
||||
#define GFX_LAYERSLOGGING_H
|
||||
|
||||
#include "FrameMetrics.h" // for FrameMetrics, etc
|
||||
#include "mozilla/gfx/MatrixFwd.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
#include "mozilla/gfx/Point.h" // for IntSize, etc
|
||||
#include "mozilla/gfx/Types.h" // for Filter, SurfaceFormat
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags
|
||||
@ -185,9 +185,26 @@ void
|
||||
AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix& m,
|
||||
const char* pfx="", const char* sfx="");
|
||||
|
||||
template<class SourceUnits, class TargetUnits>
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4& m,
|
||||
const char* pfx="", const char* sfx="");
|
||||
AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4Typed<SourceUnits, TargetUnits>& m,
|
||||
const char* pfx="", const char* sfx="")
|
||||
{
|
||||
if (m.Is2D()) {
|
||||
mozilla::gfx::Matrix matrix = m.As2D();
|
||||
AppendToString(aStream, matrix, pfx, sfx);
|
||||
return;
|
||||
}
|
||||
|
||||
aStream << pfx;
|
||||
aStream << nsPrintfCString(
|
||||
"[ %g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g; ]",
|
||||
m._11, m._12, m._13, m._14,
|
||||
m._21, m._22, m._23, m._24,
|
||||
m._31, m._32, m._33, m._34,
|
||||
m._41, m._42, m._43, m._44).get();
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix5x4& m,
|
||||
|
@ -797,6 +797,12 @@ public:
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetDestination(const nsPoint& aNewDestination) {
|
||||
mXAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.x));
|
||||
mYAxisModel.SetDestination(static_cast<int32_t>(aNewDestination.y));
|
||||
}
|
||||
|
||||
private:
|
||||
AsyncPanZoomController& mApzc;
|
||||
AxisPhysicsMSDModel mXAxisModel, mYAxisModel;
|
||||
@ -3164,6 +3170,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
CancelAnimation();
|
||||
|
||||
mFrameMetrics = aLayerMetrics;
|
||||
if (scrollOffsetUpdated) {
|
||||
AcknowledgeScrollUpdate();
|
||||
}
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
ShareCompositorFrameMetrics();
|
||||
|
||||
@ -3223,20 +3232,22 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
ToString(mFrameMetrics.GetScrollOffset()).c_str(),
|
||||
ToString(aLayerMetrics.GetScrollOffset()).c_str());
|
||||
|
||||
// Send an acknowledgement with the new scroll generation so that any
|
||||
// repaint requests later in this function go through.
|
||||
// Because of the scroll generation update, any inflight paint requests are
|
||||
// going to be ignored by layout, and so mLastDispatchedPaintMetrics
|
||||
// becomes incorrect for the purposes of calculating the LD transform. To
|
||||
// correct this we need to update mLastDispatchedPaintMetrics to be the
|
||||
// last thing we know was painted by Gecko.
|
||||
mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
|
||||
AcknowledgeScrollUpdate();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
|
||||
// Cancel the animation (which might also trigger a repaint request)
|
||||
// after we update the scroll offset above. Otherwise we can be left
|
||||
// in a state where things are out of sync.
|
||||
CancelAnimation();
|
||||
|
||||
// Because of the scroll offset update, any inflight paint requests are
|
||||
// going to be ignored by layout, and so mLastDispatchedPaintMetrics
|
||||
// becomes incorrect for the purposes of calculating the LD transform. To
|
||||
// correct this we need to update mLastDispatchedPaintMetrics to be the
|
||||
// last thing we know was painted by Gecko.
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
|
||||
// Since the scroll offset has changed, we need to recompute the
|
||||
// displayport margins and send them to layout. Otherwise there might be
|
||||
// scenarios where for example we scroll from the top of a page (where the
|
||||
@ -3253,34 +3264,26 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
// thread. This flag will be reset by the main thread when it receives
|
||||
// the scroll update acknowledgement.
|
||||
|
||||
APZC_LOG("%p smooth scrolling from %s to %s\n", this,
|
||||
APZC_LOG("%p smooth scrolling from %s to %s in state %d\n", this,
|
||||
Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
|
||||
Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str());
|
||||
Stringify(aLayerMetrics.GetSmoothScrollOffset()).c_str(),
|
||||
mState);
|
||||
|
||||
CancelAnimation();
|
||||
|
||||
// It's important to not send repaint requests between this
|
||||
// CopySmoothScrollInfo call and the sending of the scroll update
|
||||
// acknowledgement below, otherwise that repaint request will get rejected
|
||||
// on the main thread but mLastPaintRequestMetrics will have the new scroll
|
||||
// generation; this would leave the main thread out of date.
|
||||
// See comment on the similar code in the |if (scrollOffsetUpdated)| block
|
||||
// above.
|
||||
mFrameMetrics.CopySmoothScrollInfoFrom(aLayerMetrics);
|
||||
AcknowledgeScrollUpdate();
|
||||
mLastDispatchedPaintMetrics = aLayerMetrics;
|
||||
|
||||
if (mState == SMOOTH_SCROLL && mAnimation) {
|
||||
APZC_LOG("%p updating destination on existing animation\n", this);
|
||||
RefPtr<SmoothScrollAnimation> animation(
|
||||
static_cast<SmoothScrollAnimation*>(mAnimation.get()));
|
||||
animation->SetDestination(
|
||||
CSSPoint::ToAppUnits(aLayerMetrics.GetSmoothScrollOffset()));
|
||||
} else {
|
||||
CancelAnimation();
|
||||
StartSmoothScroll(ScrollSource::DOM);
|
||||
|
||||
scrollOffsetUpdated = true; // Ensure that AcknowledgeScrollUpdate is called
|
||||
}
|
||||
|
||||
if (scrollOffsetUpdated) {
|
||||
// Once layout issues a scroll offset update, it becomes impervious to
|
||||
// scroll offset updates from APZ until we acknowledge the update it sent.
|
||||
// This prevents APZ updates from clobbering scroll updates from other
|
||||
// more "legitimate" sources like content scripts.
|
||||
RefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, aLayerMetrics.GetScrollGeneration());
|
||||
controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
|
||||
aLayerMetrics.GetScrollGeneration());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3290,6 +3293,21 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
UpdateSharedCompositorFrameMetrics();
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::AcknowledgeScrollUpdate() const
|
||||
{
|
||||
// Once layout issues a scroll offset update, it becomes impervious to
|
||||
// scroll offset updates from APZ until we acknowledge the update it sent.
|
||||
// This prevents APZ updates from clobbering scroll updates from other
|
||||
// more "legitimate" sources like content scripts.
|
||||
RefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, mFrameMetrics.GetScrollGeneration());
|
||||
controller->AcknowledgeScrollUpdate(mFrameMetrics.GetScrollId(),
|
||||
mFrameMetrics.GetScrollGeneration());
|
||||
}
|
||||
}
|
||||
|
||||
const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() const {
|
||||
mMonitor.AssertCurrentThreadIn();
|
||||
return mFrameMetrics;
|
||||
|
@ -829,11 +829,6 @@ private:
|
||||
|
||||
PanGestureBlockState* CurrentPanGestureBlock();
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to manage
|
||||
* pan gestures.
|
||||
*/
|
||||
|
||||
private:
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to manage
|
||||
@ -889,6 +884,8 @@ private:
|
||||
// Returns whether overscroll is allowed during an event.
|
||||
bool AllowScrollHandoffInCurrentBlock() const;
|
||||
|
||||
void AcknowledgeScrollUpdate() const;
|
||||
|
||||
/* ===================================================================
|
||||
* The functions and members in this section are used to make ancestor chains
|
||||
* out of APZC instances. These chains can only be walked or manipulated
|
||||
|
@ -84,8 +84,8 @@ GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAn
|
||||
// With containerless scrolling, the offending post-scale is on the
|
||||
// parent layer of the displayport-ancestor, which we don't reach in this
|
||||
// loop, so we don't need to worry about it.
|
||||
const FrameMetrics& metrics = iter.Metrics();
|
||||
transform.PostScale(metrics.GetPresShellResolution(), metrics.GetPresShellResolution(), 1.f);
|
||||
float presShellResolution = iter.GetPresShellResolution();
|
||||
transform.PostScale(presShellResolution, presShellResolution, 1.0f);
|
||||
}
|
||||
}
|
||||
return ViewAs<LayerToParentLayerMatrix4x4>(transform);
|
||||
|
@ -120,6 +120,7 @@ class mozilla::gl::SkiaGLGlue : public GenericAtomicRefCounted {
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsIGfxInfo.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "VsyncSource.h"
|
||||
@ -1067,11 +1068,10 @@ gfxPlatform::ComputeTileSize()
|
||||
if (gfxPrefs::LayersTilesAdjust()) {
|
||||
gfx::IntSize screenSize = GetScreenSize();
|
||||
if (screenSize.width > 0) {
|
||||
// For the time being tiles larger than 512 probably do not make much
|
||||
// sense. This is due to e.g. increased rasterisation time outweighing
|
||||
// the decreased composition time, or large increases in memory usage
|
||||
// for screens slightly wider than a higher power of two.
|
||||
w = h = screenSize.width >= 512 ? 512 : 256;
|
||||
// Choose a size so that there are between 2 and 4 tiles per screen width.
|
||||
// FIXME: we should probably make sure this is within the max texture size,
|
||||
// but I think everything should at least support 1024
|
||||
w = h = clamped(NextPowerOfTwo(screenSize.width) / 4, 256, 1024);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
|
@ -719,7 +719,7 @@ struct JSClass {
|
||||
// application.
|
||||
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
|
||||
#define JSCLASS_GLOBAL_SLOT_COUNT \
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 35)
|
||||
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 36)
|
||||
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
|
@ -685,9 +685,6 @@ function CreateArrayIterator(obj, kind) {
|
||||
return CreateArrayIteratorAt(obj, kind, 0);
|
||||
}
|
||||
|
||||
function ArrayIteratorIdentity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
function ArrayIteratorNext() {
|
||||
if (!IsObject(this) || !IsArrayIterator(this)) {
|
||||
|
@ -118,7 +118,6 @@ const Class MapIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
const JSFunctionSpec MapIteratorObject::methods[] = {
|
||||
JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
|
||||
JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -144,11 +143,9 @@ GlobalObject::initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
|
||||
if (!base)
|
||||
return false;
|
||||
Rooted<MapIteratorObject*> proto(cx,
|
||||
NewObjectWithGivenProto<MapIteratorObject>(cx, base));
|
||||
RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, base));
|
||||
if (!proto)
|
||||
return false;
|
||||
proto->setSlot(MapIteratorObject::RangeSlot, PrivateValue(nullptr));
|
||||
if (!JS_DefineFunctions(cx, proto, MapIteratorObject::methods))
|
||||
return false;
|
||||
global->setReservedSlot(MAP_ITERATOR_PROTO, ObjectValue(*proto));
|
||||
@ -844,7 +841,6 @@ const Class SetIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
const JSFunctionSpec SetIteratorObject::methods[] = {
|
||||
JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
|
||||
JS_FN("next", next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -869,11 +865,9 @@ GlobalObject::initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
Rooted<JSObject*> base(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
|
||||
if (!base)
|
||||
return false;
|
||||
Rooted<SetIteratorObject*> proto(cx,
|
||||
NewObjectWithGivenProto<SetIteratorObject>(cx, base));
|
||||
RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, base));
|
||||
if (!proto)
|
||||
return false;
|
||||
proto->setSlot(SetIteratorObject::RangeSlot, PrivateValue(nullptr));
|
||||
if (!JS_DefineFunctions(cx, proto, SetIteratorObject::methods))
|
||||
return false;
|
||||
global->setReservedSlot(SET_ITERATOR_PROTO, ObjectValue(*proto));
|
||||
|
@ -2040,6 +2040,9 @@ ASTSerializer::declaration(ParseNode* pn, MutableHandleValue dst)
|
||||
case PNK_FUNCTION:
|
||||
return function(pn, AST_FUNC_DECL, dst);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
return function(pn->pn_left, AST_FUNC_DECL, dst);
|
||||
|
||||
case PNK_VAR:
|
||||
return variableDeclaration(pn, false, dst);
|
||||
|
||||
@ -2411,6 +2414,9 @@ ASTSerializer::statement(ParseNode* pn, MutableHandleValue dst)
|
||||
case PNK_VAR:
|
||||
return declaration(pn, dst);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
return declaration(pn->pn_left, dst);
|
||||
|
||||
case PNK_LETBLOCK:
|
||||
return letBlock(pn, dst);
|
||||
|
||||
|
@ -199,10 +199,6 @@ function String_iterator() {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
function StringIteratorIdentity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
function StringIteratorNext() {
|
||||
if (!IsObject(this) || !IsStringIterator(this)) {
|
||||
return callFunction(CallStringIteratorMethodIfWrapped, this,
|
||||
|
@ -1372,20 +1372,20 @@ BytecodeEmitter::emitVarIncDec(ParseNode* pn)
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::atBodyLevel() const
|
||||
BytecodeEmitter::atBodyLevel(StmtInfoBCE* stmt) const
|
||||
{
|
||||
// 'eval' and non-syntactic scripts are always under an invisible lexical
|
||||
// scope, but since it is not syntactic, it should still be considered at
|
||||
// body level.
|
||||
if (sc->staticScope()->is<StaticEvalObject>()) {
|
||||
bool bl = !innermostStmt()->enclosing;
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
|
||||
bool bl = !stmt->enclosing;
|
||||
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, stmt->staticScope
|
||||
->as<StaticBlockObject>()
|
||||
.enclosingStaticScope() == sc->staticScope());
|
||||
return bl;
|
||||
}
|
||||
return !innermostStmt() || sc->isModuleBox();
|
||||
return !stmt || sc->isModuleBox();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@ -1738,7 +1738,7 @@ BytecodeEmitter::bindNameToSlotHelper(ParseNode* pn)
|
||||
MOZ_ASSERT(dn->isDefn());
|
||||
pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
|
||||
} else if (pn->isDefn()) {
|
||||
dn = (Definition*) pn;
|
||||
dn = &pn->as<Definition>();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
@ -2344,6 +2344,10 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
||||
MOZ_ASSERT(pn->pn_count == 1);
|
||||
return checkSideEffects(pn->pn_head, answer);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
return checkSideEffects(pn->pn_left, answer);
|
||||
|
||||
case PNK_ARGSBODY:
|
||||
*answer = true;
|
||||
return true;
|
||||
@ -3084,11 +3088,23 @@ BytecodeEmitter::emitSwitch(ParseNode* pn)
|
||||
if (!enterBlockScope(&stmtInfo, cases->pn_objbox, JSOP_UNINITIALIZED, 0))
|
||||
return false;
|
||||
|
||||
stmtInfo.type = StmtType::SWITCH;
|
||||
stmtInfo.update = top = offset();
|
||||
|
||||
// Advance |cases| to refer to the switch case list.
|
||||
cases = cases->expr();
|
||||
|
||||
// A switch statement may contain hoisted functions inside its
|
||||
// cases. The PNX_FUNCDEFS flag is propagated from the STATEMENTLIST
|
||||
// bodies of the cases to the case list.
|
||||
if (cases->pn_xflags & PNX_FUNCDEFS) {
|
||||
for (ParseNode* caseNode = cases->pn_head; caseNode; caseNode = caseNode->pn_next) {
|
||||
if (caseNode->pn_right->pn_xflags & PNX_FUNCDEFS) {
|
||||
if (!emitHoistedFunctionsInList(caseNode->pn_right))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmtInfo.type = StmtType::SWITCH;
|
||||
stmtInfo.update = top = offset();
|
||||
} else {
|
||||
MOZ_ASSERT(cases->isKind(PNK_STATEMENTLIST));
|
||||
top = offset();
|
||||
@ -4363,7 +4379,7 @@ BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption)
|
||||
* i' to be hoisted out of the loop.
|
||||
*/
|
||||
MOZ_ASSERT(binding->isOp(JSOP_NOP));
|
||||
MOZ_ASSERT(emitOption != DefineVars);
|
||||
MOZ_ASSERT(emitOption != DefineVars && emitOption != AnnexB);
|
||||
|
||||
/*
|
||||
* To allow the front end to rewrite var f = x; as f = x; when a
|
||||
@ -4424,13 +4440,18 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
|
||||
op == JSOP_STRICTSETGNAME)
|
||||
{
|
||||
MOZ_ASSERT(emitOption != PushInitialValues);
|
||||
JSOp bindOp;
|
||||
if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME)
|
||||
bindOp = JSOP_BINDNAME;
|
||||
else
|
||||
bindOp = JSOP_BINDGNAME;
|
||||
if (!emitIndex32(bindOp, atomIndex))
|
||||
if (op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME) {
|
||||
if (!emitIndex32(JSOP_BINDGNAME, atomIndex))
|
||||
return false;
|
||||
} else if (emitOption == AnnexB) {
|
||||
// Annex B vars always go on the nearest variable environment,
|
||||
// even if scopes on the chain contain same-named bindings.
|
||||
if (!emit1(JSOP_BINDVAR))
|
||||
return false;
|
||||
} else {
|
||||
if (!emitIndex32(JSOP_BINDNAME, atomIndex))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool oldEmittingForInit = emittingForInit;
|
||||
@ -4454,7 +4475,7 @@ BytecodeEmitter::emitSingleVariable(ParseNode* pn, ParseNode* binding, ParseNode
|
||||
|
||||
// If we are not initializing, nothing to pop. If we are initializing
|
||||
// lets, we must emit the pops.
|
||||
if (emitOption == InitializeVars) {
|
||||
if (emitOption == InitializeVars || emitOption == AnnexB) {
|
||||
MOZ_ASSERT_IF(binding->isDefn(), initializer == binding->pn_expr);
|
||||
if (!binding->pn_scopecoord.isFree()) {
|
||||
if (!emitVarOp(binding, op))
|
||||
@ -5292,6 +5313,28 @@ BytecodeEmitter::emitLetBlock(ParseNode* pnLet)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitHoistedFunctionsInList(ParseNode* list)
|
||||
{
|
||||
MOZ_ASSERT(list->pn_xflags & PNX_FUNCDEFS);
|
||||
|
||||
for (ParseNode* pn = list->pn_head; pn; pn = pn->pn_next) {
|
||||
if (!sc->strict()) {
|
||||
while (pn->isKind(PNK_LABEL))
|
||||
pn = pn->as<LabeledStatement>().statement();
|
||||
}
|
||||
|
||||
if (pn->isKind(PNK_ANNEXB_FUNCTION) ||
|
||||
(pn->isKind(PNK_FUNCTION) && pn->functionIsHoisted()))
|
||||
{
|
||||
if (!emitTree(pn))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
|
||||
// the comment on emitSwitch.
|
||||
MOZ_NEVER_INLINE bool
|
||||
@ -5303,7 +5346,17 @@ BytecodeEmitter::emitLexicalScope(ParseNode* pn)
|
||||
if (!enterBlockScope(&stmtInfo, pn->pn_objbox, JSOP_UNINITIALIZED, 0))
|
||||
return false;
|
||||
|
||||
if (!emitTree(pn->pn_expr))
|
||||
ParseNode* body = pn->pn_expr;
|
||||
|
||||
if (body->isKind(PNK_STATEMENTLIST) && body->pn_xflags & PNX_FUNCDEFS) {
|
||||
// This block contains function statements whose definitions are
|
||||
// hoisted to the top of the block. Emit these as a separate pass
|
||||
// before the rest of the block.
|
||||
if (!emitHoistedFunctionsInList(body))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitTree(body))
|
||||
return false;
|
||||
|
||||
if (!leaveNestedScope(&stmtInfo))
|
||||
@ -6164,6 +6217,12 @@ BytecodeEmitter::emitComprehensionFor(ParseNode* compFor)
|
||||
MOZ_NEVER_INLINE bool
|
||||
BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
{
|
||||
ParseNode* assignmentForAnnexB = nullptr;
|
||||
if (pn->isKind(PNK_ANNEXB_FUNCTION)) {
|
||||
assignmentForAnnexB = pn->pn_right;
|
||||
pn = pn->pn_left;
|
||||
}
|
||||
|
||||
FunctionBox* funbox = pn->pn_funbox;
|
||||
RootedFunction fun(cx, funbox->function());
|
||||
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
|
||||
@ -6174,9 +6233,25 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
* function will be seen by emitFunction in two places.
|
||||
*/
|
||||
if (funbox->wasEmitted) {
|
||||
// Annex B block-scoped functions are hoisted like any other
|
||||
// block-scoped function to the top of their scope. When their
|
||||
// definitions are seen for the second time, we need to emit the
|
||||
// assignment that assigns the function to the outer 'var' binding.
|
||||
if (assignmentForAnnexB) {
|
||||
if (assignmentForAnnexB->isKind(PNK_VAR)) {
|
||||
if (!emitVariables(assignmentForAnnexB, AnnexB))
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(assignmentForAnnexB->isKind(PNK_ASSIGN));
|
||||
if (!emitTree(assignmentForAnnexB))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(fun->hasScript(), fun->nonLazyScript());
|
||||
MOZ_ASSERT(pn->functionIsHoisted());
|
||||
MOZ_ASSERT(sc->isFunctionBox());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6284,10 +6359,28 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
||||
* For modules, we record the function and instantiate the binding during
|
||||
* ModuleDeclarationInstantiation(), before the script is run.
|
||||
*/
|
||||
if (sc->isGlobalContext()) {
|
||||
|
||||
// Check for functions that were parsed under labeled statements per ES6
|
||||
// Annex B.3.2.
|
||||
bool blockScopedFunction = !atBodyLevel();
|
||||
if (!sc->strict() && blockScopedFunction) {
|
||||
StmtInfoBCE* stmt = innermostStmt();
|
||||
while (stmt && stmt->type == StmtType::LABEL)
|
||||
stmt = stmt->enclosing;
|
||||
blockScopedFunction = !atBodyLevel(stmt);
|
||||
}
|
||||
|
||||
if (blockScopedFunction) {
|
||||
if (!emitIndexOp(JSOP_LAMBDA, index))
|
||||
return false;
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_INITLEXICAL);
|
||||
if (!emitVarOp(pn, pn->getOp()))
|
||||
return false;
|
||||
if (!emit1(JSOP_POP))
|
||||
return false;
|
||||
} else if (sc->isGlobalContext()) {
|
||||
MOZ_ASSERT(pn->pn_scopecoord.isFree());
|
||||
MOZ_ASSERT(pn->getOp() == JSOP_NOP);
|
||||
MOZ_ASSERT(atBodyLevel());
|
||||
switchToPrologue();
|
||||
if (!emitIndex32(JSOP_DEFFUN, index))
|
||||
return false;
|
||||
@ -7924,7 +8017,6 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn)
|
||||
// Carefully emit everything in the right order:
|
||||
// 1. Defaults and Destructuring for each argument
|
||||
// 2. Functions
|
||||
ParseNode* pnchild = pnlast->pn_head;
|
||||
bool hasDefaults = sc->asFunctionBox()->hasDefaults();
|
||||
ParseNode* rest = nullptr;
|
||||
bool restIsDefn = false;
|
||||
@ -7985,22 +8077,12 @@ BytecodeEmitter::emitArgsBody(ParseNode *pn)
|
||||
}
|
||||
}
|
||||
if (pnlast->pn_xflags & PNX_FUNCDEFS) {
|
||||
// This block contains top-level function definitions. To ensure
|
||||
// that we emit the bytecode defining them before the rest of code
|
||||
// in the block we use a separate pass over functions. During the
|
||||
// main pass later the emitter will add JSOP_NOP with source notes
|
||||
// for the function to preserve the original functions position
|
||||
// when decompiling.
|
||||
//
|
||||
// Currently this is used only for functions, as compile-as-we go
|
||||
// mode for scripts does not allow separate emitter passes.
|
||||
for (ParseNode* pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
|
||||
if (pn2->isKind(PNK_FUNCTION) && pn2->functionIsHoisted()) {
|
||||
if (!emitTree(pn2))
|
||||
// This function contains top-level inner function definitions. To
|
||||
// ensure that we emit the bytecode defining them before the rest
|
||||
// of code in the block we use a separate pass over functions.
|
||||
if (!emitHoistedFunctionsInList(pnlast))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return emitTree(pnlast);
|
||||
}
|
||||
|
||||
@ -8218,6 +8300,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, EmitLineNumberNote emitLineNote)
|
||||
|
||||
switch (pn->getKind()) {
|
||||
case PNK_FUNCTION:
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
if (!emitFunction(pn))
|
||||
return false;
|
||||
break;
|
||||
|
@ -121,7 +121,12 @@ enum VarEmitOption {
|
||||
// Emit code to evaluate initializer expressions and leave those values on
|
||||
// the stack. This is used to implement `for (let/const ...;;)` and
|
||||
// deprecated `let` blocks.
|
||||
PushInitialValues
|
||||
PushInitialValues,
|
||||
|
||||
// Like InitializeVars, but bind using BINDVAR instead of
|
||||
// BINDNAME/BINDGNAME. Only used for emitting declarations synthesized for
|
||||
// Annex B block-scoped function semantics.
|
||||
AnnexB,
|
||||
};
|
||||
|
||||
struct BytecodeEmitter
|
||||
@ -249,7 +254,10 @@ struct BytecodeEmitter
|
||||
return parser->blockScopes[dn->pn_blockid];
|
||||
}
|
||||
|
||||
bool atBodyLevel() const;
|
||||
bool atBodyLevel(StmtInfoBCE* stmt) const;
|
||||
bool atBodyLevel() const {
|
||||
return atBodyLevel(innermostStmt());
|
||||
}
|
||||
uint32_t computeHops(ParseNode* pn, BytecodeEmitter** bceOfDefOut);
|
||||
bool isAliasedName(BytecodeEmitter* bceOfDef, ParseNode* pn);
|
||||
bool computeDefinitionIsAliased(BytecodeEmitter* bceOfDef, Definition* dn, JSOp* op);
|
||||
@ -464,6 +472,8 @@ struct BytecodeEmitter
|
||||
MOZ_NEVER_INLINE bool emitFunction(ParseNode* pn, bool needsProto = false);
|
||||
MOZ_NEVER_INLINE bool emitObject(ParseNode* pn);
|
||||
|
||||
bool emitHoistedFunctionsInList(ParseNode* pn);
|
||||
|
||||
bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, PropListType type);
|
||||
|
||||
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
||||
|
@ -100,6 +100,11 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
MOZ_ASSERT(node->isArity(PN_BINARY));
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
case PNK_MODULE:
|
||||
*result = false;
|
||||
return true;
|
||||
@ -1783,6 +1788,9 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
||||
case PNK_FUNCTION:
|
||||
return FoldFunction(cx, pn, parser, inGenexpLambda);
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
return FoldFunction(cx, pn->pn_left, parser, inGenexpLambda);
|
||||
|
||||
case PNK_MODULE:
|
||||
return FoldModule(cx, pn, parser);
|
||||
|
||||
|
@ -444,24 +444,40 @@ class FullParseHandler
|
||||
return pn;
|
||||
}
|
||||
|
||||
template <typename PC>
|
||||
bool isFunctionStmt(ParseNode* stmt, PC* pc) {
|
||||
if (!pc->sc->strict()) {
|
||||
while (stmt->isKind(PNK_LABEL))
|
||||
stmt = stmt->as<LabeledStatement>().statement();
|
||||
}
|
||||
|
||||
return stmt->isKind(PNK_FUNCTION) || stmt->isKind(PNK_ANNEXB_FUNCTION);
|
||||
}
|
||||
|
||||
template <typename PC>
|
||||
void addStatementToList(ParseNode* list, ParseNode* stmt, PC* pc) {
|
||||
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
||||
|
||||
if (stmt->isKind(PNK_FUNCTION)) {
|
||||
if (pc->atBodyLevel()) {
|
||||
list->append(stmt);
|
||||
|
||||
if (isFunctionStmt(stmt, pc)) {
|
||||
// PNX_FUNCDEFS notifies the emitter that the block contains
|
||||
// body-level function definitions that should be processed
|
||||
// before the rest of nodes.
|
||||
list->pn_xflags |= PNX_FUNCDEFS;
|
||||
} else {
|
||||
// General deoptimization was done in Parser::functionDef.
|
||||
MOZ_ASSERT_IF(pc->sc->isFunctionBox(),
|
||||
pc->sc->asFunctionBox()->hasExtensibleScope());
|
||||
}
|
||||
}
|
||||
|
||||
list->append(stmt);
|
||||
template <typename PC>
|
||||
void addCaseStatementToList(ParseNode* list, ParseNode* casepn, PC* pc) {
|
||||
MOZ_ASSERT(list->isKind(PNK_STATEMENTLIST));
|
||||
MOZ_ASSERT(casepn->isKind(PNK_CASE));
|
||||
MOZ_ASSERT(casepn->pn_right->isKind(PNK_STATEMENTLIST));
|
||||
|
||||
list->append(casepn);
|
||||
|
||||
if (casepn->pn_right->pn_xflags & PNX_FUNCDEFS)
|
||||
list->pn_xflags |= PNX_FUNCDEFS;
|
||||
}
|
||||
|
||||
bool prependInitialYield(ParseNode* stmtList, ParseNode* genName) {
|
||||
@ -656,6 +672,11 @@ class FullParseHandler
|
||||
MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
|
||||
pn->pn_funbox = funbox;
|
||||
}
|
||||
ParseNode* newFunctionDefinitionForAnnexB(ParseNode* pn, ParseNode* assignment) {
|
||||
MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
|
||||
MOZ_ASSERT(assignment->isKind(PNK_ASSIGN) || assignment->isKind(PNK_VAR));
|
||||
return new_<BinaryNode>(PNK_ANNEXB_FUNCTION, JSOP_NOP, pos(), pn, assignment);
|
||||
}
|
||||
void addFunctionArgument(ParseNode* pn, ParseNode* argpn) {
|
||||
pn->pn_body->append(argpn);
|
||||
}
|
||||
@ -727,7 +748,7 @@ class FullParseHandler
|
||||
return node->isKind(PNK_SUPERBASE);
|
||||
}
|
||||
|
||||
inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op);
|
||||
inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init);
|
||||
inline void setLexicalDeclarationOp(ParseNode* pn, JSOp op);
|
||||
|
||||
void setBeginPosition(ParseNode* pn, ParseNode* oth) {
|
||||
@ -990,7 +1011,7 @@ FullParseHandler::setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseN
|
||||
}
|
||||
|
||||
inline bool
|
||||
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op)
|
||||
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
|
||||
{
|
||||
if (pn->isUsed()) {
|
||||
pn = makeAssignment(pn, init);
|
||||
|
@ -791,6 +791,12 @@ class NameResolver
|
||||
return false;
|
||||
break;
|
||||
|
||||
case PNK_ANNEXB_FUNCTION:
|
||||
MOZ_ASSERT(cur->isArity(PN_BINARY));
|
||||
if (!resolve(cur->pn_left, prefix))
|
||||
return false;
|
||||
break;
|
||||
|
||||
// Kinds that should be handled by parent node resolution.
|
||||
|
||||
case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST
|
||||
|
@ -283,7 +283,8 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
||||
case PNK_NEWTARGET:
|
||||
case PNK_SETTHIS:
|
||||
case PNK_FOR:
|
||||
case PNK_COMPREHENSIONFOR: {
|
||||
case PNK_COMPREHENSIONFOR:
|
||||
case PNK_ANNEXB_FUNCTION: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
stack->push(pn->pn_left);
|
||||
stack->push(pn->pn_right);
|
||||
|
@ -165,6 +165,7 @@ class PackedScopeCoordinate
|
||||
F(FORIN) \
|
||||
F(FOROF) \
|
||||
F(FORHEAD) \
|
||||
F(ANNEXB_FUNCTION) \
|
||||
F(ARGSBODY) \
|
||||
F(SPREAD) \
|
||||
F(MUTATEPROTO) \
|
||||
@ -271,6 +272,9 @@ IsDeleteKind(ParseNodeKind kind)
|
||||
* pn_scopecoord: hops and var index for function
|
||||
* pn_dflags: PND_* definition/use flags (see below)
|
||||
* pn_blockid: block id number
|
||||
* PNK_ANNEXB_FUNCTION binary pn_left: PNK_FUNCTION
|
||||
* pn_right: assignment for annex B semantics for
|
||||
* block-scoped function
|
||||
* PNK_ARGSBODY list list of formal parameters with
|
||||
* PNK_NAME node with non-empty name for
|
||||
* SingleNameBinding without Initializer
|
||||
@ -780,7 +784,8 @@ class ParseNode
|
||||
isOp(JSOP_DEFFUN) || // non-body-level function statement
|
||||
isOp(JSOP_NOP) || // body-level function stmt in global code
|
||||
isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
|
||||
isOp(JSOP_GETARG)); // body-level function redeclaring formal
|
||||
isOp(JSOP_GETARG) || // body-level function redeclaring formal
|
||||
isOp(JSOP_INITLEXICAL)); // block-level function stmt
|
||||
return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
|
||||
}
|
||||
|
||||
@ -1607,6 +1612,8 @@ struct Definition : public ParseNode
|
||||
IMPORT
|
||||
};
|
||||
|
||||
static bool test(const ParseNode& pn) { return pn.isDefn(); }
|
||||
|
||||
bool canHaveInitializer() { return int(kind()) <= int(ARG); }
|
||||
|
||||
static const char* kindString(Kind kind);
|
||||
@ -1615,6 +1622,8 @@ struct Definition : public ParseNode
|
||||
if (getKind() == PNK_FUNCTION) {
|
||||
if (isOp(JSOP_GETARG))
|
||||
return ARG;
|
||||
if (isOp(JSOP_INITLEXICAL))
|
||||
return LET;
|
||||
return VAR;
|
||||
}
|
||||
MOZ_ASSERT(getKind() == PNK_NAME);
|
||||
|
@ -103,9 +103,7 @@ ParseContext<FullParseHandler>::checkLocalsOverflow(TokenStream& ts)
|
||||
static void
|
||||
MarkUsesAsHoistedLexical(ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isDefn());
|
||||
|
||||
Definition* dn = (Definition*)pn;
|
||||
Definition* dn = &pn->as<Definition>();
|
||||
ParseNode** pnup = &dn->dn_uses;
|
||||
ParseNode* pnu;
|
||||
unsigned start = pn->pn_blockid;
|
||||
@ -218,6 +216,8 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
|
||||
MOZ_ASSERT(!pn->isUsed());
|
||||
MOZ_ASSERT_IF(pn->isDefn(), pn->isPlaceholder());
|
||||
|
||||
pn->setDefn(true);
|
||||
|
||||
Definition* prevDef = nullptr;
|
||||
if (kind == Definition::LET || kind == Definition::CONSTANT)
|
||||
prevDef = decls_.lookupFirst(name);
|
||||
@ -236,7 +236,7 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
|
||||
while ((pnu = *pnup) != nullptr && pnu->pn_blockid >= start) {
|
||||
MOZ_ASSERT(pnu->pn_blockid >= bodyid);
|
||||
MOZ_ASSERT(pnu->isUsed());
|
||||
pnu->pn_lexdef = (Definition*) pn;
|
||||
pnu->pn_lexdef = &pn->as<Definition>();
|
||||
pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
|
||||
pnup = &pnu->pn_link;
|
||||
}
|
||||
@ -254,12 +254,11 @@ ParseContext<FullParseHandler>::define(TokenStream& ts,
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(kind != Definition::LET && kind != Definition::CONSTANT, !lexdeps->lookup(name));
|
||||
pn->setDefn(true);
|
||||
pn->pn_dflags &= ~PND_PLACEHOLDER;
|
||||
if (kind == Definition::CONSTANT)
|
||||
pn->pn_dflags |= PND_CONST;
|
||||
|
||||
Definition* dn = (Definition*)pn;
|
||||
Definition* dn = &pn->as<Definition>();
|
||||
switch (kind) {
|
||||
case Definition::ARG:
|
||||
MOZ_ASSERT(sc->isFunctionBox());
|
||||
@ -389,7 +388,7 @@ ParseContext<ParseHandler>::updateDecl(TokenStream& ts, JSAtom* atom, Node pn)
|
||||
Definition* oldDecl = decls_.lookupFirst(atom);
|
||||
|
||||
pn->setDefn(true);
|
||||
Definition* newDecl = (Definition*)pn;
|
||||
Definition* newDecl = &pn->template as<Definition>();
|
||||
decls_.updateFirst(atom, newDecl);
|
||||
|
||||
if (sc->isGlobalContext() || oldDecl->isDeoptimized()) {
|
||||
@ -1195,18 +1194,6 @@ template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkFunctionArguments()
|
||||
{
|
||||
/*
|
||||
* Non-top-level functions use JSOP_DEFFUN which is a dynamic scope
|
||||
* operation which means it aliases any bindings with the same name.
|
||||
*/
|
||||
if (FuncStmtSet* set = pc->funcStmts) {
|
||||
for (FuncStmtSet::Range r = set->all(); !r.empty(); r.popFront()) {
|
||||
PropertyName* name = r.front()->asPropertyName();
|
||||
if (Definition* dn = pc->decls().lookupFirst(name))
|
||||
dn->pn_dflags |= PND_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time to implement the odd semantics of 'arguments'. */
|
||||
HandlePropertyName arguments = context->names().arguments;
|
||||
|
||||
@ -1401,7 +1388,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
|
||||
for (ParseNode* pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
|
||||
MOZ_ASSERT(pnu->isUsed());
|
||||
MOZ_ASSERT(!pnu->isDefn());
|
||||
pnu->pn_lexdef = (Definition*) pn;
|
||||
pnu->pn_lexdef = &pn->as<Definition>();
|
||||
pn->pn_dflags |= pnu->pn_dflags & PND_USE2DEF_FLAGS;
|
||||
}
|
||||
pn->pn_dflags |= dn->pn_dflags & PND_USE2DEF_FLAGS;
|
||||
@ -1444,7 +1431,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
|
||||
return false;
|
||||
pn->dn_uses = lhs;
|
||||
dn->pn_link = nullptr;
|
||||
dn = (Definition*) lhs;
|
||||
dn = &lhs->as<Definition>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1455,7 +1442,7 @@ Parser<FullParseHandler>::makeDefIntoUse(Definition* dn, ParseNode* pn, HandleAt
|
||||
dn->setOp((CodeSpec[dn->getOp()].format & JOF_SET) ? JSOP_SETNAME : JSOP_GETNAME);
|
||||
dn->setDefn(false);
|
||||
dn->setUsed(true);
|
||||
dn->pn_lexdef = (Definition*) pn;
|
||||
dn->pn_lexdef = &pn->as<Definition>();
|
||||
dn->pn_scopecoord.makeFree();
|
||||
dn->pn_dflags &= ~PND_BOUND;
|
||||
return true;
|
||||
@ -1487,18 +1474,22 @@ struct BindData
|
||||
void initLexical(VarContext varContext, JSOp op, StaticBlockObject* blockObj,
|
||||
unsigned overflow)
|
||||
{
|
||||
init(LexicalBinding, op, op == JSOP_DEFCONST);
|
||||
init(LexicalBinding, op, op == JSOP_DEFCONST, false);
|
||||
letData_.varContext = varContext;
|
||||
letData_.blockObj = blockObj;
|
||||
letData_.overflow = overflow;
|
||||
}
|
||||
|
||||
void initVar(JSOp op) {
|
||||
init(VarBinding, op, false);
|
||||
init(VarBinding, op, false, false);
|
||||
}
|
||||
|
||||
void initAnnexBVar() {
|
||||
init(VarBinding, JSOP_DEFVAR, false, true);
|
||||
}
|
||||
|
||||
void initDestructuring(JSOp op) {
|
||||
init(DestructuringBinding, op, false);
|
||||
init(DestructuringBinding, op, false, false);
|
||||
}
|
||||
|
||||
void setNameNode(typename ParseHandler::Node pn) {
|
||||
@ -1521,6 +1512,11 @@ struct BindData
|
||||
return isConst_;
|
||||
}
|
||||
|
||||
bool isAnnexB() {
|
||||
MOZ_ASSERT(isInitialized());
|
||||
return isAnnexB_;
|
||||
}
|
||||
|
||||
const LetData& letData() {
|
||||
MOZ_ASSERT(kind_ == LexicalBinding);
|
||||
return letData_;
|
||||
@ -1557,17 +1553,19 @@ struct BindData
|
||||
|
||||
JSOp op_; // Prologue bytecode or nop.
|
||||
bool isConst_; // Whether this is a const binding.
|
||||
bool isAnnexB_; // Whether this is a synthesized 'var' binding for Annex B.3.
|
||||
LetData letData_;
|
||||
|
||||
bool isInitialized() {
|
||||
return kind_ != Uninitialized;
|
||||
}
|
||||
|
||||
void init(BindingKind kind, JSOp op, bool isConst) {
|
||||
void init(BindingKind kind, JSOp op, bool isConst, bool isAnnexB) {
|
||||
MOZ_ASSERT(!isInitialized());
|
||||
kind_ = kind;
|
||||
op_ = op;
|
||||
isConst_ = isConst;
|
||||
isAnnexB_ = isAnnexB;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2233,17 +2231,13 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
ParseNode** pn_, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed)
|
||||
Parser<FullParseHandler>::bindBodyLevelFunctionName(HandlePropertyName funName,
|
||||
ParseNode** pn_)
|
||||
{
|
||||
MOZ_ASSERT(pc->atBodyLevel() || !pc->sc->strict());
|
||||
|
||||
ParseNode*& pn = *pn_;
|
||||
*pbodyProcessed = false;
|
||||
|
||||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (kind == Statement) {
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
* function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
|
||||
@ -2252,20 +2246,8 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
MOZ_ASSERT(!dn->isUsed());
|
||||
MOZ_ASSERT(dn->isDefn());
|
||||
|
||||
bool throwRedeclarationError = dn->kind() == Definition::CONSTANT ||
|
||||
dn->kind() == Definition::LET;
|
||||
if (options().extraWarningsOption || throwRedeclarationError) {
|
||||
JSAutoByteString name;
|
||||
ParseReportKind reporter = throwRedeclarationError
|
||||
? ParseError
|
||||
: ParseExtraWarning;
|
||||
if (!AtomToPrintableString(context, funName, &name) ||
|
||||
!report(reporter, false, nullptr, JSMSG_REDECLARED_VAR,
|
||||
Definition::kindString(dn->kind()), name.ptr()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (dn->kind() == Definition::CONSTANT || dn->kind() == Definition::LET)
|
||||
return reportRedeclaration(nullptr, Definition::VAR, funName);
|
||||
|
||||
/*
|
||||
* Body-level function statements are effectively variable
|
||||
@ -2275,7 +2257,6 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
* the function's binding (which is mutable), so turn any existing
|
||||
* declaration into a use.
|
||||
*/
|
||||
if (bodyLevel) {
|
||||
if (dn->kind() == Definition::ARG) {
|
||||
// The exception to the above comment is when the function
|
||||
// has the same name as an argument. Then the argument node
|
||||
@ -2291,8 +2272,7 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
if (!makeDefIntoUse(dn, pn, funName))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (bodyLevel) {
|
||||
} else {
|
||||
/*
|
||||
* If this function was used before it was defined, claim the
|
||||
* pre-created definition node for this function that primaryExpr
|
||||
@ -2317,52 +2297,147 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bodyLevel) {
|
||||
MOZ_ASSERT(pn->functionIsHoisted());
|
||||
MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree());
|
||||
} else {
|
||||
/*
|
||||
* As a SpiderMonkey-specific extension, non-body-level function
|
||||
* statements (e.g., functions in an "if" or "while" block) are
|
||||
* dynamically bound when control flow reaches the statement.
|
||||
*/
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
MOZ_ASSERT(pn->pn_scopecoord.isFree());
|
||||
if (pc->sc->isFunctionBox()) {
|
||||
FunctionBox* funbox = pc->sc->asFunctionBox();
|
||||
funbox->setMightAliasLocals();
|
||||
funbox->setHasExtensibleScope();
|
||||
}
|
||||
pn->setOp(JSOP_DEFFUN);
|
||||
|
||||
/*
|
||||
* Instead of setting bindingsAccessedDynamically, which would be
|
||||
* overly conservative, remember the names of all function
|
||||
* statements and mark any bindings with the same as aliased at the
|
||||
* end of functionBody.
|
||||
*/
|
||||
if (!pc->funcStmts) {
|
||||
pc->funcStmts = alloc.new_<FuncStmtSet>(alloc);
|
||||
if (!pc->funcStmts || !pc->funcStmts->init()) {
|
||||
ReportOutOfMemory(context);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!pc->funcStmts->put(funName))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Due to the implicit declaration mechanism, 'arguments' will not
|
||||
* have decls and, even if it did, they will not be noted as closed
|
||||
* in the emitter. Thus, in the corner case of function statements
|
||||
* overridding arguments, flag the whole scope as dynamic.
|
||||
*/
|
||||
if (funName == context->names().arguments)
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
}
|
||||
|
||||
/* No further binding (in BindNameToSlot) is needed for functions. */
|
||||
pn->pn_dflags |= PND_BOUND;
|
||||
|
||||
MOZ_ASSERT(pn->functionIsHoisted());
|
||||
MOZ_ASSERT(pc->sc->isGlobalContext() == pn->pn_scopecoord.isFree());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindLexicalFunctionName(HandlePropertyName funName,
|
||||
ParseNode* pn);
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
ParseNode** pn_, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed,
|
||||
ParseNode** assignmentForAnnexBOut)
|
||||
{
|
||||
ParseNode*& pn = *pn_;
|
||||
*pbodyProcessed = false;
|
||||
|
||||
if (kind == Statement) {
|
||||
MOZ_ASSERT(assignmentForAnnexBOut);
|
||||
*assignmentForAnnexBOut = nullptr;
|
||||
|
||||
// In sloppy mode, ES6 Annex B.3.2 allows labelled function
|
||||
// declarations. Otherwise it is a parse error.
|
||||
bool bodyLevelFunction = pc->atBodyLevel();
|
||||
if (!bodyLevelFunction) {
|
||||
StmtInfoPC* stmt = pc->innermostStmt();
|
||||
if (stmt->type == StmtType::LABEL) {
|
||||
if (pc->sc->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_FUNCTION_LABEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt = pc->innermostNonLabelStmt();
|
||||
// A switch statement is always braced, so it's okay to label
|
||||
// functions in sloppy mode under switch.
|
||||
if (stmt && stmt->type != StmtType::BLOCK && stmt->type != StmtType::SWITCH) {
|
||||
report(ParseError, false, null(), JSMSG_SLOPPY_FUNCTION_LABEL);
|
||||
return false;
|
||||
}
|
||||
|
||||
bodyLevelFunction = pc->atBodyLevel(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
if (bodyLevelFunction) {
|
||||
if (!bindBodyLevelFunctionName(funName, pn_))
|
||||
return false;
|
||||
} else {
|
||||
Definition* annexDef = nullptr;
|
||||
Node synthesizedDeclarationList = null();
|
||||
|
||||
if (!pc->sc->strict()) {
|
||||
// Under non-strict mode, try ES6 Annex B.3.3 semantics. If
|
||||
// making an additional 'var' binding of the same name does
|
||||
// not throw an early error, do so. This 'var' binding would
|
||||
// be assigned the function object when its declaration is
|
||||
// reached, not at the start of the block.
|
||||
|
||||
annexDef = pc->decls().lookupFirst(funName);
|
||||
if (annexDef) {
|
||||
if (annexDef->kind() == Definition::CONSTANT ||
|
||||
annexDef->kind() == Definition::LET)
|
||||
{
|
||||
// Do not emit Annex B assignment if we would've
|
||||
// thrown a redeclaration error.
|
||||
annexDef = nullptr;
|
||||
}
|
||||
} else {
|
||||
// Synthesize a new 'var' binding if one does not exist.
|
||||
ParseNode* varNode = newBindingNode(funName, /* functionScope = */ true);
|
||||
if (!varNode)
|
||||
return false;
|
||||
|
||||
// Treat the 'var' binding as body level. Otherwise the
|
||||
// lexical binding of the function name below would result
|
||||
// in a redeclaration. That is,
|
||||
// { var x; let x; } is an early error.
|
||||
// var x; { let x; } is not.
|
||||
varNode->pn_blockid = pc->bodyid;
|
||||
|
||||
BindData<FullParseHandler> data(context);
|
||||
data.initAnnexBVar();
|
||||
data.setNameNode(varNode);
|
||||
if (!data.bind(funName, this))
|
||||
return false;
|
||||
|
||||
annexDef = &varNode->as<Definition>();
|
||||
|
||||
synthesizedDeclarationList = handler.newDeclarationList(PNK_VAR, JSOP_DEFVAR);
|
||||
if (!synthesizedDeclarationList)
|
||||
return false;
|
||||
handler.addList(synthesizedDeclarationList, annexDef);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bindLexicalFunctionName(funName, pn))
|
||||
return false;
|
||||
|
||||
if (annexDef) {
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
|
||||
// Synthesize an assignment assigning the lexical name to the
|
||||
// 'var' name for Annex B.
|
||||
|
||||
ParseNode* rhs = newName(funName);
|
||||
if (!rhs)
|
||||
return false;
|
||||
if (!noteNameUse(funName, rhs))
|
||||
return false;
|
||||
|
||||
// If we synthesized a new definition, emit the declaration to
|
||||
// ensure DEFVAR is correctly emitted in global scripts.
|
||||
// Otherwise, synthesize a simple assignment and emit that.
|
||||
if (synthesizedDeclarationList) {
|
||||
if (!handler.finishInitializerAssignment(annexDef, rhs))
|
||||
return false;
|
||||
*assignmentForAnnexBOut = synthesizedDeclarationList;
|
||||
} else {
|
||||
ParseNode* lhs = newName(funName);
|
||||
if (!lhs)
|
||||
return false;
|
||||
lhs->setOp(JSOP_SETNAME);
|
||||
|
||||
// Manually link up the LHS with the non-lexical definition.
|
||||
handler.linkUseToDef(lhs, annexDef);
|
||||
|
||||
ParseNode* assign = handler.newAssignment(PNK_ASSIGN, lhs, rhs, pc, JSOP_NOP);
|
||||
if (!assign)
|
||||
return false;
|
||||
|
||||
*assignmentForAnnexBOut = assign;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* A function expression does not introduce any binding. */
|
||||
pn->setOp(kind == Arrow ? JSOP_LAMBDA_ARROW : JSOP_LAMBDA);
|
||||
@ -2471,7 +2546,8 @@ template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
Node* pn, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed)
|
||||
bool* pbodyProcessed,
|
||||
Node* assignmentForAnnexBOut)
|
||||
{
|
||||
*pbodyProcessed = false;
|
||||
|
||||
@ -2479,10 +2555,18 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (kind == Statement) {
|
||||
*assignmentForAnnexBOut = null();
|
||||
|
||||
if (!bodyLevel) {
|
||||
// Block-scoped functions cannot yet be parsed lazily.
|
||||
return abortIfSyntaxParser();
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
* function (thereby avoiding JSOP_DEFFUN and dynamic name lookup).
|
||||
*/
|
||||
|
||||
if (DefinitionNode dn = pc->decls().lookupFirst(funName)) {
|
||||
if (dn == Definition::CONSTANT || dn == Definition::LET) {
|
||||
JSAutoByteString name;
|
||||
@ -2493,16 +2577,13 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (bodyLevel) {
|
||||
} else {
|
||||
if (pc->lexdeps.lookupDefn<SyntaxParseHandler>(funName))
|
||||
pc->lexdeps->remove(funName);
|
||||
|
||||
if (!pc->define(tokenStream, funName, *pn, Definition::VAR))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bodyLevel && funName == context->names().arguments)
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
}
|
||||
|
||||
if (kind == Arrow) {
|
||||
@ -2583,7 +2664,8 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHandling,
|
||||
HandlePropertyName funName, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind, InvokedPrediction invoked)
|
||||
GeneratorKind generatorKind, InvokedPrediction invoked,
|
||||
Node* assignmentForAnnexBOut)
|
||||
{
|
||||
MOZ_ASSERT_IF(kind == Statement, funName);
|
||||
|
||||
@ -2591,12 +2673,13 @@ Parser<ParseHandler>::functionDef(InHandling inHandling, YieldHandling yieldHand
|
||||
Node pn = handler.newFunctionDefinition();
|
||||
if (!pn)
|
||||
return null();
|
||||
handler.setBlockId(pn, pc->blockid());
|
||||
|
||||
if (invoked)
|
||||
pn = handler.setLikelyIIFE(pn);
|
||||
|
||||
bool bodyProcessed;
|
||||
if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
|
||||
if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed, assignmentForAnnexBOut))
|
||||
return null();
|
||||
|
||||
if (bodyProcessed)
|
||||
@ -2783,7 +2866,6 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
||||
if (!addFreeVariablesFromLazyFunction(fun, pc))
|
||||
return false;
|
||||
|
||||
pn->pn_blockid = outerpc->blockid();
|
||||
PropagateTransitiveParseFlags(funbox, outerpc->sc);
|
||||
return true;
|
||||
} while (false);
|
||||
@ -2801,8 +2883,6 @@ Parser<FullParseHandler>::functionArgsAndBody(InHandling inHandling, ParseNode*
|
||||
if (!leaveFunction(pn, outerpc, kind))
|
||||
return false;
|
||||
|
||||
pn->pn_blockid = outerpc->blockid();
|
||||
|
||||
/*
|
||||
* Fruit of the poisonous tree: if a closure contains a dynamic name access
|
||||
* (eval, with, etc), we consider the parent to do the same. The reason is
|
||||
@ -3044,6 +3124,24 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
// ES6 Annex B.3.4 says we can parse function declarations unbraced under if or
|
||||
// else as if it were braced. That is, |if (x) function f() {}| is parsed as
|
||||
// |if (x) { function f() {} }|.
|
||||
Maybe<AutoPushStmtInfoPC> synthesizedStmtInfoForAnnexB;
|
||||
Node synthesizedBlockForAnnexB = null();
|
||||
StmtInfoPC *stmt = pc->innermostStmt();
|
||||
if (!pc->sc->strict() && stmt) {
|
||||
if (stmt->type == StmtType::IF || stmt->type == StmtType::ELSE) {
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
synthesizedStmtInfoForAnnexB.emplace(*this, StmtType::BLOCK);
|
||||
synthesizedBlockForAnnexB = pushLexicalScope(*synthesizedStmtInfoForAnnexB);
|
||||
if (!synthesizedBlockForAnnexB)
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
RootedPropertyName name(context);
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt;
|
||||
@ -3071,12 +3169,35 @@ Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling
|
||||
return null();
|
||||
}
|
||||
|
||||
/* We forbid function statements in strict mode code. */
|
||||
if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
|
||||
!report(ParseStrictError, pc->sc->strict(), null(), JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
Node assignmentForAnnexB;
|
||||
Node fun = functionDef(InAllowed, yieldHandling, name, Statement, generatorKind,
|
||||
PredictUninvoked, &assignmentForAnnexB);
|
||||
if (!fun)
|
||||
return null();
|
||||
|
||||
return functionDef(InAllowed, yieldHandling, name, Statement, generatorKind);
|
||||
if (assignmentForAnnexB) {
|
||||
fun = handler.newFunctionDefinitionForAnnexB(fun, assignmentForAnnexB);
|
||||
if (!fun)
|
||||
return null();
|
||||
}
|
||||
|
||||
// Note that we may have synthesized a block for Annex B.3.4 without
|
||||
// having synthesized an assignment for Annex B.3.3, e.g.,
|
||||
//
|
||||
// let f = 1;
|
||||
// {
|
||||
// if (1) function f() {}
|
||||
// }
|
||||
if (synthesizedBlockForAnnexB) {
|
||||
Node body = handler.newStatementList(pc->blockid(), handler.getPosition(fun));
|
||||
if (!body)
|
||||
return null();
|
||||
handler.addStatementToList(body, fun, pc);
|
||||
handler.setLexicalScopeBody(synthesizedBlockForAnnexB, body);
|
||||
return synthesizedBlockForAnnexB;
|
||||
}
|
||||
|
||||
return fun;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -3680,19 +3801,24 @@ Parser<ParseHandler>::bindVar(BindData<ParseHandler>* data,
|
||||
StmtInfoPC* stmt = LexicalLookup(pc, name);
|
||||
|
||||
if (stmt && stmt->type == StmtType::WITH) {
|
||||
// Do not deoptimize if we are binding a synthesized 'var' binding for
|
||||
// Annex B.3.3, which states that the synthesized binding is to go on
|
||||
// the nearest VariableEnvironment. Deoptimizing here would
|
||||
// erroneously emit NAME ops when assigning to the Annex B 'var'.
|
||||
if (!data->isAnnexB()) {
|
||||
parser->handler.setFlag(pn, PND_DEOPTIMIZED);
|
||||
if (pc->sc->isFunctionBox()) {
|
||||
FunctionBox* funbox = pc->sc->asFunctionBox();
|
||||
funbox->setMightAliasLocals();
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure to indicate the need to deoptimize the script's arguments
|
||||
* object. Mark the function as if it contained a debugger statement,
|
||||
* which will deoptimize arguments as much as possible.
|
||||
*/
|
||||
// Make sure to indicate the need to deoptimize the script's
|
||||
// arguments object. Mark the function as if it contained a
|
||||
// debugger statement, which will deoptimize arguments as much as
|
||||
// possible.
|
||||
if (name == cx->names().arguments)
|
||||
pc->sc->setHasDebuggerStatement();
|
||||
}
|
||||
|
||||
// Find the nearest enclosing non-with scope that defined name, if
|
||||
// any, for redeclaration checks below.
|
||||
@ -3827,23 +3953,27 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn)
|
||||
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, HandlePropertyName name,
|
||||
ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_NAME));
|
||||
|
||||
RootedPropertyName name(context, pn->pn_atom->asPropertyName());
|
||||
|
||||
data->setNameNode(pn);
|
||||
if (!data->bind(name, this))
|
||||
return false;
|
||||
return true;
|
||||
return data->bind(name, this);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, ParseNode* pn)
|
||||
Parser<FullParseHandler>::bindUninitialized(BindData<FullParseHandler>* data, ParseNode* pn)
|
||||
{
|
||||
if (!bindUninitialized(data, pn))
|
||||
RootedPropertyName name(context, pn->name());
|
||||
return bindUninitialized(data, name, pn);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, HandlePropertyName name,
|
||||
ParseNode* pn)
|
||||
{
|
||||
if (!bindUninitialized(data, name, pn))
|
||||
return false;
|
||||
|
||||
/*
|
||||
@ -3864,6 +3994,14 @@ Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, Pars
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler>* data, ParseNode* pn)
|
||||
{
|
||||
RootedPropertyName name(context, pn->name());
|
||||
return bindInitialized(data, name, pn);
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkDestructuringName(BindData<FullParseHandler>* data, ParseNode* expr)
|
||||
@ -4412,7 +4550,7 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
|
||||
if (!bindBeforeInitializer && !data.bind(name, this))
|
||||
return null();
|
||||
|
||||
if (!handler.finishInitializerAssignment(pn2, init, data.op()))
|
||||
if (!handler.finishInitializerAssignment(pn2, init))
|
||||
return null();
|
||||
}
|
||||
}
|
||||
@ -4433,28 +4571,45 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& errorPos)
|
||||
Parser<FullParseHandler>::checkAndPrepareLexical(PrepareLexicalKind prepareWhat,
|
||||
const TokenPos& errorPos)
|
||||
{
|
||||
/*
|
||||
* This is a lexical declaration. We must be directly under a block per the
|
||||
* proposed ES4 specs, but not an implicit block created due to
|
||||
* 'for (let ...)'. If we pass this error test, make the enclosing
|
||||
* StmtInfoPC be our scope. Further let declarations in this block will
|
||||
* find this scope statement and use the same block object.
|
||||
* This is a lexical declaration. We must be directly under a block for
|
||||
* 'let' and 'const' declarations. If we pass this error test, make the
|
||||
* enclosing StmtInfoPC be our scope. Further let declarations in this
|
||||
* block will find this scope statement and use the same block object.
|
||||
*
|
||||
* Function declarations behave like 'let', except that they are allowed
|
||||
* per ES6 Annex B.3.2 to be labeled, unlike plain 'let' and 'const'
|
||||
* declarations.
|
||||
*
|
||||
* If we are the first let declaration in this block (i.e., when the
|
||||
* enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
|
||||
* we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
|
||||
*/
|
||||
StmtInfoPC* stmt = pc->innermostStmt();
|
||||
|
||||
// ES6 Annex B.3.2 does not apply in strict mode, and labeled functions in
|
||||
// strict mode should have been rejected by checkFunctionDefinition.
|
||||
MOZ_ASSERT_IF(pc->innermostStmt() &&
|
||||
pc->innermostStmt()->type == StmtType::LABEL &&
|
||||
prepareWhat == PrepareFunction,
|
||||
!pc->sc->strict());
|
||||
|
||||
StmtInfoPC* stmt = prepareWhat == PrepareFunction
|
||||
? pc->innermostNonLabelStmt()
|
||||
: pc->innermostStmt();
|
||||
if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
|
||||
reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
|
||||
isConst ? "const" : "lexical");
|
||||
reportWithOffset(ParseError, false, errorPos.begin,
|
||||
stmt->type == StmtType::LABEL
|
||||
? JSMSG_LEXICAL_DECL_LABEL
|
||||
: JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
|
||||
prepareWhat == PrepareConst ? "const" : "lexical");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt) {
|
||||
MOZ_ASSERT(pc->atBodyLevel());
|
||||
MOZ_ASSERT_IF(prepareWhat != PrepareFunction, pc->atBodyLevel());
|
||||
|
||||
/*
|
||||
* Self-hosted code must be usable against *any* global object,
|
||||
@ -4465,7 +4620,7 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
|
||||
bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->innermostScopeStmt();
|
||||
if (options().selfHostingMode && isGlobal) {
|
||||
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
|
||||
isConst ? "'const'" : "'let'");
|
||||
prepareWhat == PrepareConst ? "'const'" : "'let'");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -4491,8 +4646,12 @@ Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos& e
|
||||
* catch block (catch is a lexical scope by definition).
|
||||
*/
|
||||
MOZ_ASSERT(stmt->canBeBlockScope() && stmt->type != StmtType::CATCH);
|
||||
|
||||
if (prepareWhat == PrepareFunction) {
|
||||
stmt->isBlockScope = true;
|
||||
pc->stmtStack.linkAsInnermostScopeStmt(stmt, *blockObj);
|
||||
} else {
|
||||
pc->stmtStack.makeInnermostLexicalScope(*blockObj);
|
||||
}
|
||||
MOZ_ASSERT(!blockScopes[stmt->blockid]);
|
||||
blockScopes[stmt->blockid].set(blockObj);
|
||||
|
||||
@ -4522,33 +4681,54 @@ CurrentLexicalStaticBlock(ParseContext<FullParseHandler>* pc)
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst,
|
||||
bool
|
||||
Parser<FullParseHandler>::prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
ParseNode* pn,
|
||||
const TokenPos& pos)
|
||||
{
|
||||
BindData<FullParseHandler> data(context);
|
||||
if (!checkAndPrepareLexical(isConst, pos))
|
||||
return null();
|
||||
data.initLexical(HoistVars, isConst ? JSOP_DEFCONST : JSOP_DEFLET,
|
||||
if (!checkAndPrepareLexical(prepareWhat, pos))
|
||||
return false;
|
||||
data.initLexical(HoistVars, prepareWhat == PrepareConst ? JSOP_DEFCONST : JSOP_DEFLET,
|
||||
CurrentLexicalStaticBlock(pc), JSMSG_TOO_MANY_LOCALS);
|
||||
return bindInitialized(&data, name, pn);
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
const TokenPos& pos)
|
||||
{
|
||||
ParseNode* dn = newBindingNode(name, false);
|
||||
if (!dn)
|
||||
return null();
|
||||
handler.setPosition(dn, pos);
|
||||
|
||||
if (!bindInitialized(&data, dn))
|
||||
if (!prepareAndBindInitializedLexicalWithNode(name, prepareWhat, dn, pos))
|
||||
return null();
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindLexicalFunctionName(HandlePropertyName funName,
|
||||
ParseNode* pn)
|
||||
{
|
||||
MOZ_ASSERT(!pc->atBodyLevel());
|
||||
pn->pn_blockid = pc->blockid();
|
||||
return prepareAndBindInitializedLexicalWithNode(funName, PrepareFunction, pn, pos());
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode*
|
||||
Parser<FullParseHandler>::lexicalDeclaration(YieldHandling yieldHandling, bool isConst)
|
||||
{
|
||||
handler.disableSyntaxParser();
|
||||
|
||||
if (!checkAndPrepareLexical(isConst, pos()))
|
||||
if (!checkAndPrepareLexical(isConst ? PrepareConst : PrepareLet, pos()))
|
||||
return null();
|
||||
|
||||
/*
|
||||
@ -5060,7 +5240,7 @@ Parser<FullParseHandler>::exportDeclaration()
|
||||
default:
|
||||
tokenStream.ungetToken();
|
||||
RootedPropertyName name(context, context->names().starDefaultStar);
|
||||
binding = makeInitializedLexicalBinding(name, true, pos());
|
||||
binding = makeInitializedLexicalBinding(name, PrepareConst, pos());
|
||||
if (!binding)
|
||||
return null();
|
||||
kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
|
||||
@ -5807,7 +5987,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
||||
afterReturn = true;
|
||||
}
|
||||
}
|
||||
handler.addList(body, stmt);
|
||||
handler.addStatementToList(body, stmt, pc);
|
||||
}
|
||||
|
||||
// In ES6, lexical bindings cannot be accessed until initialized. If
|
||||
@ -5826,7 +6006,7 @@ Parser<ParseHandler>::switchStatement(YieldHandling yieldHandling)
|
||||
Node casepn = handler.newCaseOrDefault(caseBegin, caseExpr, body);
|
||||
if (!casepn)
|
||||
return null();
|
||||
handler.addList(caseList, casepn);
|
||||
handler.addCaseStatementToList(caseList, casepn, pc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6653,7 +6833,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
ParseNode* nameNode = null();
|
||||
ParseNode* methodsOrBlock = classMethods;
|
||||
if (name) {
|
||||
ParseNode* innerBinding = makeInitializedLexicalBinding(name, true, namePos);
|
||||
ParseNode* innerBinding = makeInitializedLexicalBinding(name, PrepareConst, namePos);
|
||||
if (!innerBinding)
|
||||
return null();
|
||||
|
||||
@ -6664,7 +6844,7 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
|
||||
|
||||
ParseNode* outerBinding = null();
|
||||
if (classContext == ClassStatement) {
|
||||
outerBinding = makeInitializedLexicalBinding(name, false, namePos);
|
||||
outerBinding = makeInitializedLexicalBinding(name, PrepareLet, namePos);
|
||||
if (!outerBinding)
|
||||
return null();
|
||||
}
|
||||
|
@ -53,7 +53,6 @@ struct StmtInfoPC : public StmtInfoBase
|
||||
{}
|
||||
};
|
||||
|
||||
typedef HashSet<JSAtom*, DefaultHasher<JSAtom*>, LifoAllocPolicy<Fallible>> FuncStmtSet;
|
||||
class SharedContext;
|
||||
|
||||
typedef Vector<Definition*, 16> DeclVector;
|
||||
@ -236,10 +235,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
public:
|
||||
OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */
|
||||
|
||||
FuncStmtSet* funcStmts; /* Set of (non-top-level) function statements
|
||||
that will alias any top-level bindings with
|
||||
the same name. */
|
||||
|
||||
// All inner functions in this context. Only filled in when parsing syntax.
|
||||
Rooted<TraceableVector<JSFunction*>> innerFunctions;
|
||||
|
||||
@ -277,7 +272,6 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
parserPC(&prs->pc),
|
||||
oldpc(prs->pc),
|
||||
lexdeps(prs->context),
|
||||
funcStmts(nullptr),
|
||||
innerFunctions(prs->context, TraceableVector<JSFunction*>(prs->context)),
|
||||
newDirectives(newDirectives),
|
||||
inDeclDestructuring(false)
|
||||
@ -295,6 +289,7 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
|
||||
StmtInfoPC* innermostStmt() const { return stmtStack.innermost(); }
|
||||
StmtInfoPC* innermostScopeStmt() const { return stmtStack.innermostScopeStmt(); }
|
||||
StmtInfoPC* innermostNonLabelStmt() const { return stmtStack.innermostNonLabel(); }
|
||||
JSObject* innermostStaticScope() const {
|
||||
if (StmtInfoPC* stmt = innermostScopeStmt())
|
||||
return stmt->staticScope;
|
||||
@ -308,19 +303,23 @@ struct MOZ_STACK_CLASS ParseContext : public GenericParseContext
|
||||
// function f1() { function f2() { } }
|
||||
// if (cond) { function f3() { if (cond) { function f4() { } } } }
|
||||
//
|
||||
bool atBodyLevel() {
|
||||
bool atBodyLevel(StmtInfoPC* stmt) {
|
||||
// 'eval' and non-syntactic scripts are always under an invisible
|
||||
// lexical scope, but since it is not syntactic, it should still be
|
||||
// considered at body level.
|
||||
if (sc->staticScope()->is<StaticEvalObject>()) {
|
||||
bool bl = !innermostStmt()->enclosing;
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, innermostStmt()->staticScope
|
||||
bool bl = !stmt->enclosing;
|
||||
MOZ_ASSERT_IF(bl, stmt->type == StmtType::BLOCK);
|
||||
MOZ_ASSERT_IF(bl, stmt->staticScope
|
||||
->template as<StaticBlockObject>()
|
||||
.enclosingStaticScope() == sc->staticScope());
|
||||
return bl;
|
||||
}
|
||||
return !innermostStmt();
|
||||
return !stmt;
|
||||
}
|
||||
|
||||
bool atBodyLevel() {
|
||||
return atBodyLevel(innermostStmt());
|
||||
}
|
||||
|
||||
bool atGlobalLevel() {
|
||||
@ -728,7 +727,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
|
||||
Node functionDef(InHandling inHandling, YieldHandling uieldHandling, HandlePropertyName name,
|
||||
FunctionSyntaxKind kind, GeneratorKind generatorKind,
|
||||
InvokedPrediction invoked = PredictUninvoked);
|
||||
InvokedPrediction invoked = PredictUninvoked,
|
||||
Node* assignmentForAnnexBOut = nullptr);
|
||||
bool functionArgsAndBody(InHandling inHandling, Node pn, HandleFunction fun,
|
||||
FunctionSyntaxKind kind, GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives, Directives* newDirectives);
|
||||
@ -794,8 +794,10 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node newThisName();
|
||||
|
||||
bool makeDefIntoUse(Definition* dn, Node pn, HandleAtom atom);
|
||||
bool bindLexicalFunctionName(HandlePropertyName funName, ParseNode* pn);
|
||||
bool bindBodyLevelFunctionName(HandlePropertyName funName, ParseNode** pn);
|
||||
bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
|
||||
bool* pbodyProcessed);
|
||||
bool* pbodyProcessed, Node* assignmentForAnnexBOut);
|
||||
bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
|
||||
bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
|
||||
|
||||
@ -844,8 +846,17 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
|
||||
Node objectLiteral(YieldHandling yieldHandling);
|
||||
|
||||
bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos);
|
||||
Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos);
|
||||
enum PrepareLexicalKind {
|
||||
PrepareLet,
|
||||
PrepareConst,
|
||||
PrepareFunction
|
||||
};
|
||||
bool checkAndPrepareLexical(PrepareLexicalKind prepareWhat, const TokenPos& errorPos);
|
||||
bool prepareAndBindInitializedLexicalWithNode(HandlePropertyName name,
|
||||
PrepareLexicalKind prepareWhat,
|
||||
ParseNode* pn, const TokenPos& pos);
|
||||
Node makeInitializedLexicalBinding(HandlePropertyName name, PrepareLexicalKind prepareWhat,
|
||||
const TokenPos& pos);
|
||||
|
||||
Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars);
|
||||
|
||||
@ -860,7 +871,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
bool checkDestructuringObject(BindData<ParseHandler>* data, Node objectPattern);
|
||||
bool checkDestructuringName(BindData<ParseHandler>* data, Node expr);
|
||||
|
||||
bool bindInitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn);
|
||||
bool bindInitialized(BindData<ParseHandler>* data, Node pn);
|
||||
bool bindUninitialized(BindData<ParseHandler>* data, HandlePropertyName name, Node pn);
|
||||
bool bindUninitialized(BindData<ParseHandler>* data, Node pn);
|
||||
bool makeSetCall(Node node, unsigned errnum);
|
||||
Node cloneDestructuringDefault(Node opn);
|
||||
|
@ -580,6 +580,12 @@ class MOZ_STACK_CLASS StmtInfoStack
|
||||
|
||||
StmtInfo* innermost() const { return innermostStmt_; }
|
||||
StmtInfo* innermostScopeStmt() const { return innermostScopeStmt_; }
|
||||
StmtInfo* innermostNonLabel() const {
|
||||
StmtInfo* stmt = innermost();
|
||||
while (stmt && stmt->type == StmtType::LABEL)
|
||||
stmt = stmt->enclosing;
|
||||
return stmt;
|
||||
}
|
||||
|
||||
void push(StmtInfo* stmt, StmtType type) {
|
||||
stmt->type = type;
|
||||
|
@ -290,6 +290,7 @@ class SyntaxParseHandler
|
||||
|
||||
Node newStatementList(unsigned blockid, const TokenPos& pos) { return NodeGeneric; }
|
||||
void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {}
|
||||
void addCaseStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler>* pc) {}
|
||||
bool prependInitialYield(Node stmtList, Node gen) { return true; }
|
||||
Node newEmptyStatement(const TokenPos& pos) { return NodeEmptyStatement; }
|
||||
|
||||
@ -333,6 +334,7 @@ class SyntaxParseHandler
|
||||
Node newFunctionDefinition() { return NodeHoistableDeclaration; }
|
||||
void setFunctionBody(Node pn, Node kid) {}
|
||||
void setFunctionBox(Node pn, FunctionBox* funbox) {}
|
||||
Node newFunctionDefinitionForAnnexB(Node pn, Node assignment) { return NodeHoistableDeclaration; }
|
||||
void addFunctionArgument(Node pn, Node argpn) {}
|
||||
|
||||
Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
|
||||
@ -354,7 +356,7 @@ class SyntaxParseHandler
|
||||
return NodeGeneric;
|
||||
}
|
||||
|
||||
bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
|
||||
bool finishInitializerAssignment(Node pn, Node init) { return true; }
|
||||
void setLexicalDeclarationOp(Node pn, JSOp op) {}
|
||||
|
||||
void setBeginPosition(Node pn, Node oth) {}
|
||||
|
@ -4,6 +4,6 @@
|
||||
// Flags:
|
||||
//
|
||||
|
||||
Array.prototype.iterator = (function() { { while(0) function Uint8ClampedArray() { } } });
|
||||
Array.prototype.iterator = (function() { { while(0) { function Uint8ClampedArray() { } } } });
|
||||
var s = new Set(["testing", "testing", 123]);
|
||||
assertEq(s.size(), 2);
|
||||
|
@ -1,18 +0,0 @@
|
||||
// |jit-test| ion-eager
|
||||
|
||||
var ARR = [];
|
||||
try {
|
||||
function f() {
|
||||
ARR.push(eval.prototype)
|
||||
}
|
||||
f()
|
||||
function eval()(0)
|
||||
f()
|
||||
} catch (e) {}
|
||||
|
||||
if (ARR.length !== 2)
|
||||
throw new Error("ERROR 1");
|
||||
if (typeof(ARR[0]) !== 'undefined')
|
||||
throw new Error("ERROR 2");
|
||||
if (typeof(ARR[1]) !== 'object')
|
||||
throw new Error("ERROR 3");
|
@ -1,2 +1,3 @@
|
||||
for (var x in x)
|
||||
for (var x in x) {
|
||||
function x() {}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
// collection.iterator() returns an Iterator object.
|
||||
// collection.iterator() returns an iterator object.
|
||||
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
function test(obj, name) {
|
||||
var iter = obj[Symbol.iterator]();
|
||||
assertEq(typeof iter, "object");
|
||||
assertEq(iter instanceof Iterator, true);
|
||||
assertEq(iter instanceof Iterator, false); // Not a legacy Iterator.
|
||||
assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]");
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,16 @@
|
||||
// All iterators of the same collection type share their immediate prototype.
|
||||
// Those prototype objects in turn inherit directly from Iterator.prototype.
|
||||
// Those prototype objects in turn inherit directly from %IteratorPrototype%.
|
||||
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
// Get %IteratorPrototype%.
|
||||
var iterProto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
|
||||
|
||||
function test(obj0, obj1) {
|
||||
var iter0 = obj0[Symbol.iterator](), iter1 = obj1[Symbol.iterator]();
|
||||
var proto = Object.getPrototypeOf(iter0);
|
||||
assertEq(Object.getPrototypeOf(iter1), proto);
|
||||
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
|
||||
assertEq(Object.getPrototypeOf(proto), iterProto);
|
||||
}
|
||||
|
||||
test([], [1]);
|
||||
|
@ -3,22 +3,41 @@
|
||||
load(libdir + "asserts.js");
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
function test(constructor) {
|
||||
var proto = Object.getPrototypeOf(new constructor()[Symbol.iterator]());
|
||||
var names = Object.getOwnPropertyNames(proto);
|
||||
names.sort();
|
||||
assertDeepEq(names, ['next']);
|
||||
assertEq(proto.hasOwnProperty(Symbol.iterator), true);
|
||||
var iterProto = null;
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(proto, 'next');
|
||||
function test(constructor) {
|
||||
var iter = new constructor()[Symbol.iterator]();
|
||||
assertDeepEq(Reflect.ownKeys(iter), []);
|
||||
|
||||
// Iterator prototypes only have a .next property.
|
||||
// At least until we support @@toStringTag.
|
||||
var proto1 = Object.getPrototypeOf(iter);
|
||||
|
||||
var names = Reflect.ownKeys(proto1);
|
||||
names.sort();
|
||||
assertDeepEq(Reflect.ownKeys(proto1), ['next']);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(proto1, 'next');
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.writable, true);
|
||||
|
||||
assertEq(proto[Symbol.iterator](), proto);
|
||||
assertIteratorDone(proto, undefined);
|
||||
// %IteratorPrototype%
|
||||
var proto2 = Object.getPrototypeOf(proto1);
|
||||
assertEq(Object.getPrototypeOf(proto2), Object.prototype);
|
||||
assertEq(Object.prototype.toString.call(proto2), "[object Object]");
|
||||
|
||||
assertDeepEq(Reflect.ownKeys(proto2), [Symbol.iterator]);
|
||||
assertEq(proto2[Symbol.iterator](), proto2);
|
||||
|
||||
// Check there's a single %IteratorPrototype% object.
|
||||
if (iterProto === null)
|
||||
iterProto = proto2;
|
||||
else
|
||||
assertEq(iterProto, proto2);
|
||||
}
|
||||
|
||||
//test(Array);
|
||||
test(Array);
|
||||
test(String);
|
||||
test(Map);
|
||||
test(Set);
|
||||
|
@ -3,11 +3,11 @@
|
||||
load(libdir + "iteration.js");
|
||||
|
||||
var proto = Object.getPrototypeOf([][Symbol.iterator]());
|
||||
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
|
||||
var iterProto = Object.getPrototypeOf(proto);
|
||||
proto = Object.getPrototypeOf([].keys());
|
||||
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
|
||||
assertEq(Object.getPrototypeOf(proto), iterProto);
|
||||
proto = Object.getPrototypeOf([].entries());
|
||||
assertEq(Object.getPrototypeOf(proto), Iterator.prototype);
|
||||
assertEq(Object.getPrototypeOf(proto), iterProto);
|
||||
|
||||
function check(it) {
|
||||
assertEq(typeof it, 'object');
|
||||
|
@ -53,15 +53,13 @@ assertBuiltinFunction(String.prototype, Symbol.iterator, 0);
|
||||
var iter = ""[Symbol.iterator]();
|
||||
var iterProto = Object.getPrototypeOf(iter);
|
||||
|
||||
// StringIterator.prototype inherits from Object.prototype
|
||||
assertEq(Object.getPrototypeOf(iterProto), Object.prototype);
|
||||
// StringIterator.prototype inherits from %IteratorPrototype%. Check it's the
|
||||
// same object as %ArrayIteratorPrototype%'s proto.
|
||||
assertEq(Object.getPrototypeOf(iterProto),
|
||||
Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
|
||||
|
||||
// Own properties for StringIterator.prototype: "next"
|
||||
arraysEqual(Object.getOwnPropertyNames(iterProto).sort(), ["next"]);
|
||||
assertEq(iterProto.hasOwnProperty(Symbol.iterator), true);
|
||||
|
||||
// StringIterator.prototype[@@iterator] is a built-in function
|
||||
assertBuiltinFunction(iterProto, Symbol.iterator, 0);
|
||||
|
||||
// StringIterator.prototype.next is a built-in function
|
||||
assertBuiltinFunction(iterProto, "next", 0);
|
||||
|
@ -8,7 +8,11 @@ try {
|
||||
}
|
||||
}(), function() {}))
|
||||
} catch (e) {};
|
||||
var log = "";
|
||||
evaluate(`
|
||||
try {
|
||||
function x() {}
|
||||
assertEq(String(b), "function () {}");
|
||||
} catch (e) { throw (e); }
|
||||
} catch (e) { log += "e"; }
|
||||
`);
|
||||
assertEq(log, "e");
|
||||
|
@ -2259,6 +2259,26 @@ BaselineCompiler::emit_JSOP_BINDGNAME()
|
||||
return emit_JSOP_BINDNAME();
|
||||
}
|
||||
|
||||
typedef JSObject* (*BindVarFn)(JSContext*, HandleObject);
|
||||
static const VMFunction BindVarInfo = FunctionInfo<BindVarFn>(jit::BindVar);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_BINDVAR()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
masm.loadPtr(frame.addressOfScopeChain(), R0.scratchReg());
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
if (!callVM(BindVarInfo))
|
||||
return false;
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_SETPROP()
|
||||
{
|
||||
|
@ -141,6 +141,7 @@ namespace jit {
|
||||
_(JSOP_DELNAME) \
|
||||
_(JSOP_GETIMPORT) \
|
||||
_(JSOP_GETINTRINSIC) \
|
||||
_(JSOP_BINDVAR) \
|
||||
_(JSOP_DEFVAR) \
|
||||
_(JSOP_DEFCONST) \
|
||||
_(JSOP_DEFLET) \
|
||||
|
@ -158,6 +158,7 @@ BytecodeAnalysis::init(TempAllocator& alloc, GSNCache& gsn)
|
||||
|
||||
case JSOP_GETNAME:
|
||||
case JSOP_BINDNAME:
|
||||
case JSOP_BINDVAR:
|
||||
case JSOP_SETNAME:
|
||||
case JSOP_STRICTSETNAME:
|
||||
case JSOP_DELNAME:
|
||||
|
@ -8428,6 +8428,16 @@ CodeGenerator::visitOutOfLineUnboxFloatingPoint(OutOfLineUnboxFloatingPoint* ool
|
||||
masm.jump(ool->rejoin());
|
||||
}
|
||||
|
||||
typedef JSObject* (*BindVarFn)(JSContext*, HandleObject);
|
||||
static const VMFunction BindVarInfo = FunctionInfo<BindVarFn>(jit::BindVar);
|
||||
|
||||
void
|
||||
CodeGenerator::visitCallBindVar(LCallBindVar* lir)
|
||||
{
|
||||
pushArg(ToRegister(lir->scopeChain()));
|
||||
callVM(BindVarInfo, lir);
|
||||
}
|
||||
|
||||
typedef bool (*GetPropertyFn)(JSContext*, HandleValue, HandlePropertyName, MutableHandleValue);
|
||||
static const VMFunction GetPropertyInfo = FunctionInfo<GetPropertyFn>(GetProperty);
|
||||
|
||||
|
@ -329,6 +329,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
void visitSetDOMProperty(LSetDOMProperty* lir);
|
||||
void visitCallDOMNative(LCallDOMNative* lir);
|
||||
void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir);
|
||||
void visitCallBindVar(LCallBindVar* lir);
|
||||
void visitIsCallable(LIsCallable* lir);
|
||||
void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
|
||||
void visitIsObject(LIsObject* lir);
|
||||
|
@ -1942,6 +1942,9 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
case JSOP_BINDNAME:
|
||||
return jsop_bindname(info().getName(pc));
|
||||
|
||||
case JSOP_BINDVAR:
|
||||
return jsop_bindvar();
|
||||
|
||||
case JSOP_DUP:
|
||||
current->pushSlot(current->stackDepth() - 1);
|
||||
return true;
|
||||
@ -8401,6 +8404,16 @@ IonBuilder::jsop_bindname(PropertyName* name)
|
||||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_bindvar()
|
||||
{
|
||||
MOZ_ASSERT(analysis().usesScopeChain());
|
||||
MCallBindVar* ins = MCallBindVar::New(alloc(), current->scopeChain());
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MIRType
|
||||
GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types)
|
||||
{
|
||||
|
@ -689,6 +689,7 @@ class IonBuilder
|
||||
bool jsop_intrinsic(PropertyName* name);
|
||||
bool jsop_getimport(PropertyName* name);
|
||||
bool jsop_bindname(PropertyName* name);
|
||||
bool jsop_bindvar();
|
||||
bool jsop_getelem();
|
||||
bool jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType unboxedType);
|
||||
bool jsop_getelem_typed(MDefinition* obj, MDefinition* index, ScalarTypeDescr::Type arrayType);
|
||||
|
@ -3362,6 +3362,16 @@ LIRGenerator::visitBindNameCache(MBindNameCache* ins)
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitCallBindVar(MCallBindVar* ins)
|
||||
{
|
||||
MOZ_ASSERT(ins->scopeChain()->type() == MIRType_Object);
|
||||
MOZ_ASSERT(ins->type() == MIRType_Object);
|
||||
|
||||
LCallBindVar* lir = new(alloc()) LCallBindVar(useRegister(ins->scopeChain()));
|
||||
define(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins)
|
||||
{
|
||||
|
@ -230,6 +230,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
void visitGetPropertyPolymorphic(MGetPropertyPolymorphic* ins);
|
||||
void visitSetPropertyPolymorphic(MSetPropertyPolymorphic* ins);
|
||||
void visitBindNameCache(MBindNameCache* ins);
|
||||
void visitCallBindVar(MCallBindVar* ins);
|
||||
void visitGuardObjectIdentity(MGuardObjectIdentity* ins);
|
||||
void visitGuardClass(MGuardClass* ins);
|
||||
void visitGuardObject(MGuardObject* ins);
|
||||
|
@ -10757,6 +10757,39 @@ class MBindNameCache
|
||||
}
|
||||
};
|
||||
|
||||
class MCallBindVar
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy::Data
|
||||
{
|
||||
explicit MCallBindVar(MDefinition* scopeChain)
|
||||
: MUnaryInstruction(scopeChain)
|
||||
{
|
||||
setResultType(MIRType_Object);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(CallBindVar)
|
||||
|
||||
static MCallBindVar* New(TempAllocator& alloc, MDefinition* scopeChain) {
|
||||
return new(alloc) MCallBindVar(scopeChain);
|
||||
}
|
||||
|
||||
MDefinition* scopeChain() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
|
||||
bool congruentTo(const MDefinition* ins) const override {
|
||||
if (!ins->isCallBindVar())
|
||||
return false;
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const override {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
// Guard on an object's shape.
|
||||
class MGuardShape
|
||||
: public MUnaryInstruction,
|
||||
|
@ -162,6 +162,7 @@ namespace jit {
|
||||
_(GetPropertyPolymorphic) \
|
||||
_(SetPropertyPolymorphic) \
|
||||
_(BindNameCache) \
|
||||
_(CallBindVar) \
|
||||
_(GuardShape) \
|
||||
_(GuardReceiverPolymorphic) \
|
||||
_(GuardObjectGroup) \
|
||||
|
@ -167,14 +167,21 @@ CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
|
||||
return cx->runtime()->handleInterrupt(cx);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
BindVar(JSContext* cx, HandleObject scopeChain)
|
||||
{
|
||||
JSObject* obj = scopeChain;
|
||||
while (!obj->isQualifiedVarObj())
|
||||
obj = obj->enclosingScope();
|
||||
MOZ_ASSERT(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
bool
|
||||
DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain)
|
||||
{
|
||||
// Given the ScopeChain, extract the VarObj.
|
||||
RootedObject obj(cx, scopeChain);
|
||||
while (!obj->isQualifiedVarObj())
|
||||
obj = obj->enclosingScope();
|
||||
|
||||
RootedObject obj(cx, BindVar(cx, scopeChain));
|
||||
return DefVarOperation(cx, obj, dn, attrs);
|
||||
}
|
||||
|
||||
@ -185,10 +192,7 @@ DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject sc
|
||||
Rooted<ClonedBlockObject*> lexical(cx, &NearestEnclosingExtensibleLexicalScope(scopeChain));
|
||||
|
||||
// Find the variables object.
|
||||
RootedObject varObj(cx, scopeChain);
|
||||
while (!varObj->isQualifiedVarObj())
|
||||
varObj = varObj->enclosingScope();
|
||||
|
||||
RootedObject varObj(cx, BindVar(cx, scopeChain));
|
||||
return DefLexicalOperation(cx, lexical, varObj, dn, attrs);
|
||||
}
|
||||
|
||||
@ -853,9 +857,8 @@ bool
|
||||
InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame)
|
||||
{
|
||||
RootedScript script(cx, frame->script());
|
||||
RootedObject varObj(cx, frame->scopeChain());
|
||||
while (!varObj->isQualifiedVarObj())
|
||||
varObj = varObj->enclosingScope();
|
||||
RootedObject scopeChain(cx, frame->scopeChain());
|
||||
RootedObject varObj(cx, BindVar(cx, scopeChain));
|
||||
|
||||
if (script->isForEval()) {
|
||||
// Strict eval needs its own call object.
|
||||
@ -866,13 +869,12 @@ InitGlobalOrEvalScopeObjects(JSContext* cx, BaselineFrame* frame)
|
||||
if (!frame->initStrictEvalScopeObjects(cx))
|
||||
return false;
|
||||
} else {
|
||||
RootedObject scopeChain(cx, frame->scopeChain());
|
||||
if (!CheckEvalDeclarationConflicts(cx, script, scopeChain, varObj))
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Rooted<ClonedBlockObject*> lexicalScope(cx,
|
||||
&NearestEnclosingExtensibleLexicalScope(frame->scopeChain()));
|
||||
&NearestEnclosingExtensibleLexicalScope(scopeChain));
|
||||
if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObj))
|
||||
return false;
|
||||
}
|
||||
|
@ -588,6 +588,7 @@ bool CheckOverRecursed(JSContext* cx);
|
||||
bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
|
||||
uint32_t extra, uint32_t earlyCheck);
|
||||
|
||||
JSObject* BindVar(JSContext* cx, HandleObject scopeChain);
|
||||
bool DefVar(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
|
||||
bool DefLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
|
||||
bool DefGlobalLexical(JSContext* cx, HandlePropertyName dn, unsigned attrs);
|
||||
|
@ -5775,6 +5775,22 @@ class LBindNameCache : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
class LCallBindVar : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CallBindVar)
|
||||
|
||||
explicit LCallBindVar(const LAllocation& scopeChain) {
|
||||
setOperand(0, scopeChain);
|
||||
}
|
||||
const LAllocation* scopeChain() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const MCallBindVar* mir() const {
|
||||
return mir_->toCallBindVar();
|
||||
}
|
||||
};
|
||||
|
||||
// Load a value from an object's dslots or a slots vector.
|
||||
class LLoadSlotV : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
{
|
||||
|
@ -277,6 +277,7 @@
|
||||
_(GetPropertyPolymorphicV) \
|
||||
_(GetPropertyPolymorphicT) \
|
||||
_(BindNameCache) \
|
||||
_(CallBindVar) \
|
||||
_(CallGetProperty) \
|
||||
_(GetNameCache) \
|
||||
_(CallGetIntrinsicValue) \
|
||||
|
@ -270,6 +270,9 @@ MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
|
||||
MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class")
|
||||
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_LABEL, 1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
|
||||
MSG_DEF(JSMSG_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions cannot be labelled")
|
||||
MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL, 0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
|
||||
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
|
||||
MSG_DEF(JSMSG_MALFORMED_ESCAPE, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
|
||||
MSG_DEF(JSMSG_MISSING_BINARY_DIGITS, 0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
|
||||
|
@ -543,7 +543,7 @@ Compare(T* a, T* b, size_t c)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool iterator_next(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
static inline PropertyIteratorObject*
|
||||
NewPropertyIteratorObject(JSContext* cx, unsigned flags)
|
||||
@ -580,7 +580,7 @@ NewPropertyIteratorObject(JSContext* cx, unsigned flags)
|
||||
// next method on the prototype doesn't break cross-global for .. in.
|
||||
// We don't have to do this for JSITER_ENUMERATE because that object always
|
||||
// takes an optimized path.
|
||||
RootedFunction next(cx, NewNativeFunction(cx, iterator_next, 0,
|
||||
RootedFunction next(cx, NewNativeFunction(cx, legacy_iterator_next, 0,
|
||||
HandlePropertyName(cx->names().next)));
|
||||
if (!next)
|
||||
return nullptr;
|
||||
@ -1017,7 +1017,7 @@ IsIterator(HandleValue v)
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
iterator_next_impl(JSContext* cx, const CallArgs& args)
|
||||
legacy_iterator_next_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsIterator(args.thisv()));
|
||||
|
||||
@ -1040,15 +1040,15 @@ iterator_next_impl(JSContext* cx, const CallArgs& args)
|
||||
}
|
||||
|
||||
static bool
|
||||
iterator_next(JSContext* cx, unsigned argc, Value* vp)
|
||||
legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
|
||||
return CallNonGenericMethod<IsIterator, legacy_iterator_next_impl>(cx, args);
|
||||
}
|
||||
|
||||
static const JSFunctionSpec iterator_methods[] = {
|
||||
static const JSFunctionSpec legacy_iterator_methods[] = {
|
||||
JS_SELF_HOSTED_SYM_FN(iterator, "LegacyIteratorShim", 0, 0),
|
||||
JS_FN("next", iterator_next, 0, 0),
|
||||
JS_FN("next", legacy_iterator_next, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
@ -1109,7 +1109,6 @@ const Class ArrayIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
static const JSFunctionSpec array_iterator_methods[] = {
|
||||
JS_SELF_HOSTED_SYM_FN(iterator, "ArrayIteratorIdentity", 0, 0),
|
||||
JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -1131,7 +1130,6 @@ const Class StringIteratorObject::class_ = {
|
||||
};
|
||||
|
||||
static const JSFunctionSpec string_iterator_methods[] = {
|
||||
JS_SELF_HOSTED_SYM_FN(iterator, "StringIteratorIdentity", 0, 0),
|
||||
JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
@ -1462,6 +1460,25 @@ const Class StopIterationObject::class_ = {
|
||||
stopiter_hasInstance
|
||||
};
|
||||
|
||||
static const JSFunctionSpec iterator_proto_methods[] = {
|
||||
JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
/* static */ bool
|
||||
GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
{
|
||||
if (global->getReservedSlot(ITERATOR_PROTO).isObject())
|
||||
return true;
|
||||
|
||||
RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
|
||||
if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods))
|
||||
return false;
|
||||
|
||||
global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
|
||||
{
|
||||
@ -1487,8 +1504,12 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> globa
|
||||
if (global->getReservedSlot(STRING_ITERATOR_PROTO).isObject())
|
||||
return true;
|
||||
|
||||
RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
|
||||
if (!iteratorProto)
|
||||
return false;
|
||||
|
||||
const Class* cls = &StringIteratorPrototypeClass;
|
||||
RootedObject proto(cx, global->createBlankPrototype(cx, cls));
|
||||
RootedObject proto(cx, global->createBlankPrototypeInheriting(cx, cls, iteratorProto));
|
||||
if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods))
|
||||
return false;
|
||||
|
||||
@ -1497,7 +1518,7 @@ GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> globa
|
||||
}
|
||||
|
||||
JSObject*
|
||||
js::InitIteratorClass(JSContext* cx, HandleObject obj)
|
||||
js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
Handle<GlobalObject*> global = obj.as<GlobalObject>();
|
||||
|
||||
@ -1523,7 +1544,7 @@ js::InitIteratorClass(JSContext* cx, HandleObject obj)
|
||||
return nullptr;
|
||||
if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
|
||||
return nullptr;
|
||||
if (!DefinePropertiesAndFunctions(cx, iteratorProto, nullptr, iterator_methods))
|
||||
if (!DefinePropertiesAndFunctions(cx, iteratorProto, nullptr, legacy_iterator_methods))
|
||||
return nullptr;
|
||||
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator,
|
||||
ctor, iteratorProto))
|
||||
|
@ -214,7 +214,7 @@ extern JSObject*
|
||||
CreateItrResultObject(JSContext* cx, HandleValue value, bool done);
|
||||
|
||||
extern JSObject*
|
||||
InitIteratorClass(JSContext* cx, HandleObject obj);
|
||||
InitLegacyIteratorClass(JSContext* cx, HandleObject obj);
|
||||
|
||||
extern JSObject*
|
||||
InitStopIterationClass(JSContext* cx, HandleObject obj);
|
||||
|
@ -75,7 +75,7 @@
|
||||
real(SyntaxError, 16, InitViaClassSpec, ERROR_CLASP(JSEXN_SYNTAXERR)) \
|
||||
real(TypeError, 17, InitViaClassSpec, ERROR_CLASP(JSEXN_TYPEERR)) \
|
||||
real(URIError, 18, InitViaClassSpec, ERROR_CLASP(JSEXN_URIERR)) \
|
||||
real(Iterator, 19, InitIteratorClass, OCLASP(PropertyIterator)) \
|
||||
real(Iterator, 19, InitLegacyIteratorClass,OCLASP(PropertyIterator)) \
|
||||
real(StopIteration, 20, InitStopIterationClass, OCLASP(StopIteration)) \
|
||||
real(ArrayBuffer, 21, InitArrayBufferClass, &js::ArrayBufferObject::protoClass) \
|
||||
real(Int8Array, 22, InitViaClassSpec, TYPED_ARRAY_CLASP(Int8)) \
|
||||
|
@ -19,8 +19,9 @@ var called, obj;
|
||||
function inFile1() { return "in file"; }
|
||||
called = false;
|
||||
obj = { set inFile1(v) { called = true; } };
|
||||
with (obj)
|
||||
with (obj) {
|
||||
function inFile1() { return "in file in with"; };
|
||||
}
|
||||
assertEq(inFile1(), "in file in with");
|
||||
assertEq("set" in Object.getOwnPropertyDescriptor(obj, "inFile1"), true);
|
||||
assertEq(called, false);
|
||||
@ -28,8 +29,9 @@ assertEq(called, false);
|
||||
evaluate("function notInFile1() { return 'not in file'; }");
|
||||
called = false;
|
||||
obj = { set notInFile1(v) { called = true; return "not in file 2"; } };
|
||||
with (obj)
|
||||
with (obj) {
|
||||
function notInFile1() { return "not in file in with"; };
|
||||
}
|
||||
assertEq(notInFile1(), "not in file in with");
|
||||
assertEq("set" in Object.getOwnPropertyDescriptor(obj, "notInFile1"), true);
|
||||
assertEq(called, false);
|
||||
@ -39,8 +41,9 @@ called = false;
|
||||
obj =
|
||||
Object.defineProperty({}, "inFile2",
|
||||
{ value: 42, configurable: false, enumerable: false });
|
||||
with (obj)
|
||||
with (obj) {
|
||||
function inFile2() { return "in file 2"; };
|
||||
}
|
||||
assertEq(inFile2(), "in file 2");
|
||||
assertEq(obj.inFile2, 42);
|
||||
|
||||
|
@ -9,46 +9,41 @@ assertEq(testLenientAndStrict("function f() { }",
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
|
||||
// Function statements within blocks are forbidden in strict mode code.
|
||||
assertEq(testLenientAndStrict("{ function f() { } }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
// Lambdas are always permitted within blocks.
|
||||
assertEq(testLenientAndStrict("{ (function f() { }) }",
|
||||
parsesSuccessfully,
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
|
||||
// Function statements within any sort of statement are forbidden in strict mode code.
|
||||
// Function statements within unbraced blocks are forbidden in strict mode code.
|
||||
// They are allowed only under if statements in sloppy mode.
|
||||
assertEq(testLenientAndStrict("if (true) function f() { }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("while (true) function f() { }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("do function f() { } while (true);",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("for(;;) function f() { }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("for(x in []) function f() { }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("with(o) function f() { }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
assertEq(testLenientAndStrict("x: function f() { }",
|
||||
parsesSuccessfully,
|
||||
@ -56,7 +51,7 @@ assertEq(testLenientAndStrict("x: function f() { }",
|
||||
true);
|
||||
assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }",
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
|
||||
// Lambdas are always permitted within any sort of statement.
|
||||
@ -69,7 +64,7 @@ assertEq(testLenientAndStrict("if (true) (function f() { })",
|
||||
assertEq(parsesSuccessfully("function f() { function g() { } }"),
|
||||
true);
|
||||
|
||||
// Function statements are permitted in any statement within lenient functions.
|
||||
// Function statements are permitted in if statement within lenient functions.
|
||||
assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"),
|
||||
true);
|
||||
|
||||
@ -77,8 +72,7 @@ assertEq(parseRaisesException(SyntaxError)
|
||||
("function f() { 'use strict'; if (true) function g() { } }"),
|
||||
true);
|
||||
|
||||
assertEq(parseRaisesException(SyntaxError)
|
||||
("function f() { 'use strict'; { function g() { } } }"),
|
||||
assertEq(parsesSuccessfully("function f() { 'use strict'; { function g() { } } }"),
|
||||
true);
|
||||
|
||||
assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"),
|
||||
@ -94,7 +88,7 @@ assertEq(testLenientAndStrict("function f() { }",
|
||||
true);
|
||||
assertEq(testLenientAndStrict("{ function f() { } }",
|
||||
completesNormally,
|
||||
raisesException(SyntaxError)),
|
||||
completesNormally),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -59,8 +59,10 @@ TestGeneratorFunctionPrototype();
|
||||
// Functions that we associate with generator objects are actually defined by
|
||||
// a common prototype.
|
||||
function TestGeneratorObjectPrototype() {
|
||||
// %GeneratorPrototype% must inherit from %IteratorPrototype%.
|
||||
var iterProto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
|
||||
assertEq(Object.getPrototypeOf(GeneratorObjectPrototype),
|
||||
Object.prototype);
|
||||
iterProto);
|
||||
assertEq(Object.getPrototypeOf((function*(){yield 1}).prototype),
|
||||
GeneratorObjectPrototype);
|
||||
|
||||
@ -72,6 +74,9 @@ function TestGeneratorObjectPrototype() {
|
||||
found_property_names.sort();
|
||||
|
||||
assertDeepEq(found_property_names, expected_property_names);
|
||||
|
||||
// No symbol properties, at least until we have @@toStringTag.
|
||||
assertEq(Object.getOwnPropertySymbols(GeneratorObjectPrototype).length, 0);
|
||||
}
|
||||
TestGeneratorObjectPrototype();
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
var log = "";
|
||||
|
||||
function f() {
|
||||
log += g();
|
||||
function g() { return "outer-g"; }
|
||||
|
||||
var o = { g: function () { return "with-g"; } };
|
||||
with (o) {
|
||||
// Annex B.3.3.3 says g should be set on the nearest VariableEnvironment,
|
||||
// and so should not change o.g.
|
||||
eval(`{
|
||||
function g() { return "eval-g"; }
|
||||
}`);
|
||||
}
|
||||
|
||||
log += g();
|
||||
log += o.g();
|
||||
}
|
||||
|
||||
f();
|
||||
|
||||
function h() {
|
||||
eval(`
|
||||
// Should return true, as var bindings introduced by eval are configurable.
|
||||
log += (delete q);
|
||||
{
|
||||
function q() { log += "q"; }
|
||||
// Should return false, as lexical bindings introduced by eval are not
|
||||
// configurable.
|
||||
log += (delete q);
|
||||
}
|
||||
`);
|
||||
return q;
|
||||
}
|
||||
|
||||
h()();
|
||||
|
||||
reportCompare(log, "outer-geval-gwith-gtruefalseq");
|
@ -0,0 +1,42 @@
|
||||
var log = "";
|
||||
|
||||
function f(x) {
|
||||
if (x)
|
||||
function g() { return "g0"; }
|
||||
else
|
||||
function g() { return "g1"; }
|
||||
|
||||
log += g();
|
||||
|
||||
if (x)
|
||||
function g() { return "g2"; }
|
||||
else {
|
||||
}
|
||||
|
||||
log += g();
|
||||
|
||||
if (x) {
|
||||
} else
|
||||
function g() { return "g3"; }
|
||||
|
||||
log += g();
|
||||
|
||||
if (x)
|
||||
function g() { return "g4"; }
|
||||
|
||||
log += g();
|
||||
}
|
||||
|
||||
f(true);
|
||||
f(false);
|
||||
|
||||
try {
|
||||
eval(`
|
||||
if (1)
|
||||
l: function foo() {}
|
||||
`);
|
||||
} catch (e) {
|
||||
log += "e";
|
||||
}
|
||||
|
||||
reportCompare(log, "g0g2g2g4g1g1g3g3e");
|
@ -0,0 +1,43 @@
|
||||
function expectSyntaxError(str) {
|
||||
var threwSyntaxError;
|
||||
try {
|
||||
eval(str);
|
||||
} catch (e) {
|
||||
threwSyntaxError = e instanceof SyntaxError;
|
||||
}
|
||||
assertEq(threwSyntaxError, true);
|
||||
|
||||
try {
|
||||
eval('"use strict";' + str);
|
||||
} catch (e) {
|
||||
threwSyntaxError = e instanceof SyntaxError;
|
||||
}
|
||||
assertEq(threwSyntaxError, true);
|
||||
}
|
||||
|
||||
function expectSloppyPass(str) {
|
||||
eval(str);
|
||||
|
||||
try {
|
||||
eval('"use strict";' + str);
|
||||
} catch (e) {
|
||||
threwSyntaxError = e instanceof SyntaxError;
|
||||
}
|
||||
assertEq(threwSyntaxError, true);
|
||||
}
|
||||
|
||||
expectSloppyPass(`l: function f1() {}`);
|
||||
expectSloppyPass(`l0: l: function f1() {}`);
|
||||
expectSloppyPass(`{ f1(); l: function f1() {} }`);
|
||||
expectSloppyPass(`{ f1(); l0: l: function f1() {} }`);
|
||||
expectSloppyPass(`{ f1(); l: function f1() { return 42; } } assertEq(f1(), 42);`);
|
||||
expectSloppyPass(`eval("fe(); l: function fe() {}")`);
|
||||
expectSyntaxError(`if (1) l: function f2() {}`);
|
||||
expectSyntaxError(`if (1) {} else l: function f3() {}`);
|
||||
expectSyntaxError(`do l: function f4() {} while (0)`);
|
||||
expectSyntaxError(`while (0) l: function f5() {}`);
|
||||
expectSyntaxError(`for (;;) l: function f6() {}`);
|
||||
expectSloppyPass(`switch (1) { case 1: l: function f7() {} }`);
|
||||
expectSloppyPass(`switch (1) { case 1: assertEq(f8(), 'f8'); case 2: l: function f8() { return 'f8'; } } assertEq(f8(), 'f8');`);
|
||||
|
||||
reportCompare(0, 0);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user