зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
This commit is contained in:
Коммит
465753992f
|
@ -177,3 +177,9 @@ subinclude:servo/.hgignore
|
|||
|
||||
# https://bz.mercurial-scm.org/show_bug.cgi?id=5322
|
||||
^comm/
|
||||
|
||||
# Ignore various raptor performance framework files
|
||||
^testing/raptor/.raptor-venv
|
||||
^testing/raptor/raptor-venv
|
||||
^testing/raptor/raptor/tests/.*.json
|
||||
^testing/raptor/webext/raptor/auto_gen_test_config.js
|
||||
|
|
|
@ -46,6 +46,7 @@ mozilla.pth:testing/marionette/client
|
|||
mozilla.pth:testing/marionette/harness
|
||||
mozilla.pth:testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py
|
||||
mozilla.pth:testing/marionette/puppeteer/firefox
|
||||
mozilla.pth:testing/raptor
|
||||
mozilla.pth:testing/talos
|
||||
packages.txt:testing/mozbase/packages.txt
|
||||
mozilla.pth:tools
|
||||
|
|
|
@ -264,7 +264,7 @@ ObjectActor.prototype = {
|
|||
let actor = new PropertyIteratorActor(this, request.options);
|
||||
this.registeredPool.addActor(actor);
|
||||
this.iterators.add(actor);
|
||||
return { iterator: actor.grip() };
|
||||
return { iterator: actor.form() };
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -274,7 +274,7 @@ ObjectActor.prototype = {
|
|||
let actor = new PropertyIteratorActor(this, { enumEntries: true });
|
||||
this.registeredPool.addActor(actor);
|
||||
this.iterators.add(actor);
|
||||
return { iterator: actor.grip() };
|
||||
return { iterator: actor.form() };
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
const { Cu } = require("chrome");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const { propertyIteratorSpec } = require("devtools/shared/specs/property-iterator");
|
||||
loader.lazyRequireGetter(this, "ChromeUtils");
|
||||
loader.lazyRequireGetter(this, "ObjectUtils", "devtools/server/actors/object/utils");
|
||||
|
||||
|
@ -37,43 +39,42 @@ loader.lazyRequireGetter(this, "ObjectUtils", "devtools/server/actors/object/uti
|
|||
* Regarding value filtering it just compare to the stringification
|
||||
* of the property value.
|
||||
*/
|
||||
function PropertyIteratorActor(objectActor, options) {
|
||||
if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
|
||||
this.iterator = {
|
||||
size: 0,
|
||||
propertyName: index => undefined,
|
||||
propertyDescription: index => undefined,
|
||||
};
|
||||
} else if (options.enumEntries) {
|
||||
let cls = objectActor.obj.class;
|
||||
if (cls == "Map") {
|
||||
this.iterator = enumMapEntries(objectActor);
|
||||
} else if (cls == "WeakMap") {
|
||||
this.iterator = enumWeakMapEntries(objectActor);
|
||||
} else if (cls == "Set") {
|
||||
this.iterator = enumSetEntries(objectActor);
|
||||
} else if (cls == "WeakSet") {
|
||||
this.iterator = enumWeakSetEntries(objectActor);
|
||||
const PropertyIteratorActor = protocol.ActorClassWithSpec(propertyIteratorSpec, {
|
||||
initialize(objectActor, options) {
|
||||
protocol.Actor.prototype.initialize.call(this);
|
||||
if (!DevToolsUtils.isSafeDebuggerObject(objectActor.obj)) {
|
||||
this.iterator = {
|
||||
size: 0,
|
||||
propertyName: index => undefined,
|
||||
propertyDescription: index => undefined,
|
||||
};
|
||||
} else if (options.enumEntries) {
|
||||
let cls = objectActor.obj.class;
|
||||
if (cls == "Map") {
|
||||
this.iterator = enumMapEntries(objectActor);
|
||||
} else if (cls == "WeakMap") {
|
||||
this.iterator = enumWeakMapEntries(objectActor);
|
||||
} else if (cls == "Set") {
|
||||
this.iterator = enumSetEntries(objectActor);
|
||||
} else if (cls == "WeakSet") {
|
||||
this.iterator = enumWeakSetEntries(objectActor);
|
||||
} else {
|
||||
throw new Error("Unsupported class to enumerate entries from: " + cls);
|
||||
}
|
||||
} else if (
|
||||
ObjectUtils.isArray(objectActor.obj)
|
||||
&& options.ignoreNonIndexedProperties
|
||||
&& !options.query
|
||||
) {
|
||||
this.iterator = enumArrayProperties(objectActor, options);
|
||||
} else {
|
||||
throw new Error("Unsupported class to enumerate entries from: " + cls);
|
||||
this.iterator = enumObjectProperties(objectActor, options);
|
||||
}
|
||||
} else if (
|
||||
ObjectUtils.isArray(objectActor.obj)
|
||||
&& options.ignoreNonIndexedProperties
|
||||
&& !options.query
|
||||
) {
|
||||
this.iterator = enumArrayProperties(objectActor, options);
|
||||
} else {
|
||||
this.iterator = enumObjectProperties(objectActor, options);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
PropertyIteratorActor.prototype = {
|
||||
actorPrefix: "propertyIterator",
|
||||
|
||||
grip() {
|
||||
form() {
|
||||
return {
|
||||
type: this.actorPrefix,
|
||||
type: this.typeName,
|
||||
actor: this.actorID,
|
||||
count: this.iterator.size
|
||||
};
|
||||
|
@ -95,6 +96,7 @@ PropertyIteratorActor.prototype = {
|
|||
let name = this.iterator.propertyName(i);
|
||||
ownProperties[name] = this.iterator.propertyDescription(i);
|
||||
}
|
||||
|
||||
return {
|
||||
ownProperties
|
||||
};
|
||||
|
@ -103,13 +105,7 @@ PropertyIteratorActor.prototype = {
|
|||
all() {
|
||||
return this.slice({ start: 0, count: this.iterator.size });
|
||||
}
|
||||
};
|
||||
|
||||
PropertyIteratorActor.prototype.requestTypes = {
|
||||
"names": PropertyIteratorActor.prototype.names,
|
||||
"slice": PropertyIteratorActor.prototype.slice,
|
||||
"all": PropertyIteratorActor.prototype.all,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function to create a grip from a Map/Set entry
|
||||
|
|
|
@ -76,7 +76,7 @@ function run_test() {
|
|||
let stack = Components.stack;
|
||||
while (stack) {
|
||||
info(stack.name);
|
||||
if (stack.name == "onConnect") {
|
||||
if (stack.name.includes("run_test/onConnect")) {
|
||||
// Reached back to outer function before request
|
||||
ok(true, "Complete stack");
|
||||
return;
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// See bug 1273941 to understand this choice of promise.
|
||||
// We have to keep using Promise.jsm here, because DOM Promises
|
||||
// start freezing during panel iframes destruction.
|
||||
// More info in bug 1454373 comment 15.
|
||||
const Promise = require("promise");
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,14 +4,27 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
var promise = require("promise");
|
||||
var defer = require("devtools/shared/defer");
|
||||
const { extend } = require("devtools/shared/extend");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
var {getStack, callFunctionWithAsyncStack} = require("devtools/shared/platform/stack");
|
||||
var {settleAll} = require("devtools/shared/DevToolsUtils");
|
||||
var {lazyLoadSpec, lazyLoadFront} = require("devtools/shared/specs/index");
|
||||
|
||||
// Bug 1454373: devtools/shared/defer still uses Promise.jsm which is slower
|
||||
// than DOM Promises. So implement our own copy of `defer` based on DOM Promises.
|
||||
function defer() {
|
||||
let resolve, reject;
|
||||
let promise = new Promise(function() {
|
||||
resolve = arguments[0];
|
||||
reject = arguments[1];
|
||||
});
|
||||
return {
|
||||
resolve: resolve,
|
||||
reject: reject,
|
||||
promise: promise
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Types: named marshallers/demarshallers.
|
||||
*
|
||||
|
@ -983,7 +996,7 @@ Actor.prototype = extend(Pool.prototype, {
|
|||
},
|
||||
|
||||
_queueResponse: function(create) {
|
||||
let pending = this._pendingResponse || promise.resolve(null);
|
||||
let pending = this._pendingResponse || Promise.resolve(null);
|
||||
let response = create(pending);
|
||||
this._pendingResponse = response;
|
||||
}
|
||||
|
@ -1248,7 +1261,7 @@ Front.prototype = extend(Pool.prototype, {
|
|||
* represents.
|
||||
*/
|
||||
actor: function() {
|
||||
return promise.resolve(this.actorID);
|
||||
return Promise.resolve(this.actorID);
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
|
@ -1314,7 +1327,7 @@ Front.prototype = extend(Pool.prototype, {
|
|||
// Check to see if any of the preEvents returned a promise -- if so,
|
||||
// wait for their resolution before emitting. Otherwise, emit synchronously.
|
||||
if (results.some(result => result && typeof result.then === "function")) {
|
||||
promise.all(results).then(() => {
|
||||
Promise.all(results).then(() => {
|
||||
return EventEmitter.emit.apply(null, [this, event.name].concat(args));
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -152,6 +152,11 @@ const Types = exports.__TypesForTests = [
|
|||
spec: "devtools/shared/specs/promises",
|
||||
front: "devtools/shared/fronts/promises",
|
||||
},
|
||||
{
|
||||
types: ["propertyIterator"],
|
||||
spec: "devtools/shared/specs/property-iterator",
|
||||
front: null,
|
||||
},
|
||||
{
|
||||
types: ["reflow"],
|
||||
spec: "devtools/shared/specs/reflow",
|
||||
|
|
|
@ -32,6 +32,7 @@ DevToolsModules(
|
|||
'performance.js',
|
||||
'preference.js',
|
||||
'promises.js',
|
||||
'property-iterator.js',
|
||||
'reflow.js',
|
||||
'script.js',
|
||||
'source.js',
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
generateActorSpec,
|
||||
Option,
|
||||
RetVal,
|
||||
types,
|
||||
} = require("devtools/shared/protocol");
|
||||
|
||||
types.addDictType("propertyiterator.data", {
|
||||
ownProperties: "nullable:json",
|
||||
});
|
||||
|
||||
const propertyIteratorSpec = generateActorSpec({
|
||||
typeName: "propertyIterator",
|
||||
|
||||
methods: {
|
||||
names: {
|
||||
request: {
|
||||
indexes: Option(0, "array:number"),
|
||||
},
|
||||
response: RetVal("array:string")
|
||||
},
|
||||
slice: {
|
||||
request: {
|
||||
start: Option(0, "number"),
|
||||
count: Option(0, "number"),
|
||||
},
|
||||
response: RetVal("propertyiterator.data")
|
||||
},
|
||||
all: {
|
||||
request: {},
|
||||
response: RetVal("propertyiterator.data")
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
exports.propertyIteratorSpec = propertyIteratorSpec;
|
|
@ -288,8 +288,12 @@ nsDefaultURIFixup::GetFixupURIInfo(const nsACString& aStringURI,
|
|||
// really know about.
|
||||
nsCOMPtr<nsIProtocolHandler> ourHandler, extHandler;
|
||||
|
||||
ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
|
||||
extHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "default");
|
||||
if (!scheme.IsEmpty()) {
|
||||
ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(ourHandler));
|
||||
} else {
|
||||
ourHandler = extHandler;
|
||||
}
|
||||
|
||||
if (ourHandler != extHandler || !PossiblyHostPortUrl(uriString)) {
|
||||
// Just try to create an URL out of it
|
||||
|
|
|
@ -832,7 +832,8 @@ nsHostObjectProtocolHandler::Traverse(const nsACString& aUri,
|
|||
// -----------------------------------------------------------------------
|
||||
// Protocol handler
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsHostObjectProtocolHandler, nsIProtocolHandler)
|
||||
NS_IMPL_ISUPPORTS(nsHostObjectProtocolHandler, nsIProtocolHandler,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHostObjectProtocolHandler::GetDefaultPort(int32_t *result)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
#define BLOBURI_SCHEME "blob"
|
||||
#define FONTTABLEURI_SCHEME "moz-fonttable"
|
||||
|
@ -34,6 +35,7 @@ class MediaSource;
|
|||
|
||||
class nsHostObjectProtocolHandler : public nsIProtocolHandler
|
||||
, public nsIProtocolHandlerWithDynamicFlags
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
nsHostObjectProtocolHandler();
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
#ifndef mozilla_layers_APZSampler_h
|
||||
#define mozilla_layers_APZSampler_h
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/platform_thread.h" // for PlatformThreadId
|
||||
#include "mozilla/layers/AsyncCompositionManager.h" // for AsyncTransform
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "nsTArray.h"
|
||||
#include "Units.h"
|
||||
|
||||
|
@ -16,8 +20,10 @@ namespace mozilla {
|
|||
class TimeStamp;
|
||||
|
||||
namespace wr {
|
||||
class TransactionBuilder;
|
||||
struct Transaction;
|
||||
class TransactionWrapper;
|
||||
struct WrTransformProperty;
|
||||
struct WrWindowId;
|
||||
} // namespace wr
|
||||
|
||||
namespace layers {
|
||||
|
@ -37,8 +43,20 @@ class APZSampler {
|
|||
public:
|
||||
explicit APZSampler(const RefPtr<APZCTreeManager>& aApz);
|
||||
|
||||
bool PushStateToWR(wr::TransactionBuilder& aTxn,
|
||||
const TimeStamp& aSampleTime);
|
||||
void SetWebRenderWindowId(const wr::WindowId& aWindowId);
|
||||
|
||||
/**
|
||||
* This function is invoked from rust on the render backend thread when it
|
||||
* is created. It effectively tells the APZSampler "the current thread is
|
||||
* the sampler thread for this window id" and allows APZSampler to remember
|
||||
* which thread it is.
|
||||
*/
|
||||
static void SetSamplerThread(const wr::WrWindowId& aWindowId);
|
||||
static void SampleForWebRender(const wr::WrWindowId& aWindowId,
|
||||
wr::Transaction* aTxn);
|
||||
|
||||
void SetSampleTime(const TimeStamp& aSampleTime);
|
||||
void SampleForWebRender(wr::TransactionWrapper& aTxn);
|
||||
|
||||
bool SampleAnimations(const LayerMetricsWrapper& aLayer,
|
||||
const TimeStamp& aSampleTime);
|
||||
|
@ -84,8 +102,35 @@ public:
|
|||
protected:
|
||||
virtual ~APZSampler();
|
||||
|
||||
bool UsingWebRenderSamplerThread() const;
|
||||
static already_AddRefed<APZSampler> GetSampler(const wr::WrWindowId& aWindowId);
|
||||
|
||||
private:
|
||||
RefPtr<APZCTreeManager> mApz;
|
||||
|
||||
// Used to manage the mapping from a WR window id to APZSampler. These are only
|
||||
// used if WebRender is enabled. Both sWindowIdMap and mWindowId should only
|
||||
// be used while holding the sWindowIdLock.
|
||||
static StaticMutex sWindowIdLock;
|
||||
static std::unordered_map<uint64_t, APZSampler*> sWindowIdMap;
|
||||
Maybe<wr::WrWindowId> mWindowId;
|
||||
|
||||
// If WebRender is enabled, this holds the thread id of the render backend
|
||||
// thread (which is the sampler thread) for the compositor associated with
|
||||
// this APZSampler instance.
|
||||
// This is written to once during init and never cleared, and so reading it
|
||||
// from multiple threads during normal operation (after initialization)
|
||||
// without locking should be fine.
|
||||
Maybe<PlatformThreadId> mSamplerThreadId;
|
||||
#ifdef DEBUG
|
||||
// This flag is used to ensure that we don't ever try to do sampler-thread
|
||||
// stuff before the updater thread has been properly initialized.
|
||||
mutable bool mSamplerThreadQueried;
|
||||
#endif
|
||||
|
||||
Mutex mSampleTimeLock;
|
||||
// Can only be accessed or modified while holding mSampleTimeLock.
|
||||
TimeStamp mSampleTime;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
|
|
@ -110,6 +110,17 @@ struct APZCTreeManager::TreeBuildingState {
|
|||
ScrollableLayerGuid::HashIgnoringPresShellFn,
|
||||
ScrollableLayerGuid::EqualIgnoringPresShellFn> mApzcMap;
|
||||
|
||||
// This is populated with all the HitTestingTreeNodes that are scroll thumbs
|
||||
// and have a scrollthumb animation id (which indicates that they need to be
|
||||
// sampled for WebRender on the sampler thread).
|
||||
std::vector<HitTestingTreeNode*> mScrollThumbs;
|
||||
// This is populated with all the scroll target nodes. We use in conjunction
|
||||
// with mScrollThumbs to build APZCTreeManager::mScrollThumbInfo.
|
||||
std::unordered_map<ScrollableLayerGuid,
|
||||
HitTestingTreeNode*,
|
||||
ScrollableLayerGuid::HashIgnoringPresShellFn,
|
||||
ScrollableLayerGuid::EqualIgnoringPresShellFn> mScrollTargets;
|
||||
|
||||
// As the tree is traversed, the top element of this stack tracks whether
|
||||
// the parent scroll node has a perspective transform.
|
||||
std::stack<bool> mParentHasPerspective;
|
||||
|
@ -403,6 +414,19 @@ APZCTreeManager::UpdateHitTestingTreeImpl(LayersId aRootLayerTreeId,
|
|||
AsyncPanZoomController* apzc = node->GetApzc();
|
||||
aLayerMetrics.SetApzc(apzc);
|
||||
|
||||
// GetScrollbarAnimationId is only non-zero when webrender is enabled,
|
||||
// which limits the extra thumb mapping work to the webrender-enabled
|
||||
// case where it is needed.
|
||||
// Note also that when webrender is enabled, a "valid" animation id
|
||||
// is always nonzero, so we don't need to worry about handling the
|
||||
// case where WR is enabled and the animation id is zero.
|
||||
if (node->IsScrollThumbNode() && node->GetScrollbarAnimationId()) {
|
||||
state.mScrollThumbs.push_back(node);
|
||||
}
|
||||
if (apzc && node->IsPrimaryHolder()) {
|
||||
state.mScrollTargets[apzc->GetGuid()] = node;
|
||||
}
|
||||
|
||||
mApzcTreeLog << '\n';
|
||||
|
||||
// Accumulate the CSS transform between layers that have an APZC.
|
||||
|
@ -484,6 +508,29 @@ APZCTreeManager::UpdateHitTestingTreeImpl(LayersId aRootLayerTreeId,
|
|||
// APZC instances
|
||||
MutexAutoLock lock(mMapLock);
|
||||
mApzcMap = Move(state.mApzcMap);
|
||||
mScrollThumbInfo.clear();
|
||||
// For non-webrender, state.mScrollThumbs will be empty so this will be a
|
||||
// no-op.
|
||||
for (HitTestingTreeNode* thumb : state.mScrollThumbs) {
|
||||
MOZ_ASSERT(thumb->IsScrollThumbNode());
|
||||
ScrollableLayerGuid targetGuid(thumb->GetLayersId(), 0, thumb->GetScrollTargetId());
|
||||
auto it = state.mScrollTargets.find(targetGuid);
|
||||
if (it == state.mScrollTargets.end()) {
|
||||
// It could be that |thumb| is a scrollthumb for content which didn't
|
||||
// have an APZC, for example if the content isn't layerized. Regardless,
|
||||
// we can't async-scroll it so we don't need to worry about putting it
|
||||
// in mScrollThumbInfo.
|
||||
continue;
|
||||
}
|
||||
HitTestingTreeNode* target = it->second;
|
||||
mScrollThumbInfo.emplace_back(
|
||||
thumb->GetScrollbarAnimationId(),
|
||||
thumb->GetTransform(),
|
||||
thumb->GetScrollbarData(),
|
||||
targetGuid,
|
||||
target->GetTransform(),
|
||||
target->IsAncestorOf(thumb));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
|
||||
|
@ -543,122 +590,74 @@ APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
|
|||
aOriginatingLayersId, aPaintSequenceNumber);
|
||||
}
|
||||
|
||||
bool
|
||||
APZCTreeManager::PushStateToWR(wr::TransactionBuilder& aTxn,
|
||||
const TimeStamp& aSampleTime)
|
||||
void
|
||||
APZCTreeManager::SampleForWebRender(wr::TransactionWrapper& aTxn,
|
||||
const TimeStamp& aSampleTime)
|
||||
{
|
||||
AssertOnSamplerThread();
|
||||
|
||||
RecursiveMutexAutoLock lock(mTreeLock);
|
||||
|
||||
// During the first pass through the tree, we build a cache of guid->HTTN so
|
||||
// that we can find the relevant APZC instances quickly in subsequent passes,
|
||||
// such as the one below to generate scrollbar transforms. Without this, perf
|
||||
// could end up being O(n^2) instead of O(n log n) because we'd have to search
|
||||
// the tree to find the corresponding APZC every time we hit a thumb node.
|
||||
// We use the presShell-ignoring map because when we do a lookup in the map
|
||||
// for the scrollbar, we don't have (or care about) the presShellId.
|
||||
std::unordered_map<ScrollableLayerGuid,
|
||||
HitTestingTreeNode*,
|
||||
ScrollableLayerGuid::HashIgnoringPresShellFn,
|
||||
ScrollableLayerGuid::EqualIgnoringPresShellFn> httnMap;
|
||||
MutexAutoLock lock(mMapLock);
|
||||
|
||||
bool activeAnimations = false;
|
||||
LayersId lastLayersId{(uint64_t)-1};
|
||||
wr::WrPipelineId lastPipelineId;
|
||||
for (const auto& mapping : mApzcMap) {
|
||||
AsyncPanZoomController* apzc = mapping.second;
|
||||
ParentLayerPoint layerTranslation = apzc->GetCurrentAsyncTransform(
|
||||
AsyncPanZoomController::eForCompositing).mTranslation;
|
||||
|
||||
// We iterate backwards here because the HitTestingTreeNode is optimized
|
||||
// for backwards iteration. The equivalent code in AsyncCompositionManager
|
||||
// iterates forwards, but the direction shouldn't really matter in practice
|
||||
// so we do what's faster. In the future, if we need to start doing the
|
||||
// equivalent of AlignFixedAndStickyLayers here, then the order will become
|
||||
// important and we'll need to take that into consideration.
|
||||
ForEachNode<ReverseIterator>(mRootNode.get(),
|
||||
[&](HitTestingTreeNode* aNode)
|
||||
{
|
||||
if (!aNode->IsPrimaryHolder()) {
|
||||
return;
|
||||
}
|
||||
AsyncPanZoomController* apzc = aNode->GetApzc();
|
||||
MOZ_ASSERT(apzc);
|
||||
// The positive translation means the painted content is supposed to
|
||||
// move down (or to the right), and that corresponds to a reduction in
|
||||
// the scroll offset. Since we are effectively giving WR the async
|
||||
// scroll delta here, we want to negate the translation.
|
||||
ParentLayerPoint asyncScrollDelta = -layerTranslation;
|
||||
// XXX figure out what zoom-related conversions need to happen here.
|
||||
aTxn.UpdateScrollPosition(
|
||||
wr::AsPipelineId(apzc->GetGuid().mLayersId),
|
||||
apzc->GetGuid().mScrollId,
|
||||
wr::ToLayoutPoint(LayoutDevicePoint::FromUnknownPoint(asyncScrollDelta.ToUnknownPoint())));
|
||||
|
||||
if (aNode->GetLayersId() != lastLayersId) {
|
||||
// If we walked into or out of a subtree, we need to get the new
|
||||
// pipeline id.
|
||||
RefPtr<WebRenderBridgeParent> wrBridge;
|
||||
CompositorBridgeParent::CallWithIndirectShadowTree(aNode->GetLayersId(),
|
||||
[&wrBridge](LayerTreeState& aState) -> void {
|
||||
wrBridge = aState.mWrBridge;
|
||||
});
|
||||
if (!wrBridge) {
|
||||
// During shutdown we might have layer tree information for stuff
|
||||
// that has already been torn down. In that case just skip over
|
||||
// those layers.
|
||||
return;
|
||||
}
|
||||
lastPipelineId = wrBridge->PipelineId();
|
||||
lastLayersId = aNode->GetLayersId();
|
||||
}
|
||||
apzc->ReportCheckerboard(aSampleTime);
|
||||
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
|
||||
}
|
||||
|
||||
httnMap.emplace(apzc->GetGuid(), aNode);
|
||||
|
||||
ParentLayerPoint layerTranslation = apzc->GetCurrentAsyncTransform(
|
||||
AsyncPanZoomController::eForCompositing).mTranslation;
|
||||
// The positive translation means the painted content is supposed to
|
||||
// move down (or to the right), and that corresponds to a reduction in
|
||||
// the scroll offset. Since we are effectively giving WR the async
|
||||
// scroll delta here, we want to negate the translation.
|
||||
ParentLayerPoint asyncScrollDelta = -layerTranslation;
|
||||
// XXX figure out what zoom-related conversions need to happen here.
|
||||
aTxn.UpdateScrollPosition(lastPipelineId, apzc->GetGuid().mScrollId,
|
||||
wr::ToLayoutPoint(LayoutDevicePoint::FromUnknownPoint(asyncScrollDelta.ToUnknownPoint())));
|
||||
|
||||
apzc->ReportCheckerboard(aSampleTime);
|
||||
activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
|
||||
});
|
||||
|
||||
// Now we iterate over the nodes again, and generate the transforms needed
|
||||
// for scrollbar thumbs. Although we *could* do this as part of the previous
|
||||
// iteration, it's cleaner and more efficient to do it as a separate pass
|
||||
// because now we have a populated httnMap which allows O(log n) lookup here,
|
||||
// resulting in O(n log n) runtime.
|
||||
// Now collect all the async transforms needed for the scrollthumbs.
|
||||
nsTArray<wr::WrTransformProperty> scrollbarTransforms;
|
||||
ForEachNode<ReverseIterator>(mRootNode.get(),
|
||||
[&](HitTestingTreeNode* aNode)
|
||||
{
|
||||
if (!aNode->IsScrollThumbNode()) {
|
||||
return;
|
||||
}
|
||||
ScrollableLayerGuid guid(aNode->GetLayersId(), 0, aNode->GetScrollTargetId());
|
||||
auto it = httnMap.find(guid);
|
||||
if (it == httnMap.end()) {
|
||||
// A scrollbar for content which didn't have an APZC. Possibly the
|
||||
// content isn't layerized. Regardless, we can't async-scroll it so
|
||||
// we can skip the async transform on the scrollbar.
|
||||
return;
|
||||
}
|
||||
|
||||
HitTestingTreeNode* scrollTargetNode = it->second;
|
||||
AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
|
||||
MOZ_ASSERT(scrollTargetApzc);
|
||||
LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
|
||||
[&](const FrameMetrics& aMetrics) {
|
||||
return ComputeTransformForScrollThumb(
|
||||
aNode->GetTransform() * AsyncTransformMatrix(),
|
||||
scrollTargetNode->GetTransform().ToUnknownMatrix(),
|
||||
scrollTargetApzc,
|
||||
aMetrics,
|
||||
aNode->GetScrollbarData(),
|
||||
scrollTargetNode->IsAncestorOf(aNode),
|
||||
nullptr);
|
||||
});
|
||||
scrollbarTransforms.AppendElement(wr::ToWrTransformProperty(
|
||||
aNode->GetScrollbarAnimationId(),
|
||||
transform));
|
||||
});
|
||||
for (const ScrollThumbInfo& info : mScrollThumbInfo) {
|
||||
auto it = mApzcMap.find(info.mTargetGuid);
|
||||
if (it == mApzcMap.end()) {
|
||||
// It could be that |info| is a scrollthumb for content which didn't
|
||||
// have an APZC, for example if the content isn't layerized. Regardless,
|
||||
// we can't async-scroll it so we don't need to worry about putting it
|
||||
// in mScrollThumbInfo.
|
||||
continue;
|
||||
}
|
||||
AsyncPanZoomController* scrollTargetApzc = it->second;
|
||||
MOZ_ASSERT(scrollTargetApzc);
|
||||
LayerToParentLayerMatrix4x4 transform = scrollTargetApzc->CallWithLastContentPaintMetrics(
|
||||
[&](const FrameMetrics& aMetrics) {
|
||||
return ComputeTransformForScrollThumb(
|
||||
info.mThumbTransform * AsyncTransformMatrix(),
|
||||
info.mTargetTransform.ToUnknownMatrix(),
|
||||
scrollTargetApzc,
|
||||
aMetrics,
|
||||
info.mThumbData,
|
||||
info.mTargetIsAncestor,
|
||||
nullptr);
|
||||
});
|
||||
scrollbarTransforms.AppendElement(wr::ToWrTransformProperty(
|
||||
info.mThumbAnimationId,
|
||||
transform));
|
||||
}
|
||||
aTxn.AppendTransformProperties(scrollbarTransforms);
|
||||
|
||||
return activeAnimations;
|
||||
if (activeAnimations) {
|
||||
RefPtr<CompositorController> controller;
|
||||
CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
|
||||
[&](LayerTreeState& aState) -> void {
|
||||
controller = aState.GetCompositorController();
|
||||
});
|
||||
if (controller) {
|
||||
controller->ScheduleRenderOnCompositorThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the clip region to be used for a layer with an APZC. This function
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace mozilla {
|
|||
class MultiTouchInput;
|
||||
|
||||
namespace wr {
|
||||
class TransactionBuilder;
|
||||
class TransactionWrapper;
|
||||
class WebRenderAPI;
|
||||
struct WrTransformProperty;
|
||||
}
|
||||
|
@ -75,6 +75,25 @@ struct ScrollThumbData;
|
|||
* The interpretation of the lock ordering is that if lock A precedes lock B
|
||||
* in the ordering sequence, then you must NOT wait on A while holding B.
|
||||
*
|
||||
* In addition, the WR hit-testing codepath acquires the tree lock and then
|
||||
* blocks on the render backend thread to do the hit-test. Similar operations
|
||||
* elsewhere mean that we need to be careful with which threads are allowed
|
||||
* to acquire which locks and the order they do so. At the time of this writing,
|
||||
* https://bug1391318.bmoattachments.org/attachment.cgi?id=8965040 contains
|
||||
* the most complete description we have of the situation. The total dependency
|
||||
* ordering including both threads and locks is as follows:
|
||||
*
|
||||
* UI main thread
|
||||
* -> GPU main thread // only if GPU enabled
|
||||
* -> Compositor thread
|
||||
* -> SceneBuilder thread // only if WR enabled
|
||||
* -> APZ tree lock
|
||||
* -> RenderBackend thread // only if WR enabled
|
||||
* -> APZC map lock
|
||||
* -> APZC instance lock
|
||||
* -> APZC test lock
|
||||
*
|
||||
* where the -> annotation means the same as described above.
|
||||
* **************************************************************************
|
||||
*/
|
||||
|
||||
|
@ -196,17 +215,14 @@ public:
|
|||
|
||||
/**
|
||||
* Called when webrender is enabled, from the sampler thread. This function
|
||||
* walks through the tree of APZC instances and tells webrender about the
|
||||
* async scroll position. It also advances APZ animations to the specified
|
||||
* sample time. In effect it is the webrender equivalent of (part of) the
|
||||
* code in AsyncCompositionManager. If scrollbar transforms need updating
|
||||
* to reflect the async scroll position, the updated transforms are appended
|
||||
* to the provided transaction as well.
|
||||
* Returns true if any APZ animations are in progress and we need to keep
|
||||
* compositing.
|
||||
* populates the provided transaction with any async scroll offsets needed.
|
||||
* It also advances APZ animations to the specified sample time, and requests
|
||||
* another composite if there are still active animations.
|
||||
* In effect it is the webrender equivalent of (part of) the code in
|
||||
* AsyncCompositionManager.
|
||||
*/
|
||||
bool PushStateToWR(wr::TransactionBuilder& aTxn,
|
||||
const TimeStamp& aSampleTime);
|
||||
void SampleForWebRender(wr::TransactionWrapper& aTxn,
|
||||
const TimeStamp& aSampleTime);
|
||||
|
||||
/**
|
||||
* Walk the tree of APZCs and flushes the repaint requests for all the APZCS
|
||||
|
@ -733,15 +749,57 @@ private:
|
|||
mutable mozilla::RecursiveMutex mTreeLock;
|
||||
RefPtr<HitTestingTreeNode> mRootNode;
|
||||
|
||||
/* A map for quick access to get APZC instances by guid, without having to
|
||||
/** A lock that protects mApzcMap and mScrollThumbInfo. */
|
||||
mutable mozilla::Mutex mMapLock;
|
||||
/**
|
||||
* A map for quick access to get APZC instances by guid, without having to
|
||||
* acquire the tree lock. mMapLock must be acquired while accessing or
|
||||
* modifying mApzcMap.
|
||||
*/
|
||||
mutable mozilla::Mutex mMapLock;
|
||||
std::unordered_map<ScrollableLayerGuid,
|
||||
RefPtr<AsyncPanZoomController>,
|
||||
ScrollableLayerGuid::HashIgnoringPresShellFn,
|
||||
ScrollableLayerGuid::EqualIgnoringPresShellFn> mApzcMap;
|
||||
/**
|
||||
* A helper structure to store all the information needed to compute the
|
||||
* async transform for a scrollthumb on the sampler thread.
|
||||
*/
|
||||
struct ScrollThumbInfo {
|
||||
uint64_t mThumbAnimationId;
|
||||
CSSTransformMatrix mThumbTransform;
|
||||
ScrollbarData mThumbData;
|
||||
ScrollableLayerGuid mTargetGuid;
|
||||
CSSTransformMatrix mTargetTransform;
|
||||
bool mTargetIsAncestor;
|
||||
|
||||
ScrollThumbInfo(const uint64_t& aThumbAnimationId,
|
||||
const CSSTransformMatrix& aThumbTransform,
|
||||
const ScrollbarData& aThumbData,
|
||||
const ScrollableLayerGuid& aTargetGuid,
|
||||
const CSSTransformMatrix& aTargetTransform,
|
||||
bool aTargetIsAncestor)
|
||||
: mThumbAnimationId(aThumbAnimationId)
|
||||
, mThumbTransform(aThumbTransform)
|
||||
, mThumbData(aThumbData)
|
||||
, mTargetGuid(aTargetGuid)
|
||||
, mTargetTransform(aTargetTransform)
|
||||
, mTargetIsAncestor(aTargetIsAncestor)
|
||||
{
|
||||
MOZ_ASSERT(mTargetGuid.mScrollId == mThumbData.mTargetViewId);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* If this APZCTreeManager is being used with WebRender, this vector gets
|
||||
* populated during a layers update. It holds a package of information needed
|
||||
* to compute and set the async transforms on scroll thumbs. This information
|
||||
* is extracted from the HitTestingTreeNodes for the WebRender case because
|
||||
* accessing the HitTestingTreeNodes requires holding the tree lock which
|
||||
* we cannot do on the WR sampler thread. mScrollThumbInfo, however, can
|
||||
* be accessed while just holding the mMapLock which is safe to do on the
|
||||
* sampler thread.
|
||||
* mMapLock must be acquired while accessing or modifying mScrollThumbInfo.
|
||||
*/
|
||||
std::vector<ScrollThumbInfo> mScrollThumbInfo;
|
||||
|
||||
/* Holds the zoom constraints for scrollable layers, as determined by the
|
||||
* the main-thread gecko code. This can only be accessed on the updater
|
||||
|
|
|
@ -13,12 +13,21 @@
|
|||
#include "mozilla/layers/LayerMetricsWrapper.h"
|
||||
#include "mozilla/layers/SynchronousTask.h"
|
||||
#include "TreeTraversal.h"
|
||||
#include "mozilla/webrender/WebRenderAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
StaticMutex APZSampler::sWindowIdLock;
|
||||
std::unordered_map<uint64_t, APZSampler*> APZSampler::sWindowIdMap;
|
||||
|
||||
|
||||
APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz)
|
||||
: mApz(aApz)
|
||||
#ifdef DEBUG
|
||||
, mSamplerThreadQueried(false)
|
||||
#endif
|
||||
, mSampleTimeLock("APZSampler::mSampleTimeLock")
|
||||
{
|
||||
MOZ_ASSERT(aApz);
|
||||
mApz->SetSampler(this);
|
||||
|
@ -27,15 +36,65 @@ APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz)
|
|||
APZSampler::~APZSampler()
|
||||
{
|
||||
mApz->SetSampler(nullptr);
|
||||
|
||||
StaticMutexAutoLock lock(sWindowIdLock);
|
||||
if (mWindowId) {
|
||||
sWindowIdMap.erase(wr::AsUint64(*mWindowId));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
APZSampler::PushStateToWR(wr::TransactionBuilder& aTxn,
|
||||
const TimeStamp& aSampleTime)
|
||||
void
|
||||
APZSampler::SetWebRenderWindowId(const wr::WindowId& aWindowId)
|
||||
{
|
||||
// This function will be removed eventually since we'll have WR pull
|
||||
// the transforms from APZ instead.
|
||||
return mApz->PushStateToWR(aTxn, aSampleTime);
|
||||
StaticMutexAutoLock lock(sWindowIdLock);
|
||||
MOZ_ASSERT(!mWindowId);
|
||||
mWindowId = Some(aWindowId);
|
||||
sWindowIdMap[wr::AsUint64(aWindowId)] = this;
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
APZSampler::SetSamplerThread(const wr::WrWindowId& aWindowId)
|
||||
{
|
||||
if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
|
||||
// Ensure nobody tried to use the updater thread before this point.
|
||||
MOZ_ASSERT(!sampler->mSamplerThreadQueried);
|
||||
sampler->mSamplerThreadId = Some(PlatformThread::CurrentId());
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
APZSampler::SampleForWebRender(const wr::WrWindowId& aWindowId,
|
||||
wr::Transaction* aTransaction)
|
||||
{
|
||||
if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
|
||||
wr::TransactionWrapper txn(aTransaction);
|
||||
sampler->SampleForWebRender(txn);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
APZSampler::SetSampleTime(const TimeStamp& aSampleTime)
|
||||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
MutexAutoLock lock(mSampleTimeLock);
|
||||
mSampleTime = aSampleTime;
|
||||
}
|
||||
|
||||
void
|
||||
APZSampler::SampleForWebRender(wr::TransactionWrapper& aTxn)
|
||||
{
|
||||
AssertOnSamplerThread();
|
||||
TimeStamp sampleTime;
|
||||
{ // scope lock
|
||||
MutexAutoLock lock(mSampleTimeLock);
|
||||
|
||||
// If mSampleTime is null we're in a startup phase where the
|
||||
// WebRenderBridgeParent hasn't yet provided us with a sample time.
|
||||
// If we're that early there probably aren't any APZ animations happening
|
||||
// anyway, so using Timestamp::Now() should be fine.
|
||||
sampleTime = mSampleTime.IsNull() ? TimeStamp::Now() : mSampleTime;
|
||||
}
|
||||
mApz->SampleForWebRender(aTxn, sampleTime);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -155,8 +214,58 @@ APZSampler::AssertOnSamplerThread() const
|
|||
bool
|
||||
APZSampler::IsSamplerThread() const
|
||||
{
|
||||
if (UsingWebRenderSamplerThread()) {
|
||||
return PlatformThread::CurrentId() == *mSamplerThreadId;
|
||||
}
|
||||
return CompositorThreadHolder::IsInCompositorThread();
|
||||
}
|
||||
|
||||
bool
|
||||
APZSampler::UsingWebRenderSamplerThread() const
|
||||
{
|
||||
// If mSamplerThreadId is not set at the point that this is called, then
|
||||
// that means that either (a) WebRender is not enabled for the compositor
|
||||
// to which this APZSampler is attached or (b) we are attempting to do
|
||||
// something sampler-related before WebRender is up and running. In case
|
||||
// (a) falling back to the compositor thread is correct, and in case (b)
|
||||
// we should stop doing the sampler-related thing so early. We catch this
|
||||
// case by setting the mSamplerThreadQueried flag and asserting on WR
|
||||
// initialization.
|
||||
#ifdef DEBUG
|
||||
mSamplerThreadQueried = true;
|
||||
#endif
|
||||
return mSamplerThreadId.isSome();
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<APZSampler>
|
||||
APZSampler::GetSampler(const wr::WrWindowId& aWindowId)
|
||||
{
|
||||
RefPtr<APZSampler> sampler;
|
||||
StaticMutexAutoLock lock(sWindowIdLock);
|
||||
auto it = sWindowIdMap.find(wr::AsUint64(aWindowId));
|
||||
if (it != sWindowIdMap.end()) {
|
||||
sampler = it->second;
|
||||
}
|
||||
return sampler.forget();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
void
|
||||
apz_register_sampler(mozilla::wr::WrWindowId aWindowId)
|
||||
{
|
||||
mozilla::layers::APZSampler::SetSamplerThread(aWindowId);
|
||||
}
|
||||
|
||||
void
|
||||
apz_sample_transforms(mozilla::wr::WrWindowId aWindowId,
|
||||
mozilla::wr::Transaction *aTransaction)
|
||||
{
|
||||
mozilla::layers::APZSampler::SampleForWebRender(aWindowId, aTransaction);
|
||||
}
|
||||
|
||||
void
|
||||
apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -3577,12 +3577,14 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
|
|||
{
|
||||
AssertOnSamplerThread();
|
||||
|
||||
// This function may get called multiple with the same sample time, because
|
||||
// there may be multiple layers with this APZC, and each layer invokes this
|
||||
// function during composition. However we only want to do one animation step
|
||||
// per composition so we need to deduplicate these calls first.
|
||||
// This function may get called multiple with the same sample time, for two
|
||||
// reasons: (1) there may be multiple layers with this APZC, and each layer
|
||||
// invokes this function during composition, and (2) we might composite
|
||||
// multiple times at the same timestamp.
|
||||
// However we only want to do one animation step per composition so we need
|
||||
// to deduplicate these calls first.
|
||||
if (mLastSampleTime == aSampleTime) {
|
||||
return false;
|
||||
return (mAnimation != nullptr);
|
||||
}
|
||||
|
||||
// Sample the composited async transform once per composite. Note that we
|
||||
|
@ -3671,14 +3673,11 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
|
|||
// since the tasks are allowed to call APZCTreeManager methods which can grab
|
||||
// the tree lock.
|
||||
for (uint32_t i = 0; i < deferredTasks.Length(); ++i) {
|
||||
deferredTasks[i]->Run();
|
||||
deferredTasks[i] = nullptr;
|
||||
APZThreadUtils::RunOnControllerThread(deferredTasks[i].forget());
|
||||
}
|
||||
|
||||
// One of the deferred tasks may have started a new animation. In this case,
|
||||
// we want to ask the compositor to schedule a new composite.
|
||||
requestAnimationFrame |= (mAnimation != nullptr);
|
||||
|
||||
// If any of the deferred tasks starts a new animation, it will request a
|
||||
// new composite directly, so we can just return requestAnimationFrame here.
|
||||
return requestAnimationFrame;
|
||||
}
|
||||
|
||||
|
|
|
@ -1779,6 +1779,10 @@ CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipel
|
|||
// that the callback from the updater thread can find the right APZUpdater.
|
||||
mApzUpdater->SetWebRenderWindowId(windowId);
|
||||
}
|
||||
if (mApzSampler) {
|
||||
// Same as for mApzUpdater, but for the sampler thread.
|
||||
mApzSampler->SetWebRenderWindowId(windowId);
|
||||
}
|
||||
RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(this, Move(widget), windowId, aSize);
|
||||
if (!api) {
|
||||
mWrBridge = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
|
||||
|
|
|
@ -532,12 +532,12 @@ WebRenderBridgeParent::UpdateAPZScrollData(const wr::Epoch& aEpoch,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WebRenderBridgeParent::PushAPZStateToWR(wr::TransactionBuilder& aTxn)
|
||||
void
|
||||
WebRenderBridgeParent::SetAPZSampleTime()
|
||||
{
|
||||
CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
|
||||
if (!cbp) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (RefPtr<APZSampler> apz = cbp->GetAPZSampler()) {
|
||||
TimeStamp animationTime = cbp->GetTestingTimeStamp().valueOr(
|
||||
|
@ -548,9 +548,8 @@ WebRenderBridgeParent::PushAPZStateToWR(wr::TransactionBuilder& aTxn)
|
|||
if (frameInterval != TimeDuration::Forever()) {
|
||||
animationTime += frameInterval;
|
||||
}
|
||||
return apz->PushStateToWR(aTxn, animationTime);
|
||||
apz->SetSampleTime(animationTime);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
|
@ -1243,15 +1242,10 @@ WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::In
|
|||
ScheduleGenerateFrame();
|
||||
}
|
||||
// We do this even if the arrays are empty, because it will clear out any
|
||||
// previous properties stored on the WR side, which is desirable. Also, we
|
||||
// must do this before the PushAPZStateToWR call which will append more
|
||||
// properties, If we did this after that call, this would clobber those
|
||||
// properties.
|
||||
// previous properties store on the WR side, which is desirable.
|
||||
txn.UpdateDynamicProperties(opacityArray, transformArray);
|
||||
|
||||
if (PushAPZStateToWR(txn)) {
|
||||
ScheduleGenerateFrame();
|
||||
}
|
||||
SetAPZSampleTime();
|
||||
|
||||
wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId());
|
||||
|
||||
|
|
|
@ -220,11 +220,8 @@ private:
|
|||
|
||||
CompositorBridgeParent* GetRootCompositorBridgeParent() const;
|
||||
|
||||
// Have APZ push the async scroll state to WR. Returns true if an APZ
|
||||
// animation is in effect and we need to schedule another composition.
|
||||
// If scrollbars need their transforms updated, the transaction builder
|
||||
// is populated with the property update details via AppendTransformProperties
|
||||
bool PushAPZStateToWR(wr::TransactionBuilder& aTxn);
|
||||
// Tell APZ what the subsequent sampling's timestamp should be.
|
||||
void SetAPZSampleTime();
|
||||
|
||||
wr::Epoch GetNextWrEpoch();
|
||||
|
||||
|
|
|
@ -252,6 +252,27 @@ TransactionBuilder::UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
|
|||
wr_transaction_scroll_layer(mTxn, aPipelineId, aScrollId, aScrollPosition);
|
||||
}
|
||||
|
||||
TransactionWrapper::TransactionWrapper(Transaction* aTxn)
|
||||
: mTxn(aTxn)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TransactionWrapper::AppendTransformProperties(const nsTArray<wr::WrTransformProperty>& aTransformArray)
|
||||
{
|
||||
wr_transaction_append_transform_properties(
|
||||
mTxn,
|
||||
aTransformArray.IsEmpty() ? nullptr : aTransformArray.Elements(),
|
||||
aTransformArray.Length());
|
||||
}
|
||||
|
||||
void
|
||||
TransactionWrapper::UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
|
||||
const layers::FrameMetrics::ViewID& aScrollId,
|
||||
const wr::LayoutPoint& aScrollPosition)
|
||||
{
|
||||
wr_transaction_scroll_layer(mTxn, aPipelineId, aScrollId, aScrollPosition);
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
WebRenderAPI::InitExternalLogHandler()
|
||||
|
|
|
@ -147,6 +147,19 @@ protected:
|
|||
wr::ResourceUpdates* mResourceUpdates;
|
||||
};
|
||||
|
||||
class TransactionWrapper
|
||||
{
|
||||
public:
|
||||
explicit TransactionWrapper(Transaction* aTxn);
|
||||
|
||||
void AppendTransformProperties(const nsTArray<wr::WrTransformProperty>& aTransformArray);
|
||||
void UpdateScrollPosition(const wr::WrPipelineId& aPipelineId,
|
||||
const layers::FrameMetrics::ViewID& aScrollId,
|
||||
const wr::LayoutPoint& aScrollPosition);
|
||||
private:
|
||||
Transaction* mTxn;
|
||||
};
|
||||
|
||||
class WebRenderAPI
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderAPI);
|
||||
|
|
|
@ -12,7 +12,7 @@ use webrender::{ReadPixelsFormat, Renderer, RendererOptions, ThreadListener};
|
|||
use webrender::{ExternalImage, ExternalImageHandler, ExternalImageSource};
|
||||
use webrender::DebugFlags;
|
||||
use webrender::{ApiRecordingReceiver, BinaryRecorder};
|
||||
use webrender::{PipelineInfo, SceneBuilderHooks};
|
||||
use webrender::{AsyncPropertySampler, PipelineInfo, SceneBuilderHooks};
|
||||
use webrender::{ProgramCache, UploadMethod, VertexUsageHint};
|
||||
use thread_profiler::register_thread_with_profiler;
|
||||
use moz2d_renderer::Moz2dImageRenderer;
|
||||
|
@ -684,7 +684,10 @@ pub unsafe extern "C" fn wr_pipeline_info_delete(_info: WrPipelineInfo) {
|
|||
// the underlying vec memory
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &mut Transaction to an extern function
|
||||
extern "C" {
|
||||
// These callbacks are invoked from the scene builder thread (aka the APZ
|
||||
// updater thread)
|
||||
fn apz_register_updater(window_id: WrWindowId);
|
||||
fn apz_pre_scene_swap(window_id: WrWindowId);
|
||||
// This function takes ownership of the pipeline_info and is responsible for
|
||||
|
@ -692,6 +695,12 @@ extern "C" {
|
|||
fn apz_post_scene_swap(window_id: WrWindowId, pipeline_info: WrPipelineInfo);
|
||||
fn apz_run_updater(window_id: WrWindowId);
|
||||
fn apz_deregister_updater(window_id: WrWindowId);
|
||||
|
||||
// These callbacks are invoked from the render backend thread (aka the APZ
|
||||
// sampler thread)
|
||||
fn apz_register_sampler(window_id: WrWindowId);
|
||||
fn apz_sample_transforms(window_id: WrWindowId, transaction: &mut Transaction);
|
||||
fn apz_deregister_sampler(window_id: WrWindowId);
|
||||
}
|
||||
|
||||
struct APZCallbacks {
|
||||
|
@ -729,6 +738,35 @@ impl SceneBuilderHooks for APZCallbacks {
|
|||
}
|
||||
}
|
||||
|
||||
struct SamplerCallback {
|
||||
window_id: WrWindowId,
|
||||
}
|
||||
|
||||
impl SamplerCallback {
|
||||
pub fn new(window_id: WrWindowId) -> Self {
|
||||
SamplerCallback {
|
||||
window_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncPropertySampler for SamplerCallback {
|
||||
fn register(&self) {
|
||||
unsafe { apz_register_sampler(self.window_id) }
|
||||
}
|
||||
|
||||
fn sample(&self) -> Vec<FrameMsg> {
|
||||
let mut transaction = Transaction::new();
|
||||
unsafe { apz_sample_transforms(self.window_id, &mut transaction) };
|
||||
// TODO: also omta_sample_transforms(...)
|
||||
transaction.get_frame_ops()
|
||||
}
|
||||
|
||||
fn deregister(&self) {
|
||||
unsafe { apz_deregister_sampler(self.window_id) }
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn gecko_profiler_register_thread(name: *const ::std::os::raw::c_char);
|
||||
fn gecko_profiler_unregister_thread();
|
||||
|
@ -869,6 +907,7 @@ pub extern "C" fn wr_window_new(window_id: WrWindowId,
|
|||
renderer_id: Some(window_id.0),
|
||||
upload_method,
|
||||
scene_builder_hooks: Some(Box::new(APZCallbacks::new(window_id))),
|
||||
sampler: Some(Box::new(SamplerCallback::new(window_id))),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -1134,7 +1173,6 @@ pub extern "C" fn wr_transaction_scroll_layer(
|
|||
scroll_id: u64,
|
||||
new_scroll_origin: LayoutPoint
|
||||
) {
|
||||
assert!(unsafe { is_in_compositor_thread() });
|
||||
let scroll_id = ExternalScrollId(scroll_id, pipeline_id);
|
||||
txn.scroll_node_with_id(new_scroll_origin, scroll_id, ScrollClamping::NoClamping);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ struct FontInstanceFlags {
|
|||
};
|
||||
};
|
||||
|
||||
struct Transaction;
|
||||
struct WrWindowId;
|
||||
struct WrPipelineInfo;
|
||||
|
||||
|
@ -97,6 +98,9 @@ void apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId, mozilla::wr::WrPipel
|
|||
void apz_run_updater(mozilla::wr::WrWindowId aWindowId);
|
||||
void apz_deregister_updater(mozilla::wr::WrWindowId aWindowId);
|
||||
|
||||
void apz_register_sampler(mozilla::wr::WrWindowId aWindowId);
|
||||
void apz_sample_transforms(mozilla::wr::WrWindowId aWindowId, mozilla::wr::Transaction *aTransaction);
|
||||
void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId);
|
||||
} // extern "C"
|
||||
|
||||
// Some useful defines to stub out webrender binding functions for when we
|
||||
|
|
|
@ -978,6 +978,8 @@ extern void AddNativeFontHandle(WrFontKey aKey,
|
|||
|
||||
extern void DeleteFontData(WrFontKey aKey);
|
||||
|
||||
extern void apz_deregister_sampler(WrWindowId aWindowId);
|
||||
|
||||
extern void apz_deregister_updater(WrWindowId aWindowId);
|
||||
|
||||
extern void apz_post_scene_swap(WrWindowId aWindowId,
|
||||
|
@ -985,10 +987,15 @@ extern void apz_post_scene_swap(WrWindowId aWindowId,
|
|||
|
||||
extern void apz_pre_scene_swap(WrWindowId aWindowId);
|
||||
|
||||
extern void apz_register_sampler(WrWindowId aWindowId);
|
||||
|
||||
extern void apz_register_updater(WrWindowId aWindowId);
|
||||
|
||||
extern void apz_run_updater(WrWindowId aWindowId);
|
||||
|
||||
extern void apz_sample_transforms(WrWindowId aWindowId,
|
||||
Transaction *aTransaction);
|
||||
|
||||
extern void gecko_printf_stderr_output(const char *aMsg);
|
||||
|
||||
extern void gecko_profiler_register_thread(const char *aName);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,67 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 frontend_BinSource_macros_h
|
||||
#define frontend_BinSource_macros_h
|
||||
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is not falsy.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define BINJS_TRY(EXPR) \
|
||||
do { \
|
||||
if (!EXPR) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
} while(false)
|
||||
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is not falsy.
|
||||
// In case of success, assign the result to VAR.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define BINJS_TRY_VAR(VAR, EXPR) \
|
||||
do { \
|
||||
VAR = EXPR; \
|
||||
if (!VAR) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
} while (false)
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is not falsy.
|
||||
// In case of success, assign the result to a new variable VAR.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define BINJS_TRY_DECL(VAR, EXPR) \
|
||||
auto VAR = EXPR; \
|
||||
if (!VAR) \
|
||||
return cx_->alreadyReportedError();
|
||||
|
||||
#define BINJS_TRY_EMPL(VAR, EXPR) \
|
||||
do { \
|
||||
auto _tryEmplResult = EXPR; \
|
||||
if (!_tryEmplResult) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
VAR.emplace(_tryEmplResult.unwrap()); \
|
||||
} while (false)
|
||||
|
||||
#define BINJS_MOZ_TRY_EMPLACE(VAR, EXPR) \
|
||||
do { \
|
||||
auto _tryEmplResult = EXPR; \
|
||||
if (_tryEmplResult.isErr()) \
|
||||
return ::mozilla::Err(_tryEmplResult.unwrapErr()); \
|
||||
VAR.emplace(_tryEmplResult.unwrap()); \
|
||||
} while (false)
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is a success.
|
||||
// In case of success, unwrap and assign the result to a new variable VAR.
|
||||
//
|
||||
// In case of error, propagate the error.
|
||||
#define BINJS_MOZ_TRY_DECL(VAR, EXPR) \
|
||||
auto _##VAR = EXPR; \
|
||||
if (_##VAR.isErr()) \
|
||||
return ::mozilla::Err(_##VAR.unwrapErr()); \
|
||||
auto VAR = _##VAR.unwrap();
|
||||
|
||||
#endif // frontend_BinSource_macros_h
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include "frontend/BinSource-macros.h"
|
||||
#include "frontend/BinTokenReaderTester.h"
|
||||
#include "frontend/FullParseHandler.h"
|
||||
#include "frontend/Parser.h"
|
||||
|
@ -68,68 +69,23 @@
|
|||
//
|
||||
// They should be treated lazily (whenever we open a subscope), like bindings.
|
||||
|
||||
// Evaluate an expression, checking that the result is not 0.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define TRY(EXPR) \
|
||||
do { \
|
||||
if (!EXPR) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
} while(false)
|
||||
|
||||
|
||||
#define TRY_VAR(VAR, EXPR) \
|
||||
do { \
|
||||
VAR = EXPR; \
|
||||
if (!VAR) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
} while (false)
|
||||
|
||||
#define TRY_DECL(VAR, EXPR) \
|
||||
auto VAR = EXPR; \
|
||||
if (!VAR) \
|
||||
return cx_->alreadyReportedError();
|
||||
|
||||
#define TRY_EMPL(VAR, EXPR) \
|
||||
do { \
|
||||
auto _tryEmplResult = EXPR; \
|
||||
if (!_tryEmplResult) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
VAR.emplace(_tryEmplResult.unwrap()); \
|
||||
} while (false)
|
||||
|
||||
#define MOZ_TRY_EMPLACE(VAR, EXPR) \
|
||||
do { \
|
||||
auto _tryEmplResult = EXPR; \
|
||||
if (_tryEmplResult.isErr()) \
|
||||
return ::mozilla::Err(_tryEmplResult.unwrapErr()); \
|
||||
VAR.emplace(_tryEmplResult.unwrap()); \
|
||||
} while (false)
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
using AutoList = BinTokenReaderTester::AutoList;
|
||||
using AutoTaggedTuple = BinTokenReaderTester::AutoTaggedTuple;
|
||||
using AutoTuple = BinTokenReaderTester::AutoTuple;
|
||||
using BinFields = BinTokenReaderTester::BinFields;
|
||||
using Chars = BinTokenReaderTester::Chars;
|
||||
using NameBag = GCHashSet<JSString*>;
|
||||
using Names = GCVector<JSString*, 8>;
|
||||
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
|
||||
|
||||
// ------------- Toplevel constructions
|
||||
|
||||
JS::Result<ParseNode*>
|
||||
BinASTParser::parse(const Vector<uint8_t>& data)
|
||||
template<typename Tok> JS::Result<ParseNode*>
|
||||
BinASTParser<Tok>::parse(const Vector<uint8_t>& data)
|
||||
{
|
||||
return parse(data.begin(), data.length());
|
||||
}
|
||||
|
||||
JS::Result<ParseNode*>
|
||||
BinASTParser::parse(const uint8_t* start, const size_t length)
|
||||
template<typename Tok> JS::Result<ParseNode*>
|
||||
BinASTParser<Tok>::parse(const uint8_t* start, const size_t length)
|
||||
{
|
||||
auto result = parseAux(start, length);
|
||||
poison(); // Make sure that the parser is never used again accidentally.
|
||||
|
@ -137,8 +93,8 @@ BinASTParser::parse(const uint8_t* start, const size_t length)
|
|||
}
|
||||
|
||||
|
||||
JS::Result<ParseNode*>
|
||||
BinASTParser::parseAux(const uint8_t* start, const size_t length)
|
||||
template<typename Tok> JS::Result<ParseNode*>
|
||||
BinASTParser<Tok>::parseAux(const uint8_t* start, const size_t length)
|
||||
{
|
||||
tokenizer_.emplace(cx_, start, length);
|
||||
|
||||
|
@ -153,6 +109,8 @@ BinASTParser::parseAux(const uint8_t* start, const size_t length)
|
|||
if (!varScope.init(&globalpc))
|
||||
return cx_->alreadyReportedError();
|
||||
|
||||
MOZ_TRY(tokenizer_->readHeader());
|
||||
|
||||
ParseNode* result(nullptr);
|
||||
MOZ_TRY_VAR(result, parseProgram());
|
||||
|
||||
|
@ -164,12 +122,14 @@ BinASTParser::parseAux(const uint8_t* start, const size_t length)
|
|||
return result; // Magic conversion to Ok.
|
||||
}
|
||||
|
||||
JS::Result<FunctionBox*>
|
||||
BinASTParser::buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind)
|
||||
template<typename Tok> JS::Result<FunctionBox*>
|
||||
BinASTParser<Tok>::buildFunctionBox(GeneratorKind generatorKind,
|
||||
FunctionAsyncKind functionAsyncKind,
|
||||
FunctionSyntaxKind syntax)
|
||||
{
|
||||
// Allocate the function before walking down the tree.
|
||||
RootedFunction fun(cx_);
|
||||
TRY_VAR(fun, NewFunctionWithProto(cx_,
|
||||
BINJS_TRY_VAR(fun, NewFunctionWithProto(cx_,
|
||||
/* native = */ nullptr,
|
||||
/* nargs placeholder = */ 0,
|
||||
JSFunction::INTERPRETED_NORMAL,
|
||||
|
@ -179,7 +139,7 @@ BinASTParser::buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind fu
|
|||
gc::AllocKind::FUNCTION,
|
||||
TenuredObject
|
||||
));
|
||||
TRY_DECL(funbox, alloc_.new_<FunctionBox>(cx_,
|
||||
BINJS_TRY_DECL(funbox, alloc_.new_<FunctionBox>(cx_,
|
||||
traceListHead_,
|
||||
fun,
|
||||
/* toStringStart = */ 0,
|
||||
|
@ -189,31 +149,28 @@ BinASTParser::buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind fu
|
|||
functionAsyncKind));
|
||||
|
||||
traceListHead_ = funbox;
|
||||
FunctionSyntaxKind syntax = FunctionSyntaxKind::Expression; // FIXME - What if we're assigning?
|
||||
// FIXME: The only thing we need to know is whether this is a
|
||||
// ClassConstructor/DerivedClassConstructor
|
||||
funbox->initWithEnclosingParseContext(parseContext_, syntax);
|
||||
return funbox;
|
||||
}
|
||||
|
||||
JS::Result<ParseNode*>
|
||||
BinASTParser::buildFunction(const size_t start, const BinKind kind, ParseNode* name,
|
||||
template<typename Tok> JS::Result<ParseNode*>
|
||||
BinASTParser<Tok>::buildFunction(const size_t start, const BinKind kind, ParseNode* name,
|
||||
ParseNode* params, ParseNode* body, FunctionBox* funbox)
|
||||
{
|
||||
TokenPos pos = tokenizer_->pos(start);
|
||||
|
||||
RootedAtom atom((cx_));
|
||||
if (name)
|
||||
atom = name->name();
|
||||
atom = name->pn_atom;
|
||||
|
||||
|
||||
funbox->function()->setArgCount(uint16_t(params->pn_count));
|
||||
funbox->function()->setArgCount(params ? uint16_t(params->pn_count) : 0);
|
||||
funbox->function()->initAtom(atom);
|
||||
|
||||
// ParseNode represents the body as concatenated after the params.
|
||||
params->appendWithoutOrderAssumption(body);
|
||||
|
||||
TRY_DECL(result, kind == BinKind::FunctionDeclaration
|
||||
BINJS_TRY_DECL(result, kind == BinKind::FunctionDeclaration
|
||||
? factory_.newFunctionStatement(pos)
|
||||
: factory_.newFunctionExpression(pos));
|
||||
|
||||
|
@ -229,12 +186,12 @@ BinASTParser::buildFunction(const size_t start, const BinKind kind, ParseNode* n
|
|||
ParseContext::Scope& funScope = parseContext_->functionScope();
|
||||
ParseContext::Scope::AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
|
||||
MOZ_ASSERT(!p);
|
||||
TRY(funScope.addDeclaredName(parseContext_, p, dotThis, DeclarationKind::Var,
|
||||
BINJS_TRY(funScope.addDeclaredName(parseContext_, p, dotThis, DeclarationKind::Var,
|
||||
DeclaredNameInfo::npos));
|
||||
funbox->setHasThisBinding();
|
||||
}
|
||||
|
||||
TRY_DECL(bindings,
|
||||
BINJS_TRY_DECL(bindings,
|
||||
NewFunctionScopeData(cx_, parseContext_->functionScope(),
|
||||
/* hasParameterExprs = */ false, alloc_, parseContext_));
|
||||
|
||||
|
@ -243,48 +200,48 @@ BinASTParser::buildFunction(const size_t start, const BinKind kind, ParseNode* n
|
|||
return result;
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::parseAndUpdateCapturedNames()
|
||||
template<typename Tok> JS::Result<Ok>
|
||||
BinASTParser<Tok>::parseAndUpdateCapturedNames()
|
||||
{
|
||||
// For the moment, we do not attempt to validate the list of captured names.
|
||||
AutoList guard(*tokenizer_);
|
||||
uint32_t length = 0;
|
||||
|
||||
TRY(tokenizer_->enterList(length, guard));
|
||||
MOZ_TRY(tokenizer_->enterList(length, guard));
|
||||
RootedAtom name(cx_);
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
name = nullptr;
|
||||
|
||||
MOZ_TRY(readString(&name));
|
||||
MOZ_TRY_VAR(name, tokenizer_->readAtom());
|
||||
}
|
||||
TRY(guard.done());
|
||||
MOZ_TRY(guard.done());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::parseAndUpdateScopeNames(ParseContext::Scope& scope, DeclarationKind kind)
|
||||
template<typename Tok> JS::Result<Ok>
|
||||
BinASTParser<Tok>::parseAndUpdateScopeNames(ParseContext::Scope& scope, DeclarationKind kind)
|
||||
{
|
||||
AutoList guard(*tokenizer_);
|
||||
uint32_t length = 0;
|
||||
|
||||
TRY(tokenizer_->enterList(length, guard));
|
||||
MOZ_TRY(tokenizer_->enterList(length, guard));
|
||||
RootedAtom name(cx_);
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
name = nullptr;
|
||||
|
||||
MOZ_TRY(readString(&name));
|
||||
MOZ_TRY_VAR(name, tokenizer_->readAtom());
|
||||
auto ptr = scope.lookupDeclaredNameForAdd(name);
|
||||
if (ptr)
|
||||
return raiseError("Variable redeclaration");
|
||||
|
||||
TRY(scope.addDeclaredName(parseContext_, ptr, name.get(), kind, tokenizer_->offset()));
|
||||
BINJS_TRY(scope.addDeclaredName(parseContext_, ptr, name.get(), kind, tokenizer_->offset()));
|
||||
}
|
||||
TRY(guard.done());
|
||||
MOZ_TRY(guard.done());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::checkBinding(JSAtom* name)
|
||||
template<typename Tok> JS::Result<Ok>
|
||||
BinASTParser<Tok>::checkBinding(JSAtom* name)
|
||||
{
|
||||
// Check that the variable appears in the corresponding scope.
|
||||
ParseContext::Scope& scope =
|
||||
|
@ -299,17 +256,17 @@ BinASTParser::checkBinding(JSAtom* name)
|
|||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<ParseNode*>
|
||||
BinASTParser::appendDirectivesToBody(ParseNode* body, ParseNode* directives)
|
||||
template<typename Tok> JS::Result<ParseNode*>
|
||||
BinASTParser<Tok>::appendDirectivesToBody(ParseNode* body, ParseNode* directives)
|
||||
{
|
||||
ParseNode* result = body;
|
||||
if (directives && directives->pn_count >= 1) {
|
||||
MOZ_ASSERT(directives->isArity(PN_LIST));
|
||||
|
||||
// Convert directive list to a list of strings.
|
||||
TRY_DECL(prefix, factory_.newStatementList(directives->pn_head->pn_pos));
|
||||
BINJS_TRY_DECL(prefix, factory_.newStatementList(directives->pn_head->pn_pos));
|
||||
for (ParseNode* iter = directives->pn_head; iter != nullptr; iter = iter->pn_next) {
|
||||
TRY_DECL(statement, factory_.newExprStatement(iter, iter->pn_pos.end));
|
||||
BINJS_TRY_DECL(statement, factory_.newExprStatement(iter, iter->pn_pos.end));
|
||||
prefix->appendWithoutOrderAssumption(statement);
|
||||
}
|
||||
|
||||
|
@ -331,177 +288,88 @@ BinASTParser::appendDirectivesToBody(ParseNode* body, ParseNode* directives)
|
|||
return result;
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::readString(MutableHandleAtom out)
|
||||
{
|
||||
MOZ_ASSERT(!out);
|
||||
|
||||
Maybe<Chars> string;
|
||||
MOZ_TRY(readMaybeString(string));
|
||||
MOZ_ASSERT(string);
|
||||
|
||||
RootedAtom atom(cx_);
|
||||
TRY_VAR(atom, AtomizeUTF8Chars(cx_, (const char*)string->begin(), string->length()));
|
||||
|
||||
out.set(Move(atom));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::readMaybeString(MutableHandleAtom out)
|
||||
{
|
||||
MOZ_ASSERT(!out);
|
||||
|
||||
Maybe<Chars> string;
|
||||
MOZ_TRY(readMaybeString(string));
|
||||
if (!string) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
RootedAtom atom(cx_);
|
||||
TRY_VAR(atom, AtomizeUTF8Chars(cx_, (const char*)string->begin(), string->length()));
|
||||
|
||||
out.set(Move(atom));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::readString(Chars& result)
|
||||
{
|
||||
TRY(tokenizer_->readChars(result));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinASTParser::readMaybeString(Maybe<Chars>& out)
|
||||
{
|
||||
MOZ_ASSERT(out.isNothing());
|
||||
TRY(tokenizer_->readMaybeChars(out));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<double>
|
||||
BinASTParser::readNumber()
|
||||
{
|
||||
double result;
|
||||
TRY(tokenizer_->readDouble(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JS::Result<bool>
|
||||
BinASTParser::readBool()
|
||||
{
|
||||
bool result;
|
||||
TRY(tokenizer_->readBool(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseMissingVariableInAssertedScope(JSAtom* name)
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseMissingVariableInAssertedScope(JSAtom* name)
|
||||
{
|
||||
// For the moment, we don't trust inputs sufficiently to put the name
|
||||
// in an error message.
|
||||
return raiseError("Missing variable in AssertedScope");
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseMissingDirectEvalInAssertedScope()
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseMissingDirectEvalInAssertedScope()
|
||||
{
|
||||
return raiseError("Direct call to `eval` was not declared in AssertedScope");
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseInvalidKind(const char* superKind, const BinKind kind)
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseInvalidKind(const char* superKind, const BinKind kind)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
TRY(out.init());
|
||||
TRY(out.printf("In %s, invalid kind %s", superKind, describeBinKind(kind)));
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("In %s, invalid kind %s", superKind, describeBinKind(kind)));
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseInvalidField(const char* kind, const BinField field)
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseInvalidVariant(const char* kind, const BinVariant value)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
TRY(out.init());
|
||||
TRY(out.printf("In %s, invalid field '%s'", kind, describeBinField(field)));
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseInvalidNumberOfFields(const BinKind kind, const uint32_t expected, const uint32_t got)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
TRY(out.init());
|
||||
TRY(out.printf("In %s, invalid number of fields: expected %u, got %u",
|
||||
describeBinKind(kind), expected, got));
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseInvalidEnum(const char* kind, const Chars& value)
|
||||
{
|
||||
// We don't trust the actual chars of `value` to be properly formatted anything, so let's not use
|
||||
// them anywhere.
|
||||
return raiseError("Invalid enum");
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseMissingField(const char* kind, const BinField field)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
TRY(out.init());
|
||||
TRY(out.printf("In %s, missing field '%s'", kind, describeBinField(field)));
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("In %s, invalid variant '%s'", kind, describeBinVariant(value)));
|
||||
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseEmpty(const char* description)
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseMissingField(const char* kind, const BinField field)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
TRY(out.init());
|
||||
TRY(out.printf("Empty %s", description));
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("In %s, missing field '%s'", kind, describeBinField(field)));
|
||||
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseOOM()
|
||||
{
|
||||
ReportOutOfMemory(cx_);
|
||||
return cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseError(BinKind kind, const char* description)
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseEmpty(const char* description)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
TRY(out.init());
|
||||
TRY(out.printf("In %s, ", description));
|
||||
MOZ_ALWAYS_FALSE(tokenizer_->raiseError(out.string()));
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("Empty %s", description));
|
||||
|
||||
return cx_->alreadyReportedError();
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser::raiseError(const char* description)
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseOOM()
|
||||
{
|
||||
MOZ_ALWAYS_FALSE(tokenizer_->raiseError(description));
|
||||
return cx_->alreadyReportedError();
|
||||
return tokenizer_->raiseOOM();
|
||||
}
|
||||
|
||||
void
|
||||
BinASTParser::poison()
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseError(BinKind kind, const char* description)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("In %s, ", description));
|
||||
return tokenizer_->raiseError(out.string());
|
||||
}
|
||||
|
||||
template<typename Tok> mozilla::GenericErrorResult<JS::Error&>
|
||||
BinASTParser<Tok>::raiseError(const char* description)
|
||||
{
|
||||
return tokenizer_->raiseError(description);
|
||||
}
|
||||
|
||||
template<typename Tok> void
|
||||
BinASTParser<Tok>::poison()
|
||||
{
|
||||
tokenizer_.reset();
|
||||
}
|
||||
|
||||
void
|
||||
BinASTParser::reportErrorNoOffsetVA(unsigned errorNumber, va_list args)
|
||||
template<typename Tok> void
|
||||
BinASTParser<Tok>::reportErrorNoOffsetVA(unsigned errorNumber, va_list args)
|
||||
{
|
||||
ErrorMetadata metadata;
|
||||
metadata.filename = getFilename();
|
||||
|
@ -511,7 +379,7 @@ BinASTParser::reportErrorNoOffsetVA(unsigned errorNumber, va_list args)
|
|||
}
|
||||
|
||||
bool
|
||||
BinASTParser::hasUsedName(HandlePropertyName name)
|
||||
BinASTParserBase::hasUsedName(HandlePropertyName name)
|
||||
{
|
||||
if (UsedNamePtr p = usedNames_.lookup(name))
|
||||
return p->value().isUsedInScript(parseContext_->scriptId());
|
||||
|
@ -522,18 +390,16 @@ BinASTParser::hasUsedName(HandlePropertyName name)
|
|||
void
|
||||
TraceBinParser(JSTracer* trc, AutoGCRooter* parser)
|
||||
{
|
||||
static_cast<BinASTParser*>(parser)->trace(trc);
|
||||
static_cast<BinASTParserBase*>(parser)->trace(trc);
|
||||
}
|
||||
|
||||
|
||||
// Force class instantiation.
|
||||
// This ensures that the symbols are built, without having to export all our
|
||||
// code (and its baggage of #include and macros) in the header.
|
||||
template class BinASTParser<BinTokenReaderMultipart>;
|
||||
template class BinASTParser<BinTokenReaderTester>;
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
||||
// #undef everything, to avoid collisions with unified builds.
|
||||
|
||||
#undef TRY
|
||||
#undef TRY_VAR
|
||||
#undef TRY_DECL
|
||||
#undef TRY_EMPL
|
||||
#undef MOZ_TRY_EMPLACE
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/BinToken.h"
|
||||
#include "frontend/BinTokenReaderMultipart.h"
|
||||
#include "frontend/BinTokenReaderTester.h"
|
||||
#include "frontend/FullParseHandler.h"
|
||||
#include "frontend/ParseContext.h"
|
||||
|
@ -29,38 +31,24 @@
|
|||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
class BinASTParser;
|
||||
|
||||
/**
|
||||
* The parser for a Binary AST.
|
||||
*
|
||||
* By design, this parser never needs to backtrack or look ahead. Errors are not
|
||||
* recoverable.
|
||||
*/
|
||||
class BinASTParser : private JS::AutoGCRooter, public ErrorReporter
|
||||
class BinASTParserBase: private JS::AutoGCRooter
|
||||
{
|
||||
using Tokenizer = BinTokenReaderTester;
|
||||
using BinFields = Tokenizer::BinFields;
|
||||
using Chars = Tokenizer::Chars;
|
||||
using Names = JS::GCVector<JSString*, 8>;
|
||||
|
||||
public:
|
||||
BinASTParser(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames, const JS::ReadOnlyCompileOptions& options)
|
||||
BinASTParserBase(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames)
|
||||
: AutoGCRooter(cx, BINPARSER)
|
||||
, traceListHead_(nullptr)
|
||||
, options_(options)
|
||||
, cx_(cx)
|
||||
, alloc_(alloc)
|
||||
, traceListHead_(nullptr)
|
||||
, usedNames_(usedNames)
|
||||
, nodeAlloc_(cx, alloc)
|
||||
, keepAtoms_(cx)
|
||||
, parseContext_(nullptr)
|
||||
, usedNames_(usedNames)
|
||||
, factory_(cx, alloc, nullptr, SourceKind::Binary)
|
||||
{
|
||||
cx_->frontendCollectionPool().addActiveCompilation();
|
||||
cx->frontendCollectionPool().addActiveCompilation();
|
||||
tempPoolMark_ = alloc.mark();
|
||||
}
|
||||
~BinASTParser()
|
||||
~BinASTParserBase()
|
||||
{
|
||||
alloc_.release(tempPoolMark_);
|
||||
|
||||
|
@ -73,90 +61,7 @@ class BinASTParser : private JS::AutoGCRooter, public ErrorReporter
|
|||
|
||||
cx_->frontendCollectionPool().removeActiveCompilation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a buffer, returning a node (which may be nullptr) in case of success
|
||||
* or Nothing() in case of error.
|
||||
*
|
||||
* The instance of `ParseNode` MAY NOT survive the `BinASTParser`. Indeed,
|
||||
* destruction of the `BinASTParser` will also destroy the `ParseNode`.
|
||||
*
|
||||
* In case of error, the parser reports the JS error.
|
||||
*/
|
||||
JS::Result<ParseNode*> parse(const uint8_t* start, const size_t length);
|
||||
JS::Result<ParseNode*> parse(const Vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseAux(const uint8_t* start, const size_t length);
|
||||
|
||||
// --- Raise errors.
|
||||
//
|
||||
// These methods return a (failed) JS::Result for convenience.
|
||||
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingVariableInAssertedScope(JSAtom* name);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingDirectEvalInAssertedScope();
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidKind(const char* superKind,
|
||||
const BinKind kind);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidField(const char* kind,
|
||||
const BinField field);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidNumberOfFields(
|
||||
const BinKind kind, const uint32_t expected, const uint32_t got);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidEnum(const char* kind,
|
||||
const Chars& value);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingField(const char* kind,
|
||||
const BinField field);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseEmpty(const char* description);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseOOM();
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(const char* description);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(BinKind kind,
|
||||
const char* description);
|
||||
|
||||
|
||||
// Ensure that this parser will never be used again.
|
||||
void poison();
|
||||
|
||||
// Auto-generated methods
|
||||
#include "frontend/BinSource-auto.h"
|
||||
|
||||
// --- Auxiliary parsing functions
|
||||
template<size_t N>
|
||||
JS::Result<Ok, JS::Error&>
|
||||
checkFields(const BinKind kind, const BinFields& actual, const BinField (&expected)[N]);
|
||||
JS::Result<Ok, JS::Error&>
|
||||
checkFields0(const BinKind kind, const BinFields& actual);
|
||||
|
||||
JS::Result<ParseNode*>
|
||||
buildFunction(const size_t start, const BinKind kind, ParseNode* name, ParseNode* params,
|
||||
ParseNode* body, FunctionBox* funbox);
|
||||
JS::Result<FunctionBox*>
|
||||
buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind);
|
||||
|
||||
// Parse full scope information to a specific var scope / let scope combination.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScope(ParseContext::Scope& varScope,
|
||||
ParseContext::Scope& letScope);
|
||||
// Parse a list of names and add it to a given scope.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScopeNames(ParseContext::Scope& scope,
|
||||
DeclarationKind kind);
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateCapturedNames();
|
||||
MOZ_MUST_USE JS::Result<Ok> checkBinding(JSAtom* name);
|
||||
|
||||
// --- Utilities.
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> appendDirectivesToBody(ParseNode* body,
|
||||
ParseNode* directives);
|
||||
|
||||
// Read a string
|
||||
MOZ_MUST_USE JS::Result<Ok> readString(Chars& out);
|
||||
MOZ_MUST_USE JS::Result<Ok> readMaybeString(Maybe<Chars>& out);
|
||||
MOZ_MUST_USE JS::Result<Ok> readString(MutableHandleAtom out);
|
||||
MOZ_MUST_USE JS::Result<Ok> readMaybeString(MutableHandleAtom out);
|
||||
MOZ_MUST_USE JS::Result<bool> readBool();
|
||||
MOZ_MUST_USE JS::Result<double> readNumber();
|
||||
|
||||
const ReadOnlyCompileOptions& options() const override {
|
||||
return this->options_;
|
||||
}
|
||||
|
||||
public:
|
||||
// Names
|
||||
|
||||
|
||||
|
@ -199,7 +104,129 @@ class BinASTParser : private JS::AutoGCRooter, public ErrorReporter
|
|||
|
||||
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
|
||||
|
||||
// Needs access to AutoGCRooter.
|
||||
friend void TraceBinParser(JSTracer* trc, AutoGCRooter* parser);
|
||||
|
||||
protected:
|
||||
JSContext* cx_;
|
||||
|
||||
// ---- Memory-related stuff
|
||||
protected:
|
||||
LifoAlloc& alloc_;
|
||||
ObjectBox* traceListHead_;
|
||||
UsedNameTracker& usedNames_;
|
||||
private:
|
||||
LifoAlloc::Mark tempPoolMark_;
|
||||
ParseNodeAllocator nodeAlloc_;
|
||||
|
||||
// Root atoms and objects allocated for the parse tree.
|
||||
AutoKeepAtoms keepAtoms_;
|
||||
|
||||
// ---- Parsing-related stuff
|
||||
protected:
|
||||
ParseContext* parseContext_;
|
||||
FullParseHandler factory_;
|
||||
|
||||
friend class BinParseContext;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The parser for a Binary AST.
|
||||
*
|
||||
* By design, this parser never needs to backtrack or look ahead. Errors are not
|
||||
* recoverable.
|
||||
*/
|
||||
template<typename Tok>
|
||||
class BinASTParser : public BinASTParserBase, public ErrorReporter
|
||||
{
|
||||
public:
|
||||
using Tokenizer = Tok;
|
||||
|
||||
using AutoList = typename Tokenizer::AutoList;
|
||||
using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
|
||||
using AutoTuple = typename Tokenizer::AutoTuple;
|
||||
using BinFields = typename Tokenizer::BinFields;
|
||||
using Chars = typename Tokenizer::Chars;
|
||||
|
||||
public:
|
||||
BinASTParser(JSContext* cx, LifoAlloc& alloc, UsedNameTracker& usedNames, const JS::ReadOnlyCompileOptions& options)
|
||||
: BinASTParserBase(cx, alloc, usedNames)
|
||||
, options_(options)
|
||||
{
|
||||
}
|
||||
~BinASTParser()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a buffer, returning a node (which may be nullptr) in case of success
|
||||
* or Nothing() in case of error.
|
||||
*
|
||||
* The instance of `ParseNode` MAY NOT survive the `BinASTParser`. Indeed,
|
||||
* destruction of the `BinASTParser` will also destroy the `ParseNode`.
|
||||
*
|
||||
* In case of error, the parser reports the JS error.
|
||||
*/
|
||||
JS::Result<ParseNode*> parse(const uint8_t* start, const size_t length);
|
||||
JS::Result<ParseNode*> parse(const Vector<uint8_t>& data);
|
||||
|
||||
private:
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> parseAux(const uint8_t* start, const size_t length);
|
||||
|
||||
// --- Raise errors.
|
||||
//
|
||||
// These methods return a (failed) JS::Result for convenience.
|
||||
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingVariableInAssertedScope(JSAtom* name);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingDirectEvalInAssertedScope();
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidKind(const char* superKind,
|
||||
const BinKind kind);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidVariant(const char* kind,
|
||||
const BinVariant value);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingField(const char* kind,
|
||||
const BinField field);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseEmpty(const char* description);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseOOM();
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(const char* description);
|
||||
MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(BinKind kind,
|
||||
const char* description);
|
||||
|
||||
|
||||
// Ensure that this parser will never be used again.
|
||||
void poison();
|
||||
|
||||
// Auto-generated methods
|
||||
#include "frontend/BinSource-auto.h"
|
||||
|
||||
// --- Auxiliary parsing functions
|
||||
JS::Result<ParseNode*>
|
||||
buildFunction(const size_t start, const BinKind kind, ParseNode* name, ParseNode* params,
|
||||
ParseNode* body, FunctionBox* funbox);
|
||||
JS::Result<FunctionBox*>
|
||||
buildFunctionBox(GeneratorKind generatorKind, FunctionAsyncKind functionAsyncKind,
|
||||
FunctionSyntaxKind syntax);
|
||||
|
||||
// Parse full scope information to a specific var scope / let scope combination.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScope(ParseContext::Scope& varScope,
|
||||
ParseContext::Scope& letScope);
|
||||
// Parse a list of names and add it to a given scope.
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateScopeNames(ParseContext::Scope& scope,
|
||||
DeclarationKind kind);
|
||||
MOZ_MUST_USE JS::Result<Ok> parseAndUpdateCapturedNames();
|
||||
MOZ_MUST_USE JS::Result<Ok> checkBinding(JSAtom* name);
|
||||
|
||||
// --- Utilities.
|
||||
|
||||
MOZ_MUST_USE JS::Result<ParseNode*> appendDirectivesToBody(ParseNode* body,
|
||||
ParseNode* directives);
|
||||
|
||||
private: // Implement ErrorReporter
|
||||
const ReadOnlyCompileOptions& options_;
|
||||
|
||||
const ReadOnlyCompileOptions& options() const override {
|
||||
return this->options_;
|
||||
}
|
||||
|
||||
virtual void lineAndColumnAt(size_t offset, uint32_t* line, uint32_t* column) const override {
|
||||
*line = 0;
|
||||
|
@ -223,34 +250,37 @@ class BinASTParser : private JS::AutoGCRooter, public ErrorReporter
|
|||
return this->options_.filename();
|
||||
}
|
||||
|
||||
ObjectBox* traceListHead_;
|
||||
const ReadOnlyCompileOptions& options_;
|
||||
JSContext* cx_;
|
||||
LifoAlloc& alloc_;
|
||||
LifoAlloc::Mark tempPoolMark_;
|
||||
ParseNodeAllocator nodeAlloc_;
|
||||
|
||||
// Root atoms and objects allocated for the parse tree.
|
||||
AutoKeepAtoms keepAtoms_;
|
||||
|
||||
// The current ParseContext, holding directives, etc.
|
||||
ParseContext* parseContext_;
|
||||
UsedNameTracker& usedNames_;
|
||||
Maybe<Tokenizer> tokenizer_;
|
||||
FullParseHandler factory_;
|
||||
VariableDeclarationKind variableDeclarationKind_;
|
||||
|
||||
friend class BinParseContext;
|
||||
friend class AutoVariableDeclarationKind;
|
||||
|
||||
// Needs access to AutoGCRooter.
|
||||
friend void TraceBinParser(JSTracer* trc, AutoGCRooter* parser);
|
||||
// Helper class: Restore field `variableDeclarationKind` upon leaving a scope.
|
||||
class MOZ_RAII AutoVariableDeclarationKind {
|
||||
public:
|
||||
explicit AutoVariableDeclarationKind(BinASTParser<Tok>* parser
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: parser_(parser)
|
||||
, kind(parser->variableDeclarationKind_)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
~AutoVariableDeclarationKind() {
|
||||
parser_->variableDeclarationKind_ = kind;
|
||||
}
|
||||
private:
|
||||
BinASTParser<Tok>* parser_;
|
||||
BinASTParser<Tok>::VariableDeclarationKind kind;
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
};
|
||||
|
||||
class BinParseContext : public ParseContext
|
||||
{
|
||||
public:
|
||||
BinParseContext(JSContext* cx, BinASTParser* parser, SharedContext* sc,
|
||||
template<typename Tok>
|
||||
BinParseContext(JSContext* cx, BinASTParser<Tok>* parser, SharedContext* sc,
|
||||
Directives* newDirectives)
|
||||
: ParseContext(cx, parser->parseContext_, sc, *parser,
|
||||
parser->usedNames_, newDirectives, /* isFull = */ true)
|
||||
|
@ -258,6 +288,9 @@ class BinParseContext : public ParseContext
|
|||
};
|
||||
|
||||
|
||||
extern template class BinASTParser<BinTokenReaderMultipart>;
|
||||
extern template class BinASTParser<BinTokenReaderTester>;
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -190,6 +190,17 @@ typedef (ExportAllFrom or
|
|||
|
||||
typedef (ImportNamespace or Import) ImportDeclaration;
|
||||
|
||||
typedef (EagerFunctionDeclaration or SkippableFunctionDeclaration) FunctionDeclaration;
|
||||
|
||||
typedef (EagerFunctionExpression or SkippableFunctionExpression) FunctionExpression;
|
||||
|
||||
typedef (EagerMethod or SkippableMethod) Method;
|
||||
|
||||
typedef (EagerGetter or SkippableGetter) Getter;
|
||||
|
||||
typedef (EagerSetter or SkippableSetter) Setter;
|
||||
|
||||
typedef (EagerArrowExpression or SkippableArrowExpression) ArrowExpression;
|
||||
|
||||
// bindings
|
||||
|
||||
|
@ -412,7 +423,7 @@ interface ExportLocalSpecifier : Node {
|
|||
// `MethodDefinition :: PropertyName ( UniqueFormalParameters ) { FunctionBody }`,
|
||||
// `GeneratorMethod :: * PropertyName ( UniqueFormalParameters ) { GeneratorBody }`,
|
||||
// `AsyncMethod :: async PropertyName ( UniqueFormalParameters ) { AsyncFunctionBody }`
|
||||
interface Method : Node {
|
||||
interface EagerMethod : Node {
|
||||
// True for `AsyncMethod`, false otherwise.
|
||||
attribute boolean isAsync;
|
||||
// True for `GeneratorMethod`, false otherwise.
|
||||
|
@ -425,15 +436,23 @@ interface Method : Node {
|
|||
attribute FunctionBody body;
|
||||
};
|
||||
|
||||
[Skippable] interface SkippableMethod : Node {
|
||||
attribute EagerMethod skipped;
|
||||
};
|
||||
|
||||
// `get PropertyName ( ) { FunctionBody }`
|
||||
interface Getter : Node {
|
||||
interface EagerGetter : Node {
|
||||
attribute AssertedVarScope? bodyScope;
|
||||
attribute PropertyName name;
|
||||
attribute FunctionBody body;
|
||||
};
|
||||
|
||||
[Skippable] interface SkippableGetter : Node {
|
||||
attribute EagerGetter skipped;
|
||||
};
|
||||
|
||||
// `set PropertyName ( PropertySetParameterList ) { FunctionBody }`
|
||||
interface Setter : Node {
|
||||
interface EagerSetter : Node {
|
||||
attribute AssertedParameterScope? parameterScope;
|
||||
attribute AssertedVarScope? bodyScope;
|
||||
attribute PropertyName name;
|
||||
|
@ -442,6 +461,10 @@ interface Setter : Node {
|
|||
attribute FunctionBody body;
|
||||
};
|
||||
|
||||
[Skippable] interface SkippableSetter : Node {
|
||||
attribute EagerSetter skipped;
|
||||
};
|
||||
|
||||
// `PropertyDefinition :: PropertyName : AssignmentExpression`
|
||||
interface DataProperty : Node {
|
||||
attribute PropertyName name;
|
||||
|
@ -505,7 +528,7 @@ interface ArrayExpression : Node {
|
|||
|
||||
// `ArrowFunction`,
|
||||
// `AsyncArrowFunction`
|
||||
interface ArrowExpression : Node {
|
||||
interface EagerArrowExpression : Node {
|
||||
// True for `AsyncArrowFunction`, false otherwise.
|
||||
attribute boolean isAsync;
|
||||
attribute AssertedParameterScope? parameterScope;
|
||||
|
@ -514,6 +537,10 @@ interface ArrowExpression : Node {
|
|||
attribute (FunctionBody or Expression) body;
|
||||
};
|
||||
|
||||
[Skippable] interface SkippableArrowExpression : Node {
|
||||
attribute EagerArrowExpression skipped;
|
||||
};
|
||||
|
||||
// `AssignmentExpression :: LeftHandSideExpression = AssignmentExpression`
|
||||
interface AssignmentExpression : Node {
|
||||
// The `LeftHandSideExpression`.
|
||||
|
@ -575,7 +602,7 @@ interface ConditionalExpression : Node {
|
|||
// `FunctionExpression`,
|
||||
// `GeneratorExpression`,
|
||||
// `AsyncFunctionExpression`,
|
||||
interface FunctionExpression : Node {
|
||||
interface EagerFunctionExpression : Node {
|
||||
attribute boolean isAsync;
|
||||
attribute boolean isGenerator;
|
||||
attribute AssertedParameterScope? parameterScope;
|
||||
|
@ -585,6 +612,10 @@ interface FunctionExpression : Node {
|
|||
attribute FunctionBody body;
|
||||
};
|
||||
|
||||
[Skippable] interface SkippableFunctionExpression : Node {
|
||||
attribute EagerFunctionExpression skipped;
|
||||
};
|
||||
|
||||
// `IdentifierReference`
|
||||
interface IdentifierExpression : Node {
|
||||
attribute Identifier name;
|
||||
|
@ -818,10 +849,12 @@ interface FunctionBody : Node {
|
|||
attribute FrozenArray<Statement> statements;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// `FunctionDeclaration`,
|
||||
// `GeneratorDeclaration`,
|
||||
// `AsyncFunctionDeclaration`
|
||||
interface FunctionDeclaration : Node {
|
||||
interface EagerFunctionDeclaration : Node {
|
||||
attribute boolean isAsync;
|
||||
attribute boolean isGenerator;
|
||||
attribute AssertedParameterScope? parameterScope;
|
||||
|
@ -831,6 +864,10 @@ interface FunctionDeclaration : Node {
|
|||
attribute FunctionBody body;
|
||||
};
|
||||
|
||||
[Skippable] interface SkippableFunctionDeclaration : Node {
|
||||
attribute EagerFunctionDeclaration skipped;
|
||||
};
|
||||
|
||||
interface Script : Node {
|
||||
attribute AssertedVarScope? scope;
|
||||
attribute FrozenArray<Directive> directives;
|
||||
|
@ -868,4 +905,4 @@ interface VariableDeclaration : Node {
|
|||
interface VariableDeclarator : Node {
|
||||
attribute Binding binding;
|
||||
attribute Expression? init;
|
||||
};
|
||||
};
|
|
@ -21,6 +21,7 @@ cpp:
|
|||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Vector.h"
|
||||
|
||||
#include "frontend/BinSource-macros.h"
|
||||
#include "frontend/BinSource.h"
|
||||
#include "frontend/BinTokenReaderTester.h"
|
||||
#include "frontend/FullParseHandler.h"
|
||||
|
@ -35,116 +36,26 @@ cpp:
|
|||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
using AutoList = BinTokenReaderTester::AutoList;
|
||||
using AutoTaggedTuple = BinTokenReaderTester::AutoTaggedTuple;
|
||||
using AutoTuple = BinTokenReaderTester::AutoTuple;
|
||||
using BinFields = BinTokenReaderTester::BinFields;
|
||||
using Chars = BinTokenReaderTester::Chars;
|
||||
using NameBag = GCHashSet<JSString*>;
|
||||
using Names = GCVector<JSString*, 8>;
|
||||
using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is not falsy.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define TRY(EXPR) \
|
||||
do { \
|
||||
if (!EXPR) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
} while(false)
|
||||
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is not falsy.
|
||||
// In case of success, assign the result to VAR.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define TRY_VAR(VAR, EXPR) \
|
||||
do { \
|
||||
VAR = EXPR; \
|
||||
if (!VAR) \
|
||||
return cx_->alreadyReportedError(); \
|
||||
} while (false)
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is not falsy.
|
||||
// In case of success, assign the result to a new variable VAR.
|
||||
//
|
||||
// Throw `cx->alreadyReportedError()` if it returns 0/nullptr.
|
||||
#define TRY_DECL(VAR, EXPR) \
|
||||
auto VAR = EXPR; \
|
||||
if (!VAR) \
|
||||
return cx_->alreadyReportedError();
|
||||
|
||||
// Evaluate an expression EXPR, checking that the result is a success.
|
||||
// In case of success, unwrap and assign the result to a new variable VAR.
|
||||
//
|
||||
// In case of error, propagate the error.
|
||||
#define MOZ_TRY_DECL(VAR, EXPR) \
|
||||
auto _##VAR = EXPR; \
|
||||
if (_##VAR.isErr()) \
|
||||
return ::mozilla::Err(_##VAR.unwrapErr()); \
|
||||
auto VAR = _##VAR.unwrap();
|
||||
|
||||
// Ensure that we are visiting the right fields.
|
||||
template<size_t N>
|
||||
JS::Result<Ok, JS::Error&>
|
||||
BinASTParser::checkFields(const BinKind kind, const BinFields& actual, const BinField (&expected)[N])
|
||||
{
|
||||
if (actual.length() != N)
|
||||
return raiseInvalidNumberOfFields(kind, N, actual.length());
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (actual[i] != expected[i])
|
||||
return raiseInvalidField(describeBinKind(kind), actual[i]);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Special case for N=0, as empty arrays are not permitted in C++
|
||||
JS::Result<Ok, JS::Error&>
|
||||
BinASTParser::checkFields0(const BinKind kind, const BinFields& actual)
|
||||
{
|
||||
if (actual.length() != 0)
|
||||
return raiseInvalidNumberOfFields(kind, 0, actual.length());
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Compare a bunch of `uint8_t` values (as returned by the tokenizer_) with
|
||||
// a string literal (and ONLY a string literal).
|
||||
template<size_t N>
|
||||
bool operator==(const Chars& left, const char (&right)[N]) {
|
||||
return BinTokenReaderTester::equals(left, right);
|
||||
template<typename Tok, size_t N>
|
||||
bool operator==(const typename Tok::Chars& left, const char (&right)[N]) {
|
||||
return Tok::equals(left, right);
|
||||
}
|
||||
|
||||
// Helper class: Restore field `variableDeclarationKind_` upon leaving a scope.
|
||||
class MOZ_RAII AutoVariableDeclarationKind {
|
||||
public:
|
||||
explicit AutoVariableDeclarationKind(BinASTParser* parser
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
|
||||
parser_(parser),
|
||||
kind(parser->variableDeclarationKind_)
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
}
|
||||
~AutoVariableDeclarationKind() {
|
||||
parser_->variableDeclarationKind_ = kind;
|
||||
}
|
||||
private:
|
||||
BinASTParser* parser_;
|
||||
BinASTParser::VariableDeclarationKind kind;
|
||||
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
||||
};
|
||||
|
||||
footer: |
|
||||
|
||||
#undef TRY
|
||||
#undef TRY_VAR
|
||||
#undef TRY_DECL
|
||||
#undef MOZ_TRY_DECL
|
||||
// Force class instantiation.
|
||||
// This ensures that the symbols are built, without having to export all our
|
||||
// code (and its baggage of #include and macros) in the header.
|
||||
template class BinASTParser<BinTokenReaderMultipart>;
|
||||
template class BinASTParser<BinTokenReaderTester>;
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
||||
|
||||
hpp:
|
||||
# Rules for generating BinSource-class.h
|
||||
class:
|
||||
|
@ -196,6 +107,24 @@ hpp:
|
|||
*
|
||||
* (sorted by alphabetical order)
|
||||
*/
|
||||
field:
|
||||
doc: |
|
||||
/**
|
||||
* The different variants of Binary AST string enums, as per
|
||||
* the specifications of Binary AST, as a single macro and
|
||||
* `enum class`.
|
||||
*
|
||||
* Separate enum classes are also defined in BinSource-auto.h.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```c++
|
||||
* #define WITH_VARIANT(CPP_NAME, SPEC_NAME) ...
|
||||
* FOR_EACH_BIN_VARIANT(WITH_VARIANT)
|
||||
* ```
|
||||
*
|
||||
* (sorted by alphabetical order)
|
||||
*/
|
||||
|
||||
header: |
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
|
@ -253,6 +182,11 @@ hpp:
|
|||
*/
|
||||
const char* describeBinField(const BinField& kind);
|
||||
|
||||
/**
|
||||
* Return a string describing a `BinVariant`.
|
||||
*/
|
||||
const char* describeBinVariant(const BinVariant& kind);
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
@ -260,7 +194,7 @@ hpp:
|
|||
|
||||
Arguments:
|
||||
init:
|
||||
TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newList(ParseNodeKind::ParamsBody, tokenizer_->pos(start)));
|
||||
append:
|
||||
factory_.addList(/* list = */ result, /* child = */ item);
|
||||
|
||||
|
@ -312,19 +246,19 @@ AssertedVarScope:
|
|||
|
||||
AssignmentExpression:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newAssignment(ParseNodeKind::Assign, binding, expression));
|
||||
BINJS_TRY_DECL(result, factory_.newAssignment(ParseNodeKind::Assign, binding, expression));
|
||||
|
||||
AssignmentTargetIdentifier:
|
||||
build: |
|
||||
if (!IsIdentifier(name))
|
||||
return raiseError("Invalid identifier");
|
||||
TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
|
||||
BindingIdentifier:
|
||||
build: |
|
||||
if (!IsIdentifier(name))
|
||||
return raiseError("Invalid identifier");
|
||||
TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
|
||||
BinaryExpression:
|
||||
build: |
|
||||
|
@ -415,7 +349,7 @@ BinaryExpression:
|
|||
left->appendWithoutOrderAssumption(right);
|
||||
result = left;
|
||||
} else {
|
||||
TRY_DECL(list, factory_.newList(pnk, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(list, factory_.newList(pnk, tokenizer_->pos(start)));
|
||||
|
||||
list->appendWithoutOrderAssumption(left);
|
||||
list->appendWithoutOrderAssumption(right);
|
||||
|
@ -426,10 +360,10 @@ Block:
|
|||
init: |
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::Block);
|
||||
ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
|
||||
TRY(currentScope.init(parseContext_));
|
||||
BINJS_TRY(currentScope.init(parseContext_));
|
||||
build: |
|
||||
TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
|
||||
TRY_DECL(result, factory_.newLexicalScope(*bindings, statements));
|
||||
BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
|
||||
BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, statements));
|
||||
|
||||
BreakStatement:
|
||||
fields:
|
||||
|
@ -437,12 +371,13 @@ BreakStatement:
|
|||
block:
|
||||
replace: |
|
||||
RootedAtom label(cx_);
|
||||
MOZ_TRY(readMaybeString(&label));
|
||||
MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom());
|
||||
|
||||
if (label && !IsIdentifier(label))
|
||||
return raiseError("Invalid identifier");
|
||||
build: |
|
||||
if (label) {
|
||||
if (!IsIdentifier(label))
|
||||
return raiseError("Invalid identifier");
|
||||
|
||||
auto validity = parseContext_->checkBreakStatement(label->asPropertyName());
|
||||
|
||||
if (validity.isErr()) {
|
||||
|
@ -454,7 +389,7 @@ BreakStatement:
|
|||
}
|
||||
}
|
||||
}
|
||||
TRY_DECL(result, factory_.newBreakStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newBreakStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));
|
||||
|
||||
CallExpression:
|
||||
build: |
|
||||
|
@ -480,17 +415,17 @@ CatchClause:
|
|||
init: |
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::Catch);
|
||||
ParseContext::Scope currentScope(cx_, parseContext_, usedNames_);
|
||||
TRY(currentScope.init(parseContext_));
|
||||
BINJS_TRY(currentScope.init(parseContext_));
|
||||
build: |
|
||||
// Export implicit variables to the scope.
|
||||
// FIXME: Handle cases other than Name.
|
||||
MOZ_ASSERT(binding->isKind(ParseNodeKind::Name));
|
||||
auto ptr = currentScope.lookupDeclaredNameForAdd(binding->name());
|
||||
TRY(currentScope.addDeclaredName(parseContext_, ptr, binding->name(), DeclarationKind::Let, start));
|
||||
BINJS_TRY(currentScope.addDeclaredName(parseContext_, ptr, binding->name(), DeclarationKind::Let, start));
|
||||
|
||||
TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
|
||||
TRY_DECL(result, factory_.newLexicalScope(*bindings, body));
|
||||
TRY(factory_.setupCatchScope(result, binding, body));
|
||||
BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, currentScope, alloc_, parseContext_));
|
||||
BINJS_TRY_DECL(result, factory_.newLexicalScope(*bindings, body));
|
||||
BINJS_TRY(factory_.setupCatchScope(result, binding, body));
|
||||
|
||||
CompoundAssignmentExpression:
|
||||
build: |
|
||||
|
@ -533,19 +468,19 @@ CompoundAssignmentExpression:
|
|||
pnk = ParseNodeKind::BitAndAssign;
|
||||
break;
|
||||
}
|
||||
TRY_DECL(result, factory_.newAssignment(pnk, binding, expression));
|
||||
BINJS_TRY_DECL(result, factory_.newAssignment(pnk, binding, expression));
|
||||
|
||||
ComputedMemberAssignmentTarget:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
||||
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
||||
|
||||
ComputedMemberExpression:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
||||
BINJS_TRY_DECL(result, factory_.newPropertyByValue(object, expression, start));
|
||||
|
||||
ConditionalExpression:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newConditional(test, consequent, alternate));
|
||||
BINJS_TRY_DECL(result, factory_.newConditional(test, consequent, alternate));
|
||||
|
||||
ContinueStatement:
|
||||
fields:
|
||||
|
@ -553,12 +488,13 @@ ContinueStatement:
|
|||
block:
|
||||
replace: |
|
||||
RootedAtom label(cx_);
|
||||
MOZ_TRY(readMaybeString(&label));
|
||||
MOZ_TRY_VAR(label, tokenizer_->readMaybeAtom());
|
||||
|
||||
if (label && !IsIdentifier(label))
|
||||
return raiseError("ContinueStatement - Label MUST be an identifier");
|
||||
build: |
|
||||
if (label) {
|
||||
if (!IsIdentifier(label))
|
||||
return raiseError("ContinueStatement - Label MUST be an identifier");
|
||||
|
||||
auto validity = parseContext_->checkContinueStatement(label ? label->asPropertyName() : nullptr);
|
||||
if (validity.isErr()) {
|
||||
switch (validity.unwrapErr()) {
|
||||
|
@ -570,33 +506,117 @@ ContinueStatement:
|
|||
}
|
||||
}
|
||||
|
||||
TRY_DECL(result, factory_.newContinueStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newContinueStatement(label ? label->asPropertyName() : nullptr, tokenizer_->pos(start)));
|
||||
|
||||
DataProperty:
|
||||
build: |
|
||||
if (!factory_.isUsableAsObjectPropertyName(name))
|
||||
return raiseError("DataProperty key kind");
|
||||
|
||||
TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, expression, AccessorType::None));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, expression, AccessorType::None));
|
||||
|
||||
Directive:
|
||||
build: |
|
||||
TokenPos pos = tokenizer_->pos(start);
|
||||
TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));
|
||||
BINJS_TRY_DECL(result, factory_.newStringLiteral(rawValue, pos));
|
||||
|
||||
DoWhileStatement:
|
||||
init:
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::DoLoop);
|
||||
build:
|
||||
TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newDoWhileStatement(body, test, tokenizer_->pos(start)));
|
||||
|
||||
EagerFunctionDeclaration:
|
||||
init: |
|
||||
const auto syntax = FunctionSyntaxKind::Statement;
|
||||
inherits: EagerFunctionExpression
|
||||
|
||||
EagerFunctionExpression:
|
||||
init: |
|
||||
const auto syntax = FunctionSyntaxKind::Expression;
|
||||
fields:
|
||||
parameterScope:
|
||||
before: |
|
||||
BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
|
||||
isGenerator ? GeneratorKind::Generator
|
||||
: GeneratorKind::NotGenerator,
|
||||
isAsync ? FunctionAsyncKind::AsyncFunction
|
||||
: FunctionAsyncKind::SyncFunction,
|
||||
syntax));
|
||||
|
||||
// Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
|
||||
BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
|
||||
BINJS_TRY(funpc.init());
|
||||
parseContext_->functionScope().useAsVarScope(parseContext_);
|
||||
MOZ_ASSERT(parseContext_->isFunctionBox());
|
||||
|
||||
ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
|
||||
BINJS_TRY(lexicalScope.init(parseContext_));
|
||||
build: |
|
||||
BINJS_TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
|
||||
BINJS_TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
|
||||
BINJS_MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
|
||||
|
||||
EagerGetter:
|
||||
fields:
|
||||
body:
|
||||
before: |
|
||||
BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
|
||||
GeneratorKind::NotGenerator,
|
||||
FunctionAsyncKind::SyncFunction,
|
||||
FunctionSyntaxKind::Getter));
|
||||
|
||||
// Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
|
||||
BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
|
||||
BINJS_TRY(funpc.init());
|
||||
parseContext_->functionScope().useAsVarScope(parseContext_);
|
||||
MOZ_ASSERT(parseContext_->isFunctionBox());
|
||||
|
||||
ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
|
||||
BINJS_TRY(lexicalScope.init(parseContext_));
|
||||
build: |
|
||||
ParseNode* params = new_<ListNode>(ParseNodeKind::ParamsBody, tokenizer_->pos(start));
|
||||
BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Getter));
|
||||
|
||||
EagerMethod:
|
||||
init: |
|
||||
const auto syntax = FunctionSyntaxKind::Method;
|
||||
inherits: EagerFunctionExpression
|
||||
build: |
|
||||
BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));
|
||||
|
||||
EagerSetter:
|
||||
fields:
|
||||
body:
|
||||
before: |
|
||||
BINJS_MOZ_TRY_DECL(funbox, buildFunctionBox(
|
||||
GeneratorKind::NotGenerator,
|
||||
FunctionAsyncKind::SyncFunction,
|
||||
FunctionSyntaxKind::Setter));
|
||||
|
||||
// Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
|
||||
BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
|
||||
BINJS_TRY(funpc.init());
|
||||
parseContext_->functionScope().useAsVarScope(parseContext_);
|
||||
MOZ_ASSERT(parseContext_->isFunctionBox());
|
||||
|
||||
ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
|
||||
BINJS_TRY(lexicalScope.init(parseContext_));
|
||||
build: |
|
||||
ParseNode* params = new_<ListNode>(ParseNodeKind::ParamsBody, param->pn_pos);
|
||||
factory_.addList(params, param);
|
||||
BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
|
||||
|
||||
EmptyStatement:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newEmptyStatement(tokenizer_->pos(start)));
|
||||
|
||||
ExpressionStatement:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));
|
||||
BINJS_TRY_DECL(result, factory_.newExprStatement(expression, tokenizer_->offset()));
|
||||
|
||||
ForInOfBinding:
|
||||
init:
|
||||
|
@ -609,7 +629,7 @@ ForInOfBinding:
|
|||
kind_ == VariableDeclarationKind::Let
|
||||
? ParseNodeKind::Let
|
||||
: ParseNodeKind::Var;
|
||||
TRY_DECL(result, factory_.newDeclarationList(pnk, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newDeclarationList(pnk, tokenizer_->pos(start)));
|
||||
factory_.addList(result, binding);
|
||||
|
||||
|
||||
|
@ -622,21 +642,21 @@ ForInStatement:
|
|||
// or `for (const x in ...)`-style declarations. Detail on the
|
||||
// declaration is stored as part of `scope`.
|
||||
ParseContext::Scope scope(cx_, parseContext_, usedNames_);
|
||||
TRY(scope.init(parseContext_));
|
||||
BINJS_TRY(scope.init(parseContext_));
|
||||
build: |
|
||||
TRY_DECL(forHead, factory_.newForInOrOfHead(ParseNodeKind::ForIn, left, right, tokenizer_->pos(start)));
|
||||
TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
|
||||
BINJS_TRY_DECL(forHead, factory_.newForInOrOfHead(ParseNodeKind::ForIn, left, right, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /*flags*/ 0));
|
||||
|
||||
if (!scope.isEmpty()) {
|
||||
TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
|
||||
TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
|
||||
BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
|
||||
BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
|
||||
}
|
||||
|
||||
FormalParameters:
|
||||
build: |
|
||||
auto result = items;
|
||||
if (rest) {
|
||||
TRY_DECL(spread, factory_.newSpread(start, rest));
|
||||
BINJS_TRY_DECL(spread, factory_.newSpread(start, rest));
|
||||
factory_.addList(result, spread);
|
||||
}
|
||||
|
||||
|
@ -648,55 +668,30 @@ ForStatement:
|
|||
// or `for (const x; ...; ...)`-style declarations. Detail on the
|
||||
// declaration is stored as part of `BINJS_Scope`.
|
||||
ParseContext::Scope scope(cx_, parseContext_, usedNames_);
|
||||
TRY(scope.init(parseContext_));
|
||||
BINJS_TRY(scope.init(parseContext_));
|
||||
build: |
|
||||
TRY_DECL(forHead, factory_.newForHead(init, test, update, tokenizer_->pos(start)));
|
||||
TRY_DECL(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
|
||||
BINJS_TRY_DECL(forHead, factory_.newForHead(init, test, update, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newForStatement(start, forHead, body, /* iflags = */ 0));
|
||||
|
||||
if (!scope.isEmpty()) {
|
||||
TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
|
||||
TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
|
||||
BINJS_TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
|
||||
BINJS_TRY_VAR(result, factory_.newLexicalScope(*bindings, result));
|
||||
}
|
||||
|
||||
FunctionBody:
|
||||
build: |
|
||||
MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
|
||||
BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
|
||||
|
||||
FunctionDeclaration:
|
||||
inherits: FunctionExpression
|
||||
|
||||
FunctionExpression:
|
||||
fields:
|
||||
parameterScope:
|
||||
before: |
|
||||
MOZ_TRY_DECL(funbox, buildFunctionBox(
|
||||
isGenerator ? GeneratorKind::Generator
|
||||
: GeneratorKind::NotGenerator,
|
||||
isAsync ? FunctionAsyncKind::AsyncFunction
|
||||
: FunctionAsyncKind::SyncFunction));
|
||||
|
||||
// Push a new ParseContext. It will be used to parse `scope`, the arguments, the function.
|
||||
BinParseContext funpc(cx_, this, funbox, /* newDirectives = */ nullptr);
|
||||
TRY(funpc.init());
|
||||
parseContext_->functionScope().useAsVarScope(parseContext_);
|
||||
MOZ_ASSERT(parseContext_->isFunctionBox());
|
||||
|
||||
ParseContext::Scope lexicalScope(cx_, parseContext_, usedNames_);
|
||||
TRY(lexicalScope.init(parseContext_));
|
||||
build: |
|
||||
TRY_DECL(lexicalScopeData, NewLexicalScopeData(cx_, lexicalScope, alloc_, parseContext_));
|
||||
TRY_VAR(body, factory_.newLexicalScope(*lexicalScopeData, body));
|
||||
MOZ_TRY_DECL(result, buildFunction(start, kind, name, params, body, funbox));
|
||||
|
||||
IdentifierExpression:
|
||||
build: |
|
||||
if (!IsIdentifier(name))
|
||||
return raiseError("Invalid identifier");
|
||||
TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
BINJS_TRY_DECL(result, factory_.newName(name->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
|
||||
IfStatement:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newIfStatement(start, test, consequent, alternate));
|
||||
BINJS_TRY_DECL(result, factory_.newIfStatement(start, test, consequent, alternate));
|
||||
|
||||
LabelledStatement:
|
||||
fields:
|
||||
|
@ -706,26 +701,26 @@ LabelledStatement:
|
|||
return raiseError("Invalid identifier");
|
||||
ParseContext::LabelStatement stmt(parseContext_, label);
|
||||
build:
|
||||
TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));
|
||||
BINJS_TRY_DECL(result, factory_.newLabeledStatement(label->asPropertyName(), body, start));
|
||||
|
||||
ListOfDirective:
|
||||
init:
|
||||
TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
|
||||
append:
|
||||
factory_.addStatementToList(result, item);
|
||||
|
||||
ListOfObjectProperty:
|
||||
init:
|
||||
TRY_DECL(result, factory_.newObjectLiteral(start));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectLiteral(start));
|
||||
|
||||
ListOfOptionalSpreadElementOrExpression:
|
||||
init:
|
||||
TRY_DECL(result, factory_.newArrayLiteral(start));
|
||||
BINJS_TRY_DECL(result, factory_.newArrayLiteral(start));
|
||||
append: |
|
||||
if (item)
|
||||
factory_.addArrayElement(result, item); // Infallible.
|
||||
else
|
||||
TRY(factory_.addElision(result, tokenizer_->pos(start)));
|
||||
BINJS_TRY(factory_.addElision(result, tokenizer_->pos(start)));
|
||||
|
||||
ListOfParameter:
|
||||
init: |
|
||||
|
@ -735,7 +730,7 @@ ListOfParameter:
|
|||
|
||||
ListOfStatement:
|
||||
init:
|
||||
TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
|
||||
append:
|
||||
factory_.addStatementToList(result, item);
|
||||
|
||||
|
@ -748,43 +743,43 @@ ListOfStatement:
|
|||
|
||||
ListOfSwitchCase:
|
||||
init:
|
||||
TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newStatementList(tokenizer_->pos(start)));
|
||||
append:
|
||||
factory_.addCaseStatementToList(result, item);
|
||||
|
||||
ListOfVariableDeclarator:
|
||||
init: |
|
||||
TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::Const /*Placeholder*/,
|
||||
BINJS_TRY_DECL(result, factory_.newDeclarationList(ParseNodeKind::Const /*Placeholder*/,
|
||||
tokenizer_->pos(start)));
|
||||
|
||||
LiteralBooleanExpression:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newBooleanLiteral(value, tokenizer_->pos(start)));
|
||||
|
||||
LiteralNumericExpression:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newNumber(value, DecimalPoint::HasDecimal, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newNumber(value, DecimalPoint::HasDecimal, tokenizer_->pos(start)));
|
||||
|
||||
LiteralNullExpression:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newNullLiteral(tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newNullLiteral(tokenizer_->pos(start)));
|
||||
|
||||
LiteralPropertyName:
|
||||
build: |
|
||||
ParseNode* result;
|
||||
uint32_t index;
|
||||
if (value->isIndex(&index))
|
||||
TRY_VAR(result, factory_.newNumber(index, NoDecimal, TokenPos(start, tokenizer_->offset())));
|
||||
BINJS_TRY_VAR(result, factory_.newNumber(index, NoDecimal, TokenPos(start, tokenizer_->offset())));
|
||||
else
|
||||
TRY_VAR(result, factory_.newObjectLiteralPropertyName(value, tokenizer_->pos(start)));
|
||||
BINJS_TRY_VAR(result, factory_.newObjectLiteralPropertyName(value, tokenizer_->pos(start)));
|
||||
|
||||
LiteralRegExpExpression:
|
||||
fields:
|
||||
flags:
|
||||
block:
|
||||
replace:
|
||||
replace: |
|
||||
Chars flags(cx_);
|
||||
MOZ_TRY(readString(flags));
|
||||
MOZ_TRY(tokenizer_->readChars(flags));
|
||||
build: |
|
||||
RegExpFlag reflags = NoFlags;
|
||||
for (auto c : flags) {
|
||||
|
@ -799,28 +794,22 @@ LiteralRegExpExpression:
|
|||
else if (c == 'u' && !(reflags & UnicodeFlag))
|
||||
reflags = RegExpFlag(reflags | UnicodeFlag);
|
||||
else
|
||||
return raiseInvalidEnum("RegExpLiteral", flags);
|
||||
return raiseError("Invalid regexp flags");
|
||||
}
|
||||
|
||||
|
||||
Rooted<RegExpObject*> reobj(cx_);
|
||||
TRY_VAR(reobj, RegExpObject::create(cx_,
|
||||
BINJS_TRY_VAR(reobj, RegExpObject::create(cx_,
|
||||
pattern,
|
||||
reflags,
|
||||
alloc_,
|
||||
TenuredObject));
|
||||
|
||||
TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this));
|
||||
BINJS_TRY_DECL(result, factory_.newRegExp(reobj, tokenizer_->pos(start), *this));
|
||||
|
||||
LiteralStringExpression:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));
|
||||
|
||||
Method:
|
||||
inherits: FunctionExpression
|
||||
build: |
|
||||
MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
|
||||
TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::None));
|
||||
BINJS_TRY_DECL(result, factory_.newStringLiteral(value, tokenizer_->pos(start)));
|
||||
|
||||
NewExpression:
|
||||
build: |
|
||||
|
@ -853,11 +842,11 @@ ReturnStatement:
|
|||
|
||||
parseContext_->functionBox()->usesReturn = true;
|
||||
build:
|
||||
TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newReturnStatement(expression, tokenizer_->pos(start)));
|
||||
|
||||
Script:
|
||||
build:
|
||||
MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
|
||||
BINJS_MOZ_TRY_DECL(result, appendDirectivesToBody(/* body = */ statements, /* directives = */ directives));
|
||||
|
||||
Setter:
|
||||
inherits: Method
|
||||
|
@ -865,29 +854,29 @@ Setter:
|
|||
const auto isAsync = false;
|
||||
const auto isGenerator = false;
|
||||
build: |
|
||||
TRY_DECL(params, factory_.newList(ParseNodeKind::ParamsBody, param));
|
||||
MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
|
||||
TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
|
||||
BINJS_TRY_DECL(params, factory_.newList(ParseNodeKind::ParamsBody, param));
|
||||
BINJS_MOZ_TRY_DECL(method, buildFunction(start, kind, name, params, body, funbox));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, method, AccessorType::Setter));
|
||||
|
||||
ShorthandProperty:
|
||||
build: |
|
||||
if (!factory_.isUsableAsObjectPropertyName(name))
|
||||
TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
|
||||
BINJS_TRY_VAR(name, factory_.newObjectLiteralPropertyName(name->name(), tokenizer_->pos(start)));
|
||||
|
||||
TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));
|
||||
BINJS_TRY_DECL(result, factory_.newObjectMethodOrPropertyDefinition(name, name, AccessorType::None));
|
||||
|
||||
SwitchCase:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));
|
||||
BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, test, consequent));
|
||||
|
||||
SwitchDefault:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));
|
||||
BINJS_TRY_DECL(result, factory_.newCaseOrDefault(start, nullptr, consequent));
|
||||
|
||||
SwitchStatement:
|
||||
build: |
|
||||
TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
|
||||
TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));
|
||||
BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
|
||||
BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));
|
||||
|
||||
SwitchStatementWithDefault:
|
||||
build: |
|
||||
|
@ -900,16 +889,16 @@ SwitchStatementWithDefault:
|
|||
factory_.addList(cases, iter);
|
||||
iter = next;
|
||||
}
|
||||
TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
|
||||
TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));
|
||||
BINJS_TRY_DECL(scope, factory_.newLexicalScope(nullptr, cases));
|
||||
BINJS_TRY_DECL(result, factory_.newSwitchStatement(start, discriminant, scope));
|
||||
|
||||
StaticMemberAssignmentTarget:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
||||
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
||||
|
||||
StaticMemberExpression:
|
||||
build: |
|
||||
TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
||||
BINJS_TRY_DECL(result, factory_.newPropertyAccess(object, property->asPropertyName(), start));
|
||||
|
||||
ThisExpression:
|
||||
build: |
|
||||
|
@ -919,13 +908,13 @@ ThisExpression:
|
|||
TokenPos pos = tokenizer_->pos(start);
|
||||
ParseNode* thisName(nullptr);
|
||||
if (parseContext_->sc()->thisBinding() == ThisBinding::Function)
|
||||
TRY_VAR(thisName, factory_.newName(cx_->names().dotThis, pos, cx_));
|
||||
BINJS_TRY_VAR(thisName, factory_.newName(cx_->names().dotThis, pos, cx_));
|
||||
|
||||
TRY_DECL(result, factory_.newThisLiteral(pos, thisName));
|
||||
BINJS_TRY_DECL(result, factory_.newThisLiteral(pos, thisName));
|
||||
|
||||
ThrowStatement:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newThrowStatement(expression, tokenizer_->pos(start)));
|
||||
BINJS_TRY_DECL(result, factory_.newThrowStatement(expression, tokenizer_->pos(start)));
|
||||
|
||||
TryCatchStatement:
|
||||
fields:
|
||||
|
@ -936,9 +925,9 @@ TryCatchStatement:
|
|||
before: |
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::Try);
|
||||
ParseContext::Scope scope(cx_, parseContext_, usedNames_);
|
||||
TRY(scope.init(parseContext_));
|
||||
BINJS_TRY(scope.init(parseContext_));
|
||||
build:
|
||||
TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, /* finally = */ nullptr));
|
||||
BINJS_TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, /* finally = */ nullptr));
|
||||
|
||||
TryFinallyStatement:
|
||||
fields:
|
||||
|
@ -949,7 +938,7 @@ TryFinallyStatement:
|
|||
before: |
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::Try);
|
||||
ParseContext::Scope scope(cx_, parseContext_, usedNames_);
|
||||
TRY(scope.init(parseContext_));
|
||||
BINJS_TRY(scope.init(parseContext_));
|
||||
finalizer:
|
||||
block:
|
||||
declare:
|
||||
|
@ -957,9 +946,9 @@ TryFinallyStatement:
|
|||
before: |
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::Finally);
|
||||
ParseContext::Scope scope(cx_, parseContext_, usedNames_);
|
||||
TRY(scope.init(parseContext_));
|
||||
BINJS_TRY(scope.init(parseContext_));
|
||||
build:
|
||||
TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, finalizer));
|
||||
BINJS_TRY_DECL(result, factory_.newTryStatement(start, body, catchClause, finalizer));
|
||||
|
||||
UnaryExpression:
|
||||
build: |
|
||||
|
@ -1005,7 +994,7 @@ UnaryExpression:
|
|||
break;
|
||||
}
|
||||
}
|
||||
TRY_DECL(result, factory_.newUnary(pnk, start, operand));
|
||||
BINJS_TRY_DECL(result, factory_.newUnary(pnk, start, operand));
|
||||
|
||||
UpdateExpression:
|
||||
build: |
|
||||
|
@ -1020,7 +1009,7 @@ UpdateExpression:
|
|||
: ParseNodeKind::PostDecrement;
|
||||
break;
|
||||
}
|
||||
TRY_DECL(result, factory_.newUnary(pnk, start, operand));
|
||||
BINJS_TRY_DECL(result, factory_.newUnary(pnk, start, operand));
|
||||
|
||||
VariableDeclaration:
|
||||
init:
|
||||
|
@ -1059,7 +1048,7 @@ VariableDeclarator:
|
|||
// `var foo [= bar]``
|
||||
MOZ_TRY(checkBinding(binding->pn_atom->asPropertyName()));
|
||||
|
||||
TRY_VAR(result, factory_.newName(binding->pn_atom->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
BINJS_TRY_VAR(result, factory_.newName(binding->pn_atom->asPropertyName(), tokenizer_->pos(start), cx_));
|
||||
if (init)
|
||||
result->pn_expr = init;
|
||||
} else {
|
||||
|
@ -1070,15 +1059,15 @@ VariableDeclarator:
|
|||
}
|
||||
|
||||
MOZ_CRASH("Unimplemented: AssertedScope check for BindingPattern variable declaration");
|
||||
TRY_VAR(result, factory_.newAssignment(ParseNodeKind::Assign, binding, init));
|
||||
BINJS_TRY_VAR(result, factory_.newAssignment(ParseNodeKind::Assign, binding, init));
|
||||
}
|
||||
|
||||
WhileStatement:
|
||||
init:
|
||||
ParseContext::Statement stmt(parseContext_, StatementKind::WhileLoop);
|
||||
build:
|
||||
TRY_DECL(result, factory_.newWhileStatement(start, test, body));
|
||||
BINJS_TRY_DECL(result, factory_.newWhileStatement(start, test, body));
|
||||
|
||||
WithStatement:
|
||||
build:
|
||||
TRY_DECL(result, factory_.newWithStatement(start, object, body));
|
||||
BINJS_TRY_DECL(result, factory_.newWithStatement(start, object, body));
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 frontend_BinSourceSupport_h
|
||||
#define frontend_BinSourceSupport_h
|
||||
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "frontend/BinToken.h"
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "js/Result.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
// Support for parsing JS Binary ASTs.
|
||||
struct BinaryASTSupport {
|
||||
using BinVariant = js::frontend::BinVariant;
|
||||
using BinField = js::frontend::BinField;
|
||||
using BinKind = js::frontend::BinKind;
|
||||
|
||||
// A structure designed to perform fast char* + length lookup
|
||||
// without copies.
|
||||
struct CharSlice {
|
||||
const char* start_;
|
||||
uint32_t byteLen_;
|
||||
CharSlice(const CharSlice& other)
|
||||
: start_(other.start_)
|
||||
, byteLen_(other.byteLen_)
|
||||
{ }
|
||||
CharSlice(const char* start, const uint32_t byteLen)
|
||||
: start_(start)
|
||||
, byteLen_(byteLen)
|
||||
{ }
|
||||
const char* begin() const {
|
||||
return start_;
|
||||
}
|
||||
const char* end() const {
|
||||
return start_ + byteLen_;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
void dump() const {
|
||||
for (auto c: *this)
|
||||
fprintf(stderr, "%c", c);
|
||||
|
||||
fprintf(stderr, " (%d)", byteLen_);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
typedef const CharSlice Lookup;
|
||||
static js::HashNumber hash(Lookup l) {
|
||||
return mozilla::HashString(l.start_, l.byteLen_);
|
||||
}
|
||||
static bool match(const Lookup key, Lookup lookup) {
|
||||
if (key.byteLen_ != lookup.byteLen_)
|
||||
return false;
|
||||
return strncmp(key.start_, lookup.start_, key.byteLen_) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
JS::Result<const BinVariant*> binVariant(JSContext*, const CharSlice);
|
||||
JS::Result<const BinField*> binField(JSContext*, const CharSlice);
|
||||
JS::Result<const BinKind*> binKind(JSContext*, const CharSlice);
|
||||
|
||||
private:
|
||||
// A HashMap that can be queried without copies from a CharSlice key.
|
||||
// Initialized on first call. Keys are CharSlices into static strings.
|
||||
using BinKindMap = js::HashMap<const CharSlice, BinKind, CharSlice, js::SystemAllocPolicy>;
|
||||
BinKindMap binKindMap_;
|
||||
|
||||
using BinFieldMap = js::HashMap<const CharSlice, BinField, CharSlice, js::SystemAllocPolicy>;
|
||||
BinFieldMap binFieldMap_;
|
||||
|
||||
using BinVariantMap = js::HashMap<const CharSlice, BinVariant, CharSlice, js::SystemAllocPolicy>;
|
||||
BinVariantMap binVariantMap_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // frontend_BinSourceSupport_h
|
|
@ -6,30 +6,122 @@
|
|||
|
||||
#include "frontend/BinToken.h"
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "frontend/BinSourceRuntimeSupport.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "gc/Zone.h"
|
||||
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
const char* BINKIND_DESCRIPTIONS[] = {
|
||||
#define WITH_KIND(_, SPEC_NAME) #SPEC_NAME,
|
||||
FOR_EACH_BIN_KIND(WITH_KIND)
|
||||
#undef WITH_KIND
|
||||
const BinaryASTSupport::CharSlice BINKIND_DESCRIPTIONS[] = {
|
||||
#define WITH_VARIANT(_, SPEC_NAME) BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
|
||||
FOR_EACH_BIN_KIND(WITH_VARIANT)
|
||||
#undef WITH_VARIANT
|
||||
};
|
||||
|
||||
const char* BINFIELD_DESCRIPTIONS[] = {
|
||||
#define WITH_FIELD(_, SPEC_NAME) #SPEC_NAME,
|
||||
FOR_EACH_BIN_FIELD(WITH_FIELD)
|
||||
#undef WITH_FIELD
|
||||
const BinaryASTSupport::CharSlice BINFIELD_DESCRIPTIONS[] = {
|
||||
#define WITH_VARIANT(_, SPEC_NAME) BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
|
||||
FOR_EACH_BIN_FIELD(WITH_VARIANT)
|
||||
#undef WITH_VARIANT
|
||||
};
|
||||
|
||||
const char* describeBinKind(const BinKind& kind) {
|
||||
return BINKIND_DESCRIPTIONS[static_cast<size_t>(kind)];
|
||||
const BinaryASTSupport::CharSlice BINVARIANT_DESCRIPTIONS[] = {
|
||||
#define WITH_VARIANT(_, SPEC_NAME) BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
|
||||
FOR_EACH_BIN_VARIANT(WITH_VARIANT)
|
||||
#undef WITH_VARIANT
|
||||
};
|
||||
|
||||
const BinaryASTSupport::CharSlice&
|
||||
getBinKind(const BinKind& variant)
|
||||
{
|
||||
return BINKIND_DESCRIPTIONS[static_cast<size_t>(variant)];
|
||||
}
|
||||
|
||||
const char* describeBinField(const BinField& field) {
|
||||
return BINFIELD_DESCRIPTIONS[static_cast<size_t>(field)];
|
||||
const BinaryASTSupport::CharSlice&
|
||||
getBinVariant(const BinVariant& variant)
|
||||
{
|
||||
return BINVARIANT_DESCRIPTIONS[static_cast<size_t>(variant)];
|
||||
}
|
||||
|
||||
const BinaryASTSupport::CharSlice&
|
||||
getBinField(const BinField& variant)
|
||||
{
|
||||
return BINFIELD_DESCRIPTIONS[static_cast<size_t>(variant)];
|
||||
}
|
||||
|
||||
const char* describeBinKind(const BinKind& variant)
|
||||
{
|
||||
return getBinKind(variant).begin();
|
||||
}
|
||||
|
||||
const char* describeBinField(const BinField& variant)
|
||||
{
|
||||
return getBinField(variant).begin();
|
||||
}
|
||||
|
||||
const char* describeBinVariant(const BinVariant& variant)
|
||||
{
|
||||
return getBinVariant(variant).begin();
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
|
||||
|
||||
JS::Result<const js::frontend::BinKind*>
|
||||
BinaryASTSupport::binKind(JSContext* cx, const CharSlice key)
|
||||
{
|
||||
if (!binKindMap_.initialized()) {
|
||||
// Initialize lazily.
|
||||
if (!binKindMap_.init(frontend::BINKIND_LIMIT))
|
||||
return cx->alreadyReportedError();
|
||||
|
||||
for (size_t i = 0; i < frontend::BINKIND_LIMIT; ++i) {
|
||||
const BinKind variant = static_cast<BinKind>(i);
|
||||
const CharSlice& key = getBinKind(variant);
|
||||
auto ptr = binKindMap_.lookupForAdd(key);
|
||||
MOZ_ASSERT(!ptr);
|
||||
if (!binKindMap_.add(ptr, key, variant))
|
||||
return cx->alreadyReportedError();
|
||||
}
|
||||
}
|
||||
|
||||
auto ptr = binKindMap_.lookup(key);
|
||||
if (!ptr)
|
||||
return nullptr;
|
||||
|
||||
return &ptr->value();
|
||||
}
|
||||
|
||||
JS::Result<const js::frontend::BinVariant*>
|
||||
BinaryASTSupport::binVariant(JSContext* cx, const CharSlice key) {
|
||||
if (!binVariantMap_.initialized()) {
|
||||
// Initialize lazily.
|
||||
if (!binVariantMap_.init(frontend::BINVARIANT_LIMIT))
|
||||
return cx->alreadyReportedError();
|
||||
|
||||
for (size_t i = 0; i < frontend::BINVARIANT_LIMIT; ++i) {
|
||||
const BinVariant variant = static_cast<BinVariant>(i);
|
||||
const CharSlice& key = getBinVariant(variant);
|
||||
auto ptr = binVariantMap_.lookupForAdd(key);
|
||||
MOZ_ASSERT(!ptr);
|
||||
if (!binVariantMap_.add(ptr, key, variant))
|
||||
return cx->alreadyReportedError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto ptr = binVariantMap_.lookup(key);
|
||||
if (!ptr)
|
||||
return nullptr;
|
||||
|
||||
return &ptr->value();
|
||||
}
|
||||
|
||||
} // namespace js
|
||||
|
|
|
@ -61,178 +61,189 @@ namespace frontend {
|
|||
* (sorted by alphabetical order)
|
||||
*/
|
||||
#define FOR_EACH_BIN_KIND(F) \
|
||||
F(Arguments, Arguments) \
|
||||
F(ArrayAssignmentTarget, ArrayAssignmentTarget) \
|
||||
F(ArrayBinding, ArrayBinding) \
|
||||
F(ArrayExpression, ArrayExpression) \
|
||||
F(ArrowExpression, ArrowExpression) \
|
||||
F(AssertedBlockScope, AssertedBlockScope) \
|
||||
F(AssertedParameterScope, AssertedParameterScope) \
|
||||
F(AssertedVarScope, AssertedVarScope) \
|
||||
F(AssignmentExpression, AssignmentExpression) \
|
||||
F(AssignmentTarget, AssignmentTarget) \
|
||||
F(AssignmentTargetIdentifier, AssignmentTargetIdentifier) \
|
||||
F(AssignmentTargetOrAssignmentTargetWithInitializer, AssignmentTargetOrAssignmentTargetWithInitializer) \
|
||||
F(AssignmentTargetPattern, AssignmentTargetPattern) \
|
||||
F(AssignmentTargetProperty, AssignmentTargetProperty) \
|
||||
F(AssignmentTargetPropertyIdentifier, AssignmentTargetPropertyIdentifier) \
|
||||
F(AssignmentTargetPropertyProperty, AssignmentTargetPropertyProperty) \
|
||||
F(AssignmentTargetWithInitializer, AssignmentTargetWithInitializer) \
|
||||
F(AwaitExpression, AwaitExpression) \
|
||||
F(BinaryExpression, BinaryExpression) \
|
||||
F(BinaryOperator, BinaryOperator) \
|
||||
F(Binding, Binding) \
|
||||
F(BindingIdentifier, BindingIdentifier) \
|
||||
F(BindingOrBindingWithInitializer, BindingOrBindingWithInitializer) \
|
||||
F(BindingPattern, BindingPattern) \
|
||||
F(BindingProperty, BindingProperty) \
|
||||
F(BindingPropertyIdentifier, BindingPropertyIdentifier) \
|
||||
F(BindingPropertyProperty, BindingPropertyProperty) \
|
||||
F(BindingWithInitializer, BindingWithInitializer) \
|
||||
F(Block, Block) \
|
||||
F(BreakStatement, BreakStatement) \
|
||||
F(CallExpression, CallExpression) \
|
||||
F(CatchClause, CatchClause) \
|
||||
F(ClassDeclaration, ClassDeclaration) \
|
||||
F(ClassElement, ClassElement) \
|
||||
F(ClassExpression, ClassExpression) \
|
||||
F(CompoundAssignmentExpression, CompoundAssignmentExpression) \
|
||||
F(CompoundAssignmentOperator, CompoundAssignmentOperator) \
|
||||
F(ComputedMemberAssignmentTarget, ComputedMemberAssignmentTarget) \
|
||||
F(ComputedMemberExpression, ComputedMemberExpression) \
|
||||
F(ComputedPropertyName, ComputedPropertyName) \
|
||||
F(ConditionalExpression, ConditionalExpression) \
|
||||
F(ContinueStatement, ContinueStatement) \
|
||||
F(DataProperty, DataProperty) \
|
||||
F(DebuggerStatement, DebuggerStatement) \
|
||||
F(Directive, Directive) \
|
||||
F(DoWhileStatement, DoWhileStatement) \
|
||||
F(EmptyStatement, EmptyStatement) \
|
||||
F(Export, Export) \
|
||||
F(ExportAllFrom, ExportAllFrom) \
|
||||
F(ExportDeclaration, ExportDeclaration) \
|
||||
F(ExportDefault, ExportDefault) \
|
||||
F(ExportFrom, ExportFrom) \
|
||||
F(ExportFromSpecifier, ExportFromSpecifier) \
|
||||
F(ExportLocalSpecifier, ExportLocalSpecifier) \
|
||||
F(ExportLocals, ExportLocals) \
|
||||
F(Expression, Expression) \
|
||||
F(ExpressionOrSuper, ExpressionOrSuper) \
|
||||
F(ExpressionOrTemplateElement, ExpressionOrTemplateElement) \
|
||||
F(ExpressionStatement, ExpressionStatement) \
|
||||
F(ForInOfBinding, ForInOfBinding) \
|
||||
F(ForInOfBindingOrAssignmentTarget, ForInOfBindingOrAssignmentTarget) \
|
||||
F(ForInStatement, ForInStatement) \
|
||||
F(ForOfStatement, ForOfStatement) \
|
||||
F(ForStatement, ForStatement) \
|
||||
F(FormalParameters, FormalParameters) \
|
||||
F(FunctionBody, FunctionBody) \
|
||||
F(FunctionBodyOrExpression, FunctionBodyOrExpression) \
|
||||
F(FunctionDeclaration, FunctionDeclaration) \
|
||||
F(FunctionDeclarationOrClassDeclarationOrExpression, FunctionDeclarationOrClassDeclarationOrExpression) \
|
||||
F(FunctionDeclarationOrClassDeclarationOrVariableDeclaration, FunctionDeclarationOrClassDeclarationOrVariableDeclaration) \
|
||||
F(FunctionExpression, FunctionExpression) \
|
||||
F(Getter, Getter) \
|
||||
F(Identifier, Identifier) \
|
||||
F(IdentifierExpression, IdentifierExpression) \
|
||||
F(IdentifierName, IdentifierName) \
|
||||
F(IfStatement, IfStatement) \
|
||||
F(Import, Import) \
|
||||
F(ImportDeclaration, ImportDeclaration) \
|
||||
F(ImportDeclarationOrExportDeclarationOrStatement, ImportDeclarationOrExportDeclarationOrStatement) \
|
||||
F(ImportNamespace, ImportNamespace) \
|
||||
F(ImportSpecifier, ImportSpecifier) \
|
||||
F(IterationStatement, IterationStatement) \
|
||||
F(Label, Label) \
|
||||
F(LabelledStatement, LabelledStatement) \
|
||||
F(ListOfAssignmentTargetOrAssignmentTargetWithInitializer, ListOfAssignmentTargetOrAssignmentTargetWithInitializer) \
|
||||
F(ListOfAssignmentTargetProperty, ListOfAssignmentTargetProperty) \
|
||||
F(ListOfBindingProperty, ListOfBindingProperty) \
|
||||
F(ListOfClassElement, ListOfClassElement) \
|
||||
F(ListOfDirective, ListOfDirective) \
|
||||
F(ListOfExportFromSpecifier, ListOfExportFromSpecifier) \
|
||||
F(ListOfExportLocalSpecifier, ListOfExportLocalSpecifier) \
|
||||
F(ListOfExpressionOrTemplateElement, ListOfExpressionOrTemplateElement) \
|
||||
F(ListOfIdentifierName, ListOfIdentifierName) \
|
||||
F(ListOfImportDeclarationOrExportDeclarationOrStatement, ListOfImportDeclarationOrExportDeclarationOrStatement) \
|
||||
F(ListOfImportSpecifier, ListOfImportSpecifier) \
|
||||
F(ListOfObjectProperty, ListOfObjectProperty) \
|
||||
F(ListOfOptionalBindingOrBindingWithInitializer, ListOfOptionalBindingOrBindingWithInitializer) \
|
||||
F(ListOfOptionalSpreadElementOrExpression, ListOfOptionalSpreadElementOrExpression) \
|
||||
F(ListOfParameter, ListOfParameter) \
|
||||
F(ListOfStatement, ListOfStatement) \
|
||||
F(ListOfSwitchCase, ListOfSwitchCase) \
|
||||
F(ListOfVariableDeclarator, ListOfVariableDeclarator) \
|
||||
F(Literal, Literal) \
|
||||
F(LiteralBooleanExpression, LiteralBooleanExpression) \
|
||||
F(LiteralInfinityExpression, LiteralInfinityExpression) \
|
||||
F(LiteralNullExpression, LiteralNullExpression) \
|
||||
F(LiteralNumericExpression, LiteralNumericExpression) \
|
||||
F(LiteralPropertyName, LiteralPropertyName) \
|
||||
F(LiteralRegExpExpression, LiteralRegExpExpression) \
|
||||
F(LiteralStringExpression, LiteralStringExpression) \
|
||||
F(Method, Method) \
|
||||
F(MethodDefinition, MethodDefinition) \
|
||||
F(Module, Module) \
|
||||
F(NewExpression, NewExpression) \
|
||||
F(NewTargetExpression, NewTargetExpression) \
|
||||
F(ObjectAssignmentTarget, ObjectAssignmentTarget) \
|
||||
F(ObjectBinding, ObjectBinding) \
|
||||
F(ObjectExpression, ObjectExpression) \
|
||||
F(ObjectProperty, ObjectProperty) \
|
||||
F(OptionalAssertedBlockScope, OptionalAssertedBlockScope) \
|
||||
F(OptionalAssertedParameterScope, OptionalAssertedParameterScope) \
|
||||
F(OptionalAssertedVarScope, OptionalAssertedVarScope) \
|
||||
F(OptionalAssignmentTarget, OptionalAssignmentTarget) \
|
||||
F(OptionalBinding, OptionalBinding) \
|
||||
F(OptionalBindingIdentifier, OptionalBindingIdentifier) \
|
||||
F(OptionalBindingOrBindingWithInitializer, OptionalBindingOrBindingWithInitializer) \
|
||||
F(OptionalCatchClause, OptionalCatchClause) \
|
||||
F(OptionalExpression, OptionalExpression) \
|
||||
F(OptionalIdentifierName, OptionalIdentifierName) \
|
||||
F(OptionalLabel, OptionalLabel) \
|
||||
F(OptionalSpreadElementOrExpression, OptionalSpreadElementOrExpression) \
|
||||
F(OptionalStatement, OptionalStatement) \
|
||||
F(OptionalVariableDeclarationOrExpression, OptionalVariableDeclarationOrExpression) \
|
||||
F(Parameter, Parameter) \
|
||||
F(Program, Program) \
|
||||
F(PropertyName, PropertyName) \
|
||||
F(ReturnStatement, ReturnStatement) \
|
||||
F(Script, Script) \
|
||||
F(Setter, Setter) \
|
||||
F(ShorthandProperty, ShorthandProperty) \
|
||||
F(SimpleAssignmentTarget, SimpleAssignmentTarget) \
|
||||
F(SpreadElement, SpreadElement) \
|
||||
F(SpreadElementOrExpression, SpreadElementOrExpression) \
|
||||
F(Statement, Statement) \
|
||||
F(StaticMemberAssignmentTarget, StaticMemberAssignmentTarget) \
|
||||
F(StaticMemberExpression, StaticMemberExpression) \
|
||||
F(Super, Super) \
|
||||
F(SwitchCase, SwitchCase) \
|
||||
F(SwitchDefault, SwitchDefault) \
|
||||
F(SwitchStatement, SwitchStatement) \
|
||||
F(SwitchStatementWithDefault, SwitchStatementWithDefault) \
|
||||
F(TemplateElement, TemplateElement) \
|
||||
F(TemplateExpression, TemplateExpression) \
|
||||
F(ThisExpression, ThisExpression) \
|
||||
F(ThrowStatement, ThrowStatement) \
|
||||
F(TryCatchStatement, TryCatchStatement) \
|
||||
F(TryFinallyStatement, TryFinallyStatement) \
|
||||
F(UnaryExpression, UnaryExpression) \
|
||||
F(UnaryOperator, UnaryOperator) \
|
||||
F(UpdateExpression, UpdateExpression) \
|
||||
F(UpdateOperator, UpdateOperator) \
|
||||
F(VariableDeclaration, VariableDeclaration) \
|
||||
F(VariableDeclarationKind, VariableDeclarationKind) \
|
||||
F(VariableDeclarationOrExpression, VariableDeclarationOrExpression) \
|
||||
F(VariableDeclarator, VariableDeclarator) \
|
||||
F(WhileStatement, WhileStatement) \
|
||||
F(WithStatement, WithStatement) \
|
||||
F(YieldExpression, YieldExpression) \
|
||||
F(YieldStarExpression, YieldStarExpression) \
|
||||
F(_Null, _Null) \
|
||||
F(string, string)
|
||||
|
||||
F(_Null, "") \
|
||||
F(Arguments, "Arguments") \
|
||||
F(ArrayAssignmentTarget, "ArrayAssignmentTarget") \
|
||||
F(ArrayBinding, "ArrayBinding") \
|
||||
F(ArrayExpression, "ArrayExpression") \
|
||||
F(ArrowExpression, "ArrowExpression") \
|
||||
F(AssertedBlockScope, "AssertedBlockScope") \
|
||||
F(AssertedParameterScope, "AssertedParameterScope") \
|
||||
F(AssertedVarScope, "AssertedVarScope") \
|
||||
F(AssignmentExpression, "AssignmentExpression") \
|
||||
F(AssignmentTarget, "AssignmentTarget") \
|
||||
F(AssignmentTargetIdentifier, "AssignmentTargetIdentifier") \
|
||||
F(AssignmentTargetOrAssignmentTargetWithInitializer, "AssignmentTargetOrAssignmentTargetWithInitializer") \
|
||||
F(AssignmentTargetPattern, "AssignmentTargetPattern") \
|
||||
F(AssignmentTargetProperty, "AssignmentTargetProperty") \
|
||||
F(AssignmentTargetPropertyIdentifier, "AssignmentTargetPropertyIdentifier") \
|
||||
F(AssignmentTargetPropertyProperty, "AssignmentTargetPropertyProperty") \
|
||||
F(AssignmentTargetWithInitializer, "AssignmentTargetWithInitializer") \
|
||||
F(AwaitExpression, "AwaitExpression") \
|
||||
F(BinaryExpression, "BinaryExpression") \
|
||||
F(BinaryOperator, "BinaryOperator") \
|
||||
F(Binding, "Binding") \
|
||||
F(BindingIdentifier, "BindingIdentifier") \
|
||||
F(BindingOrBindingWithInitializer, "BindingOrBindingWithInitializer") \
|
||||
F(BindingPattern, "BindingPattern") \
|
||||
F(BindingProperty, "BindingProperty") \
|
||||
F(BindingPropertyIdentifier, "BindingPropertyIdentifier") \
|
||||
F(BindingPropertyProperty, "BindingPropertyProperty") \
|
||||
F(BindingWithInitializer, "BindingWithInitializer") \
|
||||
F(Block, "Block") \
|
||||
F(BreakStatement, "BreakStatement") \
|
||||
F(CallExpression, "CallExpression") \
|
||||
F(CatchClause, "CatchClause") \
|
||||
F(ClassDeclaration, "ClassDeclaration") \
|
||||
F(ClassElement, "ClassElement") \
|
||||
F(ClassExpression, "ClassExpression") \
|
||||
F(CompoundAssignmentExpression, "CompoundAssignmentExpression") \
|
||||
F(CompoundAssignmentOperator, "CompoundAssignmentOperator") \
|
||||
F(ComputedMemberAssignmentTarget, "ComputedMemberAssignmentTarget") \
|
||||
F(ComputedMemberExpression, "ComputedMemberExpression") \
|
||||
F(ComputedPropertyName, "ComputedPropertyName") \
|
||||
F(ConditionalExpression, "ConditionalExpression") \
|
||||
F(ContinueStatement, "ContinueStatement") \
|
||||
F(DataProperty, "DataProperty") \
|
||||
F(DebuggerStatement, "DebuggerStatement") \
|
||||
F(Directive, "Directive") \
|
||||
F(DoWhileStatement, "DoWhileStatement") \
|
||||
F(EagerArrowExpression, "EagerArrowExpression") \
|
||||
F(EagerFunctionDeclaration, "EagerFunctionDeclaration") \
|
||||
F(EagerFunctionExpression, "EagerFunctionExpression") \
|
||||
F(EagerGetter, "EagerGetter") \
|
||||
F(EagerMethod, "EagerMethod") \
|
||||
F(EagerSetter, "EagerSetter") \
|
||||
F(EmptyStatement, "EmptyStatement") \
|
||||
F(Export, "Export") \
|
||||
F(ExportAllFrom, "ExportAllFrom") \
|
||||
F(ExportDeclaration, "ExportDeclaration") \
|
||||
F(ExportDefault, "ExportDefault") \
|
||||
F(ExportFrom, "ExportFrom") \
|
||||
F(ExportFromSpecifier, "ExportFromSpecifier") \
|
||||
F(ExportLocalSpecifier, "ExportLocalSpecifier") \
|
||||
F(ExportLocals, "ExportLocals") \
|
||||
F(Expression, "Expression") \
|
||||
F(ExpressionOrSuper, "ExpressionOrSuper") \
|
||||
F(ExpressionOrTemplateElement, "ExpressionOrTemplateElement") \
|
||||
F(ExpressionStatement, "ExpressionStatement") \
|
||||
F(ForInOfBinding, "ForInOfBinding") \
|
||||
F(ForInOfBindingOrAssignmentTarget, "ForInOfBindingOrAssignmentTarget") \
|
||||
F(ForInStatement, "ForInStatement") \
|
||||
F(ForOfStatement, "ForOfStatement") \
|
||||
F(ForStatement, "ForStatement") \
|
||||
F(FormalParameters, "FormalParameters") \
|
||||
F(FunctionBody, "FunctionBody") \
|
||||
F(FunctionBodyOrExpression, "FunctionBodyOrExpression") \
|
||||
F(FunctionDeclaration, "FunctionDeclaration") \
|
||||
F(FunctionDeclarationOrClassDeclarationOrExpression, "FunctionDeclarationOrClassDeclarationOrExpression") \
|
||||
F(FunctionDeclarationOrClassDeclarationOrVariableDeclaration, "FunctionDeclarationOrClassDeclarationOrVariableDeclaration") \
|
||||
F(FunctionExpression, "FunctionExpression") \
|
||||
F(Getter, "Getter") \
|
||||
F(Identifier, "Identifier") \
|
||||
F(IdentifierExpression, "IdentifierExpression") \
|
||||
F(IdentifierName, "IdentifierName") \
|
||||
F(IfStatement, "IfStatement") \
|
||||
F(Import, "Import") \
|
||||
F(ImportDeclaration, "ImportDeclaration") \
|
||||
F(ImportDeclarationOrExportDeclarationOrStatement, "ImportDeclarationOrExportDeclarationOrStatement") \
|
||||
F(ImportNamespace, "ImportNamespace") \
|
||||
F(ImportSpecifier, "ImportSpecifier") \
|
||||
F(IterationStatement, "IterationStatement") \
|
||||
F(Label, "Label") \
|
||||
F(LabelledStatement, "LabelledStatement") \
|
||||
F(ListOfAssignmentTargetOrAssignmentTargetWithInitializer, "ListOfAssignmentTargetOrAssignmentTargetWithInitializer") \
|
||||
F(ListOfAssignmentTargetProperty, "ListOfAssignmentTargetProperty") \
|
||||
F(ListOfBindingProperty, "ListOfBindingProperty") \
|
||||
F(ListOfClassElement, "ListOfClassElement") \
|
||||
F(ListOfDirective, "ListOfDirective") \
|
||||
F(ListOfExportFromSpecifier, "ListOfExportFromSpecifier") \
|
||||
F(ListOfExportLocalSpecifier, "ListOfExportLocalSpecifier") \
|
||||
F(ListOfExpressionOrTemplateElement, "ListOfExpressionOrTemplateElement") \
|
||||
F(ListOfIdentifierName, "ListOfIdentifierName") \
|
||||
F(ListOfImportDeclarationOrExportDeclarationOrStatement, "ListOfImportDeclarationOrExportDeclarationOrStatement") \
|
||||
F(ListOfImportSpecifier, "ListOfImportSpecifier") \
|
||||
F(ListOfObjectProperty, "ListOfObjectProperty") \
|
||||
F(ListOfOptionalBindingOrBindingWithInitializer, "ListOfOptionalBindingOrBindingWithInitializer") \
|
||||
F(ListOfOptionalSpreadElementOrExpression, "ListOfOptionalSpreadElementOrExpression") \
|
||||
F(ListOfParameter, "ListOfParameter") \
|
||||
F(ListOfStatement, "ListOfStatement") \
|
||||
F(ListOfSwitchCase, "ListOfSwitchCase") \
|
||||
F(ListOfVariableDeclarator, "ListOfVariableDeclarator") \
|
||||
F(Literal, "Literal") \
|
||||
F(LiteralBooleanExpression, "LiteralBooleanExpression") \
|
||||
F(LiteralInfinityExpression, "LiteralInfinityExpression") \
|
||||
F(LiteralNullExpression, "LiteralNullExpression") \
|
||||
F(LiteralNumericExpression, "LiteralNumericExpression") \
|
||||
F(LiteralPropertyName, "LiteralPropertyName") \
|
||||
F(LiteralRegExpExpression, "LiteralRegExpExpression") \
|
||||
F(LiteralStringExpression, "LiteralStringExpression") \
|
||||
F(Method, "Method") \
|
||||
F(MethodDefinition, "MethodDefinition") \
|
||||
F(Module, "Module") \
|
||||
F(NewExpression, "NewExpression") \
|
||||
F(NewTargetExpression, "NewTargetExpression") \
|
||||
F(ObjectAssignmentTarget, "ObjectAssignmentTarget") \
|
||||
F(ObjectBinding, "ObjectBinding") \
|
||||
F(ObjectExpression, "ObjectExpression") \
|
||||
F(ObjectProperty, "ObjectProperty") \
|
||||
F(OptionalAssertedBlockScope, "OptionalAssertedBlockScope") \
|
||||
F(OptionalAssertedParameterScope, "OptionalAssertedParameterScope") \
|
||||
F(OptionalAssertedVarScope, "OptionalAssertedVarScope") \
|
||||
F(OptionalAssignmentTarget, "OptionalAssignmentTarget") \
|
||||
F(OptionalBinding, "OptionalBinding") \
|
||||
F(OptionalBindingIdentifier, "OptionalBindingIdentifier") \
|
||||
F(OptionalBindingOrBindingWithInitializer, "OptionalBindingOrBindingWithInitializer") \
|
||||
F(OptionalCatchClause, "OptionalCatchClause") \
|
||||
F(OptionalExpression, "OptionalExpression") \
|
||||
F(OptionalIdentifierName, "OptionalIdentifierName") \
|
||||
F(OptionalLabel, "OptionalLabel") \
|
||||
F(OptionalSpreadElementOrExpression, "OptionalSpreadElementOrExpression") \
|
||||
F(OptionalStatement, "OptionalStatement") \
|
||||
F(OptionalVariableDeclarationOrExpression, "OptionalVariableDeclarationOrExpression") \
|
||||
F(Parameter, "Parameter") \
|
||||
F(Program, "Program") \
|
||||
F(PropertyName, "PropertyName") \
|
||||
F(ReturnStatement, "ReturnStatement") \
|
||||
F(Script, "Script") \
|
||||
F(Setter, "Setter") \
|
||||
F(ShorthandProperty, "ShorthandProperty") \
|
||||
F(SimpleAssignmentTarget, "SimpleAssignmentTarget") \
|
||||
F(SkippableArrowExpression, "SkippableArrowExpression") \
|
||||
F(SkippableFunctionDeclaration, "SkippableFunctionDeclaration") \
|
||||
F(SkippableFunctionExpression, "SkippableFunctionExpression") \
|
||||
F(SkippableGetter, "SkippableGetter") \
|
||||
F(SkippableMethod, "SkippableMethod") \
|
||||
F(SkippableSetter, "SkippableSetter") \
|
||||
F(SpreadElement, "SpreadElement") \
|
||||
F(SpreadElementOrExpression, "SpreadElementOrExpression") \
|
||||
F(Statement, "Statement") \
|
||||
F(StaticMemberAssignmentTarget, "StaticMemberAssignmentTarget") \
|
||||
F(StaticMemberExpression, "StaticMemberExpression") \
|
||||
F(Super, "Super") \
|
||||
F(SwitchCase, "SwitchCase") \
|
||||
F(SwitchDefault, "SwitchDefault") \
|
||||
F(SwitchStatement, "SwitchStatement") \
|
||||
F(SwitchStatementWithDefault, "SwitchStatementWithDefault") \
|
||||
F(TemplateElement, "TemplateElement") \
|
||||
F(TemplateExpression, "TemplateExpression") \
|
||||
F(ThisExpression, "ThisExpression") \
|
||||
F(ThrowStatement, "ThrowStatement") \
|
||||
F(TryCatchStatement, "TryCatchStatement") \
|
||||
F(TryFinallyStatement, "TryFinallyStatement") \
|
||||
F(UnaryExpression, "UnaryExpression") \
|
||||
F(UnaryOperator, "UnaryOperator") \
|
||||
F(UpdateExpression, "UpdateExpression") \
|
||||
F(UpdateOperator, "UpdateOperator") \
|
||||
F(VariableDeclaration, "VariableDeclaration") \
|
||||
F(VariableDeclarationKind, "VariableDeclarationKind") \
|
||||
F(VariableDeclarationOrExpression, "VariableDeclarationOrExpression") \
|
||||
F(VariableDeclarator, "VariableDeclarator") \
|
||||
F(WhileStatement, "WhileStatement") \
|
||||
F(WithStatement, "WithStatement") \
|
||||
F(YieldExpression, "YieldExpression") \
|
||||
F(YieldStarExpression, "YieldStarExpression") \
|
||||
F(String, "string")
|
||||
|
||||
enum class BinKind {
|
||||
#define EMIT_ENUM(name, _) name,
|
||||
|
@ -241,85 +252,91 @@ enum class BinKind {
|
|||
};
|
||||
|
||||
// The number of distinct values of BinKind.
|
||||
const size_t BINKIND_LIMIT = 171;
|
||||
const size_t BINKIND_LIMIT = 183;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The different fields of Binary AST nodes, as per the specifications of
|
||||
* Binary AST.
|
||||
* The different variants of Binary AST string enums, as per
|
||||
* the specifications of Binary AST, as a single macro and
|
||||
* `enum class`.
|
||||
*
|
||||
* Separate enum classes are also defined in BinSource-auto.h.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```c++
|
||||
* #define WITH_FIELD(CPP_NAME, SPEC_NAME) ...
|
||||
* FOR_EACH_BIN_FIELD(WITH_FIELD)
|
||||
* #define WITH_VARIANT(CPP_NAME, SPEC_NAME) ...
|
||||
* FOR_EACH_BIN_VARIANT(WITH_VARIANT)
|
||||
* ```
|
||||
*
|
||||
* (sorted by alphabetical order)
|
||||
*/
|
||||
#define FOR_EACH_BIN_FIELD(F) \
|
||||
F(Alternate, alternate) \
|
||||
F(Arguments, arguments) \
|
||||
F(Binding, binding) \
|
||||
F(Body, body) \
|
||||
F(BodyScope, bodyScope) \
|
||||
F(Callee, callee) \
|
||||
F(CapturedNames, capturedNames) \
|
||||
F(Cases, cases) \
|
||||
F(CatchClause, catchClause) \
|
||||
F(Consequent, consequent) \
|
||||
F(Declaration, declaration) \
|
||||
F(Declarators, declarators) \
|
||||
F(DefaultBinding, defaultBinding) \
|
||||
F(DefaultCase, defaultCase) \
|
||||
F(Directives, directives) \
|
||||
F(Discriminant, discriminant) \
|
||||
F(Elements, elements) \
|
||||
F(ExportedName, exportedName) \
|
||||
F(Expression, expression) \
|
||||
F(Finalizer, finalizer) \
|
||||
F(Flags, flags) \
|
||||
F(HasDirectEval, hasDirectEval) \
|
||||
F(Init, init) \
|
||||
F(IsAsync, isAsync) \
|
||||
F(IsGenerator, isGenerator) \
|
||||
F(IsPrefix, isPrefix) \
|
||||
F(IsStatic, isStatic) \
|
||||
F(Items, items) \
|
||||
F(Kind, kind) \
|
||||
F(Label, label) \
|
||||
F(Left, left) \
|
||||
F(LexicallyDeclaredNames, lexicallyDeclaredNames) \
|
||||
F(Method, method) \
|
||||
F(ModuleSpecifier, moduleSpecifier) \
|
||||
F(Name, name) \
|
||||
F(NamedExports, namedExports) \
|
||||
F(NamedImports, namedImports) \
|
||||
F(NamespaceBinding, namespaceBinding) \
|
||||
F(Object, object) \
|
||||
F(Operand, operand) \
|
||||
F(Operator, operator) \
|
||||
F(Param, param) \
|
||||
F(ParameterNames, parameterNames) \
|
||||
F(ParameterScope, parameterScope) \
|
||||
F(Params, params) \
|
||||
F(Pattern, pattern) \
|
||||
F(PostDefaultCases, postDefaultCases) \
|
||||
F(PreDefaultCases, preDefaultCases) \
|
||||
F(Properties, properties) \
|
||||
F(Property, property) \
|
||||
F(RawValue, rawValue) \
|
||||
F(Rest, rest) \
|
||||
F(Right, right) \
|
||||
F(Scope, scope) \
|
||||
F(Statements, statements) \
|
||||
F(Super, super) \
|
||||
F(Tag, tag) \
|
||||
F(Test, test) \
|
||||
F(Update, update) \
|
||||
F(Value, value) \
|
||||
F(VarDeclaredNames, varDeclaredNames)
|
||||
|
||||
F(Offset, "_offset") \
|
||||
F(Alternate, "alternate") \
|
||||
F(Arguments, "arguments") \
|
||||
F(Binding, "binding") \
|
||||
F(Body, "body") \
|
||||
F(BodyScope, "bodyScope") \
|
||||
F(Callee, "callee") \
|
||||
F(CapturedNames, "capturedNames") \
|
||||
F(Cases, "cases") \
|
||||
F(CatchClause, "catchClause") \
|
||||
F(Consequent, "consequent") \
|
||||
F(Declaration, "declaration") \
|
||||
F(Declarators, "declarators") \
|
||||
F(DefaultBinding, "defaultBinding") \
|
||||
F(DefaultCase, "defaultCase") \
|
||||
F(Directives, "directives") \
|
||||
F(Discriminant, "discriminant") \
|
||||
F(Elements, "elements") \
|
||||
F(ExportedName, "exportedName") \
|
||||
F(Expression, "expression") \
|
||||
F(Finalizer, "finalizer") \
|
||||
F(Flags, "flags") \
|
||||
F(HasDirectEval, "hasDirectEval") \
|
||||
F(Init, "init") \
|
||||
F(IsAsync, "isAsync") \
|
||||
F(IsGenerator, "isGenerator") \
|
||||
F(IsPrefix, "isPrefix") \
|
||||
F(IsStatic, "isStatic") \
|
||||
F(Items, "items") \
|
||||
F(Kind, "kind") \
|
||||
F(Label, "label") \
|
||||
F(Left, "left") \
|
||||
F(LexicallyDeclaredNames, "lexicallyDeclaredNames") \
|
||||
F(Method, "method") \
|
||||
F(ModuleSpecifier, "moduleSpecifier") \
|
||||
F(Name, "name") \
|
||||
F(NamedExports, "namedExports") \
|
||||
F(NamedImports, "namedImports") \
|
||||
F(NamespaceBinding, "namespaceBinding") \
|
||||
F(Object, "object") \
|
||||
F(Operand, "operand") \
|
||||
F(Operator, "operator") \
|
||||
F(Param, "param") \
|
||||
F(ParameterNames, "parameterNames") \
|
||||
F(ParameterScope, "parameterScope") \
|
||||
F(Params, "params") \
|
||||
F(Pattern, "pattern") \
|
||||
F(PostDefaultCases, "postDefaultCases") \
|
||||
F(PreDefaultCases, "preDefaultCases") \
|
||||
F(Properties, "properties") \
|
||||
F(Property, "property") \
|
||||
F(RawValue, "rawValue") \
|
||||
F(Rest, "rest") \
|
||||
F(Right, "right") \
|
||||
F(Scope, "scope") \
|
||||
F(Skipped, "skipped") \
|
||||
F(Statements, "statements") \
|
||||
F(Super, "super") \
|
||||
F(Tag, "tag") \
|
||||
F(Test, "test") \
|
||||
F(Update, "update") \
|
||||
F(Value, "value") \
|
||||
F(VarDeclaredNames, "varDeclaredNames")
|
||||
|
||||
enum class BinField {
|
||||
#define EMIT_ENUM(name, _) name,
|
||||
|
@ -328,7 +345,69 @@ enum class BinField {
|
|||
};
|
||||
|
||||
// The number of distinct values of BinField.
|
||||
const size_t BINFIELD_LIMIT = 61;
|
||||
const size_t BINFIELD_LIMIT = 63;
|
||||
|
||||
|
||||
|
||||
#define FOR_EACH_BIN_VARIANT(F) \
|
||||
F(BinaryOperatorBitAnd, "&") \
|
||||
F(BinaryOperatorBitOr, "|") \
|
||||
F(BinaryOperatorBitXor, "^") \
|
||||
F(BinaryOperatorComma, ",") \
|
||||
F(BinaryOperatorDiv, "/") \
|
||||
F(BinaryOperatorEq, "==") \
|
||||
F(BinaryOperatorGeqThan, ">=") \
|
||||
F(BinaryOperatorGreaterThan, ">") \
|
||||
F(BinaryOperatorIn, "in") \
|
||||
F(BinaryOperatorInstanceof, "instanceof") \
|
||||
F(BinaryOperatorLeqThan, "<=") \
|
||||
F(BinaryOperatorLessThan, "<") \
|
||||
F(BinaryOperatorLogicalAnd, "&&") \
|
||||
F(BinaryOperatorLogicalOr, "||") \
|
||||
F(BinaryOperatorLsh, "<<") \
|
||||
F(BinaryOperatorMod, "%") \
|
||||
F(BinaryOperatorMul, "*") \
|
||||
F(BinaryOperatorNeq, "!=") \
|
||||
F(BinaryOperatorOrUnaryOperatorMinus, "-") \
|
||||
F(BinaryOperatorOrUnaryOperatorPlus, "+") \
|
||||
F(BinaryOperatorPow, "**") \
|
||||
F(BinaryOperatorRsh, ">>") \
|
||||
F(BinaryOperatorStrictEq, "===") \
|
||||
F(BinaryOperatorStrictNeq, "!==") \
|
||||
F(BinaryOperatorUrsh, ">>>") \
|
||||
F(CompoundAssignmentOperatorBitAndAssign, "&=") \
|
||||
F(CompoundAssignmentOperatorBitOrAssign, "|=") \
|
||||
F(CompoundAssignmentOperatorBitXorAssign, "^=") \
|
||||
F(CompoundAssignmentOperatorDivAssign, "/=") \
|
||||
F(CompoundAssignmentOperatorLshAssign, "<<=") \
|
||||
F(CompoundAssignmentOperatorMinusAssign, "-=") \
|
||||
F(CompoundAssignmentOperatorModAssign, "%=") \
|
||||
F(CompoundAssignmentOperatorMulAssign, "*=") \
|
||||
F(CompoundAssignmentOperatorPlusAssign, "+=") \
|
||||
F(CompoundAssignmentOperatorPowAssign, "**=") \
|
||||
F(CompoundAssignmentOperatorRshAssign, ">>=") \
|
||||
F(CompoundAssignmentOperatorUrshAssign, ">>>=") \
|
||||
F(UnaryOperatorBitNot, "~") \
|
||||
F(UnaryOperatorDelete, "delete") \
|
||||
F(UnaryOperatorNot, "!") \
|
||||
F(UnaryOperatorTypeof, "typeof") \
|
||||
F(UnaryOperatorVoid, "void") \
|
||||
F(UpdateOperatorDecr, "--") \
|
||||
F(UpdateOperatorIncr, "++") \
|
||||
F(VariableDeclarationKindConst, "const") \
|
||||
F(VariableDeclarationKindLet, "let") \
|
||||
F(VariableDeclarationKindVar, "var")
|
||||
|
||||
enum class BinVariant {
|
||||
#define EMIT_ENUM(name, _) name,
|
||||
FOR_EACH_BIN_VARIANT(EMIT_ENUM)
|
||||
#undef EMIT_ENUM
|
||||
};
|
||||
|
||||
// The number of distinct values of BinVariant.
|
||||
const size_t BINVARIANT_LIMIT = 47;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a string describing a `BinKind`.
|
||||
|
@ -340,6 +419,11 @@ const char* describeBinKind(const BinKind& kind);
|
|||
*/
|
||||
const char* describeBinField(const BinField& kind);
|
||||
|
||||
/**
|
||||
* Return a string describing a `BinVariant`.
|
||||
*/
|
||||
const char* describeBinVariant(const BinVariant& kind);
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 "frontend/BinTokenReaderBase.h"
|
||||
|
||||
#include "frontend/BinSource-macros.h"
|
||||
#include "gc/Zone.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
template<typename T> using ErrorResult = mozilla::GenericErrorResult<T>;
|
||||
|
||||
// We use signalling NaN (which doesn't exist in the JS syntax)
|
||||
// to represent a `null` number.
|
||||
const uint64_t NULL_FLOAT_REPRESENTATION = 0x7FF0000000000001;
|
||||
|
||||
void
|
||||
BinTokenReaderBase::updateLatestKnownGood()
|
||||
{
|
||||
MOZ_ASSERT(current_ >= start_);
|
||||
const size_t update = current_ - start_;
|
||||
MOZ_ASSERT(update >= latestKnownGoodPos_);
|
||||
latestKnownGoodPos_ = update;
|
||||
}
|
||||
|
||||
ErrorResult<JS::Error&>
|
||||
BinTokenReaderBase::raiseError(const char* description)
|
||||
{
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
TokenPos pos = this->pos();
|
||||
JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
|
||||
description, pos.begin, pos.end);
|
||||
return cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
ErrorResult<JS::Error&>
|
||||
BinTokenReaderBase::raiseOOM()
|
||||
{
|
||||
ReportOutOfMemory(cx_);
|
||||
return cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
ErrorResult<JS::Error&>
|
||||
BinTokenReaderBase::raiseInvalidNumberOfFields(const BinKind kind, const uint32_t expected, const uint32_t got)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("In %s, invalid number of fields: expected %u, got %u",
|
||||
describeBinKind(kind), expected, got));
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
ErrorResult<JS::Error&>
|
||||
BinTokenReaderBase::raiseInvalidField(const char* kind, const BinField field)
|
||||
{
|
||||
Sprinter out(cx_);
|
||||
BINJS_TRY(out.init());
|
||||
BINJS_TRY(out.printf("In %s, invalid field '%s'", kind, describeBinField(field)));
|
||||
return raiseError(out.string());
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
BinTokenReaderBase::offset() const
|
||||
{
|
||||
return current_ - start_;
|
||||
}
|
||||
|
||||
TokenPos
|
||||
BinTokenReaderBase::pos()
|
||||
{
|
||||
return pos(offset());
|
||||
}
|
||||
|
||||
TokenPos
|
||||
BinTokenReaderBase::pos(size_t start)
|
||||
{
|
||||
TokenPos pos;
|
||||
pos.begin = start;
|
||||
pos.end = current_ - start_;
|
||||
MOZ_ASSERT(pos.end >= pos.begin);
|
||||
return pos;
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderBase::readBuf(uint8_t* bytes, uint32_t len)
|
||||
{
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
MOZ_ASSERT(len > 0);
|
||||
|
||||
if (stop_ < current_ + len)
|
||||
return raiseError("Buffer exceeds length");
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
*bytes++ = *current_++;
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<uint8_t>
|
||||
BinTokenReaderBase::readByte()
|
||||
{
|
||||
uint8_t byte;
|
||||
MOZ_TRY(readBuf(&byte, 1));
|
||||
return byte;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 frontend_BinTokenReaderBase_h
|
||||
#define frontend_BinTokenReaderBase_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/BinToken.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace JS;
|
||||
|
||||
// A constant used by tokenizers to represent a null float.
|
||||
extern const uint64_t NULL_FLOAT_REPRESENTATION;
|
||||
|
||||
class MOZ_STACK_CLASS BinTokenReaderBase
|
||||
{
|
||||
public:
|
||||
template<typename T> using ErrorResult = mozilla::GenericErrorResult<T>;
|
||||
|
||||
/**
|
||||
* Return the position of the latest token.
|
||||
*/
|
||||
TokenPos pos();
|
||||
TokenPos pos(size_t startOffset);
|
||||
size_t offset() const;
|
||||
|
||||
/**
|
||||
* Poison this tokenizer.
|
||||
*/
|
||||
void poison();
|
||||
|
||||
/**
|
||||
* Raise an error.
|
||||
*
|
||||
* Once `raiseError` has been called, the tokenizer is poisoned.
|
||||
*/
|
||||
MOZ_MUST_USE ErrorResult<JS::Error&> raiseError(const char* description);
|
||||
MOZ_MUST_USE ErrorResult<JS::Error&> raiseOOM();
|
||||
MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidNumberOfFields(
|
||||
const BinKind kind, const uint32_t expected, const uint32_t got);
|
||||
MOZ_MUST_USE ErrorResult<JS::Error&> raiseInvalidField(const char* kind,
|
||||
const BinField field);
|
||||
|
||||
protected:
|
||||
BinTokenReaderBase(JSContext* cx, const uint8_t* start, const size_t length)
|
||||
: cx_(cx)
|
||||
, start_(start)
|
||||
, current_(start)
|
||||
, stop_(start + length)
|
||||
, latestKnownGoodPos_(0)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Read a single byte.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<uint8_t> readByte();
|
||||
|
||||
/**
|
||||
* Read several bytes.
|
||||
*
|
||||
* If there is not enough data, or if the tokenizer has previously been
|
||||
* poisoned, return an error.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> readBuf(uint8_t* bytes, uint32_t len);
|
||||
|
||||
/**
|
||||
* Read a sequence of chars, ensuring that they match an expected
|
||||
* sequence of chars.
|
||||
*
|
||||
* @param value The sequence of chars to expect, NUL-terminated.
|
||||
*/
|
||||
template <size_t N>
|
||||
MOZ_MUST_USE JS::Result<Ok> readConst(const char (&value)[N])
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
if (!matchConst(value, false))
|
||||
return raiseError("Could not find expected literal");
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a sequence of chars, consuming the bytes only if they match an expected
|
||||
* sequence of chars.
|
||||
*
|
||||
* @param value The sequence of chars to expect, NUL-terminated.
|
||||
* @param expectNul If true, expect NUL in the stream, otherwise don't.
|
||||
* @return true if `value` represents the next few chars in the
|
||||
* internal buffer, false otherwise. If `true`, the chars are consumed,
|
||||
* otherwise there is no side-effect.
|
||||
*/
|
||||
template <size_t N>
|
||||
MOZ_MUST_USE bool matchConst(const char (&value)[N], bool expectNul) {
|
||||
MOZ_ASSERT(N > 0);
|
||||
MOZ_ASSERT(value[N - 1] == 0);
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
|
||||
if (current_ + N - 1 > stop_)
|
||||
return false;
|
||||
|
||||
// Perform lookup, without side-effects.
|
||||
if (!std::equal(current_, current_ + N + (expectNul ? 0 : -1)/*implicit NUL*/, value))
|
||||
return false;
|
||||
|
||||
// Looks like we have a match. Now perform side-effects
|
||||
current_ += N + (expectNul ? 0 : -1);
|
||||
updateLatestKnownGood();
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateLatestKnownGood();
|
||||
|
||||
JSContext* cx_;
|
||||
|
||||
// `true` if we have encountered an error. Errors are non recoverable.
|
||||
// Attempting to read from a poisoned tokenizer will cause assertion errors.
|
||||
bool poisoned_;
|
||||
|
||||
// The first byte of the buffer. Not owned.
|
||||
const uint8_t* start_;
|
||||
|
||||
// The current position.
|
||||
const uint8_t* current_;
|
||||
|
||||
// The last+1 byte of the buffer.
|
||||
const uint8_t* stop_;
|
||||
|
||||
// Latest known good position. Used for error reporting.
|
||||
size_t latestKnownGoodPos_;
|
||||
|
||||
private:
|
||||
BinTokenReaderBase(const BinTokenReaderBase&) = delete;
|
||||
BinTokenReaderBase(BinTokenReaderBase&&) = delete;
|
||||
BinTokenReaderBase& operator=(BinTokenReaderBase&) = delete;
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
#endif // frontend_BinTokenReaderBase_h
|
|
@ -0,0 +1,440 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 "frontend/BinTokenReaderMultipart.h"
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/BinSource-macros.h"
|
||||
#include "frontend/BinSourceRuntimeSupport.h"
|
||||
|
||||
#include "gc/Zone.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
// The magic header, at the start of every binjs file.
|
||||
const char MAGIC_HEADER[] = "BINJS";
|
||||
// The latest format version understood by this tokenizer.
|
||||
const uint32_t MAGIC_FORMAT_VERSION = 0;
|
||||
|
||||
// The headers at the start of each section of the binjs file.
|
||||
const char SECTION_HEADER_GRAMMAR[] = "[GRAMMAR]";
|
||||
const char SECTION_HEADER_STRINGS[] = "[STRINGS]";
|
||||
const char SECTION_HEADER_TREE[] = "[TREE]";
|
||||
|
||||
// The (only) internal compression mechanism understood by this parser.
|
||||
const char COMPRESSION_IDENTITY[] = "identity;";
|
||||
|
||||
|
||||
// The maximal number of distinct strings that may be declared in a
|
||||
// single file.
|
||||
const uint32_t MAX_NUMBER_OF_STRINGS = 32768;
|
||||
|
||||
using AutoList = BinTokenReaderMultipart::AutoList;
|
||||
using AutoTaggedTuple = BinTokenReaderMultipart::AutoTaggedTuple;
|
||||
using AutoTuple = BinTokenReaderMultipart::AutoTuple;
|
||||
using CharSlice = BinaryASTSupport::CharSlice;
|
||||
using Chars = BinTokenReaderMultipart::Chars;
|
||||
|
||||
BinTokenReaderMultipart::BinTokenReaderMultipart(JSContext* cx, const uint8_t* start, const size_t length)
|
||||
: BinTokenReaderBase(cx, start, length)
|
||||
, grammarTable_(cx)
|
||||
, atomsTable_(cx, AtomVector(cx))
|
||||
, slicesTable_(cx)
|
||||
, posBeforeTree_(nullptr)
|
||||
{ }
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::readHeader()
|
||||
{
|
||||
// Check that we don't call this function twice.
|
||||
MOZ_ASSERT(!posBeforeTree_);
|
||||
|
||||
// Read global headers.
|
||||
MOZ_TRY(readConst(MAGIC_HEADER));
|
||||
BINJS_MOZ_TRY_DECL(version, readInternalUint32());
|
||||
|
||||
// For the moment, MAGIC_FORMAT_VERSION is 0. Once we have a story
|
||||
// on backwards compatibility of the binary container, we will
|
||||
// probably want to change this to `if (version > MAGIC_FORMAT_VERSION)`.
|
||||
if (version != MAGIC_FORMAT_VERSION)
|
||||
return raiseError("Format version not implemented");
|
||||
|
||||
// Start reading grammar.
|
||||
MOZ_TRY(readConst(SECTION_HEADER_GRAMMAR));
|
||||
MOZ_TRY(readConst(COMPRESSION_IDENTITY)); // For the moment, we only support identity compression.
|
||||
BINJS_MOZ_TRY_DECL(grammarByteLen, readInternalUint32());
|
||||
const auto posBeforeGrammar = current_;
|
||||
|
||||
if (posBeforeGrammar + grammarByteLen > stop_ || posBeforeGrammar + grammarByteLen < current_) // Sanity check.
|
||||
return raiseError("Invalid byte length in grammar table");
|
||||
|
||||
BINJS_MOZ_TRY_DECL(grammarNumberOfEntries, readInternalUint32());
|
||||
if (grammarNumberOfEntries > BINKIND_LIMIT) // Sanity check.
|
||||
return raiseError("Invalid number of entries in grammar table");
|
||||
|
||||
// This table maps BinKind index -> BinKind.
|
||||
// Initialize and populate.
|
||||
if (!grammarTable_.reserve(grammarNumberOfEntries))
|
||||
return raiseOOM();
|
||||
|
||||
for (uint32_t i = 0; i < grammarNumberOfEntries; ++i) {
|
||||
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
|
||||
if (current_ + byteLen > stop_)
|
||||
return raiseError("Invalid byte length in grammar table");
|
||||
if (current_ + byteLen < current_) // Overflow.
|
||||
return raiseError("Invalid byte length in grammar table");
|
||||
CharSlice name((const char*)current_, byteLen);
|
||||
current_ += byteLen;
|
||||
|
||||
BINJS_MOZ_TRY_DECL(kind, cx_->runtime()->binast().binKind(cx_, name));
|
||||
if (!kind)
|
||||
return raiseError("Invalid entry in grammar table");
|
||||
|
||||
grammarTable_.infallibleAppend(*kind); // We called `reserve` before the loop.
|
||||
}
|
||||
if (current_ != grammarByteLen + posBeforeGrammar)
|
||||
return raiseError("The length of the grammar table didn't match its contents.");
|
||||
|
||||
// Start reading strings
|
||||
MOZ_TRY(readConst(SECTION_HEADER_STRINGS));
|
||||
MOZ_TRY(readConst(COMPRESSION_IDENTITY)); // For the moment, we only support identity compression.
|
||||
BINJS_MOZ_TRY_DECL(stringsByteLen, readInternalUint32());
|
||||
const auto posBeforeStrings = current_;
|
||||
|
||||
if (posBeforeStrings + stringsByteLen > stop_ || posBeforeStrings + stringsByteLen < current_) // Sanity check.
|
||||
return raiseError("Invalid byte length in strings table");
|
||||
|
||||
BINJS_MOZ_TRY_DECL(stringsNumberOfEntries, readInternalUint32());
|
||||
if (stringsNumberOfEntries > MAX_NUMBER_OF_STRINGS) // Sanity check.
|
||||
return raiseError("Too many entries in strings table");
|
||||
|
||||
// This table maps String index -> String.
|
||||
// Initialize and populate.
|
||||
if (!atomsTable_.reserve(stringsNumberOfEntries))
|
||||
return raiseOOM();
|
||||
if (!slicesTable_.reserve(stringsNumberOfEntries))
|
||||
return raiseOOM();
|
||||
if (!variantsTable_.init())
|
||||
return cx_->alreadyReportedError();
|
||||
|
||||
RootedAtom atom(cx_);
|
||||
for (uint32_t i = 0; i < stringsNumberOfEntries; ++i) {
|
||||
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
|
||||
if (current_ + byteLen > stop_ || current_ + byteLen < current_)
|
||||
return raiseError("Invalid byte length in individual string");
|
||||
|
||||
// Check null string.
|
||||
if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
|
||||
atom = nullptr;
|
||||
} else {
|
||||
BINJS_TRY_VAR(atom, Atomize(cx_, (const char*)current_, byteLen));
|
||||
}
|
||||
|
||||
// Populate `atomsTable_`: i => atom.
|
||||
atomsTable_.infallibleAppend(atom); // We have reserved before entering the loop.
|
||||
|
||||
// Populate `slicesTable_`: i => slice
|
||||
Chars slice((const char*)current_, byteLen);
|
||||
slicesTable_.infallibleAppend(Move(slice)); // We have reserved before entering the loop.
|
||||
|
||||
current_ += byteLen;
|
||||
}
|
||||
|
||||
if (posBeforeStrings + stringsByteLen != current_)
|
||||
return raiseError("The length of the strings table didn't match its contents.");
|
||||
|
||||
// Start reading AST.
|
||||
MOZ_TRY(readConst(SECTION_HEADER_TREE));
|
||||
MOZ_TRY(readConst(COMPRESSION_IDENTITY)); // For the moment, we only support identity compression.
|
||||
posBeforeTree_ = current_;
|
||||
|
||||
BINJS_MOZ_TRY_DECL(treeByteLen, readInternalUint32());
|
||||
|
||||
if (posBeforeTree_ + treeByteLen > stop_ || posBeforeTree_ + treeByteLen < posBeforeTree_) // Sanity check.
|
||||
return raiseError("Invalid byte length in tree table");
|
||||
|
||||
// At this stage, we're ready to start reading the tree.
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<bool>
|
||||
BinTokenReaderMultipart::readBool()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
BINJS_MOZ_TRY_DECL(byte, readByte());
|
||||
|
||||
switch (byte) {
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
return true;
|
||||
case 2:
|
||||
return raiseError("Not implemented: null boolean value");
|
||||
default:
|
||||
return raiseError("Invalid boolean value");
|
||||
}
|
||||
}
|
||||
|
||||
// Nullable doubles (little-endian)
|
||||
//
|
||||
// NULL_FLOAT_REPRESENTATION (signaling NaN) => null
|
||||
// anything other 64 bit sequence => IEEE-764 64-bit floating point number
|
||||
JS::Result<double>
|
||||
BinTokenReaderMultipart::readDouble()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
|
||||
uint8_t bytes[8];
|
||||
MOZ_ASSERT(sizeof(bytes) == sizeof(double));
|
||||
MOZ_TRY(readBuf(reinterpret_cast<uint8_t*>(bytes), ArrayLength(bytes)));
|
||||
|
||||
// Decode little-endian.
|
||||
const uint64_t asInt = LittleEndian::readUint64(bytes);
|
||||
|
||||
if (asInt == NULL_FLOAT_REPRESENTATION)
|
||||
return raiseError("Not implemented: null double value");
|
||||
|
||||
// Canonicalize NaN, just to make sure another form of signalling NaN
|
||||
// doesn't slip past us.
|
||||
const double asDouble = CanonicalizeNaN(BitwiseCast<double>(asInt));
|
||||
return asDouble;
|
||||
}
|
||||
|
||||
|
||||
// A single atom is represented as an index into the table of strings.
|
||||
JS::Result<JSAtom*>
|
||||
BinTokenReaderMultipart::readMaybeAtom()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
|
||||
|
||||
if (index >= atomsTable_.length())
|
||||
return raiseError("Invalid index to strings table");
|
||||
return atomsTable_[index].get();
|
||||
}
|
||||
|
||||
JS::Result<JSAtom*>
|
||||
BinTokenReaderMultipart::readAtom()
|
||||
{
|
||||
BINJS_MOZ_TRY_DECL(maybe, readMaybeAtom());
|
||||
|
||||
if (!maybe)
|
||||
return raiseError("Empty string");
|
||||
|
||||
return maybe;
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::readChars(Chars& out)
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
|
||||
|
||||
if (index >= slicesTable_.length())
|
||||
return raiseError("Invalid index to strings table for string enum");
|
||||
|
||||
out = slicesTable_[index];
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<BinVariant>
|
||||
BinTokenReaderMultipart::readVariant()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
|
||||
|
||||
if (index >= slicesTable_.length())
|
||||
return raiseError("Invalid index to strings table for string enum");
|
||||
|
||||
auto variantsPtr = variantsTable_.lookupForAdd(index);
|
||||
if (variantsPtr)
|
||||
return variantsPtr->value();
|
||||
|
||||
// Either we haven't cached the result yet or this is not a variant.
|
||||
// Check in the slices table and, in case of success, cache the result.
|
||||
|
||||
// Note that we stop parsing if we attempt to readVariant() with an
|
||||
// ill-formed variant, so we don't run the risk of feching an ill-variant
|
||||
// more than once.
|
||||
Chars slice = slicesTable_[index]; // We have checked `index` above.
|
||||
BINJS_MOZ_TRY_DECL(variant, cx_->runtime()->binast().binVariant(cx_, slice));
|
||||
|
||||
if (!variant)
|
||||
return raiseError("Invalid string enum variant");
|
||||
|
||||
if (!variantsTable_.add(variantsPtr, index, *variant))
|
||||
return raiseOOM();
|
||||
|
||||
return *variant;
|
||||
}
|
||||
|
||||
// Untagged tuple:
|
||||
// - contents (specified by the higher-level grammar);
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::enterUntaggedTuple(AutoTuple& guard)
|
||||
{
|
||||
guard.init();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
// Tagged tuples:
|
||||
// - uint32_t index in table [grammar];
|
||||
// - content (specified by the higher-level grammar);
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::enterTaggedTuple(BinKind& tag, BinTokenReaderMultipart::BinFields&, AutoTaggedTuple& guard)
|
||||
{
|
||||
BINJS_MOZ_TRY_DECL(index, readInternalUint32());
|
||||
if (index >= grammarTable_.length())
|
||||
return raiseError("Invalid index to grammar table");
|
||||
|
||||
tag = grammarTable_[index];
|
||||
|
||||
// Enter the body.
|
||||
guard.init();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// List:
|
||||
//
|
||||
// - uint32_t number of items;
|
||||
// - contents (specified by higher-level grammar);
|
||||
//
|
||||
// The total byte length of `number of items` + `contents` must be `byte length`.
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::enterList(uint32_t& items, AutoList& guard)
|
||||
{
|
||||
guard.init();
|
||||
|
||||
MOZ_TRY_VAR(items, readInternalUint32());
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
void
|
||||
BinTokenReaderMultipart::AutoBase::init()
|
||||
{
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
BinTokenReaderMultipart::AutoBase::AutoBase(BinTokenReaderMultipart& reader)
|
||||
: reader_(reader)
|
||||
{ }
|
||||
|
||||
BinTokenReaderMultipart::AutoBase::~AutoBase()
|
||||
{
|
||||
// By now, the `AutoBase` must have been deinitialized by calling `done()`.
|
||||
// The only case in which we can accept not calling `done()` is if we have
|
||||
// bailed out because of an error.
|
||||
MOZ_ASSERT_IF(initialized_, reader_.cx_->isExceptionPending());
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::AutoBase::checkPosition(const uint8_t* expectedEnd)
|
||||
{
|
||||
if (reader_.current_ != expectedEnd)
|
||||
return reader_.raiseError("Caller did not consume the expected set of bytes");
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
BinTokenReaderMultipart::AutoList::AutoList(BinTokenReaderMultipart& reader)
|
||||
: AutoBase(reader)
|
||||
{ }
|
||||
|
||||
void
|
||||
BinTokenReaderMultipart::AutoList::init()
|
||||
{
|
||||
AutoBase::init();
|
||||
}
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::AutoList::done()
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
initialized_ = false;
|
||||
if (reader_.cx_->isExceptionPending()) {
|
||||
// Already errored, no need to check further.
|
||||
return reader_.cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
// Internal uint32_t
|
||||
//
|
||||
// Encoded as variable length number.
|
||||
|
||||
MOZ_MUST_USE JS::Result<uint32_t>
|
||||
BinTokenReaderMultipart::readInternalUint32()
|
||||
{
|
||||
uint32_t result = 0;
|
||||
uint32_t shift = 0;
|
||||
while (true) {
|
||||
MOZ_ASSERT(shift < 32);
|
||||
uint32_t byte;
|
||||
MOZ_TRY_VAR(byte, readByte());
|
||||
|
||||
const uint32_t newResult = result | (byte >> 1) << shift;
|
||||
if (newResult < result)
|
||||
return raiseError("Overflow during readInternalUint32");
|
||||
|
||||
result = newResult;
|
||||
shift += 7;
|
||||
|
||||
if ((byte & 1) == 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BinTokenReaderMultipart::AutoTaggedTuple::AutoTaggedTuple(BinTokenReaderMultipart& reader)
|
||||
: AutoBase(reader)
|
||||
{ }
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::AutoTaggedTuple::done()
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
initialized_ = false;
|
||||
if (reader_.cx_->isExceptionPending()) {
|
||||
// Already errored, no need to check further.
|
||||
return reader_.cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
BinTokenReaderMultipart::AutoTuple::AutoTuple(BinTokenReaderMultipart& reader)
|
||||
: AutoBase(reader)
|
||||
{ }
|
||||
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderMultipart::AutoTuple::done()
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
initialized_ = false;
|
||||
if (reader_.cx_->isExceptionPending()) {
|
||||
// Already errored, no need to check further.
|
||||
return reader_.cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
// Check suffix.
|
||||
return Ok();
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
|
||||
|
||||
} // namespace js
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 frontend_BinTokenReaderMultipart_h
|
||||
#define frontend_BinTokenReaderMultipart_h
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/BinToken.h"
|
||||
#include "frontend/BinTokenReaderBase.h"
|
||||
|
||||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace JS;
|
||||
|
||||
/**
|
||||
* A token reader implementing the "multipart" serialization format for BinAST.
|
||||
*
|
||||
* This serialization format, which is also supported by the reference
|
||||
* implementation of the BinAST compression suite, is designed to be
|
||||
* space- and time-efficient.
|
||||
*
|
||||
* As other token readers for the BinAST:
|
||||
*
|
||||
* - the reader does not support error recovery;
|
||||
* - the reader does not support lookahead or pushback.
|
||||
*/
|
||||
class MOZ_STACK_CLASS BinTokenReaderMultipart: public BinTokenReaderBase
|
||||
{
|
||||
public:
|
||||
class AutoList;
|
||||
class AutoTuple;
|
||||
class AutoTaggedTuple;
|
||||
|
||||
using CharSlice = BinaryASTSupport::CharSlice;
|
||||
|
||||
// This implementation of `BinFields` is effectively `void`, as the format
|
||||
// does not embed field information.
|
||||
class BinFields {
|
||||
public:
|
||||
explicit BinFields(JSContext*) {}
|
||||
};
|
||||
struct Chars: public CharSlice {
|
||||
explicit Chars(JSContext*)
|
||||
: CharSlice(nullptr, 0)
|
||||
{ }
|
||||
Chars(const char* start, const uint32_t byteLen)
|
||||
: CharSlice(start, byteLen)
|
||||
{ }
|
||||
Chars(const Chars& other) = default;
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a token reader.
|
||||
*
|
||||
* Does NOT copy the buffer.
|
||||
*/
|
||||
BinTokenReaderMultipart(JSContext* cx, const uint8_t* start, const size_t length);
|
||||
|
||||
/**
|
||||
* Construct a token reader.
|
||||
*
|
||||
* Does NOT copy the buffer.
|
||||
*/
|
||||
BinTokenReaderMultipart(JSContext* cx, const Vector<uint8_t>& chars);
|
||||
|
||||
/**
|
||||
* Read the header of the file.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> readHeader();
|
||||
|
||||
// --- Primitive values.
|
||||
//
|
||||
// Note that the underlying format allows for a `null` value for primitive
|
||||
// values.
|
||||
//
|
||||
// Reading will return an error either in case of I/O error or in case of
|
||||
// a format problem. Reading if an exception in pending is an error and
|
||||
// will cause assertion failures. Do NOT attempt to read once an exception
|
||||
// has been cleared: the token reader does NOT support recovery, by design.
|
||||
|
||||
/**
|
||||
* Read a single `true | false` value.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<bool> readBool();
|
||||
|
||||
/**
|
||||
* Read a single `number` value.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<double> readDouble();
|
||||
|
||||
/**
|
||||
* Read a single `string | null` value.
|
||||
*
|
||||
* Fails if that string is not valid UTF-8.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom();
|
||||
MOZ_MUST_USE JS::Result<JSAtom*> readAtom();
|
||||
|
||||
|
||||
/**
|
||||
* Read a single `string | null` value.
|
||||
*
|
||||
* MAY check if that string is not valid UTF-8.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> readChars(Chars&);
|
||||
|
||||
/**
|
||||
* Read a single `BinVariant | null` value.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Maybe<BinVariant>> readMaybeVariant();
|
||||
MOZ_MUST_USE JS::Result<BinVariant> readVariant();
|
||||
|
||||
// --- Composite values.
|
||||
//
|
||||
// The underlying format does NOT allows for a `null` composite value.
|
||||
//
|
||||
// Reading will return an error either in case of I/O error or in case of
|
||||
// a format problem. Reading from a poisoned tokenizer is an error and
|
||||
// will cause assertion failures.
|
||||
|
||||
/**
|
||||
* Start reading a list.
|
||||
*
|
||||
* @param length (OUT) The number of elements in the list.
|
||||
* @param guard (OUT) A guard, ensuring that we read the list correctly.
|
||||
*
|
||||
* The `guard` is dedicated to ensuring that reading the list has consumed
|
||||
* exactly all the bytes from that list. The `guard` MUST therefore be
|
||||
* destroyed at the point where the caller has reached the end of the list.
|
||||
* If the caller has consumed too few/too many bytes, this will be reported
|
||||
* in the call go `guard.done()`.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> enterList(uint32_t& length, AutoList& guard);
|
||||
|
||||
/**
|
||||
* Start reading a tagged tuple.
|
||||
*
|
||||
* @param tag (OUT) The tag of the tuple.
|
||||
* @param fields Ignored, provided for API compatibility.
|
||||
* @param guard (OUT) A guard, ensuring that we read the tagged tuple correctly.
|
||||
*
|
||||
* The `guard` is dedicated to ensuring that reading the list has consumed
|
||||
* exactly all the bytes from that tuple. The `guard` MUST therefore be
|
||||
* destroyed at the point where the caller has reached the end of the tuple.
|
||||
* If the caller has consumed too few/too many bytes, this will be reported
|
||||
* in the call go `guard.done()`.
|
||||
*
|
||||
* @return out If the header of the tuple is invalid.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinKind& tag, BinTokenReaderMultipart::BinFields& fields, AutoTaggedTuple& guard);
|
||||
|
||||
/**
|
||||
* Start reading an untagged tuple.
|
||||
*
|
||||
* @param guard (OUT) A guard, ensuring that we read the tuple correctly.
|
||||
*
|
||||
* The `guard` is dedicated to ensuring that reading the list has consumed
|
||||
* exactly all the bytes from that tuple. The `guard` MUST therefore be
|
||||
* destroyed at the point where the caller has reached the end of the tuple.
|
||||
* If the caller has consumed too few/too many bytes, this will be reported
|
||||
* in the call go `guard.done()`.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> enterUntaggedTuple(AutoTuple& guard);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read a single uint32_t.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
|
||||
|
||||
private:
|
||||
// A mapping grammar index => BinKind, as defined by the [GRAMMAR]
|
||||
// section of the file. Populated during readHeader().
|
||||
Vector<BinKind> grammarTable_;
|
||||
|
||||
// A mapping string index => BinVariant as extracted from the [STRINGS]
|
||||
// section of the file. Populated lazily.
|
||||
js::HashMap<uint32_t, BinVariant, DefaultHasher<uint32_t>, SystemAllocPolicy> variantsTable_;
|
||||
|
||||
// A mapping index => JSAtom, as defined by the [STRINGS]
|
||||
// section of the file. Populated during readHeader().
|
||||
using AtomVector = GCVector<JSAtom*>;
|
||||
JS::Rooted<AtomVector> atomsTable_;
|
||||
|
||||
// The same mapping, but as CharSlice. Populated during readHeader().
|
||||
// The slices are into the source buffer.
|
||||
Vector<Chars> slicesTable_;
|
||||
|
||||
const uint8_t* posBeforeTree_;
|
||||
|
||||
BinTokenReaderMultipart(const BinTokenReaderMultipart&) = delete;
|
||||
BinTokenReaderMultipart(BinTokenReaderMultipart&&) = delete;
|
||||
BinTokenReaderMultipart& operator=(BinTokenReaderMultipart&) = delete;
|
||||
|
||||
public:
|
||||
// The following classes are used whenever we encounter a tuple/tagged tuple/list
|
||||
// to make sure that:
|
||||
//
|
||||
// - if the construct "knows" its byte length, we have exactly consumed all
|
||||
// the bytes (otherwise, this means that the file is corrupted, perhaps on
|
||||
// purpose, so we need to reject the stream);
|
||||
// - if the construct has a footer, once we are done reading it, we have
|
||||
// reached the footer (this is to aid with debugging).
|
||||
//
|
||||
// In either case, the caller MUST call method `done()` of the guard once
|
||||
// it is done reading the tuple/tagged tuple/list, to report any pending error.
|
||||
|
||||
// Base class used by other Auto* classes.
|
||||
class MOZ_STACK_CLASS AutoBase
|
||||
{
|
||||
protected:
|
||||
explicit AutoBase(BinTokenReaderMultipart& reader);
|
||||
~AutoBase();
|
||||
|
||||
// Raise an error if we are not in the expected position.
|
||||
MOZ_MUST_USE JS::Result<Ok> checkPosition(const uint8_t* expectedPosition);
|
||||
|
||||
friend BinTokenReaderMultipart;
|
||||
void init();
|
||||
|
||||
// Set to `true` if `init()` has been called. Reset to `false` once
|
||||
// all conditions have been checked.
|
||||
bool initialized_;
|
||||
BinTokenReaderMultipart& reader_;
|
||||
};
|
||||
|
||||
// Guard class used to ensure that `enterList` is used properly.
|
||||
class MOZ_STACK_CLASS AutoList : public AutoBase
|
||||
{
|
||||
public:
|
||||
explicit AutoList(BinTokenReaderMultipart& reader);
|
||||
|
||||
// Check that we have properly read to the end of the list.
|
||||
MOZ_MUST_USE JS::Result<Ok> done();
|
||||
|
||||
protected:
|
||||
friend BinTokenReaderMultipart;
|
||||
void init();
|
||||
};
|
||||
|
||||
// Guard class used to ensure that `enterTaggedTuple` is used properly.
|
||||
class MOZ_STACK_CLASS AutoTaggedTuple : public AutoBase
|
||||
{
|
||||
public:
|
||||
explicit AutoTaggedTuple(BinTokenReaderMultipart& reader);
|
||||
|
||||
// Check that we have properly read to the end of the tuple.
|
||||
MOZ_MUST_USE JS::Result<Ok> done();
|
||||
};
|
||||
|
||||
// Guard class used to ensure that `readTuple` is used properly.
|
||||
class MOZ_STACK_CLASS AutoTuple : public AutoBase
|
||||
{
|
||||
public:
|
||||
explicit AutoTuple(BinTokenReaderMultipart& reader);
|
||||
|
||||
// Check that we have properly read to the end of the tuple.
|
||||
MOZ_MUST_USE JS::Result<Ok> done();
|
||||
};
|
||||
|
||||
// Compare a `Chars` and a string literal (ONLY a string literal).
|
||||
template <size_t N>
|
||||
static bool equals(const Chars& left, const char (&right)[N]) {
|
||||
MOZ_ASSERT(N > 0);
|
||||
MOZ_ASSERT(right[N - 1] == 0);
|
||||
if (left.byteLen_ + 1 /* implicit NUL */ != N)
|
||||
return false;
|
||||
|
||||
if (!std::equal(left.start_, left.start_ + left.byteLen_, right))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
static JS::Result<Ok, JS::Error&>
|
||||
checkFields(const BinKind kind, const BinFields& actual, const BinField (&expected)[N]) {
|
||||
// Not implemented in this tokenizer.
|
||||
return Ok();
|
||||
}
|
||||
|
||||
static JS::Result<Ok, JS::Error&>
|
||||
checkFields0(const BinKind kind, const BinFields& actual) {
|
||||
// Not implemented in this tokenizer.
|
||||
return Ok();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
#endif // frontend_BinTokenReaderMultipart_h
|
|
@ -4,9 +4,12 @@
|
|||
* 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 "frontend/BinTokenReaderTester.h"
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
|
||||
#include "frontend/BinSource-macros.h"
|
||||
#include "gc/Zone.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -18,156 +21,76 @@ using AutoTaggedTuple = BinTokenReaderTester::AutoTaggedTuple;
|
|||
using AutoTuple = BinTokenReaderTester::AutoTuple;
|
||||
|
||||
BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, const uint8_t* start, const size_t length)
|
||||
: cx_(cx)
|
||||
, start_(start)
|
||||
, current_(start)
|
||||
, stop_(start + length)
|
||||
, latestKnownGoodPos_(0)
|
||||
: BinTokenReaderBase(cx, start, length)
|
||||
{ }
|
||||
|
||||
BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, const Vector<uint8_t>& chars)
|
||||
: cx_(cx)
|
||||
, start_(chars.begin())
|
||||
, current_(chars.begin())
|
||||
, stop_(chars.end())
|
||||
, latestKnownGoodPos_(0)
|
||||
BinTokenReaderTester::BinTokenReaderTester(JSContext* cx, const Vector<uint8_t>& buf)
|
||||
: BinTokenReaderBase(cx, buf.begin(), buf.length())
|
||||
{ }
|
||||
|
||||
bool
|
||||
BinTokenReaderTester::raiseError(const char* description)
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::readHeader()
|
||||
{
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
TokenPos pos = this->pos();
|
||||
JS_ReportErrorASCII(cx_, "BinAST parsing error: %s at offsets %u => %u",
|
||||
description, pos.begin, pos.end);
|
||||
return false;
|
||||
// This format does not have a header.
|
||||
return Ok();
|
||||
}
|
||||
|
||||
bool
|
||||
BinTokenReaderTester::readBuf(uint8_t* bytes, uint32_t len)
|
||||
{
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
MOZ_ASSERT(len > 0);
|
||||
|
||||
if (stop_ < current_ + len)
|
||||
return raiseError("Buffer exceeds length");
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
*bytes++ = *current_++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BinTokenReaderTester::readByte(uint8_t* byte)
|
||||
{
|
||||
return readBuf(byte, 1);
|
||||
}
|
||||
|
||||
|
||||
// Nullable booleans:
|
||||
//
|
||||
// 0 => false
|
||||
// 1 => true
|
||||
// 2 => null
|
||||
bool
|
||||
BinTokenReaderTester::readMaybeBool(Maybe<bool>& result)
|
||||
JS::Result<bool>
|
||||
BinTokenReaderTester::readBool()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
uint8_t byte;
|
||||
if (!readByte(&byte))
|
||||
return false;
|
||||
BINJS_MOZ_TRY_DECL(byte, readByte());
|
||||
|
||||
switch (byte) {
|
||||
case 0:
|
||||
result = Some(false);
|
||||
break;
|
||||
return false;
|
||||
case 1:
|
||||
result = Some(true);
|
||||
break;
|
||||
return true;
|
||||
case 2:
|
||||
result = Nothing();
|
||||
break;
|
||||
return raiseError("Not implemented: null boolean value");
|
||||
default:
|
||||
return raiseError("Invalid boolean value");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BinTokenReaderTester::readBool(bool& out)
|
||||
{
|
||||
Maybe<bool> result;
|
||||
|
||||
if (!readMaybeBool(result))
|
||||
return false;
|
||||
|
||||
if (result.isNothing())
|
||||
return raiseError("Empty boolean value");
|
||||
|
||||
out = *result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Nullable doubles (little-endian)
|
||||
//
|
||||
// 0x7FF0000000000001 (signaling NaN) => null
|
||||
// NULL_FLOAT_REPRESENTATION (signaling NaN) => null
|
||||
// anything other 64 bit sequence => IEEE-764 64-bit floating point number
|
||||
bool
|
||||
BinTokenReaderTester::readMaybeDouble(Maybe<double>& result)
|
||||
JS::Result<double>
|
||||
BinTokenReaderTester::readDouble()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
|
||||
uint8_t bytes[8];
|
||||
MOZ_ASSERT(sizeof(bytes) == sizeof(double));
|
||||
if (!readBuf(reinterpret_cast<uint8_t*>(bytes), ArrayLength(bytes)))
|
||||
return false;
|
||||
MOZ_TRY(readBuf(reinterpret_cast<uint8_t*>(bytes), ArrayLength(bytes)));
|
||||
|
||||
// Decode little-endian.
|
||||
const uint64_t asInt = LittleEndian::readUint64(bytes);
|
||||
|
||||
if (asInt == 0x7FF0000000000001) {
|
||||
result = Nothing();
|
||||
} else {
|
||||
// Canonicalize NaN, just to make sure another form of signalling NaN
|
||||
// doesn't slip past us.
|
||||
const double asDouble = CanonicalizeNaN(BitwiseCast<double>(asInt));
|
||||
result = Some(asDouble);
|
||||
}
|
||||
if (asInt == NULL_FLOAT_REPRESENTATION)
|
||||
return raiseError("Not implemented: null double value");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BinTokenReaderTester::readDouble(double& out)
|
||||
{
|
||||
Maybe<double> result;
|
||||
|
||||
if (!readMaybeDouble(result))
|
||||
return false;
|
||||
|
||||
if (result.isNothing())
|
||||
return raiseError("Empty double value");
|
||||
|
||||
out = *result;
|
||||
return true;
|
||||
// Canonicalize NaN, just to make sure another form of signalling NaN
|
||||
// doesn't slip past us.
|
||||
const double asDouble = CanonicalizeNaN(BitwiseCast<double>(asInt));
|
||||
return asDouble;
|
||||
}
|
||||
|
||||
// Internal uint32_t
|
||||
//
|
||||
// Encoded as 4 bytes, little-endian.
|
||||
bool
|
||||
BinTokenReaderTester::readInternalUint32(uint32_t* result)
|
||||
MOZ_MUST_USE JS::Result<uint32_t>
|
||||
BinTokenReaderTester::readInternalUint32()
|
||||
{
|
||||
uint8_t bytes[4];
|
||||
MOZ_ASSERT(sizeof(bytes) == sizeof(uint32_t));
|
||||
if (!readBuf(bytes, 4))
|
||||
return false;
|
||||
MOZ_TRY(readBuf(bytes, 4));
|
||||
|
||||
// Decode little-endian.
|
||||
*result = LittleEndian::readUint32(bytes);
|
||||
|
||||
return true;
|
||||
return LittleEndian::readUint32(bytes);
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,18 +103,17 @@ BinTokenReaderTester::readInternalUint32(uint32_t* result)
|
|||
//
|
||||
// The special sequence of bytes `[255, 0]` (which is an invalid UTF-8 sequence)
|
||||
// is reserved to `null`.
|
||||
bool
|
||||
BinTokenReaderTester::readMaybeChars(Maybe<Chars>& out)
|
||||
JS::Result<JSAtom*>
|
||||
BinTokenReaderTester::readMaybeAtom()
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
|
||||
if (!readConst("<string>"))
|
||||
return false;
|
||||
MOZ_TRY(readConst("<string>"));
|
||||
|
||||
RootedAtom result(cx_);
|
||||
|
||||
// 1. Read byteLength
|
||||
uint32_t byteLen;
|
||||
if (!readInternalUint32(&byteLen))
|
||||
return false;
|
||||
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
|
||||
|
||||
// 2. Reject if we can't read
|
||||
if (current_ + byteLen < current_) // Check for overflows
|
||||
|
@ -202,111 +124,135 @@ BinTokenReaderTester::readMaybeChars(Maybe<Chars>& out)
|
|||
|
||||
if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
|
||||
// 3. Special case: null string.
|
||||
out = Nothing();
|
||||
result = nullptr;
|
||||
} else {
|
||||
// 4. Other strings (bytes are copied)
|
||||
out.emplace(cx_);
|
||||
if (!out->resize(byteLen)) {
|
||||
ReportOutOfMemory(cx_);
|
||||
return false;
|
||||
}
|
||||
PodCopy(out->begin(), current_, byteLen);
|
||||
BINJS_TRY_VAR(result, Atomize(cx_, (const char*)current_, byteLen));
|
||||
}
|
||||
|
||||
current_ += byteLen;
|
||||
if (!readConst("</string>"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
MOZ_TRY(readConst("</string>"));
|
||||
return result.get();
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
// Nullable strings:
|
||||
// - "<string>" (not counted in byte length)
|
||||
// - byte length (not counted in byte length)
|
||||
// - bytes (UTF-8)
|
||||
// - "</string>" (not counted in byte length)
|
||||
//
|
||||
// The special sequence of bytes `[255, 0]` (which is an invalid UTF-8 sequence)
|
||||
// is reserved to `null`.
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::readChars(Chars& out)
|
||||
{
|
||||
Maybe<Chars> result;
|
||||
|
||||
if (!readMaybeChars(result))
|
||||
return false;
|
||||
|
||||
if (result.isNothing())
|
||||
return raiseError("Empty string");
|
||||
|
||||
out = Move(*result);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
bool
|
||||
BinTokenReaderTester::matchConst(const char (&value)[N])
|
||||
{
|
||||
MOZ_ASSERT(N > 0);
|
||||
MOZ_ASSERT(value[N - 1] == 0);
|
||||
MOZ_ASSERT(!cx_->isExceptionPending());
|
||||
|
||||
if (current_ + N - 1 > stop_)
|
||||
return false;
|
||||
|
||||
// Perform lookup, without side-effects.
|
||||
if (!std::equal(current_, current_ + N - 1 /*implicit NUL*/, value))
|
||||
return false;
|
||||
|
||||
// Looks like we have a match. Now perform side-effects
|
||||
current_ += N - 1;
|
||||
updateLatestKnownGood();
|
||||
return true;
|
||||
|
||||
MOZ_TRY(readConst("<string>"));
|
||||
|
||||
// 1. Read byteLength
|
||||
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
|
||||
|
||||
// 2. Reject if we can't read
|
||||
if (current_ + byteLen < current_) // Check for overflows
|
||||
return raiseError("Arithmetics overflow: string is too long");
|
||||
|
||||
if (current_ + byteLen > stop_)
|
||||
return raiseError("Not enough bytes to read chars");
|
||||
|
||||
if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
|
||||
// 3. Special case: null string.
|
||||
return raiseError("Empty string");
|
||||
}
|
||||
|
||||
// 4. Other strings (bytes are copied)
|
||||
if (!out.resize(byteLen))
|
||||
return raiseOOM();
|
||||
|
||||
PodCopy(out.begin(), current_, byteLen);
|
||||
|
||||
current_ += byteLen;
|
||||
|
||||
MOZ_TRY(readConst("</string>"));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
JS::Result<JSAtom*>
|
||||
BinTokenReaderTester::readAtom()
|
||||
{
|
||||
RootedAtom atom(cx_);
|
||||
MOZ_TRY_VAR(atom, readMaybeAtom());
|
||||
|
||||
if (!atom)
|
||||
return raiseError("Empty string");
|
||||
return atom.get();
|
||||
}
|
||||
|
||||
JS::Result<BinVariant>
|
||||
BinTokenReaderTester::readVariant()
|
||||
{
|
||||
MOZ_TRY(readConst("<string>"));
|
||||
BINJS_MOZ_TRY_DECL(byteLen, readInternalUint32());
|
||||
|
||||
// 2. Reject if we can't read
|
||||
if (current_ + byteLen < current_) // Check for overflows
|
||||
return raiseError("Arithmetics overflow: string is too long");
|
||||
|
||||
if (current_ + byteLen > stop_)
|
||||
return raiseError("Not enough bytes to read chars");
|
||||
|
||||
if (byteLen == 2 && *current_ == 255 && *(current_ + 1) == 0) {
|
||||
// 3. Special case: null string.
|
||||
return raiseError("Empty variant");
|
||||
}
|
||||
|
||||
BinaryASTSupport::CharSlice slice((const char*)current_, byteLen);
|
||||
current_ += byteLen;
|
||||
|
||||
BINJS_MOZ_TRY_DECL(variant, cx_->runtime()->binast().binVariant(cx_, slice));
|
||||
if (!variant)
|
||||
return raiseError("Not a variant");
|
||||
|
||||
MOZ_TRY(readConst("</string>"));
|
||||
return *variant;
|
||||
}
|
||||
|
||||
// Untagged tuple:
|
||||
// - "<tuple>";
|
||||
// - contents (specified by the higher-level grammar);
|
||||
// - "</tuple>"
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::enterUntaggedTuple(AutoTuple& guard)
|
||||
{
|
||||
if (!readConst("<tuple>"))
|
||||
return false;
|
||||
MOZ_TRY(readConst("<tuple>"));
|
||||
|
||||
guard.init();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
bool
|
||||
BinTokenReaderTester::readConst(const char (&value)[N])
|
||||
{
|
||||
updateLatestKnownGood();
|
||||
if (!matchConst(value))
|
||||
return raiseError("Could not find expected literal");
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Tagged tuples:
|
||||
// - "<tuple>"
|
||||
// - "<head>"
|
||||
// - "<tuple>";
|
||||
// - "<head>";
|
||||
// - non-null string `name`, followed by \0 (see `readString()`);
|
||||
// - uint32_t number of fields;
|
||||
// - array of `number of fields` non-null strings followed each by \0 (see `readString()`);
|
||||
// - "</head>"
|
||||
// - "</head>";
|
||||
// - content (specified by the higher-level grammar);
|
||||
// - "</tuple>"
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::enterTaggedTuple(BinKind& tag, BinFields& fields, AutoTaggedTuple& guard)
|
||||
{
|
||||
// Header
|
||||
if (!readConst("<tuple>"))
|
||||
return false;
|
||||
|
||||
if (!readConst("<head>"))
|
||||
return false;
|
||||
MOZ_TRY(readConst("<tuple>"));
|
||||
MOZ_TRY(readConst("<head>"));
|
||||
|
||||
// This would probably be much faster with a HashTable, but we don't
|
||||
// really care about the speed of BinTokenReaderTester.
|
||||
do {
|
||||
|
||||
#define FIND_MATCH(CONSTRUCTOR, NAME) \
|
||||
if (matchConst(#NAME "\0")) { \
|
||||
if (matchConst(NAME, true)) { \
|
||||
tag = BinKind::CONSTRUCTOR; \
|
||||
break; \
|
||||
} // else
|
||||
|
@ -319,13 +265,11 @@ BinTokenReaderTester::enterTaggedTuple(BinKind& tag, BinFields& fields, AutoTagg
|
|||
} while(false);
|
||||
|
||||
// Now fields.
|
||||
uint32_t fieldNum;
|
||||
if (!readInternalUint32(&fieldNum))
|
||||
return false;
|
||||
BINJS_MOZ_TRY_DECL(fieldNum, readInternalUint32());
|
||||
|
||||
fields.clear();
|
||||
if (!fields.reserve(fieldNum))
|
||||
return raiseError("Out of memory");
|
||||
return raiseOOM();
|
||||
|
||||
for (uint32_t i = 0; i < fieldNum; ++i) {
|
||||
// This would probably be much faster with a HashTable, but we don't
|
||||
|
@ -334,7 +278,7 @@ BinTokenReaderTester::enterTaggedTuple(BinKind& tag, BinFields& fields, AutoTagg
|
|||
do {
|
||||
|
||||
#define FIND_MATCH(CONSTRUCTOR, NAME) \
|
||||
if (matchConst(#NAME "\0")) { \
|
||||
if (matchConst(NAME, true)) { \
|
||||
field = BinField::CONSTRUCTOR; \
|
||||
break; \
|
||||
} // else
|
||||
|
@ -359,13 +303,11 @@ BinTokenReaderTester::enterTaggedTuple(BinKind& tag, BinFields& fields, AutoTagg
|
|||
}
|
||||
|
||||
// End of header
|
||||
|
||||
if (!readConst("</head>"))
|
||||
return false;
|
||||
MOZ_TRY(readConst("</head>"));
|
||||
|
||||
// Enter the body.
|
||||
guard.init();
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// List:
|
||||
|
@ -377,61 +319,14 @@ BinTokenReaderTester::enterTaggedTuple(BinKind& tag, BinFields& fields, AutoTagg
|
|||
// - "</list>" (not counted in byte length)
|
||||
//
|
||||
// The total byte length of `number of items` + `contents` must be `byte length`.
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::enterList(uint32_t& items, AutoList& guard)
|
||||
{
|
||||
if (!readConst("<list>"))
|
||||
return false;
|
||||
MOZ_TRY(readConst("<list>"));
|
||||
guard.init();
|
||||
|
||||
uint32_t byteLen;
|
||||
if (!readInternalUint32(&byteLen))
|
||||
return false;
|
||||
|
||||
const uint8_t* stop = current_ + byteLen;
|
||||
|
||||
if (stop < current_) // Check for overflows
|
||||
return raiseError("Arithmetics overflow: list is too long");
|
||||
|
||||
if (stop > this->stop_)
|
||||
return raiseError("Incorrect list length");
|
||||
|
||||
guard.init(stop);
|
||||
|
||||
if (!readInternalUint32(&items))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BinTokenReaderTester::updateLatestKnownGood()
|
||||
{
|
||||
MOZ_ASSERT(current_ >= start_);
|
||||
const size_t update = current_ - start_;
|
||||
MOZ_ASSERT(update >= latestKnownGoodPos_);
|
||||
latestKnownGoodPos_ = update;
|
||||
}
|
||||
|
||||
size_t
|
||||
BinTokenReaderTester::offset() const
|
||||
{
|
||||
return latestKnownGoodPos_;
|
||||
}
|
||||
|
||||
TokenPos
|
||||
BinTokenReaderTester::pos()
|
||||
{
|
||||
return pos(latestKnownGoodPos_);
|
||||
}
|
||||
|
||||
TokenPos
|
||||
BinTokenReaderTester::pos(size_t start)
|
||||
{
|
||||
TokenPos pos;
|
||||
pos.begin = start;
|
||||
pos.end = current_ - start_;
|
||||
MOZ_ASSERT(pos.end >= pos.begin);
|
||||
return pos;
|
||||
MOZ_TRY_VAR(items, readInternalUint32());
|
||||
return Ok();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -452,13 +347,13 @@ BinTokenReaderTester::AutoBase::~AutoBase()
|
|||
MOZ_ASSERT_IF(initialized_, reader_.cx_->isExceptionPending());
|
||||
}
|
||||
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::AutoBase::checkPosition(const uint8_t* expectedEnd)
|
||||
{
|
||||
if (reader_.current_ != expectedEnd)
|
||||
return reader_.raiseError("Caller did not consume the expected set of bytes");
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
BinTokenReaderTester::AutoList::AutoList(BinTokenReaderTester& reader)
|
||||
|
@ -466,74 +361,67 @@ BinTokenReaderTester::AutoList::AutoList(BinTokenReaderTester& reader)
|
|||
{ }
|
||||
|
||||
void
|
||||
BinTokenReaderTester::AutoList::init(const uint8_t* expectedEnd)
|
||||
BinTokenReaderTester::AutoList::init()
|
||||
{
|
||||
AutoBase::init();
|
||||
this->expectedEnd_ = expectedEnd;
|
||||
}
|
||||
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::AutoList::done()
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
initialized_ = false;
|
||||
if (reader_.cx_->isExceptionPending()) {
|
||||
// Already errored, no need to check further.
|
||||
return false;
|
||||
return reader_.cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
// Check that we have consumed the exact number of bytes.
|
||||
if (!checkPosition(expectedEnd_))
|
||||
return false;
|
||||
|
||||
// Check suffix.
|
||||
if (!reader_.readConst("</list>"))
|
||||
return false;
|
||||
MOZ_TRY(reader_.readConst("</list>"));
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
BinTokenReaderTester::AutoTaggedTuple::AutoTaggedTuple(BinTokenReaderTester& reader)
|
||||
: AutoBase(reader)
|
||||
{ }
|
||||
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::AutoTaggedTuple::done()
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
initialized_ = false;
|
||||
if (reader_.cx_->isExceptionPending()) {
|
||||
// Already errored, no need to check further.
|
||||
return false;
|
||||
return reader_.cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
// Check suffix.
|
||||
if (!reader_.readConst("</tuple>"))
|
||||
return false;
|
||||
MOZ_TRY(reader_.readConst("</tuple>"));
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
BinTokenReaderTester::AutoTuple::AutoTuple(BinTokenReaderTester& reader)
|
||||
: AutoBase(reader)
|
||||
{ }
|
||||
|
||||
bool
|
||||
JS::Result<Ok>
|
||||
BinTokenReaderTester::AutoTuple::done()
|
||||
{
|
||||
MOZ_ASSERT(initialized_);
|
||||
initialized_ = false;
|
||||
if (reader_.cx_->isExceptionPending()) {
|
||||
// Already errored, no need to check further.
|
||||
return false;
|
||||
return reader_.cx_->alreadyReportedError();
|
||||
}
|
||||
|
||||
// Check suffix.
|
||||
if (!reader_.readConst("</tuple>"))
|
||||
return false;
|
||||
MOZ_TRY(reader_.readConst("</tuple>"));
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "frontend/BinToken.h"
|
||||
#include "frontend/TokenStream.h"
|
||||
#include "frontend/BinTokenReaderBase.h"
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
|
@ -49,7 +49,7 @@ using namespace JS;
|
|||
* - it does not support any form of look ahead, push back;
|
||||
* - it does not support any form of error recovery.
|
||||
*/
|
||||
class MOZ_STACK_CLASS BinTokenReaderTester
|
||||
class MOZ_STACK_CLASS BinTokenReaderTester: public BinTokenReaderBase
|
||||
{
|
||||
public:
|
||||
// A list of fields, in the order in which they appear in the stream.
|
||||
|
@ -79,6 +79,11 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
*/
|
||||
BinTokenReaderTester(JSContext* cx, const Vector<uint8_t>& chars);
|
||||
|
||||
/**
|
||||
* Read the header of the file.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> readHeader();
|
||||
|
||||
// --- Primitive values.
|
||||
//
|
||||
// Note that the underlying format allows for a `null` value for primitive
|
||||
|
@ -90,43 +95,48 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
// has been cleared: the token reader does NOT support recovery, by design.
|
||||
|
||||
/**
|
||||
* Read a single `true | false | null` value.
|
||||
*
|
||||
* @param out Set to `Nothing` if the data specifies that the value is `null`.
|
||||
* Otherwise, `Some(true)` or `Some(false)`.
|
||||
*
|
||||
* @return false If a boolean could not be read. In this case, an error
|
||||
* has been raised.
|
||||
* Read a single `true | false` value.
|
||||
*/
|
||||
MOZ_MUST_USE bool readMaybeBool(Maybe<bool>& out);
|
||||
MOZ_MUST_USE bool readBool(bool& out);
|
||||
MOZ_MUST_USE JS::Result<bool> readBool();
|
||||
|
||||
/**
|
||||
* Read a single `number | null` value.
|
||||
*
|
||||
* @param out Set to `Nothing` if the data specifies that the value is `null`.
|
||||
* Otherwise, `Some(x)`, where `x` is a valid `double` (i.e. either a non-NaN
|
||||
* or a canonical NaN).
|
||||
*
|
||||
* Read a single `number` value.
|
||||
* @return false If a double could not be read. In this case, an error
|
||||
* has been raised.
|
||||
*/
|
||||
MOZ_MUST_USE bool readMaybeDouble(Maybe<double>& out);
|
||||
MOZ_MUST_USE bool readDouble(double& out);
|
||||
MOZ_MUST_USE JS::Result<double> readDouble();
|
||||
|
||||
/**
|
||||
* Read a single `string | null` value.
|
||||
*
|
||||
* @param out Set to `Nothing` if the data specifies that the value is `null`.
|
||||
* Otherwise, `Some(x)`, where `x` is a `string`.
|
||||
* Fails if that string is not valid UTF-8.
|
||||
*
|
||||
* WARNING: At this stage, the `string` encoding has NOT been validated.
|
||||
*
|
||||
* @return false If a string could not be read. In this case, an error
|
||||
* has been raised.
|
||||
* The returned `JSAtom*` may be `nullptr`.
|
||||
*/
|
||||
MOZ_MUST_USE bool readMaybeChars(Maybe<Chars>& out);
|
||||
MOZ_MUST_USE bool readChars(Chars& out);
|
||||
MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom();
|
||||
|
||||
/**
|
||||
* Read a single `string` value.
|
||||
*
|
||||
* Fails if that string is not valid UTF-8 or in case of `null` string.
|
||||
*
|
||||
* The returned `JSAtom*` is never `nullptr`.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<JSAtom*> readAtom();
|
||||
|
||||
|
||||
/**
|
||||
* Read a single `string | null` value.
|
||||
*
|
||||
* There is no guarantee that the string is valid UTF-8.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Ok> readChars(Chars&);
|
||||
|
||||
/**
|
||||
* Read a single `BinVariant | null` value.
|
||||
*/
|
||||
MOZ_MUST_USE JS::Result<Maybe<BinVariant>> readMaybeVariant();
|
||||
MOZ_MUST_USE JS::Result<BinVariant> readVariant();
|
||||
|
||||
// --- Composite values.
|
||||
//
|
||||
|
@ -150,7 +160,7 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
*
|
||||
* @return out If the header of the list is invalid.
|
||||
*/
|
||||
MOZ_MUST_USE bool enterList(uint32_t& length, AutoList& guard);
|
||||
MOZ_MUST_USE JS::Result<Ok> enterList(uint32_t& length, AutoList& guard);
|
||||
|
||||
/**
|
||||
* Start reading a tagged tuple.
|
||||
|
@ -167,7 +177,7 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
*
|
||||
* @return out If the header of the tuple is invalid.
|
||||
*/
|
||||
MOZ_MUST_USE bool enterTaggedTuple(BinKind& tag, BinTokenReaderTester::BinFields& fields, AutoTaggedTuple& guard);
|
||||
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinKind& tag, BinTokenReaderTester::BinFields& fields, AutoTaggedTuple& guard);
|
||||
|
||||
/**
|
||||
* Start reading an untagged tuple.
|
||||
|
@ -182,98 +192,12 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
*
|
||||
* @return out If the header of the tuple is invalid.
|
||||
*/
|
||||
MOZ_MUST_USE bool enterUntaggedTuple(AutoTuple& guard);
|
||||
|
||||
/**
|
||||
* Return the position of the latest token.
|
||||
*/
|
||||
TokenPos pos();
|
||||
TokenPos pos(size_t startOffset);
|
||||
size_t offset() const;
|
||||
|
||||
/**
|
||||
* Raise an error.
|
||||
*
|
||||
* Once `raiseError` has been called, the tokenizer is poisoned.
|
||||
*/
|
||||
MOZ_MUST_USE bool raiseError(const char* description);
|
||||
|
||||
/**
|
||||
* Poison this tokenizer.
|
||||
*/
|
||||
void poison();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Read a single byte.
|
||||
*/
|
||||
MOZ_MUST_USE bool readByte(uint8_t* byte);
|
||||
|
||||
/**
|
||||
* Read several bytes.
|
||||
*
|
||||
* If there is not enough data, or if the tokenizer has previously been
|
||||
* poisoned, return `false` and report an exception.
|
||||
*/
|
||||
MOZ_MUST_USE bool readBuf(uint8_t* bytes, uint32_t len);
|
||||
MOZ_MUST_USE JS::Result<Ok> enterUntaggedTuple(AutoTuple& guard);
|
||||
|
||||
/**
|
||||
* Read a single uint32_t.
|
||||
*/
|
||||
MOZ_MUST_USE bool readInternalUint32(uint32_t*);
|
||||
|
||||
/**
|
||||
* Read a sequence of chars, ensuring that they match an expected
|
||||
* sequence of chars.
|
||||
*
|
||||
* @param value The sequence of chars to expect, NUL-terminated. The NUL
|
||||
* is not expected in the stream.
|
||||
*/
|
||||
template <size_t N>
|
||||
MOZ_MUST_USE bool readConst(const char (&value)[N]);
|
||||
|
||||
/**
|
||||
* Read a sequence of chars, consuming the bytes only if they match an expected
|
||||
* sequence of chars.
|
||||
*
|
||||
* @param value The sequence of chars to expect, NUL-terminated. The NUL
|
||||
* is not expected in the stream.
|
||||
* @return true if `value` (minus NUL) represents the next few chars in the
|
||||
* internal buffer, false otherwise. If `true`, the chars are consumed,
|
||||
* otherwise there is no side-effect.
|
||||
*/
|
||||
template <size_t N>
|
||||
MOZ_MUST_USE bool matchConst(const char (&value)[N]);
|
||||
|
||||
/**
|
||||
* Update the "latest known good" position, which is used during error
|
||||
* reporting.
|
||||
*/
|
||||
void updateLatestKnownGood();
|
||||
|
||||
private:
|
||||
JSContext* cx_;
|
||||
|
||||
// `true` if we have encountered an error. Errors are non recoverable.
|
||||
// Attempting to read from a poisoned tokenizer will cause assertion errors.
|
||||
bool poisoned_;
|
||||
|
||||
// The first byte of the buffer. Not owned.
|
||||
const uint8_t* start_;
|
||||
|
||||
// The current position.
|
||||
const uint8_t* current_;
|
||||
|
||||
// The last+1 byte of the buffer.
|
||||
const uint8_t* stop_;
|
||||
|
||||
|
||||
// Latest known good position. Used for error reporting.
|
||||
size_t latestKnownGoodPos_;
|
||||
|
||||
BinTokenReaderTester(const BinTokenReaderTester&) = delete;
|
||||
BinTokenReaderTester(BinTokenReaderTester&&) = delete;
|
||||
BinTokenReaderTester& operator=(BinTokenReaderTester&) = delete;
|
||||
MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
|
||||
|
||||
public:
|
||||
// The following classes are used whenever we encounter a tuple/tagged tuple/list
|
||||
|
@ -296,7 +220,7 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
~AutoBase();
|
||||
|
||||
// Raise an error if we are not in the expected position.
|
||||
MOZ_MUST_USE bool checkPosition(const uint8_t* expectedPosition);
|
||||
MOZ_MUST_USE JS::Result<Ok> checkPosition(const uint8_t* expectedPosition);
|
||||
|
||||
friend BinTokenReaderTester;
|
||||
void init();
|
||||
|
@ -314,12 +238,10 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
explicit AutoList(BinTokenReaderTester& reader);
|
||||
|
||||
// Check that we have properly read to the end of the list.
|
||||
MOZ_MUST_USE bool done();
|
||||
MOZ_MUST_USE JS::Result<Ok> done();
|
||||
protected:
|
||||
friend BinTokenReaderTester;
|
||||
void init(const uint8_t* expectedEnd);
|
||||
private:
|
||||
const uint8_t* expectedEnd_;
|
||||
void init();
|
||||
};
|
||||
|
||||
// Guard class used to ensure that `enterTaggedTuple` is used properly.
|
||||
|
@ -329,7 +251,7 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
explicit AutoTaggedTuple(BinTokenReaderTester& reader);
|
||||
|
||||
// Check that we have properly read to the end of the tuple.
|
||||
MOZ_MUST_USE bool done();
|
||||
MOZ_MUST_USE JS::Result<Ok> done();
|
||||
};
|
||||
|
||||
// Guard class used to ensure that `readTuple` is used properly.
|
||||
|
@ -339,7 +261,7 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
explicit AutoTuple(BinTokenReaderTester& reader);
|
||||
|
||||
// Check that we have properly read to the end of the tuple.
|
||||
MOZ_MUST_USE bool done();
|
||||
MOZ_MUST_USE JS::Result<Ok> done();
|
||||
};
|
||||
|
||||
// Compare a `Chars` and a string literal (ONLY a string literal).
|
||||
|
@ -355,6 +277,31 @@ class MOZ_STACK_CLASS BinTokenReaderTester
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure that we are visiting the right fields.
|
||||
template<size_t N>
|
||||
JS::Result<Ok, JS::Error&> checkFields(const BinKind kind, const BinFields& actual,
|
||||
const BinField (&expected)[N])
|
||||
{
|
||||
if (actual.length() != N)
|
||||
return raiseInvalidNumberOfFields(kind, N, actual.length());
|
||||
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
if (actual[i] != expected[i])
|
||||
return raiseInvalidField(describeBinKind(kind), actual[i]);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
// Special case for N=0, as empty arrays are not permitted in C++
|
||||
JS::Result<Ok, JS::Error&> checkFields0(const BinKind kind, const BinFields& actual)
|
||||
{
|
||||
if (actual.length() != 0)
|
||||
return raiseInvalidNumberOfFields(kind, 0, actual.length());
|
||||
|
||||
return Ok();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
|
|
|
@ -100,6 +100,9 @@ struct GlobalRules {
|
|||
/// Documentation for the `BinField` class enum.
|
||||
hpp_tokens_field_doc: Option<String>,
|
||||
|
||||
/// Documentation for the `BinVariant` class enum.
|
||||
hpp_tokens_variants_doc: Option<String>,
|
||||
|
||||
/// Per-node rules.
|
||||
per_node: HashMap<NodeName, NodeRules>,
|
||||
}
|
||||
|
@ -115,6 +118,7 @@ impl GlobalRules {
|
|||
let mut hpp_tokens_footer = None;
|
||||
let mut hpp_tokens_kind_doc = None;
|
||||
let mut hpp_tokens_field_doc = None;
|
||||
let mut hpp_tokens_variants_doc = None;
|
||||
let mut per_node = HashMap::new();
|
||||
|
||||
for (node_key, node_entries) in rules.iter() {
|
||||
|
@ -140,6 +144,8 @@ impl GlobalRules {
|
|||
.unwrap_or_else(|_| panic!("Rule hpp.tokens.kind.doc must be a string"));
|
||||
update_rule(&mut hpp_tokens_field_doc, &node_entries["tokens"]["field"]["doc"])
|
||||
.unwrap_or_else(|_| panic!("Rule hpp.tokens.field.doc must be a string"));
|
||||
update_rule(&mut hpp_tokens_variants_doc, &node_entries["tokens"]["variants"]["doc"])
|
||||
.unwrap_or_else(|_| panic!("Rule hpp.tokens.variants.doc must be a string"));
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
|
@ -245,6 +251,7 @@ impl GlobalRules {
|
|||
hpp_tokens_footer,
|
||||
hpp_tokens_kind_doc,
|
||||
hpp_tokens_field_doc,
|
||||
hpp_tokens_variants_doc,
|
||||
per_node,
|
||||
}
|
||||
}
|
||||
|
@ -316,6 +323,11 @@ struct CPPExporter {
|
|||
|
||||
/// All parsers of options.
|
||||
option_parsers_to_generate: Vec<OptionParserData>,
|
||||
|
||||
/// A mapping from symbol (e.g. `+`, `-`, `instanceof`, ...) to the
|
||||
/// name of the symbol as part of `enum class BinVariant`
|
||||
/// (e.g. `UnaryOperatorDelete`).
|
||||
variants_by_symbol: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl CPPExporter {
|
||||
|
@ -337,7 +349,7 @@ impl CPPExporter {
|
|||
elements: content_node_name
|
||||
});
|
||||
} else if let TypeSpec::Array { ref contents, ref supports_empty } = *typedef.spec() {
|
||||
let content_name = TypeName::type_(&**contents); // FIXME: Wait, do we have an implementation of type names in two places?
|
||||
let content_name = TypeName::type_(&**contents);
|
||||
let content_node_name = syntax.get_node_name(&content_name)
|
||||
.unwrap_or_else(|| panic!("While generating an array parser, could not find node name {}", content_name))
|
||||
.clone();
|
||||
|
@ -351,11 +363,39 @@ impl CPPExporter {
|
|||
list_parsers_to_generate.sort_by(|a, b| str::cmp(a.name.to_str(), b.name.to_str()));
|
||||
option_parsers_to_generate.sort_by(|a, b| str::cmp(a.name.to_str(), b.name.to_str()));
|
||||
|
||||
// Prepare variant_by_symbol, which will let us lookup the BinVariant name of
|
||||
// a symbol. Since some symbols can appear in several enums (e.g. "+"
|
||||
// is both a unary and a binary operator), we need to collect all the
|
||||
// string enums that contain each symbol and come up with a unique name
|
||||
// (note that there is no guarantee of unicity – if collisions show up,
|
||||
// we may need to tweak the name generation algorithm).
|
||||
let mut enum_by_string : HashMap<String, Vec<NodeName>> = HashMap::new();
|
||||
for (name, enum_) in syntax.string_enums_by_name().iter() {
|
||||
for string in enum_.strings().iter() {
|
||||
let vec = enum_by_string.entry(string.clone())
|
||||
.or_insert_with(|| vec![]);
|
||||
vec.push(name.clone());
|
||||
}
|
||||
}
|
||||
let variants_by_symbol = enum_by_string.drain()
|
||||
.map(|(string, names)| {
|
||||
let expanded = format!("{names}{symbol}",
|
||||
names = names.iter()
|
||||
.map(NodeName::to_str)
|
||||
.sorted()
|
||||
.into_iter()
|
||||
.format("Or"),
|
||||
symbol = string.to_cpp_enum_case());
|
||||
(string, expanded)
|
||||
})
|
||||
.collect();
|
||||
|
||||
CPPExporter {
|
||||
syntax,
|
||||
rules,
|
||||
list_parsers_to_generate,
|
||||
option_parsers_to_generate,
|
||||
variants_by_symbol,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -385,7 +425,7 @@ impl CPPExporter {
|
|||
fn get_method_definition_start(&self, name: &NodeName, default_type_ok: &str, prefix: &str, args: &str) -> String {
|
||||
let type_ok = self.get_type_ok(name, default_type_ok);
|
||||
let kind = name.to_class_cases();
|
||||
format!("JS::Result<{type_ok}>\nBinASTParser::parse{prefix}{kind}({args})",
|
||||
format!("template<typename Tok> JS::Result<{type_ok}>\nBinASTParser<Tok>::parse{prefix}{kind}({args})",
|
||||
prefix = prefix,
|
||||
type_ok = type_ok,
|
||||
kind = kind,
|
||||
|
@ -406,10 +446,11 @@ impl CPPExporter {
|
|||
let node_names = self.syntax.node_names()
|
||||
.keys()
|
||||
.sorted();
|
||||
buffer.push_str(&format!("\n#define FOR_EACH_BIN_KIND(F) \\\n{nodes}\n\n",
|
||||
buffer.push_str(&format!("\n#define FOR_EACH_BIN_KIND(F) \\\n{nodes}\n",
|
||||
nodes = node_names.iter()
|
||||
.map(|name| format!(" F({name}, {name})",
|
||||
name = name))
|
||||
.map(|name| format!(" F({enum_name}, \"{spec_name}\")",
|
||||
enum_name = name.to_cpp_enum_case(),
|
||||
spec_name = name))
|
||||
.format(" \\\n")));
|
||||
buffer.push_str("
|
||||
enum class BinKind {
|
||||
|
@ -419,7 +460,7 @@ enum class BinKind {
|
|||
};
|
||||
");
|
||||
|
||||
buffer.push_str(&format!("\n// The number of distinct values of BinKind.\nconst size_t BINKIND_LIMIT = {};\n", self.syntax.node_names().len()));
|
||||
buffer.push_str(&format!("\n// The number of distinct values of BinKind.\nconst size_t BINKIND_LIMIT = {};\n\n\n", self.syntax.node_names().len()));
|
||||
buffer.push_str("\n\n");
|
||||
if self.rules.hpp_tokens_field_doc.is_some() {
|
||||
buffer.push_str(&self.rules.hpp_tokens_field_doc.reindent(""));
|
||||
|
@ -428,9 +469,9 @@ enum class BinKind {
|
|||
let field_names = self.syntax.field_names()
|
||||
.keys()
|
||||
.sorted();
|
||||
buffer.push_str(&format!("\n#define FOR_EACH_BIN_FIELD(F) \\\n{nodes}\n\n",
|
||||
buffer.push_str(&format!("\n#define FOR_EACH_BIN_FIELD(F) \\\n{nodes}\n",
|
||||
nodes = field_names.iter()
|
||||
.map(|name| format!(" F({enum_name}, {spec_name})",
|
||||
.map(|name| format!(" F({enum_name}, \"{spec_name}\")",
|
||||
spec_name = name,
|
||||
enum_name = name.to_cpp_enum_case()))
|
||||
.format(" \\\n")));
|
||||
|
@ -441,7 +482,34 @@ enum class BinField {
|
|||
#undef EMIT_ENUM
|
||||
};
|
||||
");
|
||||
buffer.push_str(&format!("\n// The number of distinct values of BinField.\nconst size_t BINFIELD_LIMIT = {};\n", self.syntax.field_names().len()));
|
||||
buffer.push_str(&format!("\n// The number of distinct values of BinField.\nconst size_t BINFIELD_LIMIT = {};\n\n\n", self.syntax.field_names().len()));
|
||||
|
||||
if self.rules.hpp_tokens_variants_doc.is_some() {
|
||||
buffer.push_str(&self.rules.hpp_tokens_variants_doc.reindent(""));
|
||||
}
|
||||
let enum_variants : Vec<_> = self.variants_by_symbol
|
||||
.iter()
|
||||
.sorted_by(|&(ref symbol_1, ref name_1), &(ref symbol_2, ref name_2)| {
|
||||
Ord::cmp(name_1, name_2)
|
||||
.then_with(|| Ord::cmp(symbol_1, symbol_2))
|
||||
});
|
||||
|
||||
buffer.push_str(&format!("\n#define FOR_EACH_BIN_VARIANT(F) \\\n{nodes}\n",
|
||||
nodes = enum_variants.into_iter()
|
||||
.map(|(symbol, name)| format!(" F({variant_name}, \"{spec_name}\")",
|
||||
spec_name = symbol,
|
||||
variant_name = name))
|
||||
.format(" \\\n")));
|
||||
|
||||
buffer.push_str("
|
||||
enum class BinVariant {
|
||||
#define EMIT_ENUM(name, _) name,
|
||||
FOR_EACH_BIN_VARIANT(EMIT_ENUM)
|
||||
#undef EMIT_ENUM
|
||||
};
|
||||
");
|
||||
buffer.push_str(&format!("\n// The number of distinct values of BinVariant.\nconst size_t BINVARIANT_LIMIT = {};\n\n\n",
|
||||
self.variants_by_symbol.len()));
|
||||
|
||||
buffer.push_str(&self.rules.hpp_tokens_footer.reindent(""));
|
||||
buffer.push_str("\n");
|
||||
|
@ -520,7 +588,7 @@ enum class BinField {
|
|||
.iter()
|
||||
.sorted_by(|a, b| str::cmp(a.0.to_str(), b.0.to_str()));
|
||||
for (kind, _) in string_enums_by_name {
|
||||
let type_ok = format!("BinASTParser::{kind}", kind = kind.to_class_cases());
|
||||
let type_ok = format!("typename BinASTParser<Tok>::{kind}", kind = kind.to_class_cases());
|
||||
let rendered = self.get_method_signature(kind, &type_ok, "", "");
|
||||
buffer.push_str(&rendered.reindent(""));
|
||||
buffer.push_str("\n");
|
||||
|
@ -606,11 +674,11 @@ impl CPPExporter {
|
|||
AutoTaggedTuple guard(*tokenizer_);
|
||||
const auto start = tokenizer_->offset();
|
||||
|
||||
TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
|
||||
MOZ_TRY_DECL(result, parseSum{kind}(start, kind, fields));
|
||||
BINJS_MOZ_TRY_DECL(result, parseSum{kind}(start, kind, fields));
|
||||
|
||||
TRY(guard.done());
|
||||
MOZ_TRY(guard.done());
|
||||
return result;
|
||||
}}\n",
|
||||
bnf = rendered_bnf,
|
||||
|
@ -622,10 +690,11 @@ impl CPPExporter {
|
|||
let mut buffer_cases = String::new();
|
||||
for node in nodes {
|
||||
buffer_cases.push_str(&format!("
|
||||
case BinKind::{kind}:
|
||||
MOZ_TRY_VAR(result, parseInterface{kind}(start, kind, fields));
|
||||
case BinKind::{variant_name}:
|
||||
MOZ_TRY_VAR(result, parseInterface{class_name}(start, kind, fields));
|
||||
break;",
|
||||
kind = node.to_class_cases()));
|
||||
class_name = node.to_class_cases(),
|
||||
variant_name = node.to_cpp_enum_case()));
|
||||
}
|
||||
buffer.push_str(&format!("\n{first_line}
|
||||
{{
|
||||
|
@ -691,15 +760,15 @@ impl CPPExporter {
|
|||
AutoList guard(*tokenizer_);
|
||||
|
||||
const auto start = tokenizer_->offset();
|
||||
TRY(tokenizer_->enterList(length, guard));{empty_check}
|
||||
MOZ_TRY(tokenizer_->enterList(length, guard));{empty_check}
|
||||
{init}
|
||||
|
||||
for (uint32_t i = 0; i < length; ++i) {{
|
||||
MOZ_TRY_DECL(item, parse{inner}());
|
||||
BINJS_MOZ_TRY_DECL(item, parse{inner}());
|
||||
{append}
|
||||
}}
|
||||
|
||||
TRY(guard.done());
|
||||
MOZ_TRY(guard.done());
|
||||
return result;
|
||||
}}\n",
|
||||
first_line = first_line,
|
||||
|
@ -766,7 +835,7 @@ impl CPPExporter {
|
|||
BinFields fields(cx_);
|
||||
AutoTaggedTuple guard(*tokenizer_);
|
||||
|
||||
TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
{type_ok} result;
|
||||
if (kind == BinKind::{null}) {{
|
||||
result = {default_value};
|
||||
|
@ -774,14 +843,14 @@ impl CPPExporter {
|
|||
const auto start = tokenizer_->offset();
|
||||
MOZ_TRY_VAR(result, parseInterface{contents}(start, kind, fields));
|
||||
}}
|
||||
TRY(guard.done());
|
||||
MOZ_TRY(guard.done());
|
||||
|
||||
return result;
|
||||
}}
|
||||
|
||||
",
|
||||
first_line = self.get_method_definition_start(&parser.name, "ParseNode*", "", ""),
|
||||
null = self.syntax.get_null_name().to_str(),
|
||||
null = self.syntax.get_null_name().to_cpp_enum_case(),
|
||||
contents = parser.elements.to_class_cases(),
|
||||
type_ok = type_ok,
|
||||
default_value = default_value,
|
||||
|
@ -796,15 +865,15 @@ impl CPPExporter {
|
|||
BinFields fields(cx_);
|
||||
AutoTaggedTuple guard(*tokenizer_);
|
||||
|
||||
TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
{type_ok} result;
|
||||
if (kind == BinKind::_Null) {{
|
||||
if (kind == BinKind::{null}) {{
|
||||
result = {default_value};
|
||||
}} else {{
|
||||
const auto start = tokenizer_->offset();
|
||||
MOZ_TRY_VAR(result, parseSum{contents}(start, kind, fields));
|
||||
}}
|
||||
TRY(guard.done());
|
||||
MOZ_TRY(guard.done());
|
||||
|
||||
return result;
|
||||
}}
|
||||
|
@ -814,31 +883,36 @@ impl CPPExporter {
|
|||
contents = parser.elements.to_class_cases(),
|
||||
type_ok = type_ok,
|
||||
default_value = default_value,
|
||||
null = self.syntax.get_null_name().to_cpp_enum_case(),
|
||||
));
|
||||
}
|
||||
&TypeSpec::String => {
|
||||
let build_result = rules_for_this_node.init.reindent(" ");
|
||||
|
||||
buffer.push_str(&format!("{first_line}
|
||||
let first_line = self.get_method_definition_start(&parser.name, "ParseNode*", "", "");
|
||||
if build_result.len() == 0 {
|
||||
buffer.push_str(&format!("{first_line}
|
||||
{{
|
||||
RootedAtom string(cx_);
|
||||
MOZ_TRY(readMaybeString(&string));
|
||||
|
||||
{build}
|
||||
|
||||
{return_}
|
||||
return raiseError(\"FIXME: Not implemented yet ({kind})\");
|
||||
}}
|
||||
|
||||
",
|
||||
first_line = self.get_method_definition_start(&parser.name, "ParseNode*", "", ""),
|
||||
build = build_result,
|
||||
return_ = if build_result.len() == 0 {
|
||||
format!("return raiseError(\"FIXME: Not implemented yet ({kind})\");\n",
|
||||
kind = parser.name.to_str())
|
||||
} else {
|
||||
"return result;".to_string()
|
||||
}
|
||||
));
|
||||
first_line = first_line,
|
||||
kind = parser.name.to_str()));
|
||||
} else {
|
||||
buffer.push_str(&format!("{first_line}
|
||||
{{
|
||||
BINJS_MOZ_TRY_DECL(result, tokenizer_->readMaybeAtom());
|
||||
|
||||
{build}
|
||||
|
||||
return result;
|
||||
}}
|
||||
|
||||
",
|
||||
first_line = first_line,
|
||||
build = build_result,
|
||||
));
|
||||
}
|
||||
}
|
||||
_else => unimplemented!("{:?}", _else)
|
||||
}
|
||||
|
@ -872,11 +946,11 @@ impl CPPExporter {
|
|||
BinFields fields(cx_);
|
||||
AutoTaggedTuple guard(*tokenizer_);
|
||||
|
||||
TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
MOZ_TRY(tokenizer_->enterTaggedTuple(kind, fields, guard));
|
||||
const auto start = tokenizer_->offset();
|
||||
|
||||
MOZ_TRY_DECL(result, parseInterface{kind}(start, kind, fields));
|
||||
TRY(guard.done());
|
||||
BINJS_MOZ_TRY_DECL(result, parseInterface{kind}(start, kind, fields));
|
||||
MOZ_TRY(guard.done());
|
||||
|
||||
return result;
|
||||
}}
|
||||
|
@ -908,19 +982,28 @@ impl CPPExporter {
|
|||
Some(IsNullable { is_nullable: false, content: Primitive::Number }) => {
|
||||
if needs_block {
|
||||
(Some(format!("double {var_name};", var_name = var_name)),
|
||||
Some(format!("MOZ_TRY_VAR({var_name}, readNumber());", var_name = var_name)))
|
||||
Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readDouble());", var_name = var_name)))
|
||||
} else {
|
||||
(None,
|
||||
Some(format!("MOZ_TRY_DECL({var_name}, readNumber());", var_name = var_name)))
|
||||
Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readDouble());", var_name = var_name)))
|
||||
}
|
||||
}
|
||||
Some(IsNullable { is_nullable: false, content: Primitive::Boolean }) => {
|
||||
if needs_block {
|
||||
(Some(format!("bool {var_name};", var_name = var_name)),
|
||||
Some(format!("MOZ_TRY_VAR({var_name}, readBool());", var_name = var_name)))
|
||||
Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readBool());", var_name = var_name)))
|
||||
} else {
|
||||
(None,
|
||||
Some(format!("MOZ_TRY_DECL({var_name}, readBool());", var_name = var_name)))
|
||||
Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readBool());", var_name = var_name)))
|
||||
}
|
||||
}
|
||||
Some(IsNullable { is_nullable: false, content: Primitive::Offset }) => {
|
||||
if needs_block {
|
||||
(Some(format!("uint32_t {var_name};", var_name = var_name)),
|
||||
Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readOffset());", var_name = var_name)))
|
||||
} else {
|
||||
(None,
|
||||
Some(format!("BINJS_MOZ_TRY_DECL({var_name}, tokenizer_->readOffset());", var_name = var_name)))
|
||||
}
|
||||
}
|
||||
Some(IsNullable { content: Primitive::Void, .. }) => {
|
||||
|
@ -930,7 +1013,7 @@ impl CPPExporter {
|
|||
}
|
||||
Some(IsNullable { is_nullable: false, content: Primitive::String }) => {
|
||||
(Some(format!("RootedAtom {var_name}(cx_);", var_name = var_name)),
|
||||
Some(format!("MOZ_TRY(readString(&{var_name}));", var_name = var_name)))
|
||||
Some(format!("MOZ_TRY_VAR({var_name}, tokenizer_->readAtom());", var_name = var_name)))
|
||||
}
|
||||
Some(IsNullable { content: Primitive::Interface(ref interface), ..})
|
||||
if &self.get_type_ok(interface.name(), "?") == "Ok" =>
|
||||
|
@ -953,7 +1036,7 @@ impl CPPExporter {
|
|||
))
|
||||
} else {
|
||||
(None,
|
||||
Some(format!("MOZ_TRY_DECL({var_name}, parse{typename}());",
|
||||
Some(format!("BINJS_MOZ_TRY_DECL({var_name}, parse{typename}());",
|
||||
var_name = var_name,
|
||||
typename = typename)))
|
||||
}
|
||||
|
@ -1031,9 +1114,9 @@ impl CPPExporter {
|
|||
));
|
||||
} else {
|
||||
let check_fields = if number_of_fields == 0 {
|
||||
format!("MOZ_TRY(checkFields0(kind, fields));")
|
||||
format!("MOZ_TRY(tokenizer_->checkFields0(kind, fields));")
|
||||
} else {
|
||||
format!("MOZ_TRY(checkFields(kind, fields, {fields_type_list}));",
|
||||
format!("MOZ_TRY(tokenizer_->checkFields(kind, fields, {fields_type_list}));",
|
||||
fields_type_list = fields_type_list)
|
||||
};
|
||||
buffer.push_str(&format!("{first_line}
|
||||
|
@ -1043,7 +1126,8 @@ impl CPPExporter {
|
|||
|
||||
{check_fields}
|
||||
{pre}{fields_implem}
|
||||
{post}return result;
|
||||
{post}
|
||||
return result;
|
||||
}}
|
||||
|
||||
",
|
||||
|
@ -1051,7 +1135,7 @@ impl CPPExporter {
|
|||
fields_implem = fields_implem,
|
||||
pre = init,
|
||||
post = build_result,
|
||||
kind = kind,
|
||||
kind = name.to_cpp_enum_case(),
|
||||
first_line = first_line,
|
||||
));
|
||||
}
|
||||
|
@ -1097,18 +1181,21 @@ impl CPPExporter {
|
|||
.iter()
|
||||
.sorted_by(|a, b| str::cmp(a.0.to_str(), b.0.to_str()));
|
||||
for (kind, enum_) in string_enums_by_name {
|
||||
let convert = format!("{cases}
|
||||
|
||||
return raiseInvalidEnum(\"{kind}\", chars);",
|
||||
let convert = format!(" switch (variant) {{
|
||||
{cases}
|
||||
default:
|
||||
return raiseInvalidVariant(\"{kind}\", variant);
|
||||
}}",
|
||||
kind = kind,
|
||||
cases = enum_.strings()
|
||||
.iter()
|
||||
.map(|string| {
|
||||
format!(" if (chars == \"{string}\")
|
||||
return {kind}::{variant};",
|
||||
string = string,
|
||||
.map(|symbol| {
|
||||
format!(" case BinVariant::{binvariant_variant}:
|
||||
return {kind}::{specialized_variant};",
|
||||
kind = kind,
|
||||
variant = string.to_cpp_enum_case()
|
||||
specialized_variant = symbol.to_cpp_enum_case(),
|
||||
binvariant_variant = self.variants_by_symbol.get(symbol)
|
||||
.unwrap()
|
||||
)
|
||||
})
|
||||
.format("\n")
|
||||
|
@ -1123,9 +1210,7 @@ impl CPPExporter {
|
|||
);
|
||||
buffer.push_str(&format!("{rendered_doc}{first_line}
|
||||
{{
|
||||
// Unoptimized implementation.
|
||||
Chars chars(cx_);
|
||||
MOZ_TRY(readString(chars));
|
||||
BINJS_MOZ_TRY_DECL(variant, tokenizer_->readVariant());
|
||||
|
||||
{convert}
|
||||
}}
|
||||
|
@ -1133,7 +1218,7 @@ impl CPPExporter {
|
|||
",
|
||||
rendered_doc = rendered_doc,
|
||||
convert = convert,
|
||||
first_line = self.get_method_definition_start(kind, &format!("BinASTParser::{kind}", kind = kind), "", "")
|
||||
first_line = self.get_method_definition_start(kind, &format!("typename BinASTParser<Tok>::{kind}", kind = kind), "", "")
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1216,8 +1301,8 @@ fn main() {
|
|||
|
||||
println!("...verifying grammar");
|
||||
let mut builder = Importer::import(&ast);
|
||||
let fake_root = builder.node_name("");
|
||||
let null = builder.node_name("_Null");
|
||||
let fake_root = builder.node_name("@@ROOT@@"); // Unused
|
||||
let null = builder.node_name(""); // Used
|
||||
builder.add_interface(&null)
|
||||
.unwrap();
|
||||
let syntax = builder.into_spec(SpecOptions {
|
||||
|
|
|
@ -56,7 +56,7 @@ testBinASTReaderFuzz(const uint8_t* buf, size_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
js::frontend::BinASTParser reader(gCx, gCx->tempLifoAlloc(), binUsedNames, options);
|
||||
js::frontend::BinASTParser<js::frontend::BinTokenReaderTester> reader(gCx, gCx->tempLifoAlloc(), binUsedNames, options);
|
||||
|
||||
// Will be deallocated once `reader` goes out of scope.
|
||||
auto binParsed = reader.parse(binSource);
|
||||
|
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/boolean-001.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/boolean-001.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,47 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: boolean-001.js
|
||||
Description: Corresponds to ecma/Boolean/15.6.4.2-4-n.js
|
||||
|
||||
The toString function is not generic; it generates
|
||||
a runtime error if its this value is not a Boolean
|
||||
object. Therefore it cannot be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: june 27, 1997
|
||||
*/
|
||||
var SECTION = "boolean-001.js";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Boolean.prototype.toString()";
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION +" "+ TITLE );
|
||||
|
||||
var exception = "No exception thrown";
|
||||
var result = "Failed";
|
||||
|
||||
var TO_STRING = Boolean.prototype.toString;
|
||||
|
||||
try {
|
||||
var s = new String("Not a Boolean");
|
||||
s.toString = TO_STRING;
|
||||
s.toString();
|
||||
} catch ( e ) {
|
||||
result = "Passed!";
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"Assigning Boolean.prototype.toString to a String object "+
|
||||
"(threw " +exception +")",
|
||||
"Passed!",
|
||||
result );
|
||||
|
||||
test();
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/boolean-002.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/boolean-002.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,51 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: boolean-001.js
|
||||
Description: Corresponds to ecma/Boolean/15.6.4.3-4-n.js
|
||||
|
||||
15.6.4.3 Boolean.prototype.valueOf()
|
||||
Returns this boolean value.
|
||||
|
||||
The valueOf function is not generic; it generates
|
||||
a runtime error if its this value is not a Boolean
|
||||
object. Therefore it cannot be transferred to other
|
||||
kinds of objects for use as a method.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 09 september 1998
|
||||
*/
|
||||
var SECTION = "boolean-002.js";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Boolean.prototype.valueOf()";
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION +" "+ TITLE );
|
||||
|
||||
|
||||
var exception = "No exception thrown";
|
||||
var result = "Failed";
|
||||
|
||||
var VALUE_OF = Boolean.prototype.valueOf;
|
||||
|
||||
try {
|
||||
var s = new String("Not a Boolean");
|
||||
s.valueOf = VALUE_0F;
|
||||
s.valueOf();
|
||||
} catch ( e ) {
|
||||
result = "Passed!";
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"Assigning Boolean.prototype.valueOf to a String object "+
|
||||
"(threw " +exception +")",
|
||||
"Passed!",
|
||||
result );
|
||||
|
||||
test();
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/browser.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/browser.binjs
Normal file
Двоичный файл не отображается.
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/date-002.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/date-002.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,54 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: date-002.js
|
||||
Corresponds To: 15.9.5.23-3-n.js
|
||||
ECMA Section: 15.9.5.23
|
||||
Description: Date.prototype.setTime
|
||||
|
||||
1. If the this value is not a Date object, generate a runtime error.
|
||||
2. Call ToNumber(time).
|
||||
3. Call TimeClip(Result(1)).
|
||||
4. Set the [[Value]] property of the this value to Result(2).
|
||||
5. Return the value of the [[Value]] property of the this value.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "date-002";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Date.prototype.setTime()";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
var MYDATE = new MyDate();
|
||||
result = MYDATE.setTime(0);
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"MYDATE = new MyDate(); MYDATE.setTime(0)" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
||||
function MyDate(value) {
|
||||
this.value = value;
|
||||
this.setTime = Date.prototype.setTime;
|
||||
return this;
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/date-003.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/date-003.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,56 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: date-003.js
|
||||
Corresponds To 15.9.5.3-1.js
|
||||
ECMA Section: 15.9.5.3-1 Date.prototype.valueOf
|
||||
Description:
|
||||
|
||||
The valueOf function returns a number, which is this time value.
|
||||
|
||||
The valueOf function is not generic; it generates a runtime error if
|
||||
its this value is not a Date object. Therefore it cannot be transferred
|
||||
to other kinds of objects for use as a method.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "date-003";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Date.prototype.valueOf";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
var OBJ = new MyObject( new Date(0) );
|
||||
result = OBJ.valueOf();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"OBJ = new MyObject( new Date(0)); OBJ.valueOf()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
||||
function MyObject( value ) {
|
||||
this.value = value;
|
||||
this.valueOf = Date.prototype.valueOf;
|
||||
// The following line causes an infinte loop
|
||||
// this.toString = new Function( "return this+\"\";");
|
||||
return this;
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/date-004.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/date-004.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,50 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: date-004.js
|
||||
Corresponds To: 15.9.5.4-2-n.js
|
||||
ECMA Section: 15.9.5.4-1 Date.prototype.getTime
|
||||
Description:
|
||||
|
||||
1. If the this value is not an object whose [[Class]] property is "Date",
|
||||
generate a runtime error.
|
||||
2. Return this time value.
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "date-004";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Date.prototype.getTime";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
var MYDATE = new MyDate();
|
||||
result = MYDATE.getTime();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"MYDATE = new MyDate(); MYDATE.getTime()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
||||
function MyDate( value ) {
|
||||
this.value = value;
|
||||
this.getTime = Date.prototype.getTime;
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-001.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-001.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,45 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-001
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* Call error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-001";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: CallError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
Call_1();
|
||||
|
||||
test();
|
||||
|
||||
function Call_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
Math();
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"Math() [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-002.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-002.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,45 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-002
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* Construct error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-002";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: ConstructError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
Construct_1();
|
||||
|
||||
test();
|
||||
|
||||
function Construct_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
result = new Math();
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"new Math() [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-003.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-003.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,49 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-003
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* Target error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-003";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: TargetError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
Target_1();
|
||||
|
||||
test();
|
||||
|
||||
function Target_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
string = new String("hi");
|
||||
string.toString = Boolean.prototype.toString;
|
||||
string.toString();
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"string = new String(\"hi\");"+
|
||||
"string.toString = Boolean.prototype.toString" +
|
||||
"string.toString() [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-004.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-004.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,45 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-004
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* ToObject error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-004";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: ToObjectError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
ToObject_1();
|
||||
|
||||
test();
|
||||
|
||||
function ToObject_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
result = foo["bar"];
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"foo[\"bar\"] [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-005.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-005.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,45 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-005
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* ToObject error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-005";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: ToObjectError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
ToObject_1();
|
||||
|
||||
test();
|
||||
|
||||
function ToObject_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
result = foo["bar"];
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"foo[\"bar\"] [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-006.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-006.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,56 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-006
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* ToPrimitive error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-006";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: TypeError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
ToPrimitive_1();
|
||||
|
||||
test();
|
||||
|
||||
|
||||
/**
|
||||
* Getting the [[DefaultValue]] of any instances of MyObject
|
||||
* should result in a runtime error in ToPrimitive.
|
||||
*/
|
||||
|
||||
function MyObject() {
|
||||
this.toString = void 0;
|
||||
this.valueOf = void 0;
|
||||
}
|
||||
|
||||
function ToPrimitive_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
result = new MyObject() + new MyObject();
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"new MyObject() + new MyObject() [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-007.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-007.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,57 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-007
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* DefaultValue error.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-007";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: TypeError";
|
||||
var BUGNUMBER="318250";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
DefaultValue_1();
|
||||
|
||||
test();
|
||||
|
||||
|
||||
/**
|
||||
* Getting the [[DefaultValue]] of any instances of MyObject
|
||||
* should result in a runtime error in ToPrimitive.
|
||||
*/
|
||||
|
||||
function MyObject() {
|
||||
this.toString = void 0;
|
||||
this.valueOf = new Object();
|
||||
}
|
||||
|
||||
function DefaultValue_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
result = new MyObject() + new MyObject();
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"new MyObject() + new MyObject() [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-008.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-008.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,44 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-008
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* SyntaxError.
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-008";
|
||||
var VERSION = "js1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: SyntaxError";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
Syntax_1();
|
||||
|
||||
test();
|
||||
|
||||
function Syntax_1() {
|
||||
result = "failed: no exception thrown";
|
||||
exception = null;
|
||||
|
||||
try {
|
||||
result = eval("continue;");
|
||||
} catch ( e ) {
|
||||
result = "passed: threw exception",
|
||||
exception = e.toString();
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"eval(\"continue\") [ exception is " + exception +" ]",
|
||||
"passed: threw exception",
|
||||
result );
|
||||
}
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-009.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-009.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,53 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
* File Name: exception-009
|
||||
* ECMA Section:
|
||||
* Description: Tests for JavaScript Standard Exceptions
|
||||
*
|
||||
* Regression test for nested try blocks.
|
||||
*
|
||||
* http://scopus.mcom.com/bugsplat/show_bug.cgi?id=312964
|
||||
*
|
||||
* Author: christine@netscape.com
|
||||
* Date: 31 August 1998
|
||||
*/
|
||||
var SECTION = "exception-009";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Tests for JavaScript Standard Exceptions: SyntaxError";
|
||||
var BUGNUMBER= "312964";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
try {
|
||||
expect = "passed: no exception thrown";
|
||||
result = expect;
|
||||
Nested_1();
|
||||
} catch ( e ) {
|
||||
result = "failed: threw " + e;
|
||||
} finally {
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"nested try",
|
||||
expect,
|
||||
result );
|
||||
}
|
||||
|
||||
|
||||
test();
|
||||
|
||||
function Nested_1() {
|
||||
try {
|
||||
try {
|
||||
} catch (a) {
|
||||
} finally {
|
||||
}
|
||||
} catch (b) {
|
||||
} finally {
|
||||
}
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-010-n.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-010-n.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,25 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
var SECTION = "exception-010";
|
||||
var VERSION = "ECMA_2";
|
||||
startTest();
|
||||
var TITLE = "Don't Crash throwing null";
|
||||
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
print("Null throw test.");
|
||||
print("BUGNUMBER: 21799");
|
||||
|
||||
DESCRIPTION = "throw null";
|
||||
EXPECTED = "error";
|
||||
|
||||
new TestCase( SECTION, "throw null", "error", eval("throw null" ));
|
||||
|
||||
test();
|
||||
|
||||
print("FAILED!: Should have exited with uncaught exception.");
|
||||
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-011-n.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/exception-011-n.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,26 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
var SECTION = "exception-011";
|
||||
var VERSION = "ECMA_2";
|
||||
startTest();
|
||||
var TITLE = "Don't Crash throwing undefined";
|
||||
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
print("Undefined throw test.");
|
||||
|
||||
DESCRIPTION = "throw undefined";
|
||||
EXPECTED = "error";
|
||||
|
||||
new TestCase( SECTION, "throw undefined", "error", eval("throw (void 0)") );
|
||||
|
||||
test();
|
||||
|
||||
print("FAILED!: Should have exited with uncaught exception.");
|
||||
|
||||
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-001.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-001.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,50 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-001.js
|
||||
Corresponds to: ecma/Expressions/11.12-2-n.js
|
||||
ECMA Section: 11.12
|
||||
Description:
|
||||
|
||||
The grammar for a ConditionalExpression in ECMAScript is a little bit
|
||||
different from that in C and Java, which each allow the second
|
||||
subexpression to be an Expression but restrict the third expression to
|
||||
be a ConditionalExpression. The motivation for this difference in
|
||||
ECMAScript is to allow an assignment expression to be governed by either
|
||||
arm of a conditional and to eliminate the confusing and fairly useless
|
||||
case of a comma expression as the center expression.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 09 september 1998
|
||||
*/
|
||||
var SECTION = "expression-001";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Conditional operator ( ? : )"
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " " + TITLE );
|
||||
|
||||
// the following expression should be an error in JS.
|
||||
|
||||
var result = "Failed"
|
||||
var exception = "No exception was thrown";
|
||||
|
||||
try {
|
||||
eval("var MY_VAR = true ? \"EXPR1\", \"EXPR2\" : \"EXPR3\"");
|
||||
} catch ( e ) {
|
||||
result = "Passed";
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"comma expression in a conditional statement "+
|
||||
"(threw "+ exception +")",
|
||||
"Passed",
|
||||
result );
|
||||
|
||||
|
||||
test();
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-002.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-002.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,60 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expressions-002.js
|
||||
Corresponds to: ecma/Expressions/11.2.1-3-n.js
|
||||
ECMA Section: 11.2.1 Property Accessors
|
||||
Description:
|
||||
|
||||
Try to access properties of an object whose value is undefined.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 09 september 1998
|
||||
*/
|
||||
var SECTION = "expressions-002.js";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Property Accessors";
|
||||
writeHeaderToLog( SECTION + " "+TITLE );
|
||||
|
||||
startTest();
|
||||
|
||||
// go through all Native Function objects, methods, and properties and get their typeof.
|
||||
|
||||
var PROPERTY = new Array();
|
||||
var p = 0;
|
||||
|
||||
// try to access properties of primitive types
|
||||
|
||||
OBJECT = new Property( "undefined", void 0, "undefined", NaN );
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
result = OBJECT.value.valueOf();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"Get the value of an object whose value is undefined "+
|
||||
"(threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
||||
function Property( object, value, string, number ) {
|
||||
this.object = object;
|
||||
this.string = String(value);
|
||||
this.number = Number(value);
|
||||
this.valueOf = value;
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-003.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-003.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,55 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expressions-003.js
|
||||
Corresponds to: ecma/Expressions/11.2.1-3-n.js
|
||||
ECMA Section: 11.2.1 Property Accessors
|
||||
Description:
|
||||
|
||||
Try to access properties of an object whose value is undefined.
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 09 september 1998
|
||||
*/
|
||||
var SECTION = "expressions-003.js";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Property Accessors";
|
||||
writeHeaderToLog( SECTION + " "+TITLE );
|
||||
|
||||
startTest();
|
||||
|
||||
// try to access properties of primitive types
|
||||
|
||||
OBJECT = new Property( "undefined", void 0, "undefined", NaN );
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
result = OBJECT.value.toString();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"Get the toString value of an object whose value is undefined "+
|
||||
"(threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
||||
function Property( object, value, string, number ) {
|
||||
this.object = object;
|
||||
this.string = String(value);
|
||||
this.number = Number(value);
|
||||
this.value = value;
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-004.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-004.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,49 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-004.js
|
||||
Corresponds To: 11.2.1-4-n.js
|
||||
ECMA Section: 11.2.1 Property Accessors
|
||||
Description:
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 09 september 1998
|
||||
*/
|
||||
var SECTION = "expression-004";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "Property Accessors";
|
||||
writeHeaderToLog( SECTION + " "+TITLE );
|
||||
startTest();
|
||||
|
||||
var OBJECT = new Property( "null", null, "null", 0 );
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
result = OBJECT.value.toString();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"Get the toString value of an object whose value is null "+
|
||||
"(threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
||||
function Property( object, value, string, number ) {
|
||||
this.object = object;
|
||||
this.string = String(value);
|
||||
this.number = Number(value);
|
||||
this.value = value;
|
||||
}
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-005.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-005.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,41 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-005.js
|
||||
Corresponds To: 11.2.2-10-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
|
||||
var SECTION = "expression-005";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var result = "Failed";
|
||||
var expect = "Passed";
|
||||
var exception = "No exception thrown";
|
||||
|
||||
try {
|
||||
result = new Math();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"result= new Math() (threw " + exception + ")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-006.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-006.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,46 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-006.js
|
||||
Corresponds to: 11.2.2-1-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
|
||||
http://scopus/bugsplat/show_bug.cgi?id=327765
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "expression-006.js";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
var BUGNUMBER="327765";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
var OBJECT = new Object();
|
||||
result = new OBJECT();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"OBJECT = new Object; result = new OBJECT()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-007.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-007.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,44 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-007.js
|
||||
Corresponds To: 11.2.2-2-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
|
||||
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "expression-007";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
UNDEFINED = void 0;
|
||||
result = new UNDEFINED();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"UNDEFINED = void 0; result = new UNDEFINED()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-008.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-008.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,41 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-008
|
||||
Corresponds To: 11.2.2-3-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "expression-008";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var NULL = null;
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
result = new NULL();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"NULL = null; result = new NULL()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-009.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-009.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,42 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-009
|
||||
Corresponds to: ecma/Expressions/11.2.2-4-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "expression-009";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var STRING = "";
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
result = new STRING();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"STRING = ''; result = new STRING()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-010.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-010.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,43 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-010.js
|
||||
Corresponds To: 11.2.2-5-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "expression-010";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var NUMBER = 0;
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
result = new NUMBER();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"NUMBER=0, result = new NUMBER()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-011.binjs
Normal file
Двоичные данные
js/src/jsapi-tests/binast/parser/multipart/spidermonkey/ecma_2/Exceptions/expression-011.binjs
Normal file
Двоичный файл не отображается.
|
@ -0,0 +1,43 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
|
||||
|
||||
|
||||
/**
|
||||
File Name: expression-011.js
|
||||
Corresponds To: ecma/Expressions/11.2.2-6-n.js
|
||||
ECMA Section: 11.2.2. The new operator
|
||||
Description:
|
||||
Author: christine@netscape.com
|
||||
Date: 12 november 1997
|
||||
*/
|
||||
var SECTION = "expression-011";
|
||||
var VERSION = "JS1_4";
|
||||
var TITLE = "The new operator";
|
||||
|
||||
startTest();
|
||||
writeHeaderToLog( SECTION + " "+ TITLE);
|
||||
|
||||
var BOOLEAN = true;
|
||||
|
||||
var result = "Failed";
|
||||
var exception = "No exception thrown";
|
||||
var expect = "Passed";
|
||||
|
||||
try {
|
||||
var OBJECT = new BOOLEAN();
|
||||
} catch ( e ) {
|
||||
result = expect;
|
||||
exception = e.toString();
|
||||
}
|
||||
|
||||
new TestCase(
|
||||
SECTION,
|
||||
"BOOLEAN = true; result = new BOOLEAN()" +
|
||||
" (threw " + exception +")",
|
||||
expect,
|
||||
result );
|
||||
|
||||
test();
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче