Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE

This commit is contained in:
Andreea Pavel 2018-04-18 20:13:24 +03:00
Родитель 630760c6c7 59a9a86553
Коммит 465753992f
679 изменённых файлов: 18132 добавлений и 73341 удалений

Просмотреть файл

@ -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);

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 );
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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 {
}
}

Двоичный файл не отображается.

Просмотреть файл

@ -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.");

Двоичный файл не отображается.

Просмотреть файл

@ -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.");

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}

Двоичный файл не отображается.

Просмотреть файл

@ -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;
}

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Двоичный файл не отображается.

Просмотреть файл

@ -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();

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше