rename from js/src/asmjs/AsmJSValidate.cpp
rename to js/src/asmjs/AsmJS.cpp
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -11,52 +11,732 @@
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "asmjs/AsmJSValidate.h"
-
-#include "mozilla/Move.h"
+#include "asmjs/AsmJS.h"
+
+#include "mozilla/Compression.h"
+#include "mozilla/MathAlgorithms.h"
#include "jsmath.h"
#include "jsprf.h"
#include "jsutil.h"
-
-#include "asmjs/AsmJSLink.h"
-#include "asmjs/AsmJSModule.h"
+#include "jswrapper.h"
+
#include "asmjs/WasmGenerator.h"
+#include "asmjs/WasmSerialize.h"
#include "builtin/SIMD.h"
#include "frontend/Parser.h"
#include "jit/AtomicOperations.h"
#include "jit/MIR.h"
+#include "js/Class.h"
+#include "js/MemoryMetrics.h"
+#include "vm/StringBuffer.h"
#include "vm/Time.h"
+#include "vm/TypedArrayObject.h"
#include "jsobjinlines.h"
#include "frontend/ParseNode-inl.h"
#include "frontend/Parser-inl.h"
+#include "vm/ArrayBufferObject-inl.h"
using namespace js;
using namespace js::frontend;
using namespace js::jit;
using namespace js::wasm;
+using mozilla::Compression::LZ4;
using mozilla::HashGeneric;
using mozilla::IsNaN;
using mozilla::IsNegativeZero;
+using mozilla::MallocSizeOf;
using mozilla::Move;
+using mozilla::PodEqual;
+using mozilla::PodZero;
using mozilla::PositiveInfinity;
using JS::AsmJSOption;
using JS::GenericNaN;
/*****************************************************************************/
+// asm.js module object
+
+// The asm.js spec recognizes this set of builtin Math functions.
+enum AsmJSMathBuiltinFunction
+{
+ AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
+ AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
+ AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
+ AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
+ AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
+ AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max,
+ AsmJSMathBuiltin_clz32
+};
+
+// The asm.js spec will recognize this set of builtin Atomics functions.
+enum AsmJSAtomicsBuiltinFunction
+{
+ AsmJSAtomicsBuiltin_compareExchange,
+ AsmJSAtomicsBuiltin_exchange,
+ AsmJSAtomicsBuiltin_load,
+ AsmJSAtomicsBuiltin_store,
+ AsmJSAtomicsBuiltin_fence,
+ AsmJSAtomicsBuiltin_add,
+ AsmJSAtomicsBuiltin_sub,
+ AsmJSAtomicsBuiltin_and,
+ AsmJSAtomicsBuiltin_or,
+ AsmJSAtomicsBuiltin_xor,
+ AsmJSAtomicsBuiltin_isLockFree
+};
+
+// Set of known global object SIMD's attributes, i.e. types
+enum AsmJSSimdType
+{
+ AsmJSSimdType_int32x4,
+ AsmJSSimdType_float32x4
+};
+
+// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
+enum AsmJSSimdOperation
+{
+#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
+ FORALL_SIMD_OP(ASMJSSIMDOPERATION)
+#undef ASMJSSIMDOPERATION
+};
+
+// An AsmJSModule extends (via containment) a wasm::Module with the extra persistent state
+// necessary to represent a compiled asm.js module.
+class js::AsmJSModule
+{
+ public:
+ class Global
+ {
+ public:
+ enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
+ AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
+ enum VarInitKind { InitConstant, InitImport };
+ enum ConstantKind { GlobalConstant, MathConstant };
+
+ private:
+ struct CacheablePod {
+ Which which_;
+ union {
+ struct {
+ uint32_t globalDataOffset_;
+ VarInitKind initKind_;
+ union {
+ wasm::ValType importType_;
+ wasm::Val val_;
+ } u;
+ } var;
+ uint32_t ffiIndex_;
+ Scalar::Type viewType_;
+ AsmJSMathBuiltinFunction mathBuiltinFunc_;
+ AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
+ AsmJSSimdType simdCtorType_;
+ struct {
+ AsmJSSimdType type_;
+ AsmJSSimdOperation which_;
+ } simdOp;
+ struct {
+ ConstantKind kind_;
+ double value_;
+ } constant;
+ } u;
+ } pod;
+ PropertyName* name_;
+
+ friend class AsmJSModule;
+
+ Global(Which which, PropertyName* name) {
+ mozilla::PodZero(&pod); // zero padding for Valgrind
+ pod.which_ = which;
+ name_ = name;
+ MOZ_ASSERT_IF(name_, name_->isTenured());
+ }
+
+ void trace(JSTracer* trc) {
+ if (name_)
+ TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
+ }
+
+ public:
+ Global() {}
+ Which which() const {
+ return pod.which_;
+ }
+ uint32_t varGlobalDataOffset() const {
+ MOZ_ASSERT(pod.which_ == Variable);
+ return pod.u.var.globalDataOffset_;
+ }
+ VarInitKind varInitKind() const {
+ MOZ_ASSERT(pod.which_ == Variable);
+ return pod.u.var.initKind_;
+ }
+ wasm::Val varInitVal() const {
+ MOZ_ASSERT(pod.which_ == Variable);
+ MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
+ return pod.u.var.u.val_;
+ }
+ wasm::ValType varInitImportType() const {
+ MOZ_ASSERT(pod.which_ == Variable);
+ MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+ return pod.u.var.u.importType_;
+ }
+ PropertyName* varImportField() const {
+ MOZ_ASSERT(pod.which_ == Variable);
+ MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
+ return name_;
+ }
+ PropertyName* ffiField() const {
+ MOZ_ASSERT(pod.which_ == FFI);
+ return name_;
+ }
+ uint32_t ffiIndex() const {
+ MOZ_ASSERT(pod.which_ == FFI);
+ return pod.u.ffiIndex_;
+ }
+ // When a view is created from an imported constructor:
+ // var I32 = stdlib.Int32Array;
+ // var i32 = new I32(buffer);
+ // the second import has nothing to validate and thus has a null field.
+ PropertyName* maybeViewName() const {
+ MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+ return name_;
+ }
+ Scalar::Type viewType() const {
+ MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
+ return pod.u.viewType_;
+ }
+ PropertyName* mathName() const {
+ MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+ return name_;
+ }
+ PropertyName* atomicsName() const {
+ MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+ return name_;
+ }
+ AsmJSMathBuiltinFunction mathBuiltinFunction() const {
+ MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
+ return pod.u.mathBuiltinFunc_;
+ }
+ AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
+ MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
+ return pod.u.atomicsBuiltinFunc_;
+ }
+ AsmJSSimdType simdCtorType() const {
+ MOZ_ASSERT(pod.which_ == SimdCtor);
+ return pod.u.simdCtorType_;
+ }
+ PropertyName* simdCtorName() const {
+ MOZ_ASSERT(pod.which_ == SimdCtor);
+ return name_;
+ }
+ PropertyName* simdOperationName() const {
+ MOZ_ASSERT(pod.which_ == SimdOperation);
+ return name_;
+ }
+ AsmJSSimdOperation simdOperation() const {
+ MOZ_ASSERT(pod.which_ == SimdOperation);
+ return pod.u.simdOp.which_;
+ }
+ AsmJSSimdType simdOperationType() const {
+ MOZ_ASSERT(pod.which_ == SimdOperation);
+ return pod.u.simdOp.type_;
+ }
+ PropertyName* constantName() const {
+ MOZ_ASSERT(pod.which_ == Constant);
+ return name_;
+ }
+ ConstantKind constantKind() const {
+ MOZ_ASSERT(pod.which_ == Constant);
+ return pod.u.constant.kind_;
+ }
+ double constantValue() const {
+ MOZ_ASSERT(pod.which_ == Constant);
+ return pod.u.constant.value_;
+ }
+
+ WASM_DECLARE_SERIALIZABLE(Global);
+ };
+
+ typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
+
+ class Import
+ {
+ uint32_t ffiIndex_;
+ public:
+ Import() = default;
+ explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
+ uint32_t ffiIndex() const { return ffiIndex_; }
+ };
+
+ typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
+
+ class Export
+ {
+ PropertyName* name_;
+ PropertyName* maybeFieldName_;
+ struct CacheablePod {
+ uint32_t wasmIndex_;
+ uint32_t startOffsetInModule_; // Store module-start-relative offsets
+ uint32_t endOffsetInModule_; // so preserved by serialization.
+ } pod;
+
+ public:
+ Export() {}
+ Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+ uint32_t startOffsetInModule, uint32_t endOffsetInModule)
+ : name_(name),
+ maybeFieldName_(maybeFieldName)
+ {
+ MOZ_ASSERT(name_->isTenured());
+ MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
+ pod.wasmIndex_ = wasmIndex;
+ pod.startOffsetInModule_ = startOffsetInModule;
+ pod.endOffsetInModule_ = endOffsetInModule;
+ }
+
+ void trace(JSTracer* trc) {
+ TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
+ if (maybeFieldName_)
+ TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
+ }
+
+ PropertyName* name() const {
+ return name_;
+ }
+ PropertyName* maybeFieldName() const {
+ return maybeFieldName_;
+ }
+ uint32_t startOffsetInModule() const {
+ return pod.startOffsetInModule_;
+ }
+ uint32_t endOffsetInModule() const {
+ return pod.endOffsetInModule_;
+ }
+ static const uint32_t ChangeHeap = UINT32_MAX;
+ bool isChangeHeap() const {
+ return pod.wasmIndex_ == ChangeHeap;
+ }
+ uint32_t wasmIndex() const {
+ MOZ_ASSERT(!isChangeHeap());
+ return pod.wasmIndex_;
+ }
+
+ WASM_DECLARE_SERIALIZABLE(Export)
+ };
+
+ typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
+
+ typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
+
+ private:
+ UniqueWasmModule wasm_;
+ wasm::UniqueStaticLinkData linkData_;
+ struct CacheablePod {
+ uint32_t minHeapLength_;
+ uint32_t maxHeapLength_;
+ uint32_t heapLengthMask_;
+ uint32_t numFFIs_;
+ uint32_t srcLength_;
+ uint32_t srcLengthWithRightBrace_;
+ bool strict_;
+ bool hasArrayView_;
+ bool isSharedView_;
+ bool hasFixedMinHeapLength_;
+ } pod;
+ const ScriptSourceHolder scriptSource_;
+ const uint32_t srcStart_;
+ const uint32_t srcBodyStart_;
+ GlobalVector globals_;
+ ImportVector imports_;
+ ExportVector exports_;
+ PropertyName* globalArgumentName_;
+ PropertyName* importArgumentName_;
+ PropertyName* bufferArgumentName_;
+
+ public:
+ explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
+ bool strict)
+ : scriptSource_(scriptSource),
+ srcStart_(srcStart),
+ srcBodyStart_(srcBodyStart),
+ globalArgumentName_(nullptr),
+ importArgumentName_(nullptr),
+ bufferArgumentName_(nullptr)
+ {
+ mozilla::PodZero(&pod);
+ pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
+ pod.maxHeapLength_ = 0x80000000;
+ pod.strict_ = strict;
+
+ // AsmJSCheckedImmediateRange should be defined to be at most the minimum
+ // heap length so that offsets can be folded into bounds checks.
+ MOZ_ASSERT(pod.minHeapLength_ - jit::AsmJSCheckedImmediateRange <= pod.minHeapLength_);
+ }
+
+ void trace(JSTracer* trc) {
+ if (wasm_)
+ wasm_->trace(trc);
+ for (Global& global : globals_)
+ global.trace(trc);
+ for (Export& exp : exports_)
+ exp.trace(trc);
+ if (globalArgumentName_)
+ TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
+ if (importArgumentName_)
+ TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
+ if (bufferArgumentName_)
+ TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
+ }
+
+ /*************************************************************************/
+ // These functions may be used as soon as the module is constructed:
+
+ ScriptSource* scriptSource() const {
+ return scriptSource_.get();
+ }
+ bool strict() const {
+ return pod.strict_;
+ }
+
+ // srcStart() refers to the offset in the ScriptSource to the beginning of
+ // the asm.js module function. If the function has been created with the
+ // Function constructor, this will be the first character in the function
+ // source. Otherwise, it will be the opening parenthesis of the arguments
+ // list.
+ uint32_t srcStart() const {
+ return srcStart_;
+ }
+
+ // srcBodyStart() refers to the offset in the ScriptSource to the end
+ // of the 'use asm' string-literal token.
+ uint32_t srcBodyStart() const {
+ return srcBodyStart_;
+ }
+
+ // While these functions may be accessed at any time, their values will
+ // change as the module is compiled.
+ uint32_t minHeapLength() const {
+ return pod.minHeapLength_;
+ }
+ uint32_t maxHeapLength() const {
+ return pod.maxHeapLength_;
+ }
+ uint32_t heapLengthMask() const {
+ MOZ_ASSERT(pod.hasFixedMinHeapLength_);
+ return pod.heapLengthMask_;
+ }
+
+ void initGlobalArgumentName(PropertyName* n) {
+ MOZ_ASSERT(!isFinished());
+ MOZ_ASSERT_IF(n, n->isTenured());
+ globalArgumentName_ = n;
+ }
+ void initImportArgumentName(PropertyName* n) {
+ MOZ_ASSERT(!isFinished());
+ MOZ_ASSERT_IF(n, n->isTenured());
+ importArgumentName_ = n;
+ }
+ void initBufferArgumentName(PropertyName* n) {
+ MOZ_ASSERT(!isFinished());
+ MOZ_ASSERT_IF(n, n->isTenured());
+ bufferArgumentName_ = n;
+ }
+ PropertyName* globalArgumentName() const {
+ return globalArgumentName_;
+ }
+ PropertyName* importArgumentName() const {
+ return importArgumentName_;
+ }
+ PropertyName* bufferArgumentName() const {
+ return bufferArgumentName_;
+ }
+
+ bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::Variable, nullptr);
+ g.pod.u.var.initKind_ = Global::InitConstant;
+ g.pod.u.var.u.val_ = v;
+ g.pod.u.var.globalDataOffset_ = globalDataOffset;
+ return globals_.append(g);
+ }
+ bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::Variable, name);
+ g.pod.u.var.initKind_ = Global::InitImport;
+ g.pod.u.var.u.importType_ = importType;
+ g.pod.u.var.globalDataOffset_ = globalDataOffset;
+ return globals_.append(g);
+ }
+ bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
+ MOZ_ASSERT(!isFinished());
+ if (pod.numFFIs_ == UINT32_MAX)
+ return false;
+ Global g(Global::FFI, field);
+ g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
+ return globals_.append(g);
+ }
+ bool addArrayView(Scalar::Type vt, PropertyName* maybeField) {
+ MOZ_ASSERT(!isFinished());
+ pod.hasArrayView_ = true;
+ pod.isSharedView_ = false;
+ Global g(Global::ArrayView, maybeField);
+ g.pod.u.viewType_ = vt;
+ return globals_.append(g);
+ }
+ bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
+ MOZ_ASSERT(!isFinished());
+ MOZ_ASSERT(field);
+ pod.isSharedView_ = false;
+ Global g(Global::ArrayViewCtor, field);
+ g.pod.u.viewType_ = vt;
+ return globals_.append(g);
+ }
+ bool addByteLength() {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::ByteLength, nullptr);
+ return globals_.append(g);
+ }
+ bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::MathBuiltinFunction, field);
+ g.pod.u.mathBuiltinFunc_ = func;
+ return globals_.append(g);
+ }
+ bool addMathBuiltinConstant(double value, PropertyName* field) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::Constant, field);
+ g.pod.u.constant.value_ = value;
+ g.pod.u.constant.kind_ = Global::MathConstant;
+ return globals_.append(g);
+ }
+ bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::AtomicsBuiltinFunction, field);
+ g.pod.u.atomicsBuiltinFunc_ = func;
+ return globals_.append(g);
+ }
+ bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::SimdCtor, field);
+ g.pod.u.simdCtorType_ = type;
+ return globals_.append(g);
+ }
+ bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::SimdOperation, field);
+ g.pod.u.simdOp.type_ = type;
+ g.pod.u.simdOp.which_ = op;
+ return globals_.append(g);
+ }
+ bool addGlobalConstant(double value, PropertyName* name) {
+ MOZ_ASSERT(!isFinished());
+ Global g(Global::Constant, name);
+ g.pod.u.constant.value_ = value;
+ g.pod.u.constant.kind_ = Global::GlobalConstant;
+ return globals_.append(g);
+ }
+ bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
+ MOZ_ASSERT(imports_.length() == importIndex);
+ return imports_.emplaceBack(ffiIndex);
+ }
+ bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
+ uint32_t funcSrcBegin, uint32_t funcSrcEnd)
+ {
+ // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
+ // (the entire file) and ExportedFunctions store offsets relative to
+ // the beginning of the module (so that they are caching-invariant).
+ MOZ_ASSERT(!isFinished());
+ MOZ_ASSERT(srcStart_ < funcSrcBegin);
+ MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
+ return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
+ funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
+ }
+ bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
+ MOZ_ASSERT(!isFinished());
+ MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
+ MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
+ MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
+ MOZ_ASSERT(max <= pod.maxHeapLength_);
+ MOZ_ASSERT(min <= max);
+ pod.heapLengthMask_ = mask;
+ pod.minHeapLength_ = min;
+ pod.maxHeapLength_ = max;
+ pod.hasFixedMinHeapLength_ = true;
+ return true;
+ }
+
+ const GlobalVector& globals() const {
+ return globals_;
+ }
+ const ImportVector& imports() const {
+ return imports_;
+ }
+ const ExportVector& exports() const {
+ return exports_;
+ }
+
+ void setViewsAreShared() {
+ if (pod.hasArrayView_)
+ pod.isSharedView_ = true;
+ }
+ bool hasArrayView() const {
+ return pod.hasArrayView_;
+ }
+ bool isSharedView() const {
+ return pod.isSharedView_;
+ }
+ bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
+ MOZ_ASSERT(!isFinished());
+ if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
+ return false;
+ if (len > pod.maxHeapLength_)
+ return false;
+ len = RoundUpToNextValidAsmJSHeapLength(len);
+ if (len > pod.minHeapLength_)
+ pod.minHeapLength_ = len;
+ return true;
+ }
+
+ /*************************************************************************/
+ // A module isFinished() when compilation completes. After being finished,
+ // a module must be statically and dynamically linked before execution.
+
+ bool isFinished() const {
+ return !!wasm_;
+ }
+ void finish(wasm::Module* wasm, wasm::UniqueStaticLinkData linkData,
+ uint32_t endBeforeCurly, uint32_t endAfterCurly)
+ {
+ MOZ_ASSERT(!isFinished());
+
+ wasm_.reset(wasm);
+ linkData_ = Move(linkData);
+
+ MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
+ MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
+ pod.srcLength_ = endBeforeCurly - srcStart_;
+ pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
+
+ MOZ_ASSERT(isFinished());
+ }
+
+ /*************************************************************************/
+ // These accessor functions can only be used after finish():
+
+ wasm::Module& wasm() const {
+ MOZ_ASSERT(isFinished());
+ return *wasm_;
+ }
+ uint32_t numFFIs() const {
+ MOZ_ASSERT(isFinished());
+ return pod.numFFIs_;
+ }
+ uint32_t srcEndBeforeCurly() const {
+ MOZ_ASSERT(isFinished());
+ return srcStart_ + pod.srcLength_;
+ }
+ uint32_t srcEndAfterCurly() const {
+ MOZ_ASSERT(isFinished());
+ return srcStart_ + pod.srcLengthWithRightBrace_;
+ }
+ bool staticallyLink(ExclusiveContext* cx) {
+ return wasm_->staticallyLink(cx, *linkData_);
+ }
+
+ // See WASM_DECLARE_SERIALIZABLE.
+ size_t serializedSize() const;
+ uint8_t* serialize(uint8_t* cursor) const;
+ const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
+ bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const;
+ void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
+};
+
+static void
+AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj)
+{
+ AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
+ if (moduleObj.hasModule())
+ fop->delete_(&moduleObj.module());
+}
+
+static void
+AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
+{
+ AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
+ if (moduleObj.hasModule())
+ moduleObj.module().trace(trc);
+}
+
+const Class AsmJSModuleObject::class_ = {
+ "AsmJSModuleObject",
+ JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
+ JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ AsmJSModuleObject_finalize,
+ nullptr, /* call */
+ nullptr, /* hasInstance */
+ nullptr, /* construct */
+ AsmJSModuleObject_trace
+};
+
+static AsmJSModuleObject*
+NewAsmJSModuleObject(ExclusiveContext* cx)
+{
+ AutoSetNewObjectMetadata metadata(cx);
+ JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr);
+ if (!obj)
+ return nullptr;
+
+ return &obj->as<AsmJSModuleObject>();
+}
+
+bool
+AsmJSModuleObject::hasModule() const
+{
+ MOZ_ASSERT(is<AsmJSModuleObject>());
+ return !getReservedSlot(MODULE_SLOT).isUndefined();
+}
+
+void
+AsmJSModuleObject::setModule(AsmJSModule* newModule)
+{
+ MOZ_ASSERT(is<AsmJSModuleObject>());
+ if (hasModule())
+ js_delete(&module());
+ setReservedSlot(MODULE_SLOT, PrivateValue(newModule));
+}
+
+AsmJSModule&
+AsmJSModuleObject::module() const
+{
+ MOZ_ASSERT(is<AsmJSModuleObject>());
+ return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate();
+}
+
+void
+AsmJSModuleObject::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+ module().addSizeOfMisc(mallocSizeOf, code, data);
+}
+
+/*****************************************************************************/
// ParseNode utilities
static inline ParseNode*
NextNode(ParseNode* pn)
{
return pn->pn_next;
}
@@ -532,28 +1212,28 @@ class NumLit
Float32x4,
OutOfRangeInt = -1
};
private:
Which which_;
union {
Value scalar_;
- jit::SimdConstant simd_;
+ SimdConstant simd_;
} u;
public:
NumLit() = default;
NumLit(Which w, Value v) : which_(w) {
u.scalar_ = v;
MOZ_ASSERT(!isSimd());
}
- NumLit(Which w, jit::SimdConstant c) : which_(w) {
+ NumLit(Which w, SimdConstant c) : which_(w) {
u.simd_ = c;
MOZ_ASSERT(isSimd());
}
Which which() const {
return which_;
}
@@ -580,17 +1260,17 @@ class NumLit
MOZ_ASSERT(which_ != OutOfRangeInt);
return u.scalar_;
}
bool isSimd() const {
return which_ == Int32x4 || which_ == Float32x4;
}
- const jit::SimdConstant& simdValue() const {
+ const SimdConstant& simdValue() const {
MOZ_ASSERT(isSimd());
return u.simd_;
}
bool valid() const {
return which_ != OutOfRangeInt;
}
@@ -819,38 +1499,38 @@ class Type
return ValType::F32;
else if (isDouble())
return ValType::F64;
else if (isInt32x4())
return ValType::I32x4;
return ValType::F32x4;
}
- jit::MIRType toMIRType() const {
+ MIRType toMIRType() const {
switch (which_) {
case Double:
case DoubleLit:
case MaybeDouble:
- return jit::MIRType_Double;
+ return MIRType_Double;
case Float:
case Floatish:
case MaybeFloat:
- return jit::MIRType_Float32;
+ return MIRType_Float32;
case Fixnum:
case Int:
case Signed:
case Unsigned:
case Intish:
- return jit::MIRType_Int32;
+ return MIRType_Int32;
case Int32x4:
- return jit::MIRType_Int32x4;
+ return MIRType_Int32x4;
case Float32x4:
- return jit::MIRType_Float32x4;
+ return MIRType_Float32x4;
case Void:
- return jit::MIRType_None;
+ return MIRType_None;
}
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid Type");
}
AsmJSSimdType simdType() const {
MOZ_ASSERT(isSimd());
switch (which_) {
case Int32x4:
@@ -3659,17 +4339,17 @@ CheckAtomicsStore(FunctionValidator& f,
f.patchU8(needsBoundsCheckAt, uint8_t(needsBoundsCheck));
f.patchU8(viewTypeAt, uint8_t(viewType));
*type = rhsType;
return true;
}
static bool
-CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, js::jit::AtomicOp op)
+CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op)
{
if (CallArgListLength(call) != 3)
return f.fail(call, "Atomics binary operator must be passed 3 arguments");
ParseNode* arrayArg = CallArgList(call);
ParseNode* indexArg = NextNode(arrayArg);
ParseNode* valueArg = NextNode(indexArg);
@@ -6803,16 +7483,1309 @@ CheckModule(ExclusiveContext* cx, AsmJSP
if (!m.finish(slowFuncs))
return false;
*time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
return true;
}
+/*****************************************************************************/
+// Runtime calls to asm.js module exports
+
+static AsmJSModuleObject&
+FunctionToModuleObject(JSFunction* fun)
+{
+ MOZ_ASSERT(IsAsmJSFunction(fun) || IsAsmJSModule(fun));
+ const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_MODULE_SLOT);
+ return v.toObject().as<AsmJSModuleObject>();
+}
+
+static unsigned
+FunctionToExportIndex(JSFunction* fun)
+{
+ MOZ_ASSERT(IsAsmJSFunction(fun));
+ const Value& v = fun->getExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT);
+ return v.toInt32();
+}
+
+static bool
+ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
+{
+ HandleValue bufferArg = args.get(0);
+ if (!IsArrayBuffer(bufferArg)) {
+ ReportIncompatible(cx, args);
+ return false;
+ }
+
+ Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
+ uint32_t heapLength = newBuffer->byteLength();
+ if (heapLength & module.heapLengthMask() ||
+ heapLength < module.minHeapLength() ||
+ heapLength > module.maxHeapLength())
+ {
+ args.rval().set(BooleanValue(false));
+ return true;
+ }
+
+ if (!module.hasArrayView()) {
+ args.rval().set(BooleanValue(true));
+ return true;
+ }
+
+ MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
+
+ bool useSignalHandlers = module.wasm().compileArgs().useSignalHandlersForOOB;
+ if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
+ return false;
+
+ args.rval().set(BooleanValue(module.wasm().changeHeap(newBuffer, cx)));
+ return true;
+}
+
+static bool
+CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedFunction callee(cx, &args.callee().as<JSFunction>());
+
+ AsmJSModule& module = FunctionToModuleObject(callee).module();
+ const AsmJSModule::Export& exp = module.exports()[FunctionToExportIndex(callee)];
+
+ // The heap-changing function is a special-case and is implemented by C++.
+ if (exp.isChangeHeap())
+ return ChangeHeap(cx, module, args);
+
+ return module.wasm().callExport(cx, exp.wasmIndex(), args);
+}
+
+/*****************************************************************************/
+// Link-time validation
+
+static bool
+LinkFail(JSContext* cx, const char* str)
+{
+ JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
+ nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
+ return false;
+}
+
+static bool
+GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
+{
+ if (!objVal.isObject())
+ return LinkFail(cx, "accessing property of non-object");
+
+ RootedObject obj(cx, &objVal.toObject());
+ if (IsScriptedProxy(obj))
+ return LinkFail(cx, "accessing property of a Proxy");
+
+ Rooted<PropertyDescriptor> desc(cx);
+ RootedId id(cx, NameToId(field));
+ if (!GetPropertyDescriptor(cx, obj, id, &desc))
+ return false;
+
+ if (!desc.object())
+ return LinkFail(cx, "property not present on object");
+
+ if (!desc.isDataDescriptor())
+ return LinkFail(cx, "property is not a data property");
+
+ v.set(desc.value());
+ return true;
+}
+
+static bool
+HasPureCoercion(JSContext* cx, HandleValue v)
+{
+ if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v))
+ return true;
+
+ // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
+ // bug that generates code that passes functions for some imports. To avoid
+ // breaking all the code that contains this bug, we make an exception for
+ // functions that don't have user-defined valueOf or toString, for their
+ // coercions are not observable and coercion via ToNumber/ToInt32
+ // definitely produces NaN/0. We should remove this special case later once
+ // most apps have been built with newer Emscripten.
+ jsid toString = NameToId(cx->names().toString);
+ if (v.toObject().is<JSFunction>() &&
+ HasObjectValueOf(&v.toObject(), cx) &&
+ ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData,
+ HandleValue importVal)
+{
+ void* datum = globalData + global.varGlobalDataOffset();
+
+ switch (global.varInitKind()) {
+ case AsmJSModule::Global::InitConstant: {
+ Val v = global.varInitVal();
+ switch (v.type()) {
+ case ValType::I32:
+ *(int32_t*)datum = v.i32();
+ break;
+ case ValType::I64:
+ MOZ_CRASH("int64");
+ case ValType::F32:
+ *(float*)datum = v.f32();
+ break;
+ case ValType::F64:
+ *(double*)datum = v.f64();
+ break;
+ case ValType::I32x4:
+ memcpy(datum, v.i32x4(), Simd128DataSize);
+ break;
+ case ValType::F32x4:
+ memcpy(datum, v.f32x4(), Simd128DataSize);
+ break;
+ }
+ break;
+ }
+
+ case AsmJSModule::Global::InitImport: {
+ RootedPropertyName field(cx, global.varImportField());
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, importVal, field, &v))
+ return false;
+
+ if (!v.isPrimitive() && !HasPureCoercion(cx, v))
+ return LinkFail(cx, "Imported values must be primitives");
+
+ switch (global.varInitImportType()) {
+ case ValType::I32:
+ if (!ToInt32(cx, v, (int32_t*)datum))
+ return false;
+ break;
+ case ValType::I64:
+ MOZ_CRASH("int64");
+ case ValType::F32:
+ if (!RoundFloat32(cx, v, (float*)datum))
+ return false;
+ break;
+ case ValType::F64:
+ if (!ToNumber(cx, v, (double*)datum))
+ return false;
+ break;
+ case ValType::I32x4: {
+ SimdConstant simdConstant;
+ if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
+ return false;
+ memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
+ break;
+ }
+ case ValType::F32x4: {
+ SimdConstant simdConstant;
+ if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
+ return false;
+ memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool
+ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal,
+ AutoVectorRooter<JSFunction*>* ffis)
+{
+ RootedPropertyName field(cx, global.ffiField());
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, importVal, field, &v))
+ return false;
+
+ if (!v.isObject() || !v.toObject().is<JSFunction>())
+ return LinkFail(cx, "FFI imports must be functions");
+
+ (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
+ return true;
+}
+
+static bool
+ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+ RootedPropertyName field(cx, global.maybeViewName());
+ if (!field)
+ return true;
+
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, globalVal, field, &v))
+ return false;
+
+ bool tac = IsTypedArrayConstructor(v, global.viewType());
+ if (!tac)
+ return LinkFail(cx, "bad typed array constructor");
+
+ return true;
+}
+
+static bool
+ValidateByteLength(JSContext* cx, HandleValue globalVal)
+{
+ RootedPropertyName field(cx, cx->names().byteLength);
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, globalVal, field, &v))
+ return false;
+
+ if (!v.isObject() || !v.toObject().isBoundFunction())
+ return LinkFail(cx, "byteLength must be a bound function object");
+
+ RootedFunction fun(cx, &v.toObject().as<JSFunction>());
+
+ RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
+ if (!IsNativeFunction(boundTarget, fun_call))
+ return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
+
+ RootedValue boundThis(cx, fun->getBoundFunctionThis());
+ if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
+ return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
+
+ return true;
+}
+
+static bool
+ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
+ return false;
+
+ RootedPropertyName field(cx, global.mathName());
+ if (!GetDataProperty(cx, v, field, &v))
+ return false;
+
+ Native native = nullptr;
+ switch (global.mathBuiltinFunction()) {
+ case AsmJSMathBuiltin_sin: native = math_sin; break;
+ case AsmJSMathBuiltin_cos: native = math_cos; break;
+ case AsmJSMathBuiltin_tan: native = math_tan; break;
+ case AsmJSMathBuiltin_asin: native = math_asin; break;
+ case AsmJSMathBuiltin_acos: native = math_acos; break;
+ case AsmJSMathBuiltin_atan: native = math_atan; break;
+ case AsmJSMathBuiltin_ceil: native = math_ceil; break;
+ case AsmJSMathBuiltin_floor: native = math_floor; break;
+ case AsmJSMathBuiltin_exp: native = math_exp; break;
+ case AsmJSMathBuiltin_log: native = math_log; break;
+ case AsmJSMathBuiltin_pow: native = math_pow; break;
+ case AsmJSMathBuiltin_sqrt: native = math_sqrt; break;
+ case AsmJSMathBuiltin_min: native = math_min; break;
+ case AsmJSMathBuiltin_max: native = math_max; break;
+ case AsmJSMathBuiltin_abs: native = math_abs; break;
+ case AsmJSMathBuiltin_atan2: native = math_atan2; break;
+ case AsmJSMathBuiltin_imul: native = math_imul; break;
+ case AsmJSMathBuiltin_clz32: native = math_clz32; break;
+ case AsmJSMathBuiltin_fround: native = math_fround; break;
+ }
+
+ if (!IsNativeFunction(v, native))
+ return LinkFail(cx, "bad Math.* builtin function");
+
+ return true;
+}
+
+static PropertyName*
+SimdTypeToName(JSContext* cx, AsmJSSimdType type)
+{
+ switch (type) {
+ case AsmJSSimdType_int32x4: return cx->names().int32x4;
+ case AsmJSSimdType_float32x4: return cx->names().float32x4;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
+}
+
+static SimdTypeDescr::Type
+AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
+{
+ switch (type) {
+ case AsmJSSimdType_int32x4: return Int32x4::type;
+ case AsmJSSimdType_float32x4: return Float32x4::type;
+ }
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
+}
+
+static bool
+ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal,
+ MutableHandleValue out)
+{
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
+ return false;
+
+ AsmJSSimdType type;
+ if (global.which() == AsmJSModule::Global::SimdCtor)
+ type = global.simdCtorType();
+ else
+ type = global.simdOperationType();
+
+ RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
+ if (!GetDataProperty(cx, v, simdTypeName, &v))
+ return false;
+
+ if (!v.isObject())
+ return LinkFail(cx, "bad SIMD type");
+
+ RootedObject simdDesc(cx, &v.toObject());
+ if (!simdDesc->is<SimdTypeDescr>())
+ return LinkFail(cx, "bad SIMD type");
+
+ if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type())
+ return LinkFail(cx, "bad SIMD type");
+
+ out.set(v);
+ return true;
+}
+
+static bool
+ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+ RootedValue _(cx);
+ return ValidateSimdType(cx, global, globalVal, &_);
+}
+
+static bool
+ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+ // SIMD operations are loaded from the SIMD type, so the type must have been
+ // validated before the operation.
+ RootedValue v(cx);
+ JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
+
+ RootedPropertyName opName(cx, global.simdOperationName());
+ if (!GetDataProperty(cx, v, opName, &v))
+ return false;
+
+ Native native = nullptr;
+ switch (global.simdOperationType()) {
+#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break;
+#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break;
+#define FALLTHROUGH(op) case AsmJSSimdOperation_##op:
+ case AsmJSSimdType_int32x4:
+ switch (global.simdOperation()) {
+ FOREACH_INT32X4_SIMD_OP(SET_NATIVE_INT32X4)
+ FOREACH_COMMONX4_SIMD_OP(SET_NATIVE_INT32X4)
+ FOREACH_FLOAT32X4_SIMD_OP(FALLTHROUGH)
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+ "place");
+ }
+ break;
+ case AsmJSSimdType_float32x4:
+ switch (global.simdOperation()) {
+ FOREACH_FLOAT32X4_SIMD_OP(SET_NATIVE_FLOAT32X4)
+ FOREACH_COMMONX4_SIMD_OP(SET_NATIVE_FLOAT32X4)
+ FOREACH_INT32X4_SIMD_OP(FALLTHROUGH)
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+ "place");
+ }
+ break;
+#undef FALLTHROUGH
+#undef SET_NATIVE_FLOAT32X4
+#undef SET_NATIVE_INT32X4
+#undef SET_NATIVE
+ }
+ if (!native || !IsNativeFunction(v, native))
+ return LinkFail(cx, "bad SIMD.type.* operation");
+ return true;
+}
+
+static bool
+ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+ RootedValue v(cx);
+ if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v))
+ return false;
+ RootedPropertyName field(cx, global.atomicsName());
+ if (!GetDataProperty(cx, v, field, &v))
+ return false;
+
+ Native native = nullptr;
+ switch (global.atomicsBuiltinFunction()) {
+ case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break;
+ case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break;
+ case AsmJSAtomicsBuiltin_load: native = atomics_load; break;
+ case AsmJSAtomicsBuiltin_store: native = atomics_store; break;
+ case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break;
+ case AsmJSAtomicsBuiltin_add: native = atomics_add; break;
+ case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break;
+ case AsmJSAtomicsBuiltin_and: native = atomics_and; break;
+ case AsmJSAtomicsBuiltin_or: native = atomics_or; break;
+ case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break;
+ case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break;
+ }
+
+ if (!IsNativeFunction(v, native))
+ return LinkFail(cx, "bad Atomics.* builtin function");
+
+ return true;
+}
+
+static bool
+ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
+{
+ RootedPropertyName field(cx, global.constantName());
+ RootedValue v(cx, globalVal);
+
+ if (global.constantKind() == AsmJSModule::Global::MathConstant) {
+ if (!GetDataProperty(cx, v, cx->names().Math, &v))
+ return false;
+ }
+
+ if (!GetDataProperty(cx, v, field, &v))
+ return false;
+
+ if (!v.isNumber())
+ return LinkFail(cx, "math / global constant value needs to be a number");
+
+ // NaN != NaN
+ if (IsNaN(global.constantValue())) {
+ if (!IsNaN(v.toNumber()))
+ return LinkFail(cx, "global constant value needs to be NaN");
+ } else {
+ if (v.toNumber() != global.constantValue())
+ return LinkFail(cx, "global constant value mismatch");
+ }
+
+ return true;
+}
+
+static bool
+CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal,
+ MutableHandle<ArrayBufferObjectMaybeShared*> buffer)
+{
+ if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal))
+ return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
+
+ if (!module.isSharedView() && !IsArrayBuffer(bufferVal))
+ return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
+
+ buffer.set(&AsAnyArrayBuffer(bufferVal));
+ uint32_t heapLength = buffer->byteLength();
+
+ if (!IsValidAsmJSHeapLength(heapLength)) {
+ UniqueChars msg(
+ JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
+ "valid length is 0x%x",
+ heapLength,
+ RoundUpToNextValidAsmJSHeapLength(heapLength)));
+ return LinkFail(cx, msg.get());
+ }
+
+ // This check is sufficient without considering the size of the loaded datum because heap
+ // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
+ MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
+ if (heapLength < module.minHeapLength()) {
+ UniqueChars msg(
+ JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
+ "by const heap accesses and/or change-heap minimum-length requirements).",
+ heapLength,
+ module.minHeapLength()));
+ return LinkFail(cx, msg.get());
+ }
+
+ if (heapLength > module.maxHeapLength()) {
+ UniqueChars msg(
+ JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
+ heapLength,
+ module.maxHeapLength()));
+ return LinkFail(cx, msg.get());
+ }
+
+ // Shell builtins may have disabled signal handlers since the module we're
+ // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers
+ // as well for the caching case.
+ if (module.wasm().compileArgs() != CompileArgs(cx))
+ return LinkFail(cx, "Signals have been toggled since compilation");
+
+ if (buffer->is<ArrayBufferObject>()) {
+ Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
+ bool useSignalHandlers = module.wasm().compileArgs().useSignalHandlersForOOB;
+ if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
+ return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
+ }
+
+ return true;
+}
+
+static bool
+DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
+{
+ HandleValue globalVal = args.get(0);
+ HandleValue importVal = args.get(1);
+ HandleValue bufferVal = args.get(2);
+
+ Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
+ if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer))
+ return false;
+
+ AutoVectorRooter<JSFunction*> ffis(cx);
+ if (!ffis.resize(module.numFFIs()))
+ return false;
+
+ for (const AsmJSModule::Global& global : module.globals()) {
+ switch (global.which()) {
+ case AsmJSModule::Global::Variable:
+ if (!ValidateGlobalVariable(cx, global, module.wasm().globalData(), importVal))
+ return false;
+ break;
+ case AsmJSModule::Global::FFI:
+ if (!ValidateFFI(cx, global, importVal, &ffis))
+ return false;
+ break;
+ case AsmJSModule::Global::ArrayView:
+ case AsmJSModule::Global::ArrayViewCtor:
+ if (!ValidateArrayView(cx, global, globalVal))
+ return false;
+ break;
+ case AsmJSModule::Global::ByteLength:
+ if (!ValidateByteLength(cx, globalVal))
+ return false;
+ break;
+ case AsmJSModule::Global::MathBuiltinFunction:
+ if (!ValidateMathBuiltinFunction(cx, global, globalVal))
+ return false;
+ break;
+ case AsmJSModule::Global::AtomicsBuiltinFunction:
+ if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
+ return false;
+ break;
+ case AsmJSModule::Global::Constant:
+ if (!ValidateConstant(cx, global, globalVal))
+ return false;
+ break;
+ case AsmJSModule::Global::SimdCtor:
+ if (!ValidateSimdType(cx, global, globalVal))
+ return false;
+ break;
+ case AsmJSModule::Global::SimdOperation:
+ if (!ValidateSimdOperation(cx, global, globalVal))
+ return false;
+ break;
+ }
+ }
+
+ AutoVectorRooter<JSFunction*> imports(cx);
+ for (const AsmJSModule::Import& import : module.imports()) {
+ if (!imports.append(ffis[import.ffiIndex()]))
+ return false;
+ }
+
+ return module.wasm().dynamicallyLink(cx, buffer, imports);
+}
+
+static JSFunction*
+NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
+ HandleObject moduleObj, unsigned exportIndex)
+{
+ unsigned numArgs = func.isChangeHeap()
+ ? 1
+ : module.wasm().exports()[func.wasmIndex()].sig().args().length();
+
+ RootedPropertyName name(cx, func.name());
+ JSFunction* fun =
+ NewNativeConstructor(cx, CallAsmJS, numArgs, name,
+ gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
+ JSFunction::ASMJS_CTOR);
+ if (!fun)
+ return nullptr;
+
+ fun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
+ fun->setExtendedSlot(FunctionExtended::ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
+ return fun;
+}
+
+static bool
+HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
+ HandlePropertyName name)
+{
+ if (cx->isExceptionPending())
+ return false;
+
+ ScriptSource* source = module.scriptSource();
+
+ // Source discarding is allowed to affect JS semantics because it is never
+ // enabled for normal JS content.
+ bool haveSource = source->hasSourceData();
+ if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
+ return false;
+ if (!haveSource) {
+ JS_ReportError(cx, "asm.js link failure with source discarding enabled");
+ return false;
+ }
+
+ uint32_t begin = module.srcBodyStart(); // starts right after 'use asm'
+ uint32_t end = module.srcEndBeforeCurly();
+ Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
+ if (!src)
+ return false;
+
+ RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
+ name, gc::AllocKind::FUNCTION,
+ TenuredObject));
+ if (!fun)
+ return false;
+
+ Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
+ if (!formals.reserve(3))
+ return false;
+
+ if (module.globalArgumentName())
+ formals.infallibleAppend(module.globalArgumentName());
+ if (module.importArgumentName())
+ formals.infallibleAppend(module.importArgumentName());
+ if (module.bufferArgumentName())
+ formals.infallibleAppend(module.bufferArgumentName());
+
+ CompileOptions options(cx);
+ options.setMutedErrors(source->mutedErrors())
+ .setFile(source->filename())
+ .setNoScriptRval(false);
+
+ // The exported function inherits an implicit strict context if the module
+ // also inherited it somehow.
+ if (module.strict())
+ options.strictOption = true;
+
+ AutoStableStringChars stableChars(cx);
+ if (!stableChars.initTwoByte(cx, src))
+ return false;
+
+ const char16_t* chars = stableChars.twoByteRange().start().get();
+ SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
+ ? SourceBufferHolder::GiveOwnership
+ : SourceBufferHolder::NoOwnership;
+ SourceBufferHolder srcBuf(chars, end - begin, ownership);
+ if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
+ return false;
+
+ // Call the function we just recompiled.
+ args.setCallee(ObjectValue(*fun));
+ return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
+}
+
+static JSObject*
+CreateExportObject(JSContext* cx, HandleAsmJSModule moduleObj)
+{
+ AsmJSModule& module = moduleObj->module();
+ const AsmJSModule::ExportVector& exports = module.exports();
+
+ if (exports.length() == 1) {
+ const AsmJSModule::Export& func = exports[0];
+ if (!func.maybeFieldName())
+ return NewExportedFunction(cx, module, func, moduleObj, 0);
+ }
+
+ gc::AllocKind allocKind = gc::GetGCObjectKind(exports.length());
+ RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
+ if (!obj)
+ return nullptr;
+
+ for (unsigned i = 0; i < exports.length(); i++) {
+ const AsmJSModule::Export& func = exports[i];
+
+ RootedFunction fun(cx, NewExportedFunction(cx, module, func, moduleObj, i));
+ if (!fun)
+ return nullptr;
+
+ MOZ_ASSERT(func.maybeFieldName() != nullptr);
+ RootedId id(cx, NameToId(func.maybeFieldName()));
+ RootedValue val(cx, ObjectValue(*fun));
+ if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
+ return nullptr;
+ }
+
+ return obj;
+}
+
+// Implements the semantics of an asm.js module function that has been successfully validated.
+static bool
+LinkAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
+ // function and stores its module in an extended slot.
+ RootedFunction fun(cx, &args.callee().as<JSFunction>());
+ Rooted<AsmJSModuleObject*> moduleObj(cx, &FunctionToModuleObject(fun));
+
+ // When a module is linked, it is dynamically specialized to the given
+ // arguments (buffer, ffis). Thus, if the module is linked again (it is just
+ // a function so it can be called multiple times), we need to clone a new
+ // module.
+ if (moduleObj->module().wasm().dynamicallyLinked()) {
+ Rooted<AsmJSModuleObject*> clone(cx, NewAsmJSModuleObject(cx));
+ if (!clone)
+ return false;
+
+ if (!moduleObj->module().clone(cx, clone))
+ return false;
+
+ moduleObj = clone;
+ }
+
+ AsmJSModule& module = moduleObj->module();
+
+ // Link the module by performing the link-time validation checks in the
+ // asm.js spec and then patching the generated module to associate it with
+ // the given heap (ArrayBuffer) and a new global data segment (the closure
+ // state shared by the inner asm.js functions).
+ if (!DynamicallyLinkModule(cx, args, module)) {
+ // Linking failed, so reparse the entire asm.js module from scratch to
+ // get normal interpreted bytecode which we can simply Invoke. Very slow.
+ RootedPropertyName name(cx, fun->name());
+ return HandleDynamicLinkFailure(cx, args, module, name);
+ }
+
+ // Link-time validation succeeded, so wrap all the exported functions with
+ // CallAsmJS builtins that trampoline into the generated code.
+ JSObject* obj = CreateExportObject(cx, moduleObj);
+ if (!obj)
+ return false;
+
+ args.rval().set(ObjectValue(*obj));
+ return true;
+}
+
+static JSFunction*
+NewModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject moduleObj)
+{
+ RootedPropertyName name(cx, origFun->name());
+
+ JSFunction::Flags flags = origFun->isLambda() ? JSFunction::ASMJS_LAMBDA_CTOR
+ : JSFunction::ASMJS_CTOR;
+ JSFunction* moduleFun =
+ NewNativeConstructor(cx, LinkAsmJS, origFun->nargs(), name,
+ gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
+ flags);
+ if (!moduleFun)
+ return nullptr;
+
+ moduleFun->setExtendedSlot(FunctionExtended::ASM_MODULE_SLOT, ObjectValue(*moduleObj));
+ return moduleFun;
+}
+
+/*****************************************************************************/
+// Caching and cloning
+
+uint8_t*
+AsmJSModule::Global::serialize(uint8_t* cursor) const
+{
+ cursor = WriteBytes(cursor, &pod, sizeof(pod));
+ cursor = SerializeName(cursor, name_);
+ return cursor;
+}
+
+size_t
+AsmJSModule::Global::serializedSize() const
+{
+ return sizeof(pod) +
+ SerializedNameSize(name_);
+}
+
+const uint8_t*
+AsmJSModule::Global::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+ (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
+ (cursor = DeserializeName(cx, cursor, &name_));
+ return cursor;
+}
+
+bool
+AsmJSModule::Global::clone(JSContext* cx, Global* out) const
+{
+ *out = *this;
+ return true;
+}
+
+uint8_t*
+AsmJSModule::Export::serialize(uint8_t* cursor) const
+{
+ cursor = SerializeName(cursor, name_);
+ cursor = SerializeName(cursor, maybeFieldName_);
+ cursor = WriteBytes(cursor, &pod, sizeof(pod));
+ return cursor;
+}
+
+size_t
+AsmJSModule::Export::serializedSize() const
+{
+ return SerializedNameSize(name_) +
+ SerializedNameSize(maybeFieldName_) +
+ sizeof(pod);
+}
+
+const uint8_t*
+AsmJSModule::Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+ (cursor = DeserializeName(cx, cursor, &name_)) &&
+ (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
+ (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
+ return cursor;
+}
+
+bool
+AsmJSModule::Export::clone(JSContext* cx, Export* out) const
+{
+ out->name_ = name_;
+ out->maybeFieldName_ = maybeFieldName_;
+ out->pod = pod;
+ return true;
+}
+
+size_t
+AsmJSModule::serializedSize() const
+{
+ MOZ_ASSERT(isFinished());
+ return wasm_->serializedSize() +
+ linkData_->serializedSize() +
+ sizeof(pod) +
+ SerializedVectorSize(globals_) +
+ SerializedPodVectorSize(imports_) +
+ SerializedVectorSize(exports_) +
+ SerializedNameSize(globalArgumentName_) +
+ SerializedNameSize(importArgumentName_) +
+ SerializedNameSize(bufferArgumentName_);
+}
+
+uint8_t*
+AsmJSModule::serialize(uint8_t* cursor) const
+{
+ MOZ_ASSERT(isFinished());
+ cursor = wasm_->serialize(cursor);
+ cursor = linkData_->serialize(cursor);
+ cursor = WriteBytes(cursor, &pod, sizeof(pod));
+ cursor = SerializeVector(cursor, globals_);
+ cursor = SerializePodVector(cursor, imports_);
+ cursor = SerializeVector(cursor, exports_);
+ cursor = SerializeName(cursor, globalArgumentName_);
+ cursor = SerializeName(cursor, importArgumentName_);
+ cursor = SerializeName(cursor, bufferArgumentName_);
+ return cursor;
+}
+
+const uint8_t*
+AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
+{
+ linkData_ = cx->make_unique<StaticLinkData>();
+ if (!linkData_)
+ return nullptr;
+
+ // To avoid GC-during-deserialization corner cases, prevent atoms from
+ // being collected.
+ AutoKeepAtoms aka(cx->perThreadData);
+
+ (cursor = Module::deserialize(cx, cursor, &wasm_)) &&
+ (cursor = linkData_->deserialize(cx, cursor)) &&
+ (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
+ (cursor = DeserializeVector(cx, cursor, &globals_)) &&
+ (cursor = DeserializePodVector(cx, cursor, &imports_)) &&
+ (cursor = DeserializeVector(cx, cursor, &exports_)) &&
+ (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
+ (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
+ (cursor = DeserializeName(cx, cursor, &bufferArgumentName_));
+
+ return cursor;
+}
+
+bool
+AsmJSModule::clone(JSContext* cx, HandleAsmJSModule obj) const
+{
+ auto out = cx->new_<AsmJSModule>(scriptSource(), srcStart_, srcBodyStart_, pod.strict_);
+ if (!out)
+ return false;
+
+ obj->setModule(out);
+
+ out->wasm_ = wasm_->clone(cx, *linkData_);
+ if (!out->wasm_)
+ return false;
+
+ out->linkData_ = cx->make_unique<StaticLinkData>();
+ if (!out->linkData_ || !linkData_->clone(cx, out->linkData_.get()))
+ return false;
+
+ out->pod = pod;
+
+ if (!CloneVector(cx, globals_, &out->globals_) ||
+ !ClonePodVector(cx, imports_, &out->imports_) ||
+ !CloneVector(cx, exports_, &out->exports_))
+ {
+ return false;
+ }
+
+ out->globalArgumentName_ = globalArgumentName_;
+ out->importArgumentName_ = importArgumentName_;
+ out->bufferArgumentName_ = bufferArgumentName_;
+ return true;
+}
+
+void
+AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data)
+{
+ if (wasm_)
+ wasm_->addSizeOfMisc(mallocSizeOf, code, data);
+
+ if (linkData_)
+ *data += linkData_->sizeOfExcludingThis(mallocSizeOf);
+
+ *data += mallocSizeOf(this) +
+ globals_.sizeOfExcludingThis(mallocSizeOf) +
+ imports_.sizeOfExcludingThis(mallocSizeOf) +
+ exports_.sizeOfExcludingThis(mallocSizeOf);
+}
+
+namespace {
+
+struct PropertyNameWrapper
+{
+ PropertyName* name;
+
+ PropertyNameWrapper()
+ : name(nullptr)
+ {}
+ explicit PropertyNameWrapper(PropertyName* name)
+ : name(name)
+ {}
+ size_t serializedSize() const {
+ return SerializedNameSize(name);
+ }
+ uint8_t* serialize(uint8_t* cursor) const {
+ return SerializeName(cursor, name);
+ }
+ const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
+ return DeserializeName(cx, cursor, &name);
+ }
+};
+
+class ModuleChars
+{
+ protected:
+ uint32_t isFunCtor_;
+ Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
+
+ public:
+ static uint32_t beginOffset(AsmJSParser& parser) {
+ return parser.pc->maybeFunction->pn_pos.begin;
+ }
+
+ static uint32_t endOffset(AsmJSParser& parser) {
+ TokenPos pos(0, 0); // initialize to silence GCC warning
+ MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos, TokenStream::Operand));
+ return pos.end;
+ }
+};
+
+class ModuleCharsForStore : ModuleChars
+{
+ uint32_t uncompressedSize_;
+ uint32_t compressedSize_;
+ Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
+
+ public:
+ bool init(AsmJSParser& parser) {
+ MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
+
+ uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
+ size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
+ if (maxCompressedSize < uncompressedSize_)
+ return false;
+
+ if (!compressedBuffer_.resize(maxCompressedSize))
+ return false;
+
+ const char16_t* chars = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
+ const char* source = reinterpret_cast<const char*>(chars);
+ size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
+ if (!compressedSize || compressedSize > UINT32_MAX)
+ return false;
+
+ compressedSize_ = compressedSize;
+
+ // For a function statement or named function expression:
+ // function f(x,y,z) { abc }
+ // the range [beginOffset, endOffset) captures the source:
+ // f(x,y,z) { abc }
+ // An unnamed function expression captures the same thing, sans 'f'.
+ // Since asm.js modules do not contain any free variables, equality of
+ // [beginOffset, endOffset) is sufficient to guarantee identical code
+ // generation, modulo MachineId.
+ //
+ // For functions created with 'new Function', function arguments are
+ // not present in the source so we must manually explicitly serialize
+ // and match the formals as a Vector of PropertyName.
+ isFunCtor_ = parser.pc->isFunctionConstructorBody();
+ if (isFunCtor_) {
+ unsigned numArgs;
+ ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
+ for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
+ if (!funCtorArgs_.append(arg->name()))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ size_t serializedSize() const {
+ return sizeof(uint32_t) +
+ sizeof(uint32_t) +
+ compressedSize_ +
+ sizeof(uint32_t) +
+ (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
+ }
+
+ uint8_t* serialize(uint8_t* cursor) const {
+ cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
+ cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
+ cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
+ cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
+ if (isFunCtor_)
+ cursor = SerializeVector(cursor, funCtorArgs_);
+ return cursor;
+ }
+};
+
+class ModuleCharsForLookup : ModuleChars
+{
+ Vector<char16_t, 0, SystemAllocPolicy> chars_;
+
+ public:
+ const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
+ uint32_t uncompressedSize;
+ cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
+
+ uint32_t compressedSize;
+ cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
+
+ if (!chars_.resize(uncompressedSize / sizeof(char16_t)))
+ return nullptr;
+
+ const char* source = reinterpret_cast<const char*>(cursor);
+ char* dest = reinterpret_cast<char*>(chars_.begin());
+ if (!LZ4::decompress(source, dest, uncompressedSize))
+ return nullptr;
+
+ cursor += compressedSize;
+
+ cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
+ if (isFunCtor_)
+ cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
+
+ return cursor;
+ }
+
+ bool match(AsmJSParser& parser) const {
+ const char16_t* parseBegin = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
+ const char16_t* parseLimit = parser.tokenStream.rawLimit();
+ MOZ_ASSERT(parseLimit >= parseBegin);
+ if (uint32_t(parseLimit - parseBegin) < chars_.length())
+ return false;
+ if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
+ return false;
+ if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
+ return false;
+ if (isFunCtor_) {
+ // For function statements, the closing } is included as the last
+ // character of the matched source. For Function constructor,
+ // parsing terminates with EOF which we must explicitly check. This
+ // prevents
+ // new Function('"use asm"; function f() {} return f')
+ // from incorrectly matching
+ // new Function('"use asm"; function f() {} return ff')
+ if (parseBegin + chars_.length() != parseLimit)
+ return false;
+ unsigned numArgs;
+ ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
+ if (funCtorArgs_.length() != numArgs)
+ return false;
+ for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
+ if (funCtorArgs_[i].name != arg->name())
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+} // unnamed namespace
+
+static JS::AsmJSCacheResult
+StoreAsmJSModuleInCache(AsmJSParser& parser, const AsmJSModule& module, ExclusiveContext* cx)
+{
+ MachineId machineId;
+ if (!machineId.extractCurrentState(cx))
+ return JS::AsmJSCache_InternalError;
+
+ ModuleCharsForStore moduleChars;
+ if (!moduleChars.init(parser))
+ return JS::AsmJSCache_InternalError;
+
+ size_t serializedSize = machineId.serializedSize() +
+ moduleChars.serializedSize() +
+ module.serializedSize();
+
+ JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
+ if (!open)
+ return JS::AsmJSCache_Disabled_Internal;
+
+ const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
+ const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
+ bool installed = parser.options().installedFile;
+
+ ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
+ JS::AsmJSCacheResult openResult =
+ open(cx->global(), installed, begin, end, serializedSize, &entry.memory, &entry.handle);
+ if (openResult != JS::AsmJSCache_Success)
+ return openResult;
+
+ uint8_t* cursor = entry.memory;
+ cursor = machineId.serialize(cursor);
+ cursor = moduleChars.serialize(cursor);
+ cursor = module.serialize(cursor);
+
+ MOZ_ASSERT(cursor == entry.memory + serializedSize);
+ return JS::AsmJSCache_Success;
+}
+
+static bool
+LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
+ bool* loadedFromCache, UniqueChars* compilationTimeReport)
+{
+ int64_t usecBefore = PRMJ_Now();
+
+ *loadedFromCache = false;
+
+ MachineId machineId;
+ if (!machineId.extractCurrentState(cx))
+ return true;
+
+ JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
+ if (!open)
+ return true;
+
+ const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
+ const char16_t* limit = parser.tokenStream.rawLimit();
+
+ ScopedCacheEntryOpenedForRead entry(cx);
+ if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
+ return true;
+
+ const uint8_t* cursor = entry.memory;
+
+ MachineId cachedMachineId;
+ cursor = cachedMachineId.deserialize(cx, cursor);
+ if (!cursor)
+ return false;
+ if (machineId != cachedMachineId)
+ return true;
+
+ ModuleCharsForLookup moduleChars;
+ cursor = moduleChars.deserialize(cx, cursor);
+ if (!moduleChars.match(parser))
+ return true;
+
+ uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
+ uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
+ bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
+
+ AsmJSModule* module = cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict);
+ if (!module)
+ return false;
+
+ moduleObj->setModule(module);
+
+ cursor = module->deserialize(cx, cursor);
+ if (!cursor)
+ return false;
+
+ bool atEnd = cursor == entry.memory + entry.serializedSize;
+ MOZ_ASSERT(atEnd, "Corrupt cache file");
+ if (!atEnd)
+ return true;
+
+ if (module->wasm().compileArgs() != CompileArgs(cx))
+ return true;
+
+ module->staticallyLink(cx);
+
+ if (!parser.tokenStream.advance(module->srcEndBeforeCurly()))
+ return false;
+
+ *loadedFromCache = true;
+
+ int64_t usecAfter = PRMJ_Now();
+ int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
+ *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
+ return true;
+}
+
+/*****************************************************************************/
+// Top-level js::CompileAsmJS
+
+static bool
+NoExceptionPending(ExclusiveContext* cx)
+{
+ return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending();
+}
+
+static bool
+Warn(AsmJSParser& parser, int errorNumber, const char* str)
+{
+ ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption &&
+ errorNumber == JSMSG_USE_ASM_TYPE_FAIL
+ ? ParseError
+ : ParseWarning;
+ parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : "");
+ return false;
+}
+
+static bool
+EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser)
+{
+#ifdef JS_CODEGEN_NONE
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of a JIT compiler");
+#endif
+
+ if (!cx->jitSupportsFloatingPoint())
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
+
+ if (cx->gcSystemPageSize() != AsmJSPageSize)
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
+
+ switch (parser.options().asmJSOption) {
+ case AsmJSOption::Disabled:
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
+ case AsmJSOption::DisabledByDebugger:
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
+ case AsmJSOption::Enabled:
+ break;
+ }
+
+ if (parser.pc->isGenerator())
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
+
+ if (parser.pc->isArrowFunction())
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
+
+ // Class constructors are also methods
+ if (parser.pc->isMethod())
+ return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by class constructor or method context");
+
+ return true;
+}
+
static UniqueChars
BuildConsoleMessage(ExclusiveContext* cx, AsmJSModule& module, unsigned time,
const SlowFunctionVector& slowFuncs, JS::AsmJSCacheResult cacheResult)
{
#ifndef JS_MORE_DETERMINISTIC
UniqueChars slowText;
if (!slowFuncs.empty()) {
slowText.reset(JS_smprintf("; %d functions compiled slowly: ", slowFuncs.length()));
@@ -6870,78 +8843,26 @@ BuildConsoleMessage(ExclusiveContext* cx
return UniqueChars(JS_smprintf("total compilation time %dms; %s%s",
time, cacheString, slowText ? slowText.get() : ""));
#else
return make_string_copy("");
#endif
}
-static bool
-Warn(AsmJSParser& parser, int errorNumber, const char* str)
-{
- ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption &&
- errorNumber == JSMSG_USE_ASM_TYPE_FAIL
- ? ParseError
- : ParseWarning;
- parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : "");
- return false;
-}
-
-static bool
-EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser)
-{
-#ifdef JS_CODEGEN_NONE
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of a JIT compiler");
-#endif
-
- if (!cx->jitSupportsFloatingPoint())
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of floating point support");
-
- if (cx->gcSystemPageSize() != AsmJSPageSize)
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by non 4KiB system page size");
-
- switch (parser.options().asmJSOption) {
- case AsmJSOption::Disabled:
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by javascript.options.asmjs in about:config");
- case AsmJSOption::DisabledByDebugger:
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
- case AsmJSOption::Enabled:
- break;
- }
-
- if (parser.pc->isGenerator())
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
-
- if (parser.pc->isArrowFunction())
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
-
- // Class constructors are also methods
- if (parser.pc->isMethod())
- return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by class constructor or method context");
-
- return true;
-}
-
-static bool
-NoExceptionPending(ExclusiveContext* cx)
-{
- return !cx->isJSContext() || !cx->asJSContext()->isExceptionPending();
-}
-
bool
-js::ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
+js::CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
{
*validated = false;
// Various conditions disable asm.js optimizations.
if (!EstablishPreconditions(cx, parser))
return NoExceptionPending(cx);
- Rooted<AsmJSModuleObject*> moduleObj(cx, AsmJSModuleObject::create(cx));
+ Rooted<AsmJSModuleObject*> moduleObj(cx, NewAsmJSModuleObject(cx));
if (!moduleObj)
return false;
// Before spending any time parsing the module, try to look it up in the
// embedding's cache using the chars about to be parsed as the key.
bool loadedFromCache;
UniqueChars message;
if (!LookupAsmJSModuleInCache(cx, parser, moduleObj, &loadedFromCache, &message))
@@ -6969,33 +8890,57 @@ js::ValidateAsmJS(ExclusiveContext* cx,
message = BuildConsoleMessage(cx, module, time, slowFuncs, cacheResult);
if (!message)
return NoExceptionPending(cx);
}
// The module function dynamically links the AsmJSModule when called and
// generates a set of functions wrapping all the exports.
FunctionBox* funbox = parser.pc->maybeFunction->pn_funbox;
- RootedFunction moduleFun(cx, NewAsmJSModuleFunction(cx, funbox->function(), moduleObj));
+ RootedFunction moduleFun(cx, NewModuleFunction(cx, funbox->function(), moduleObj));
if (!moduleFun)
return false;
// Finished! Clobber the default function created by the parser with the new
// asm.js module function. Special cases in the bytecode emitter avoid
// generating bytecode for asm.js functions, allowing this asm.js module
// function to be the finished result.
MOZ_ASSERT(funbox->function()->isInterpreted());
funbox->object = moduleFun;
// Success! Write to the console with a "warning" message.
*validated = true;
Warn(parser, JSMSG_USE_ASM_TYPE_OK, message.get());
return NoExceptionPending(cx);
}
+/*****************************************************************************/
+// asm.js module/export queries
+
+bool
+js::IsAsmJSModuleNative(Native native)
+{
+ return native == LinkAsmJS;
+}
+
+bool
+js::IsAsmJSModule(JSFunction* fun)
+{
+ return fun->isNative() && fun->maybeNative() == LinkAsmJS;
+}
+
+bool
+js::IsAsmJSFunction(JSFunction* fun)
+{
+ return fun->isNative() && fun->maybeNative() == CallAsmJS;
+}
+
+/*****************************************************************************/
+// asm.js testing natives:
+
bool
js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// See EstablishPreconditions.
#ifdef JS_CODEGEN_NONE
bool available = false;
@@ -7004,8 +8949,280 @@ js::IsAsmJSCompilationAvailable(JSContex
cx->gcSystemPageSize() == AsmJSPageSize &&
cx->runtime()->options().asmJS();
#endif
args.rval().set(BooleanValue(available));
return true;
}
+static bool
+IsMaybeWrappedNativeFunction(const Value& v, Native native, JSFunction** fun = nullptr)
+{
+ if (!v.isObject())
+ return false;
+
+ JSObject* obj = CheckedUnwrap(&v.toObject());
+ if (!obj)
+ return false;
+
+ if (!obj->is<JSFunction>())
+ return false;
+
+ if (fun)
+ *fun = &obj->as<JSFunction>();
+
+ return obj->as<JSFunction>().maybeNative() == native;
+}
+
+bool
+js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
+ args.rval().set(BooleanValue(rval));
+ return true;
+}
+
+bool
+js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
+ args.rval().set(BooleanValue(rval));
+ return true;
+}
+
+bool
+js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ JSFunction* fun;
+ if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
+ JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
+ "argument passed to isAsmJSModuleLoadedFromCache is not a "
+ "validated asm.js module");
+ return false;
+ }
+
+ bool loadedFromCache = FunctionToModuleObject(fun).module().wasm().loadedFromCache();
+
+ args.rval().set(BooleanValue(loadedFromCache));
+ return true;
+}
+
+/*****************************************************************************/
+// asm.js toString/toSource support
+
+static bool
+AppendUseStrictSource(JSContext* cx, HandleFunction fun, Handle<JSFlatString*> src, StringBuffer& out)
+{
+ // We need to add "use strict" in the body right after the opening
+ // brace.
+ size_t bodyStart = 0, bodyEnd;
+
+ // No need to test for functions created with the Function ctor as
+ // these don't implicitly inherit the "use strict" context. Strict mode is
+ // enabled for functions created with the Function ctor only if they begin with
+ // the "use strict" directive, but these functions won't validate as asm.js
+ // modules.
+
+ if (!FindBody(cx, fun, src, &bodyStart, &bodyEnd))
+ return false;
+
+ return out.appendSubstring(src, 0, bodyStart) &&
+ out.append("\n\"use strict\";\n") &&
+ out.appendSubstring(src, bodyStart, src->length() - bodyStart);
+}
+
+JSString*
+js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
+{
+ AsmJSModule& module = FunctionToModuleObject(fun).module();
+
+ uint32_t begin = module.srcStart();
+ uint32_t end = module.srcEndAfterCurly();
+ ScriptSource* source = module.scriptSource();
+ StringBuffer out(cx);
+
+ if (addParenToLambda && fun->isLambda() && !out.append("("))
+ return nullptr;
+
+ if (!out.append("function "))
+ return nullptr;
+
+ if (fun->atom() && !out.append(fun->atom()))
+ return nullptr;
+
+ bool haveSource = source->hasSourceData();
+ if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
+ return nullptr;
+
+ if (!haveSource) {
+ if (!out.append("() {\n [sourceless code]\n}"))
+ return nullptr;
+ } else {
+ // Whether the function has been created with a Function ctor
+ bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
+ if (funCtor) {
+ // Functions created with the function constructor don't have arguments in their source.
+ if (!out.append("("))
+ return nullptr;
+
+ if (PropertyName* argName = module.globalArgumentName()) {
+ if (!out.append(argName))
+ return nullptr;
+ }
+ if (PropertyName* argName = module.importArgumentName()) {
+ if (!out.append(", ") || !out.append(argName))
+ return nullptr;
+ }
+ if (PropertyName* argName = module.bufferArgumentName()) {
+ if (!out.append(", ") || !out.append(argName))
+ return nullptr;
+ }
+
+ if (!out.append(") {\n"))
+ return nullptr;
+ }
+
+ Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
+ if (!src)
+ return nullptr;
+
+ if (module.strict()) {
+ if (!AppendUseStrictSource(cx, fun, src, out))
+ return nullptr;
+ } else {
+ if (!out.append(src))
+ return nullptr;
+ }
+
+ if (funCtor && !out.append("\n}"))
+ return nullptr;
+ }
+
+ if (addParenToLambda && fun->isLambda() && !out.append(")"))
+ return nullptr;
+
+ return out.finishString();
+}
+
+JSString*
+js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
+{
+ AsmJSModule& module = FunctionToModuleObject(fun).module();
+ const AsmJSModule::Export& f = module.exports()[FunctionToExportIndex(fun)];
+ uint32_t begin = module.srcStart() + f.startOffsetInModule();
+ uint32_t end = module.srcStart() + f.endOffsetInModule();
+
+ ScriptSource* source = module.scriptSource();
+ StringBuffer out(cx);
+
+ if (!out.append("function "))
+ return nullptr;
+
+ bool haveSource = source->hasSourceData();
+ if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
+ return nullptr;
+
+ if (!haveSource) {
+ // asm.js functions can't be anonymous
+ MOZ_ASSERT(fun->atom());
+ if (!out.append(fun->atom()))
+ return nullptr;
+ if (!out.append("() {\n [sourceless code]\n}"))
+ return nullptr;
+ } else {
+ // asm.js functions cannot have been created with a Function constructor
+ // as they belong within a module.
+ MOZ_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
+
+ if (module.strict()) {
+ // AppendUseStrictSource expects its input to start right after the
+ // function name, so split the source chars from the src into two parts:
+ // the function name and the rest (arguments + body).
+
+ // asm.js functions can't be anonymous
+ MOZ_ASSERT(fun->atom());
+ if (!out.append(fun->atom()))
+ return nullptr;
+
+ size_t nameEnd = begin + fun->atom()->length();
+ Rooted<JSFlatString*> src(cx, source->substring(cx, nameEnd, end));
+ if (!src || !AppendUseStrictSource(cx, fun, src, out))
+ return nullptr;
+ } else {
+ Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
+ if (!src)
+ return nullptr;
+ if (!out.append(src))
+ return nullptr;
+ }
+ }
+
+ return out.finishString();
+}
+
+/*****************************************************************************/
+// asm.js heap
+
+static const size_t MinHeapLength = 64 * 1024;
+static_assert(MinHeapLength % AsmJSPageSize == 0, "Invalid page size");
+
+#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+
+// Targets define AsmJSImmediateRange to be the size of an address immediate,
+// and AsmJSCheckedImmediateRange, to be the size of an address immediate that
+// can be supported by signal-handler OOB handling.
+static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
+ "AsmJSImmediateRange should be the size of an unconstrained "
+ "address immediate");
+
+const size_t js::AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
+ jit::AsmJSImmediateRange +
+ AsmJSPageSize;
+
+#endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
+
+// From the asm.js spec Linking section:
+// the heap object's byteLength must be either
+// 2^n for n in [12, 24)
+// or
+// 2^24 * n for n >= 1.
+
+bool
+js::IsValidAsmJSHeapLength(uint32_t length)
+{
+ bool valid = length >= MinHeapLength &&
+ (IsPowerOfTwo(length) ||
+ (length & 0x00ffffff) == 0);
+
+ MOZ_ASSERT_IF(valid, length % AsmJSPageSize == 0);
+ MOZ_ASSERT_IF(valid, length == RoundUpToNextValidAsmJSHeapLength(length));
+
+ return valid;
+}
+
+uint32_t
+js::RoundUpToNextValidAsmJSHeapLength(uint32_t length)
+{
+ if (length <= MinHeapLength)
+ return MinHeapLength;
+
+ if (length <= 16 * 1024 * 1024)
+ return mozilla::RoundUpPow2(length);
+
+ MOZ_ASSERT(length <= 0xff000000);
+ return (length + 0x00ffffff) & ~0x00ffffff;
+}
+
+bool
+js::OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer)
+{
+ for (Module* m = cx->runtime()->linkedWasmModules; m; m = m->nextLinked()) {
+ if (buffer == m->maybeBuffer() && !m->detachHeap(cx))
+ return false;
+ }
+ return true;
+}
+
rename from js/src/asmjs/AsmJSValidate.h
rename to js/src/asmjs/AsmJS.h
--- a/js/src/asmjs/AsmJSValidate.h
+++ b/js/src/asmjs/AsmJS.h
@@ -11,117 +11,126 @@
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef asmjs_AsmJSValidate_h
-#define asmjs_AsmJSValidate_h
-
-#include "mozilla/MathAlgorithms.h"
+#ifndef asmjs_asmjs_h
+#define asmjs_asmjs_h
-#include <stddef.h>
-
-#include "jsutil.h"
-
-#include "jit/Registers.h"
-#include "js/TypeDecls.h"
+#include "vm/NativeObject.h"
namespace js {
+class AsmJSModule;
class ExclusiveContext;
namespace frontend {
template <typename ParseHandler> class Parser;
template <typename ParseHandler> struct ParseContext;
class FullParseHandler;
class ParseNode;
-} // namespace frontend
-
+}
typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
typedef frontend::ParseContext<frontend::FullParseHandler> AsmJSParseContext;
-// Takes over parsing of a function starting with "use asm". The return value
-// indicates whether an error was reported which the caller should propagate.
-// If no error was reported, the function may still fail to validate as asm.js.
-// In this case, the parser.tokenStream has been advanced an indeterminate
-// amount and the entire function should be reparsed from the beginning.
+// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
+// directly to user script) which traces and owns an AsmJSModule. The
+// AsmJSModuleObject is referenced by the external slots of the module and
+// export asm.js functions.
+
+class AsmJSModuleObject : public NativeObject
+{
+ static const unsigned MODULE_SLOT = 0;
+
+ public:
+ static const unsigned RESERVED_SLOTS = 1;
+
+ bool hasModule() const;
+ void setModule(AsmJSModule* module);
+ AsmJSModule& module() const;
+
+ void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
+
+ static const Class class_;
+};
+
+typedef Handle<AsmJSModuleObject*> HandleAsmJSModule;
+
+// This function takes over parsing of a function starting with "use asm". The
+// return value indicates whether an error was reported which the caller should
+// propagate. If no error was reported, the function may still fail to validate
+// as asm.js. In this case, the parser.tokenStream has been advanced an
+// indeterminate amount and the entire function should be reparsed from the
+// beginning.
+
extern bool
-ValidateAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
+CompileAsmJS(ExclusiveContext* cx, AsmJSParser& parser, frontend::ParseNode* stmtList,
bool* validated);
-// The minimum heap length for asm.js.
-const size_t AsmJSMinHeapLength = 64 * 1024;
+// asm.js module/export queries:
+
+extern bool
+IsAsmJSModuleNative(JSNative native);
+
+extern bool
+IsAsmJSModule(JSFunction* fun);
+
+extern bool
+IsAsmJSFunction(JSFunction* fun);
-// The assumed page size; dynamically checked in ValidateAsmJS.
-#ifdef _MIPS_ARCH_LOONGSON3A
-const size_t AsmJSPageSize = 16384;
-#else
-const size_t AsmJSPageSize = 4096;
-#endif
+// asm.js testing natives:
+
+extern bool
+IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, JS::Value* vp);
+
+extern bool
+IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
+
+extern bool
+IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp);
+
+extern bool
+IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
-static_assert(AsmJSMinHeapLength % AsmJSPageSize == 0, "Invalid page size");
+// asm.js toString/toSource support:
+
+extern JSString*
+AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
-#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+extern JSString*
+AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
+
+// asm.js heap:
+
+extern bool
+IsValidAsmJSHeapLength(uint32_t length);
-// Targets define AsmJSImmediateRange to be the size of an address immediate,
-// and AsmJSCheckedImmediateRange, to be the size of an address immediate that
-// can be supported by signal-handler OOB handling.
-static_assert(jit::AsmJSCheckedImmediateRange <= jit::AsmJSImmediateRange,
- "AsmJSImmediateRange should be the size of an unconstrained "
- "address immediate");
+extern uint32_t
+RoundUpToNextValidAsmJSHeapLength(uint32_t length);
+
+extern bool
+OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
+
+// The assumed page size; dynamically checked in CompileAsmJS.
+#ifdef _MIPS_ARCH_LOONGSON3A
+static const size_t AsmJSPageSize = 16384;
+#else
+static const size_t AsmJSPageSize = 4096;
+#endif
// To support the use of signal handlers for catching Out Of Bounds accesses,
// the internal ArrayBuffer data array is inflated to 4GiB (only the
// byteLength portion of which is accessible) so that out-of-bounds accesses
// (made using a uint32 index) are guaranteed to raise a SIGSEGV.
// Then, an additional extent is added to permit folding of immediate
// values into addresses. And finally, unaligned accesses and mask optimizations
// might also try to access a few bytes after this limit, so just inflate it by
// AsmJSPageSize.
-static const size_t AsmJSMappedSize = 4 * 1024ULL * 1024ULL * 1024ULL +
- jit::AsmJSImmediateRange +
- AsmJSPageSize;
-
-#endif // defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
-
-// From the asm.js spec Linking section:
-// the heap object's byteLength must be either
-// 2^n for n in [12, 24)
-// or
-// 2^24 * n for n >= 1.
-
-inline uint32_t
-RoundUpToNextValidAsmJSHeapLength(uint32_t length)
-{
- if (length <= AsmJSMinHeapLength)
- return AsmJSMinHeapLength;
-
- if (length <= 16 * 1024 * 1024)
- return mozilla::RoundUpPow2(length);
-
- MOZ_ASSERT(length <= 0xff000000);
- return (length + 0x00ffffff) & ~0x00ffffff;
-}
-
-inline bool
-IsValidAsmJSHeapLength(uint32_t length)
-{
- bool valid = length >= AsmJSMinHeapLength &&
- (IsPowerOfTwo(length) ||
- (length & 0x00ffffff) == 0);
-
- MOZ_ASSERT_IF(valid, length % AsmJSPageSize == 0);
- MOZ_ASSERT_IF(valid, length == RoundUpToNextValidAsmJSHeapLength(length));
-
- return valid;
-}
-
-// Return whether asm.js optimization is inhibited by the platform or
-// dynamically disabled:
-extern bool
-IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, JS::Value* vp);
+#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
+extern const size_t AsmJSMappedSize;
+#endif
} // namespace js
-#endif // asmjs_AsmJSValidate_h
+#endif // asmjs_asmjs_h
deleted file mode 100644
--- a/js/src/asmjs/AsmJSLink.cpp
+++ /dev/null
@@ -1,1184 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asmjs/AsmJSLink.h"
-
-#include "mozilla/PodOperations.h"
-
-#include "jscntxt.h"
-#include "jsmath.h"
-#include "jsprf.h"
-#include "jswrapper.h"
-
-#include "asmjs/AsmJSModule.h"
-#include "builtin/AtomicsObject.h"
-#include "builtin/SIMD.h"
-#include "frontend/BytecodeCompiler.h"
-#include "jit/Ion.h"
-#include "jit/JitCommon.h"
-#include "vm/ArrayBufferObject.h"
-#include "vm/SharedArrayObject.h"
-#include "vm/StringBuffer.h"
-
-#include "jsobjinlines.h"
-
-#include "vm/ArrayBufferObject-inl.h"
-#include "vm/NativeObject-inl.h"
-
-using namespace js;
-using namespace js::jit;
-using namespace js::wasm;
-
-using mozilla::IsNaN;
-using mozilla::PodZero;
-
-static bool
-LinkFail(JSContext* cx, const char* str)
-{
- JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage,
- nullptr, JSMSG_USE_ASM_LINK_FAIL, str);
- return false;
-}
-
-static bool
-GetDataProperty(JSContext* cx, HandleValue objVal, HandlePropertyName field, MutableHandleValue v)
-{
- if (!objVal.isObject())
- return LinkFail(cx, "accessing property of non-object");
-
- RootedObject obj(cx, &objVal.toObject());
- if (IsScriptedProxy(obj))
- return LinkFail(cx, "accessing property of a Proxy");
-
- Rooted<PropertyDescriptor> desc(cx);
- RootedId id(cx, NameToId(field));
- if (!GetPropertyDescriptor(cx, obj, id, &desc))
- return false;
-
- if (!desc.object())
- return LinkFail(cx, "property not present on object");
-
- if (!desc.isDataDescriptor())
- return LinkFail(cx, "property is not a data property");
-
- v.set(desc.value());
- return true;
-}
-
-static bool
-HasPureCoercion(JSContext* cx, HandleValue v)
-{
- if (IsVectorObject<Int32x4>(v) || IsVectorObject<Float32x4>(v))
- return true;
-
- // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
- // bug that generates code that passes functions for some imports. To avoid
- // breaking all the code that contains this bug, we make an exception for
- // functions that don't have user-defined valueOf or toString, for their
- // coercions are not observable and coercion via ToNumber/ToInt32
- // definitely produces NaN/0. We should remove this special case later once
- // most apps have been built with newer Emscripten.
- jsid toString = NameToId(cx->names().toString);
- if (v.toObject().is<JSFunction>() &&
- HasObjectValueOf(&v.toObject(), cx) &&
- ClassMethodIsNative(cx, &v.toObject().as<JSFunction>(), &JSFunction::class_, toString, fun_toString))
- {
- return true;
- }
-
- return false;
-}
-
-static bool
-ValidateGlobalVariable(JSContext* cx, const AsmJSModule::Global& global, uint8_t* globalData,
- HandleValue importVal)
-{
- void* datum = globalData + global.varGlobalDataOffset();
-
- switch (global.varInitKind()) {
- case AsmJSModule::Global::InitConstant: {
- Val v = global.varInitVal();
- switch (v.type()) {
- case ValType::I32:
- *(int32_t*)datum = v.i32();
- break;
- case ValType::I64:
- MOZ_CRASH("int64");
- case ValType::F32:
- *(float*)datum = v.f32();
- break;
- case ValType::F64:
- *(double*)datum = v.f64();
- break;
- case ValType::I32x4:
- memcpy(datum, v.i32x4(), Simd128DataSize);
- break;
- case ValType::F32x4:
- memcpy(datum, v.f32x4(), Simd128DataSize);
- break;
- }
- break;
- }
-
- case AsmJSModule::Global::InitImport: {
- RootedPropertyName field(cx, global.varImportField());
- RootedValue v(cx);
- if (!GetDataProperty(cx, importVal, field, &v))
- return false;
-
- if (!v.isPrimitive() && !HasPureCoercion(cx, v))
- return LinkFail(cx, "Imported values must be primitives");
-
- switch (global.varInitImportType()) {
- case ValType::I32:
- if (!ToInt32(cx, v, (int32_t*)datum))
- return false;
- break;
- case ValType::I64:
- MOZ_CRASH("int64");
- case ValType::F32:
- if (!RoundFloat32(cx, v, (float*)datum))
- return false;
- break;
- case ValType::F64:
- if (!ToNumber(cx, v, (double*)datum))
- return false;
- break;
- case ValType::I32x4: {
- SimdConstant simdConstant;
- if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
- return false;
- memcpy(datum, simdConstant.asInt32x4(), Simd128DataSize);
- break;
- }
- case ValType::F32x4: {
- SimdConstant simdConstant;
- if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
- return false;
- memcpy(datum, simdConstant.asFloat32x4(), Simd128DataSize);
- break;
- }
- }
- break;
- }
- }
-
- return true;
-}
-
-static bool
-ValidateFFI(JSContext* cx, const AsmJSModule::Global& global, HandleValue importVal,
- AutoVectorRooter<JSFunction*>* ffis)
-{
- RootedPropertyName field(cx, global.ffiField());
- RootedValue v(cx);
- if (!GetDataProperty(cx, importVal, field, &v))
- return false;
-
- if (!v.isObject() || !v.toObject().is<JSFunction>())
- return LinkFail(cx, "FFI imports must be functions");
-
- (*ffis)[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
- return true;
-}
-
-static bool
-ValidateArrayView(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
- RootedPropertyName field(cx, global.maybeViewName());
- if (!field)
- return true;
-
- RootedValue v(cx);
- if (!GetDataProperty(cx, globalVal, field, &v))
- return false;
-
- bool tac = IsTypedArrayConstructor(v, global.viewType());
- if (!tac)
- return LinkFail(cx, "bad typed array constructor");
-
- return true;
-}
-
-static bool
-ValidateByteLength(JSContext* cx, HandleValue globalVal)
-{
- RootedPropertyName field(cx, cx->names().byteLength);
- RootedValue v(cx);
- if (!GetDataProperty(cx, globalVal, field, &v))
- return false;
-
- if (!v.isObject() || !v.toObject().isBoundFunction())
- return LinkFail(cx, "byteLength must be a bound function object");
-
- RootedFunction fun(cx, &v.toObject().as<JSFunction>());
-
- RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
- if (!IsNativeFunction(boundTarget, fun_call))
- return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");
-
- RootedValue boundThis(cx, fun->getBoundFunctionThis());
- if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
- return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");
-
- return true;
-}
-
-static bool
-ValidateMathBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
- RootedValue v(cx);
- if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
- return false;
-
- RootedPropertyName field(cx, global.mathName());
- if (!GetDataProperty(cx, v, field, &v))
- return false;
-
- Native native = nullptr;
- switch (global.mathBuiltinFunction()) {
- case AsmJSMathBuiltin_sin: native = math_sin; break;
- case AsmJSMathBuiltin_cos: native = math_cos; break;
- case AsmJSMathBuiltin_tan: native = math_tan; break;
- case AsmJSMathBuiltin_asin: native = math_asin; break;
- case AsmJSMathBuiltin_acos: native = math_acos; break;
- case AsmJSMathBuiltin_atan: native = math_atan; break;
- case AsmJSMathBuiltin_ceil: native = math_ceil; break;
- case AsmJSMathBuiltin_floor: native = math_floor; break;
- case AsmJSMathBuiltin_exp: native = math_exp; break;
- case AsmJSMathBuiltin_log: native = math_log; break;
- case AsmJSMathBuiltin_pow: native = math_pow; break;
- case AsmJSMathBuiltin_sqrt: native = math_sqrt; break;
- case AsmJSMathBuiltin_min: native = math_min; break;
- case AsmJSMathBuiltin_max: native = math_max; break;
- case AsmJSMathBuiltin_abs: native = math_abs; break;
- case AsmJSMathBuiltin_atan2: native = math_atan2; break;
- case AsmJSMathBuiltin_imul: native = math_imul; break;
- case AsmJSMathBuiltin_clz32: native = math_clz32; break;
- case AsmJSMathBuiltin_fround: native = math_fround; break;
- }
-
- if (!IsNativeFunction(v, native))
- return LinkFail(cx, "bad Math.* builtin function");
-
- return true;
-}
-
-static PropertyName*
-SimdTypeToName(JSContext* cx, AsmJSSimdType type)
-{
- switch (type) {
- case AsmJSSimdType_int32x4: return cx->names().int32x4;
- case AsmJSSimdType_float32x4: return cx->names().float32x4;
- }
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
-}
-
-static SimdTypeDescr::Type
-AsmJSSimdTypeToTypeDescrType(AsmJSSimdType type)
-{
- switch (type) {
- case AsmJSSimdType_int32x4: return Int32x4::type;
- case AsmJSSimdType_float32x4: return Float32x4::type;
- }
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSSimdType");
-}
-
-static bool
-ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal,
- MutableHandleValue out)
-{
- RootedValue v(cx);
- if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
- return false;
-
- AsmJSSimdType type;
- if (global.which() == AsmJSModule::Global::SimdCtor)
- type = global.simdCtorType();
- else
- type = global.simdOperationType();
-
- RootedPropertyName simdTypeName(cx, SimdTypeToName(cx, type));
- if (!GetDataProperty(cx, v, simdTypeName, &v))
- return false;
-
- if (!v.isObject())
- return LinkFail(cx, "bad SIMD type");
-
- RootedObject simdDesc(cx, &v.toObject());
- if (!simdDesc->is<SimdTypeDescr>())
- return LinkFail(cx, "bad SIMD type");
-
- if (AsmJSSimdTypeToTypeDescrType(type) != simdDesc->as<SimdTypeDescr>().type())
- return LinkFail(cx, "bad SIMD type");
-
- out.set(v);
- return true;
-}
-
-static bool
-ValidateSimdType(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
- RootedValue _(cx);
- return ValidateSimdType(cx, global, globalVal, &_);
-}
-
-static bool
-ValidateSimdOperation(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
- // SIMD operations are loaded from the SIMD type, so the type must have been
- // validated before the operation.
- RootedValue v(cx);
- JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
-
- RootedPropertyName opName(cx, global.simdOperationName());
- if (!GetDataProperty(cx, v, opName, &v))
- return false;
-
- Native native = nullptr;
- switch (global.simdOperationType()) {
-#define SET_NATIVE_INT32X4(op) case AsmJSSimdOperation_##op: native = simd_int32x4_##op; break;
-#define SET_NATIVE_FLOAT32X4(op) case AsmJSSimdOperation_##op: native = simd_float32x4_##op; break;
-#define FALLTHROUGH(op) case AsmJSSimdOperation_##op:
- case AsmJSSimdType_int32x4:
- switch (global.simdOperation()) {
- FOREACH_INT32X4_SIMD_OP(SET_NATIVE_INT32X4)
- FOREACH_COMMONX4_SIMD_OP(SET_NATIVE_INT32X4)
- FOREACH_FLOAT32X4_SIMD_OP(FALLTHROUGH)
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
- "place");
- }
- break;
- case AsmJSSimdType_float32x4:
- switch (global.simdOperation()) {
- FOREACH_FLOAT32X4_SIMD_OP(SET_NATIVE_FLOAT32X4)
- FOREACH_COMMONX4_SIMD_OP(SET_NATIVE_FLOAT32X4)
- FOREACH_INT32X4_SIMD_OP(FALLTHROUGH)
- MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
- "place");
- }
- break;
-#undef FALLTHROUGH
-#undef SET_NATIVE_FLOAT32X4
-#undef SET_NATIVE_INT32X4
-#undef SET_NATIVE
- }
- if (!native || !IsNativeFunction(v, native))
- return LinkFail(cx, "bad SIMD.type.* operation");
- return true;
-}
-
-static bool
-ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
- RootedValue v(cx);
- if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v))
- return false;
- RootedPropertyName field(cx, global.atomicsName());
- if (!GetDataProperty(cx, v, field, &v))
- return false;
-
- Native native = nullptr;
- switch (global.atomicsBuiltinFunction()) {
- case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break;
- case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break;
- case AsmJSAtomicsBuiltin_load: native = atomics_load; break;
- case AsmJSAtomicsBuiltin_store: native = atomics_store; break;
- case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break;
- case AsmJSAtomicsBuiltin_add: native = atomics_add; break;
- case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break;
- case AsmJSAtomicsBuiltin_and: native = atomics_and; break;
- case AsmJSAtomicsBuiltin_or: native = atomics_or; break;
- case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break;
- case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break;
- }
-
- if (!IsNativeFunction(v, native))
- return LinkFail(cx, "bad Atomics.* builtin function");
-
- return true;
-}
-
-static bool
-ValidateConstant(JSContext* cx, const AsmJSModule::Global& global, HandleValue globalVal)
-{
- RootedPropertyName field(cx, global.constantName());
- RootedValue v(cx, globalVal);
-
- if (global.constantKind() == AsmJSModule::Global::MathConstant) {
- if (!GetDataProperty(cx, v, cx->names().Math, &v))
- return false;
- }
-
- if (!GetDataProperty(cx, v, field, &v))
- return false;
-
- if (!v.isNumber())
- return LinkFail(cx, "math / global constant value needs to be a number");
-
- // NaN != NaN
- if (IsNaN(global.constantValue())) {
- if (!IsNaN(v.toNumber()))
- return LinkFail(cx, "global constant value needs to be NaN");
- } else {
- if (v.toNumber() != global.constantValue())
- return LinkFail(cx, "global constant value mismatch");
- }
-
- return true;
-}
-
-static bool
-CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal,
- MutableHandle<ArrayBufferObjectMaybeShared*> buffer)
-{
- if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal))
- return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
-
- if (!module.isSharedView() && !IsArrayBuffer(bufferVal))
- return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
-
- buffer.set(&AsAnyArrayBuffer(bufferVal));
- uint32_t heapLength = buffer->byteLength();
-
- if (!IsValidAsmJSHeapLength(heapLength)) {
- UniqueChars msg(
- JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
- "valid length is 0x%x",
- heapLength,
- RoundUpToNextValidAsmJSHeapLength(heapLength)));
- return LinkFail(cx, msg.get());
- }
-
- // This check is sufficient without considering the size of the loaded datum because heap
- // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
- MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
- if (heapLength < module.minHeapLength()) {
- UniqueChars msg(
- JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
- "by const heap accesses and/or change-heap minimum-length requirements).",
- heapLength,
- module.minHeapLength()));
- return LinkFail(cx, msg.get());
- }
-
- if (heapLength > module.maxHeapLength()) {
- UniqueChars msg(
- JS_smprintf("ArrayBuffer byteLength 0x%x is greater than maximum length of 0x%x",
- heapLength,
- module.maxHeapLength()));
- return LinkFail(cx, msg.get());
- }
-
- // Shell builtins may have disabled signal handlers since the module we're
- // cloning was compiled. LookupAsmJSModuleInCache checks for signal handlers
- // as well for the caching case.
- if (module.wasm().compileArgs() != CompileArgs(cx))
- return LinkFail(cx, "Signals have been toggled since compilation");
-
- if (buffer->is<ArrayBufferObject>()) {
- Rooted<ArrayBufferObject*> abheap(cx, &buffer->as<ArrayBufferObject>());
- bool useSignalHandlers = module.wasm().compileArgs().useSignalHandlersForOOB;
- if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, useSignalHandlers))
- return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
- }
-
- return true;
-}
-
-static bool
-DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
-{
- HandleValue globalVal = args.get(0);
- HandleValue importVal = args.get(1);
- HandleValue bufferVal = args.get(2);
-
- Rooted<ArrayBufferObjectMaybeShared*> buffer(cx);
- if (module.hasArrayView() && !CheckBuffer(cx, module, bufferVal, &buffer))
- return false;
-
- AutoVectorRooter<JSFunction*> ffis(cx);
- if (!ffis.resize(module.numFFIs()))
- return false;
-
- for (const AsmJSModule::Global& global : module.globals()) {
- switch (global.which()) {
- case AsmJSModule::Global::Variable:
- if (!ValidateGlobalVariable(cx, global, module.wasm().globalData(), importVal))
- return false;
- break;
- case AsmJSModule::Global::FFI:
- if (!ValidateFFI(cx, global, importVal, &ffis))
- return false;
- break;
- case AsmJSModule::Global::ArrayView:
- case AsmJSModule::Global::ArrayViewCtor:
- if (!ValidateArrayView(cx, global, globalVal))
- return false;
- break;
- case AsmJSModule::Global::ByteLength:
- if (!ValidateByteLength(cx, globalVal))
- return false;
- break;
- case AsmJSModule::Global::MathBuiltinFunction:
- if (!ValidateMathBuiltinFunction(cx, global, globalVal))
- return false;
- break;
- case AsmJSModule::Global::AtomicsBuiltinFunction:
- if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
- return false;
- break;
- case AsmJSModule::Global::Constant:
- if (!ValidateConstant(cx, global, globalVal))
- return false;
- break;
- case AsmJSModule::Global::SimdCtor:
- if (!ValidateSimdType(cx, global, globalVal))
- return false;
- break;
- case AsmJSModule::Global::SimdOperation:
- if (!ValidateSimdOperation(cx, global, globalVal))
- return false;
- break;
- }
- }
-
- AutoVectorRooter<JSFunction*> imports(cx);
- for (const AsmJSModule::Import& import : module.imports()) {
- if (!imports.append(ffis[import.ffiIndex()]))
- return false;
- }
-
- return module.wasm().dynamicallyLink(cx, buffer, imports);
-}
-
-static bool
-ChangeHeap(JSContext* cx, AsmJSModule& module, const CallArgs& args)
-{
- HandleValue bufferArg = args.get(0);
- if (!IsArrayBuffer(bufferArg)) {
- ReportIncompatible(cx, args);
- return false;
- }
-
- Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
- uint32_t heapLength = newBuffer->byteLength();
- if (heapLength & module.heapLengthMask() ||
- heapLength < module.minHeapLength() ||
- heapLength > module.maxHeapLength())
- {
- args.rval().set(BooleanValue(false));
- return true;
- }
-
- if (!module.hasArrayView()) {
- args.rval().set(BooleanValue(true));
- return true;
- }
-
- MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
-
- bool useSignalHandlers = module.wasm().compileArgs().useSignalHandlersForOOB;
- if (!ArrayBufferObject::prepareForAsmJS(cx, newBuffer, useSignalHandlers))
- return false;
-
- args.rval().set(BooleanValue(module.wasm().changeHeap(newBuffer, cx)));
- return true;
-}
-
-// An asm.js function stores, in its extended slots:
-// - a pointer to the module from which it was returned
-// - its index in the ordered list of exported functions
-static const unsigned ASM_MODULE_SLOT = 0;
-static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
-
-static unsigned
-FunctionToExportIndex(HandleFunction fun)
-{
- MOZ_ASSERT(IsAsmJSFunction(fun));
- Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT);
- return v.toInt32();
-}
-
-static AsmJSModule&
-FunctionToEnclosingModule(HandleFunction fun)
-{
- return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module();
-}
-
-// This is the js::Native for functions exported by an asm.js module.
-static bool
-CallAsmJS(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs callArgs = CallArgsFromVp(argc, vp);
- RootedFunction callee(cx, &callArgs.callee().as<JSFunction>());
-
- // The heap-changing function is a special-case and is implemented by C++.
- AsmJSModule& asmJSModule = FunctionToEnclosingModule(callee);
- const AsmJSModule::Export& asmJSFunc = asmJSModule.exports()[FunctionToExportIndex(callee)];
- if (asmJSFunc.isChangeHeap())
- return ChangeHeap(cx, asmJSModule, callArgs);
-
- Module& module = asmJSModule.wasm();
- const Export& func = module.exports()[asmJSFunc.wasmIndex()];
-
- // Enable/disable profiling in the asm.js module to match the current global
- // profiling state. Don't do this if the module is already active on the
- // stack since this would leave the module in a state where profiling is
- // enabled but the stack isn't unwindable.
- if (module.profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !module.active())
- module.setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);
-
- // The calling convention for an external call into asm.js is to pass an
- // array of 16-byte values where each value contains either a coerced int32
- // (in the low word), a double value (in the low dword) or a SIMD vector
- // value, with the coercions specified by the asm.js signature. The
- // external entry point unpacks this array into the system-ABI-specified
- // registers and stack memory and then calls into the internal entry point.
- // The return value is stored in the first element of the array (which,
- // therefore, must have length >= 1).
- Vector<Module::EntryArg, 8> coercedArgs(cx);
- if (!coercedArgs.resize(Max<size_t>(1, func.sig().args().length())))
- return false;
-
- RootedValue v(cx);
- for (unsigned i = 0; i < func.sig().args().length(); ++i) {
- v = i < callArgs.length() ? callArgs[i] : UndefinedValue();
- switch (func.sig().arg(i)) {
- case ValType::I32:
- if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
- return false;
- break;
- case ValType::I64:
- MOZ_CRASH("int64");
- case ValType::F32:
- if (!RoundFloat32(cx, v, (float*)&coercedArgs[i]))
- return false;
- break;
- case ValType::F64:
- if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
- return false;
- break;
- case ValType::I32x4: {
- SimdConstant simd;
- if (!ToSimdConstant<Int32x4>(cx, v, &simd))
- return false;
- memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
- break;
- }
- case ValType::F32x4: {
- SimdConstant simd;
- if (!ToSimdConstant<Float32x4>(cx, v, &simd))
- return false;
- memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
- break;
- }
- }
- }
-
- // The correct way to handle this situation would be to allocate a new range
- // of PROT_NONE memory and module.changeHeap to this memory. That would
- // cause every access to take the out-of-bounds signal-handler path which
- // does the right thing. For now, just throw an out-of-memory exception
- // since these can technically pop out anywhere and the full fix may
- // actually OOM when trying to allocate the PROT_NONE memory.
- if (module.hasDetachedHeap()) {
- JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
- return false;
- }
-
- {
- // Push a WasmActivation to describe the asm.js frames we're about to
- // push when running this module. Additionally, push a JitActivation so
- // that the optimized asm.js-to-Ion FFI call path (which we want to be
- // very fast) can avoid doing so. The JitActivation is marked as
- // inactive so stack iteration will skip over it.
- WasmActivation activation(cx, module);
- JitActivation jitActivation(cx, /* active */ false);
-
- // Call the per-exported-function trampoline created by GenerateEntry.
- Module::EntryFuncPtr enter = module.entryTrampoline(func);
- if (!CALL_GENERATED_2(enter, coercedArgs.begin(), module.globalData()))
- return false;
- }
-
- if (callArgs.isConstructing()) {
- // By spec, when a function is called as a constructor and this function
- // returns a primary type, which is the case for all asm.js exported
- // functions, the returned value is discarded and an empty object is
- // returned instead.
- PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx);
- if (!obj)
- return false;
- callArgs.rval().set(ObjectValue(*obj));
- return true;
- }
-
- JSObject* simdObj;
- switch (func.sig().ret()) {
- case ExprType::Void:
- callArgs.rval().set(UndefinedValue());
- break;
- case ExprType::I32:
- callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
- break;
- case ExprType::I64:
- MOZ_CRASH("int64");
- case ExprType::F32:
- case ExprType::F64:
- callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0]));
- break;
- case ExprType::I32x4:
- simdObj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]);
- if (!simdObj)
- return false;
- callArgs.rval().set(ObjectValue(*simdObj));
- break;
- case ExprType::F32x4:
- simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]);
- if (!simdObj)
- return false;
- callArgs.rval().set(ObjectValue(*simdObj));
- break;
- }
-
- return true;
-}
-
-static JSFunction*
-NewExportedFunction(JSContext* cx, const AsmJSModule& module, const AsmJSModule::Export& func,
- HandleObject moduleObj, unsigned exportIndex)
-{
- unsigned numArgs = func.isChangeHeap()
- ? 1
- : module.wasm().exports()[func.wasmIndex()].sig().args().length();
-
- RootedPropertyName name(cx, func.name());
- JSFunction* fun =
- NewNativeConstructor(cx, CallAsmJS, numArgs, name,
- gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
- JSFunction::ASMJS_CTOR);
- if (!fun)
- return nullptr;
-
- fun->setExtendedSlot(ASM_MODULE_SLOT, ObjectValue(*moduleObj));
- fun->setExtendedSlot(ASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
- return fun;
-}
-
-static bool
-HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module,
- HandlePropertyName name)
-{
- if (cx->isExceptionPending())
- return false;
-
- ScriptSource* source = module.scriptSource();
-
- // Source discarding is allowed to affect JS semantics because it is never
- // enabled for normal JS content.
- bool haveSource = source->hasSourceData();
- if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
- return false;
- if (!haveSource) {
- JS_ReportError(cx, "asm.js link failure with source discarding enabled");
- return false;
- }
-
- uint32_t begin = module.srcBodyStart(); // starts right after 'use asm'
- uint32_t end = module.srcEndBeforeCurly();
- Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
- if (!src)
- return false;
-
- RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
- name, gc::AllocKind::FUNCTION,
- TenuredObject));
- if (!fun)
- return false;
-
- Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
- if (!formals.reserve(3))
- return false;
-
- if (module.globalArgumentName())
- formals.infallibleAppend(module.globalArgumentName());
- if (module.importArgumentName())
- formals.infallibleAppend(module.importArgumentName());
- if (module.bufferArgumentName())
- formals.infallibleAppend(module.bufferArgumentName());
-
- CompileOptions options(cx);
- options.setMutedErrors(source->mutedErrors())
- .setFile(source->filename())
- .setNoScriptRval(false);
-
- // The exported function inherits an implicit strict context if the module
- // also inherited it somehow.
- if (module.strict())
- options.strictOption = true;
-
- AutoStableStringChars stableChars(cx);
- if (!stableChars.initTwoByte(cx, src))
- return false;
-
- const char16_t* chars = stableChars.twoByteRange().start().get();
- SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
- ? SourceBufferHolder::GiveOwnership
- : SourceBufferHolder::NoOwnership;
- SourceBufferHolder srcBuf(chars, end - begin, ownership);
- if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
- return false;
-
- // Call the function we just recompiled.
- args.setCallee(ObjectValue(*fun));
- return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
-}
-
-static JSObject*
-CreateExportObject(JSContext* cx, HandleAsmJSModule moduleObj)
-{
- AsmJSModule& module = moduleObj->module();
- const AsmJSModule::ExportVector& exports = module.exports();
-
- if (exports.length() == 1) {
- const AsmJSModule::Export& func = exports[0];
- if (!func.maybeFieldName())
- return NewExportedFunction(cx, module, func, moduleObj, 0);
- }
-
- gc::AllocKind allocKind = gc::GetGCObjectKind(exports.length());
- RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind));
- if (!obj)
- return nullptr;
-
- for (unsigned i = 0; i < exports.length(); i++) {
- const AsmJSModule::Export& func = exports[i];
-
- RootedFunction fun(cx, NewExportedFunction(cx, module, func, moduleObj, i));
- if (!fun)
- return nullptr;
-
- MOZ_ASSERT(func.maybeFieldName() != nullptr);
- RootedId id(cx, NameToId(func.maybeFieldName()));
- RootedValue val(cx, ObjectValue(*fun));
- if (!NativeDefineProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
- return nullptr;
- }
-
- return obj;
-}
-
-static const unsigned MODULE_FUN_SLOT = 0;
-
-static AsmJSModuleObject&
-ModuleFunctionToModuleObject(JSFunction* fun)
-{
- return fun->getExtendedSlot(MODULE_FUN_SLOT).toObject().as<AsmJSModuleObject>();
-}
-
-// Implements the semantics of an asm.js module function that has been successfully validated.
-static bool
-LinkAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
-
- // The LinkAsmJS builtin (created by NewAsmJSModuleFunction) is an extended
- // function and stores its module in an extended slot.
- RootedFunction fun(cx, &args.callee().as<JSFunction>());
- Rooted<AsmJSModuleObject*> moduleObj(cx, &ModuleFunctionToModuleObject(fun));
-
- // When a module is linked, it is dynamically specialized to the given
- // arguments (buffer, ffis). Thus, if the module is linked again (it is just
- // a function so it can be called multiple times), we need to clone a new
- // module.
- if (moduleObj->module().wasm().dynamicallyLinked()) {
- Rooted<AsmJSModuleObject*> clone(cx, AsmJSModuleObject::create(cx));
- if (!clone)
- return false;
-
- if (!moduleObj->module().clone(cx, clone))
- return false;
-
- moduleObj = clone;
- }
-
- AsmJSModule& module = moduleObj->module();
-
- // Link the module by performing the link-time validation checks in the
- // asm.js spec and then patching the generated module to associate it with
- // the given heap (ArrayBuffer) and a new global data segment (the closure
- // state shared by the inner asm.js functions).
- if (!DynamicallyLinkModule(cx, args, module)) {
- // Linking failed, so reparse the entire asm.js module from scratch to
- // get normal interpreted bytecode which we can simply Invoke. Very slow.
- RootedPropertyName name(cx, fun->name());
- return HandleDynamicLinkFailure(cx, args, module, name);
- }
-
- // Link-time validation succeeded, so wrap all the exported functions with
- // CallAsmJS builtins that trampoline into the generated code.
- JSObject* obj = CreateExportObject(cx, moduleObj);
- if (!obj)
- return false;
-
- args.rval().set(ObjectValue(*obj));
- return true;
-}
-
-JSFunction*
-js::NewAsmJSModuleFunction(ExclusiveContext* cx, JSFunction* origFun, HandleObject moduleObj)
-{
- RootedPropertyName name(cx, origFun->name());
-
- JSFunction::Flags flags = origFun->isLambda() ? JSFunction::ASMJS_LAMBDA_CTOR
- : JSFunction::ASMJS_CTOR;
- JSFunction* moduleFun =
- NewNativeConstructor(cx, LinkAsmJS, origFun->nargs(), name,
- gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
- flags);
- if (!moduleFun)
- return nullptr;
-
- moduleFun->setExtendedSlot(MODULE_FUN_SLOT, ObjectValue(*moduleObj));
- return moduleFun;
-}
-
-bool
-js::IsAsmJSModuleNative(js::Native native)
-{
- return native == LinkAsmJS;
-}
-
-static bool
-IsMaybeWrappedNativeFunction(const Value& v, Native native, JSFunction** fun = nullptr)
-{
- if (!v.isObject())
- return false;
-
- JSObject* obj = CheckedUnwrap(&v.toObject());
- if (!obj)
- return false;
-
- if (!obj->is<JSFunction>())
- return false;
-
- if (fun)
- *fun = &obj->as<JSFunction>();
-
- return obj->as<JSFunction>().maybeNative() == native;
-}
-
-bool
-js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
- bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args.get(0), LinkAsmJS);
- args.rval().set(BooleanValue(rval));
- return true;
-}
-
-bool
-js::IsAsmJSModule(HandleFunction fun)
-{
- return fun->isNative() && fun->maybeNative() == LinkAsmJS;
-}
-
-static bool
-AppendUseStrictSource(JSContext* cx, HandleFunction fun, Handle<JSFlatString*> src, StringBuffer& out)
-{
- // We need to add "use strict" in the body right after the opening
- // brace.
- size_t bodyStart = 0, bodyEnd;
-
- // No need to test for functions created with the Function ctor as
- // these don't implicitly inherit the "use strict" context. Strict mode is
- // enabled for functions created with the Function ctor only if they begin with
- // the "use strict" directive, but these functions won't validate as asm.js
- // modules.
-
- if (!FindBody(cx, fun, src, &bodyStart, &bodyEnd))
- return false;
-
- return out.appendSubstring(src, 0, bodyStart) &&
- out.append("\n\"use strict\";\n") &&
- out.appendSubstring(src, bodyStart, src->length() - bodyStart);
-}
-
-JSString*
-js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
-{
- AsmJSModule& module = ModuleFunctionToModuleObject(fun).module();
-
- uint32_t begin = module.srcStart();
- uint32_t end = module.srcEndAfterCurly();
- ScriptSource* source = module.scriptSource();
- StringBuffer out(cx);
-
- if (addParenToLambda && fun->isLambda() && !out.append("("))
- return nullptr;
-
- if (!out.append("function "))
- return nullptr;
-
- if (fun->atom() && !out.append(fun->atom()))
- return nullptr;
-
- bool haveSource = source->hasSourceData();
- if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
- return nullptr;
-
- if (!haveSource) {
- if (!out.append("() {\n [sourceless code]\n}"))
- return nullptr;
- } else {
- // Whether the function has been created with a Function ctor
- bool funCtor = begin == 0 && end == source->length() && source->argumentsNotIncluded();
- if (funCtor) {
- // Functions created with the function constructor don't have arguments in their source.
- if (!out.append("("))
- return nullptr;
-
- if (PropertyName* argName = module.globalArgumentName()) {
- if (!out.append(argName))
- return nullptr;
- }
- if (PropertyName* argName = module.importArgumentName()) {
- if (!out.append(", ") || !out.append(argName))
- return nullptr;
- }
- if (PropertyName* argName = module.bufferArgumentName()) {
- if (!out.append(", ") || !out.append(argName))
- return nullptr;
- }
-
- if (!out.append(") {\n"))
- return nullptr;
- }
-
- Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
- if (!src)
- return nullptr;
-
- if (module.strict()) {
- if (!AppendUseStrictSource(cx, fun, src, out))
- return nullptr;
- } else {
- if (!out.append(src))
- return nullptr;
- }
-
- if (funCtor && !out.append("\n}"))
- return nullptr;
- }
-
- if (addParenToLambda && fun->isLambda() && !out.append(")"))
- return nullptr;
-
- return out.finishString();
-}
-
-bool
-js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
-
- JSFunction* fun;
- if (!args.hasDefined(0) || !IsMaybeWrappedNativeFunction(args[0], LinkAsmJS, &fun)) {
- JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
- "argument passed to isAsmJSModuleLoadedFromCache is not a "
- "validated asm.js module");
- return false;
- }
-
- bool loadedFromCache = ModuleFunctionToModuleObject(fun).module().wasm().loadedFromCache();
-
- args.rval().set(BooleanValue(loadedFromCache));
- return true;
-}
-
-bool
-js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp)
-{
- CallArgs args = CallArgsFromVp(argc, vp);
- bool rval = args.hasDefined(0) && IsMaybeWrappedNativeFunction(args[0], CallAsmJS);
- args.rval().set(BooleanValue(rval));
- return true;
-}
-
-bool
-js::IsAsmJSFunction(HandleFunction fun)
-{
- return fun->isNative() && fun->maybeNative() == CallAsmJS;
-}
-
-JSString*
-js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
-{
- AsmJSModule& module = FunctionToEnclosingModule(fun);
- const AsmJSModule::Export& f = module.exports()[FunctionToExportIndex(fun)];
- uint32_t begin = module.srcStart() + f.startOffsetInModule();
- uint32_t end = module.srcStart() + f.endOffsetInModule();
-
- ScriptSource* source = module.scriptSource();
- StringBuffer out(cx);
-
- if (!out.append("function "))
- return nullptr;
-
- bool haveSource = source->hasSourceData();
- if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
- return nullptr;
-
- if (!haveSource) {
- // asm.js functions can't be anonymous
- MOZ_ASSERT(fun->atom());
- if (!out.append(fun->atom()))
- return nullptr;
- if (!out.append("() {\n [sourceless code]\n}"))
- return nullptr;
- } else {
- // asm.js functions cannot have been created with a Function constructor
- // as they belong within a module.
- MOZ_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
-
- if (module.strict()) {
- // AppendUseStrictSource expects its input to start right after the
- // function name, so split the source chars from the src into two parts:
- // the function name and the rest (arguments + body).
-
- // asm.js functions can't be anonymous
- MOZ_ASSERT(fun->atom());
- if (!out.append(fun->atom()))
- return nullptr;
-
- size_t nameEnd = begin + fun->atom()->length();
- Rooted<JSFlatString*> src(cx, source->substring(cx, nameEnd, end));
- if (!src || !AppendUseStrictSource(cx, fun, src, out))
- return nullptr;
- } else {
- Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
- if (!src)
- return nullptr;
- if (!out.append(src))
- return nullptr;
- }
- }
-
- return out.finishString();
-}
deleted file mode 100644
--- a/js/src/asmjs/AsmJSLink.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef asmjs_AsmJSLink_h
-#define asmjs_AsmJSLink_h
-
-#include "NamespaceImports.h"
-
-namespace js {
-
-// Create a new JSFunction to replace originalFun as the representation of the
-// function defining the succesfully-validated module 'moduleObj'.
-extern JSFunction*
-NewAsmJSModuleFunction(ExclusiveContext* cx, JSFunction* originalFun, HandleObject moduleObj);
-
-// Return whether this is the js::Native returned by NewAsmJSModuleFunction.
-extern bool
-IsAsmJSModuleNative(JSNative native);
-
-// Return whether the given value is a function containing "use asm" that has
-// been validated according to the asm.js spec.
-extern bool
-IsAsmJSModule(JSContext* cx, unsigned argc, JS::Value* vp);
-extern bool
-IsAsmJSModule(HandleFunction fun);
-
-extern JSString*
-AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
-
-// Return whether the given value is a function containing "use asm" that was
-// loaded directly from the cache (and hence was validated previously).
-extern bool
-IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp);
-
-// Return whether the given value is a nested function in an asm.js module that
-// has been both compile- and link-time validated.
-extern bool
-IsAsmJSFunction(JSContext* cx, unsigned argc, JS::Value* vp);
-extern bool
-IsAsmJSFunction(HandleFunction fun);
-
-extern JSString*
-AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
-
-} // namespace js
-
-#endif // asmjs_AsmJSLink_h
deleted file mode 100644
--- a/js/src/asmjs/AsmJSModule.cpp
+++ /dev/null
@@ -1,622 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "asmjs/AsmJSModule.h"
-
-#include "mozilla/Compression.h"
-#include "mozilla/EnumeratedRange.h"
-#include "mozilla/PodOperations.h"
-
-#include "jsprf.h"
-
-#include "asmjs/WasmSerialize.h"
-#include "frontend/Parser.h"
-#include "js/Class.h"
-#include "js/MemoryMetrics.h"
-
-#include "jsobjinlines.h"
-
-#include "frontend/ParseNode-inl.h"
-
-using namespace js;
-using namespace js::frontend;
-using namespace js::jit;
-using namespace js::wasm;
-using mozilla::PodZero;
-using mozilla::PodEqual;
-using mozilla::Compression::LZ4;
-
-AsmJSModule::AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
- bool strict)
- : scriptSource_(scriptSource),
- srcStart_(srcStart),
- srcBodyStart_(srcBodyStart),
- globalArgumentName_(nullptr),
- importArgumentName_(nullptr),
- bufferArgumentName_(nullptr)
-{
- mozilla::PodZero(&pod);
- pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
- pod.maxHeapLength_ = 0x80000000;
- pod.strict_ = strict;
-
- // AsmJSCheckedImmediateRange should be defined to be at most the minimum
- // heap length so that offsets can be folded into bounds checks.
- MOZ_ASSERT(pod.minHeapLength_ - AsmJSCheckedImmediateRange <= pod.minHeapLength_);
-}
-
-void
-AsmJSModule::trace(JSTracer* trc)
-{
- if (wasm_)
- wasm_->trace(trc);
- for (Global& global : globals_)
- global.trace(trc);
- for (Export& exp : exports_)
- exp.trace(trc);
- if (globalArgumentName_)
- TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
- if (importArgumentName_)
- TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
- if (bufferArgumentName_)
- TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
-}
-
-void
-AsmJSModule::addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
- size_t* asmJSModuleData)
-{
- if (wasm_)
- wasm_->addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData);
-
- if (linkData_)
- *asmJSModuleData += linkData_->sizeOfExcludingThis(mallocSizeOf);
-
- *asmJSModuleData += mallocSizeOf(this) +
- globals_.sizeOfExcludingThis(mallocSizeOf) +
- imports_.sizeOfExcludingThis(mallocSizeOf) +
- exports_.sizeOfExcludingThis(mallocSizeOf);
-}
-
-void
-AsmJSModule::finish(Module* wasm, wasm::UniqueStaticLinkData linkData,
- uint32_t endBeforeCurly, uint32_t endAfterCurly)
-{
- MOZ_ASSERT(!isFinished());
-
- wasm_.reset(wasm);
- linkData_ = Move(linkData);
-
- MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
- MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
- pod.srcLength_ = endBeforeCurly - srcStart_;
- pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
-
- MOZ_ASSERT(isFinished());
-}
-
-bool
-js::OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer)
-{
- for (Module* m = cx->runtime()->linkedWasmModules; m; m = m->nextLinked()) {
- if (buffer == m->maybeBuffer() && !m->detachHeap(cx))
- return false;
- }
- return true;
-}
-
-static void
-AsmJSModuleObject_finalize(FreeOp* fop, JSObject* obj)
-{
- AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
- if (moduleObj.hasModule())
- fop->delete_(&moduleObj.module());
-}
-
-static void
-AsmJSModuleObject_trace(JSTracer* trc, JSObject* obj)
-{
- AsmJSModuleObject& moduleObj = obj->as<AsmJSModuleObject>();
- if (moduleObj.hasModule())
- moduleObj.module().trace(trc);
-}
-
-const Class AsmJSModuleObject::class_ = {
- "AsmJSModuleObject",
- JSCLASS_IS_ANONYMOUS | JSCLASS_DELAY_METADATA_CALLBACK |
- JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
- nullptr, /* addProperty */
- nullptr, /* delProperty */
- nullptr, /* getProperty */
- nullptr, /* setProperty */
- nullptr, /* enumerate */
- nullptr, /* resolve */
- nullptr, /* mayResolve */
- AsmJSModuleObject_finalize,
- nullptr, /* call */
- nullptr, /* hasInstance */
- nullptr, /* construct */
- AsmJSModuleObject_trace
-};
-
-AsmJSModuleObject*
-AsmJSModuleObject::create(ExclusiveContext* cx)
-{
- AutoSetNewObjectMetadata metadata(cx);
- JSObject* obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr);
- if (!obj)
- return nullptr;
- return &obj->as<AsmJSModuleObject>();
-}
-
-bool
-AsmJSModuleObject::hasModule() const
-{
- MOZ_ASSERT(is<AsmJSModuleObject>());
- return !getReservedSlot(MODULE_SLOT).isUndefined();
-}
-
-void
-AsmJSModuleObject::setModule(AsmJSModule* newModule)
-{
- MOZ_ASSERT(is<AsmJSModuleObject>());
- if (hasModule())
- js_delete(&module());
- setReservedSlot(MODULE_SLOT, PrivateValue(newModule));
-}
-
-AsmJSModule&
-AsmJSModuleObject::module() const
-{
- MOZ_ASSERT(is<AsmJSModuleObject>());
- return *(AsmJSModule*)getReservedSlot(MODULE_SLOT).toPrivate();
-}
-
-uint8_t*
-AsmJSModule::Global::serialize(uint8_t* cursor) const
-{
- cursor = WriteBytes(cursor, &pod, sizeof(pod));
- cursor = SerializeName(cursor, name_);
- return cursor;
-}
-
-size_t
-AsmJSModule::Global::serializedSize() const
-{
- return sizeof(pod) +
- SerializedNameSize(name_);
-}
-
-const uint8_t*
-AsmJSModule::Global::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
- (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
- (cursor = DeserializeName(cx, cursor, &name_));
- return cursor;
-}
-
-bool
-AsmJSModule::Global::clone(JSContext* cx, Global* out) const
-{
- *out = *this;
- return true;
-}
-
-uint8_t*
-AsmJSModule::Export::serialize(uint8_t* cursor) const
-{
- cursor = SerializeName(cursor, name_);
- cursor = SerializeName(cursor, maybeFieldName_);
- cursor = WriteBytes(cursor, &pod, sizeof(pod));
- return cursor;
-}
-
-size_t
-AsmJSModule::Export::serializedSize() const
-{
- return SerializedNameSize(name_) +
- SerializedNameSize(maybeFieldName_) +
- sizeof(pod);
-}
-
-const uint8_t*
-AsmJSModule::Export::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
- (cursor = DeserializeName(cx, cursor, &name_)) &&
- (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
- (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
- return cursor;
-}
-
-bool
-AsmJSModule::Export::clone(JSContext* cx, Export* out) const
-{
- out->name_ = name_;
- out->maybeFieldName_ = maybeFieldName_;
- out->pod = pod;
- return true;
-}
-
-size_t
-AsmJSModule::serializedSize() const
-{
- MOZ_ASSERT(isFinished());
- return wasm_->serializedSize() +
- linkData_->serializedSize() +
- sizeof(pod) +
- SerializedVectorSize(globals_) +
- SerializedPodVectorSize(imports_) +
- SerializedVectorSize(exports_) +
- SerializedNameSize(globalArgumentName_) +
- SerializedNameSize(importArgumentName_) +
- SerializedNameSize(bufferArgumentName_);
-}
-
-uint8_t*
-AsmJSModule::serialize(uint8_t* cursor) const
-{
- MOZ_ASSERT(isFinished());
- cursor = wasm_->serialize(cursor);
- cursor = linkData_->serialize(cursor);
- cursor = WriteBytes(cursor, &pod, sizeof(pod));
- cursor = SerializeVector(cursor, globals_);
- cursor = SerializePodVector(cursor, imports_);
- cursor = SerializeVector(cursor, exports_);
- cursor = SerializeName(cursor, globalArgumentName_);
- cursor = SerializeName(cursor, importArgumentName_);
- cursor = SerializeName(cursor, bufferArgumentName_);
- return cursor;
-}
-
-const uint8_t*
-AsmJSModule::deserialize(ExclusiveContext* cx, const uint8_t* cursor)
-{
- linkData_ = cx->make_unique<StaticLinkData>();
- if (!linkData_)
- return nullptr;
-
- // To avoid GC-during-deserialization corner cases, prevent atoms from
- // being collected.
- AutoKeepAtoms aka(cx->perThreadData);
-
- (cursor = Module::deserialize(cx, cursor, &wasm_)) &&
- (cursor = linkData_->deserialize(cx, cursor)) &&
- (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
- (cursor = DeserializeVector(cx, cursor, &globals_)) &&
- (cursor = DeserializePodVector(cx, cursor, &imports_)) &&
- (cursor = DeserializeVector(cx, cursor, &exports_)) &&
- (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
- (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
- (cursor = DeserializeName(cx, cursor, &bufferArgumentName_));
-
- return cursor;
-}
-
-bool
-AsmJSModule::clone(JSContext* cx, HandleAsmJSModule obj) const
-{
- auto out = cx->new_<AsmJSModule>(scriptSource(), srcStart_, srcBodyStart_, pod.strict_);
- if (!out)
- return false;
-
- obj->setModule(out);
-
- out->wasm_ = wasm_->clone(cx, *linkData_);
- if (!out->wasm_)
- return false;
-
- out->linkData_ = cx->make_unique<StaticLinkData>();
- if (!out->linkData_ || !linkData_->clone(cx, out->linkData_.get()))
- return false;
-
- out->pod = pod;
-
- if (!CloneVector(cx, globals_, &out->globals_) ||
- !ClonePodVector(cx, imports_, &out->imports_) ||
- !CloneVector(cx, exports_, &out->exports_))
- {
- return false;
- }
-
- out->globalArgumentName_ = globalArgumentName_;
- out->importArgumentName_ = importArgumentName_;
- out->bufferArgumentName_ = bufferArgumentName_;
- return true;
-}
-
-struct PropertyNameWrapper
-{
- PropertyName* name;
-
- PropertyNameWrapper()
- : name(nullptr)
- {}
- explicit PropertyNameWrapper(PropertyName* name)
- : name(name)
- {}
- size_t serializedSize() const {
- return SerializedNameSize(name);
- }
- uint8_t* serialize(uint8_t* cursor) const {
- return SerializeName(cursor, name);
- }
- const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
- return DeserializeName(cx, cursor, &name);
- }
-};
-
-class ModuleChars
-{
- protected:
- uint32_t isFunCtor_;
- Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
-
- public:
- static uint32_t beginOffset(AsmJSParser& parser) {
- return parser.pc->maybeFunction->pn_pos.begin;
- }
-
- static uint32_t endOffset(AsmJSParser& parser) {
- TokenPos pos(0, 0); // initialize to silence GCC warning
- MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos, TokenStream::Operand));
- return pos.end;
- }
-};
-
-class ModuleCharsForStore : ModuleChars
-{
- uint32_t uncompressedSize_;
- uint32_t compressedSize_;
- Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
-
- public:
- bool init(AsmJSParser& parser) {
- MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
-
- uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
- size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
- if (maxCompressedSize < uncompressedSize_)
- return false;
-
- if (!compressedBuffer_.resize(maxCompressedSize))
- return false;
-
- const char16_t* chars = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
- const char* source = reinterpret_cast<const char*>(chars);
- size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
- if (!compressedSize || compressedSize > UINT32_MAX)
- return false;
-
- compressedSize_ = compressedSize;
-
- // For a function statement or named function expression:
- // function f(x,y,z) { abc }
- // the range [beginOffset, endOffset) captures the source:
- // f(x,y,z) { abc }
- // An unnamed function expression captures the same thing, sans 'f'.
- // Since asm.js modules do not contain any free variables, equality of
- // [beginOffset, endOffset) is sufficient to guarantee identical code
- // generation, modulo MachineId.
- //
- // For functions created with 'new Function', function arguments are
- // not present in the source so we must manually explicitly serialize
- // and match the formals as a Vector of PropertyName.
- isFunCtor_ = parser.pc->isFunctionConstructorBody();
- if (isFunCtor_) {
- unsigned numArgs;
- ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
- for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
- if (!funCtorArgs_.append(arg->name()))
- return false;
- }
- }
-
- return true;
- }
-
- size_t serializedSize() const {
- return sizeof(uint32_t) +
- sizeof(uint32_t) +
- compressedSize_ +
- sizeof(uint32_t) +
- (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
- }
-
- uint8_t* serialize(uint8_t* cursor) const {
- cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
- cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
- cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
- cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
- if (isFunCtor_)
- cursor = SerializeVector(cursor, funCtorArgs_);
- return cursor;
- }
-};
-
-class ModuleCharsForLookup : ModuleChars
-{
- Vector<char16_t, 0, SystemAllocPolicy> chars_;
-
- public:
- const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor) {
- uint32_t uncompressedSize;
- cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
-
- uint32_t compressedSize;
- cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
-
- if (!chars_.resize(uncompressedSize / sizeof(char16_t)))
- return nullptr;
-
- const char* source = reinterpret_cast<const char*>(cursor);
- char* dest = reinterpret_cast<char*>(chars_.begin());
- if (!LZ4::decompress(source, dest, uncompressedSize))
- return nullptr;
-
- cursor += compressedSize;
-
- cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
- if (isFunCtor_)
- cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
-
- return cursor;
- }
-
- bool match(AsmJSParser& parser) const {
- const char16_t* parseBegin = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
- const char16_t* parseLimit = parser.tokenStream.rawLimit();
- MOZ_ASSERT(parseLimit >= parseBegin);
- if (uint32_t(parseLimit - parseBegin) < chars_.length())
- return false;
- if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
- return false;
- if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
- return false;
- if (isFunCtor_) {
- // For function statements, the closing } is included as the last
- // character of the matched source. For Function constructor,
- // parsing terminates with EOF which we must explicitly check. This
- // prevents
- // new Function('"use asm"; function f() {} return f')
- // from incorrectly matching
- // new Function('"use asm"; function f() {} return ff')
- if (parseBegin + chars_.length() != parseLimit)
- return false;
- unsigned numArgs;
- ParseNode* arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
- if (funCtorArgs_.length() != numArgs)
- return false;
- for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
- if (funCtorArgs_[i].name != arg->name())
- return false;
- }
- }
- return true;
- }
-};
-
-JS::AsmJSCacheResult
-js::StoreAsmJSModuleInCache(AsmJSParser& parser, const AsmJSModule& module, ExclusiveContext* cx)
-{
- MachineId machineId;
- if (!machineId.extractCurrentState(cx))
- return JS::AsmJSCache_InternalError;
-
- ModuleCharsForStore moduleChars;
- if (!moduleChars.init(parser))
- return JS::AsmJSCache_InternalError;
-
- size_t serializedSize = machineId.serializedSize() +
- moduleChars.serializedSize() +
- module.serializedSize();
-
- JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
- if (!open)
- return JS::AsmJSCache_Disabled_Internal;
-
- const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
- const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
- bool installed = parser.options().installedFile;
-
- ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
- JS::AsmJSCacheResult openResult =
- open(cx->global(), installed, begin, end, serializedSize, &entry.memory, &entry.handle);
- if (openResult != JS::AsmJSCache_Success)
- return openResult;
-
- uint8_t* cursor = entry.memory;
- cursor = machineId.serialize(cursor);
- cursor = moduleChars.serialize(cursor);
- cursor = module.serialize(cursor);
-
- MOZ_ASSERT(cursor == entry.memory + serializedSize);
- return JS::AsmJSCache_Success;
-}
-
-bool
-js::LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
- bool* loadedFromCache, UniqueChars* compilationTimeReport)
-{
- int64_t usecBefore = PRMJ_Now();
-
- *loadedFromCache = false;
-
- MachineId machineId;
- if (!machineId.extractCurrentState(cx))
- return true;
-
- JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
- if (!open)
- return true;
-
- const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
- const char16_t* limit = parser.tokenStream.rawLimit();
-
- ScopedCacheEntryOpenedForRead entry(cx);
- if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
- return true;
-
- const uint8_t* cursor = entry.memory;
-
- MachineId cachedMachineId;
- cursor = cachedMachineId.deserialize(cx, cursor);
- if (!cursor)
- return false;
- if (machineId != cachedMachineId)
- return true;
-
- ModuleCharsForLookup moduleChars;
- cursor = moduleChars.deserialize(cx, cursor);
- if (!moduleChars.match(parser))
- return true;
-
- uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
- uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
- bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
-
- AsmJSModule* module = cx->new_<AsmJSModule>(parser.ss, srcStart, srcBodyStart, strict);
- if (!module)
- return false;
-
- moduleObj->setModule(module);
-
- cursor = module->deserialize(cx, cursor);
- if (!cursor)
- return false;
-
- bool atEnd = cursor == entry.memory + entry.serializedSize;
- MOZ_ASSERT(atEnd, "Corrupt cache file");
- if (!atEnd)
- return true;
-
- if (module->wasm().compileArgs() != CompileArgs(cx))
- return true;
-
- module->staticallyLink(cx);
-
- if (!parser.tokenStream.advance(module->srcEndBeforeCurly()))
- return false;
-
- *loadedFromCache = true;
-
- int64_t usecAfter = PRMJ_Now();
- int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
- *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
- return true;
-}
-
deleted file mode 100644
--- a/js/src/asmjs/AsmJSModule.h
+++ /dev/null
@@ -1,634 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef asmjs_AsmJSModule_h
-#define asmjs_AsmJSModule_h
-
-#include "mozilla/EnumeratedArray.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/Move.h"
-#include "mozilla/PodOperations.h"
-
-#include "asmjs/AsmJSValidate.h"
-#include "asmjs/WasmModule.h"
-#include "builtin/SIMD.h"
-#include "gc/Tracer.h"
-#include "vm/TypedArrayObject.h"
-
-namespace js {
-
-class AsmJSModuleObject;
-typedef Handle<AsmJSModuleObject*> HandleAsmJSModule;
-
-// The asm.js spec recognizes this set of builtin Math functions.
-enum AsmJSMathBuiltinFunction
-{
- AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
- AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
- AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
- AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
- AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
- AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max,
- AsmJSMathBuiltin_clz32
-};
-
-// The asm.js spec will recognize this set of builtin Atomics functions.
-enum AsmJSAtomicsBuiltinFunction
-{
- AsmJSAtomicsBuiltin_compareExchange,
- AsmJSAtomicsBuiltin_exchange,
- AsmJSAtomicsBuiltin_load,
- AsmJSAtomicsBuiltin_store,
- AsmJSAtomicsBuiltin_fence,
- AsmJSAtomicsBuiltin_add,
- AsmJSAtomicsBuiltin_sub,
- AsmJSAtomicsBuiltin_and,
- AsmJSAtomicsBuiltin_or,
- AsmJSAtomicsBuiltin_xor,
- AsmJSAtomicsBuiltin_isLockFree
-};
-
-// Set of known global object SIMD's attributes, i.e. types
-enum AsmJSSimdType
-{
- AsmJSSimdType_int32x4,
- AsmJSSimdType_float32x4
-};
-
-// Set of known operations, for a given SIMD type (int32x4, float32x4,...)
-enum AsmJSSimdOperation
-{
-#define ASMJSSIMDOPERATION(op) AsmJSSimdOperation_##op,
- FORALL_SIMD_OP(ASMJSSIMDOPERATION)
-#undef ASMJSSIMDOPERATION
-};
-
-// An AsmJSModule extends (via containment) a wasm::Module with the extra persistent state
-// necessary to represent a compiled asm.js module.
-class AsmJSModule
-{
- public:
- class Global
- {
- public:
- enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
- AtomicsBuiltinFunction, Constant, SimdCtor, SimdOperation, ByteLength };
- enum VarInitKind { InitConstant, InitImport };
- enum ConstantKind { GlobalConstant, MathConstant };
-
- private:
- struct CacheablePod {
- Which which_;
- union {
- struct {
- uint32_t globalDataOffset_;
- VarInitKind initKind_;
- union {
- wasm::ValType importType_;
- wasm::Val val_;
- } u;
- } var;
- uint32_t ffiIndex_;
- Scalar::Type viewType_;
- AsmJSMathBuiltinFunction mathBuiltinFunc_;
- AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
- AsmJSSimdType simdCtorType_;
- struct {
- AsmJSSimdType type_;
- AsmJSSimdOperation which_;
- } simdOp;
- struct {
- ConstantKind kind_;
- double value_;
- } constant;
- } u;
- } pod;
- PropertyName* name_;
-
- friend class AsmJSModule;
-
- Global(Which which, PropertyName* name) {
- mozilla::PodZero(&pod); // zero padding for Valgrind
- pod.which_ = which;
- name_ = name;
- MOZ_ASSERT_IF(name_, name_->isTenured());
- }
-
- void trace(JSTracer* trc) {
- if (name_)
- TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
- }
-
- public:
- Global() {}
- Which which() const {
- return pod.which_;
- }
- uint32_t varGlobalDataOffset() const {
- MOZ_ASSERT(pod.which_ == Variable);
- return pod.u.var.globalDataOffset_;
- }
- VarInitKind varInitKind() const {
- MOZ_ASSERT(pod.which_ == Variable);
- return pod.u.var.initKind_;
- }
- wasm::Val varInitVal() const {
- MOZ_ASSERT(pod.which_ == Variable);
- MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
- return pod.u.var.u.val_;
- }
- wasm::ValType varInitImportType() const {
- MOZ_ASSERT(pod.which_ == Variable);
- MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
- return pod.u.var.u.importType_;
- }
- PropertyName* varImportField() const {
- MOZ_ASSERT(pod.which_ == Variable);
- MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
- return name_;
- }
- PropertyName* ffiField() const {
- MOZ_ASSERT(pod.which_ == FFI);
- return name_;
- }
- uint32_t ffiIndex() const {
- MOZ_ASSERT(pod.which_ == FFI);
- return pod.u.ffiIndex_;
- }
- // When a view is created from an imported constructor:
- // var I32 = stdlib.Int32Array;
- // var i32 = new I32(buffer);
- // the second import has nothing to validate and thus has a null field.
- PropertyName* maybeViewName() const {
- MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
- return name_;
- }
- Scalar::Type viewType() const {
- MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
- return pod.u.viewType_;
- }
- PropertyName* mathName() const {
- MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
- return name_;
- }
- PropertyName* atomicsName() const {
- MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
- return name_;
- }
- AsmJSMathBuiltinFunction mathBuiltinFunction() const {
- MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
- return pod.u.mathBuiltinFunc_;
- }
- AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
- MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
- return pod.u.atomicsBuiltinFunc_;
- }
- AsmJSSimdType simdCtorType() const {
- MOZ_ASSERT(pod.which_ == SimdCtor);
- return pod.u.simdCtorType_;
- }
- PropertyName* simdCtorName() const {
- MOZ_ASSERT(pod.which_ == SimdCtor);
- return name_;
- }
- PropertyName* simdOperationName() const {
- MOZ_ASSERT(pod.which_ == SimdOperation);
- return name_;
- }
- AsmJSSimdOperation simdOperation() const {
- MOZ_ASSERT(pod.which_ == SimdOperation);
- return pod.u.simdOp.which_;
- }
- AsmJSSimdType simdOperationType() const {
- MOZ_ASSERT(pod.which_ == SimdOperation);
- return pod.u.simdOp.type_;
- }
- PropertyName* constantName() const {
- MOZ_ASSERT(pod.which_ == Constant);
- return name_;
- }
- ConstantKind constantKind() const {
- MOZ_ASSERT(pod.which_ == Constant);
- return pod.u.constant.kind_;
- }
- double constantValue() const {
- MOZ_ASSERT(pod.which_ == Constant);
- return pod.u.constant.value_;
- }
-
- WASM_DECLARE_SERIALIZABLE(Global);
- };
-
- typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
-
- class Import
- {
- uint32_t ffiIndex_;
- public:
- Import() = default;
- explicit Import(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
- uint32_t ffiIndex() const { return ffiIndex_; }
- };
-
- typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
-
- class Export
- {
- PropertyName* name_;
- PropertyName* maybeFieldName_;
- struct CacheablePod {
- uint32_t wasmIndex_;
- uint32_t startOffsetInModule_; // Store module-start-relative offsets
- uint32_t endOffsetInModule_; // so preserved by serialization.
- } pod;
-
- public:
- Export() {}
- Export(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
- uint32_t startOffsetInModule, uint32_t endOffsetInModule)
- : name_(name),
- maybeFieldName_(maybeFieldName)
- {
- MOZ_ASSERT(name_->isTenured());
- MOZ_ASSERT_IF(maybeFieldName_, maybeFieldName_->isTenured());
- pod.wasmIndex_ = wasmIndex;
- pod.startOffsetInModule_ = startOffsetInModule;
- pod.endOffsetInModule_ = endOffsetInModule;
- }
-
- void trace(JSTracer* trc) {
- TraceManuallyBarrieredEdge(trc, &name_, "asm.js export name");
- if (maybeFieldName_)
- TraceManuallyBarrieredEdge(trc, &maybeFieldName_, "asm.js export field");
- }
-
- PropertyName* name() const {
- return name_;
- }
- PropertyName* maybeFieldName() const {
- return maybeFieldName_;
- }
- uint32_t startOffsetInModule() const {
- return pod.startOffsetInModule_;
- }
- uint32_t endOffsetInModule() const {
- return pod.endOffsetInModule_;
- }
- static const uint32_t ChangeHeap = UINT32_MAX;
- bool isChangeHeap() const {
- return pod.wasmIndex_ == ChangeHeap;
- }
- uint32_t wasmIndex() const {
- MOZ_ASSERT(!isChangeHeap());
- return pod.wasmIndex_;
- }
-
- WASM_DECLARE_SERIALIZABLE(Export)
- };
-
- typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
-
- typedef JS::UniquePtr<wasm::Module, JS::DeletePolicy<wasm::Module>> UniqueWasmModule;
-
- private:
- UniqueWasmModule wasm_;
- wasm::UniqueStaticLinkData linkData_;
- struct CacheablePod {
- uint32_t minHeapLength_;
- uint32_t maxHeapLength_;
- uint32_t heapLengthMask_;
- uint32_t numFFIs_;
- uint32_t srcLength_;
- uint32_t srcLengthWithRightBrace_;
- bool strict_;
- bool hasArrayView_;
- bool isSharedView_;
- bool hasFixedMinHeapLength_;
- } pod;
- const ScriptSourceHolder scriptSource_;
- const uint32_t srcStart_;
- const uint32_t srcBodyStart_;
- GlobalVector globals_;
- ImportVector imports_;
- ExportVector exports_;
- PropertyName* globalArgumentName_;
- PropertyName* importArgumentName_;
- PropertyName* bufferArgumentName_;
-
- public:
- explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
- bool strict);
- void trace(JSTracer* trc);
-
- /*************************************************************************/
- // These functions may be used as soon as the module is constructed:
-
- ScriptSource* scriptSource() const {
- return scriptSource_.get();
- }
- bool strict() const {
- return pod.strict_;
- }
-
- // srcStart() refers to the offset in the ScriptSource to the beginning of
- // the asm.js module function. If the function has been created with the
- // Function constructor, this will be the first character in the function
- // source. Otherwise, it will be the opening parenthesis of the arguments
- // list.
- uint32_t srcStart() const {
- return srcStart_;
- }
-
- // srcBodyStart() refers to the offset in the ScriptSource to the end
- // of the 'use asm' string-literal token.
- uint32_t srcBodyStart() const {
- return srcBodyStart_;
- }
-
- // While these functions may be accessed at any time, their values will
- // change as the module is compiled.
- uint32_t minHeapLength() const {
- return pod.minHeapLength_;
- }
- uint32_t maxHeapLength() const {
- return pod.maxHeapLength_;
- }
- uint32_t heapLengthMask() const {
- MOZ_ASSERT(pod.hasFixedMinHeapLength_);
- return pod.heapLengthMask_;
- }
-
- void initGlobalArgumentName(PropertyName* n) {
- MOZ_ASSERT(!isFinished());
- MOZ_ASSERT_IF(n, n->isTenured());
- globalArgumentName_ = n;
- }
- void initImportArgumentName(PropertyName* n) {
- MOZ_ASSERT(!isFinished());
- MOZ_ASSERT_IF(n, n->isTenured());
- importArgumentName_ = n;
- }
- void initBufferArgumentName(PropertyName* n) {
- MOZ_ASSERT(!isFinished());
- MOZ_ASSERT_IF(n, n->isTenured());
- bufferArgumentName_ = n;
- }
- PropertyName* globalArgumentName() const {
- return globalArgumentName_;
- }
- PropertyName* importArgumentName() const {
- return importArgumentName_;
- }
- PropertyName* bufferArgumentName() const {
- return bufferArgumentName_;
- }
-
- bool addGlobalVarInit(const wasm::Val& v, uint32_t globalDataOffset) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::Variable, nullptr);
- g.pod.u.var.initKind_ = Global::InitConstant;
- g.pod.u.var.u.val_ = v;
- g.pod.u.var.globalDataOffset_ = globalDataOffset;
- return globals_.append(g);
- }
- bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t globalDataOffset) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::Variable, name);
- g.pod.u.var.initKind_ = Global::InitImport;
- g.pod.u.var.u.importType_ = importType;
- g.pod.u.var.globalDataOffset_ = globalDataOffset;
- return globals_.append(g);
- }
- bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
- MOZ_ASSERT(!isFinished());
- if (pod.numFFIs_ == UINT32_MAX)
- return false;
- Global g(Global::FFI, field);
- g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
- return globals_.append(g);
- }
- bool addArrayView(Scalar::Type vt, PropertyName* maybeField) {
- MOZ_ASSERT(!isFinished());
- pod.hasArrayView_ = true;
- pod.isSharedView_ = false;
- Global g(Global::ArrayView, maybeField);
- g.pod.u.viewType_ = vt;
- return globals_.append(g);
- }
- bool addArrayViewCtor(Scalar::Type vt, PropertyName* field) {
- MOZ_ASSERT(!isFinished());
- MOZ_ASSERT(field);
- pod.isSharedView_ = false;
- Global g(Global::ArrayViewCtor, field);
- g.pod.u.viewType_ = vt;
- return globals_.append(g);
- }
- bool addByteLength() {
- MOZ_ASSERT(!isFinished());
- Global g(Global::ByteLength, nullptr);
- return globals_.append(g);
- }
- bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::MathBuiltinFunction, field);
- g.pod.u.mathBuiltinFunc_ = func;
- return globals_.append(g);
- }
- bool addMathBuiltinConstant(double value, PropertyName* field) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::Constant, field);
- g.pod.u.constant.value_ = value;
- g.pod.u.constant.kind_ = Global::MathConstant;
- return globals_.append(g);
- }
- bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::AtomicsBuiltinFunction, field);
- g.pod.u.atomicsBuiltinFunc_ = func;
- return globals_.append(g);
- }
- bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::SimdCtor, field);
- g.pod.u.simdCtorType_ = type;
- return globals_.append(g);
- }
- bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::SimdOperation, field);
- g.pod.u.simdOp.type_ = type;
- g.pod.u.simdOp.which_ = op;
- return globals_.append(g);
- }
- bool addGlobalConstant(double value, PropertyName* name) {
- MOZ_ASSERT(!isFinished());
- Global g(Global::Constant, name);
- g.pod.u.constant.value_ = value;
- g.pod.u.constant.kind_ = Global::GlobalConstant;
- return globals_.append(g);
- }
- bool addImport(uint32_t ffiIndex, uint32_t importIndex) {
- MOZ_ASSERT(imports_.length() == importIndex);
- return imports_.emplaceBack(ffiIndex);
- }
- bool addExport(PropertyName* name, PropertyName* maybeFieldName, uint32_t wasmIndex,
- uint32_t funcSrcBegin, uint32_t funcSrcEnd)
- {
- // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
- // (the entire file) and ExportedFunctions store offsets relative to
- // the beginning of the module (so that they are caching-invariant).
- MOZ_ASSERT(!isFinished());
- MOZ_ASSERT(srcStart_ < funcSrcBegin);
- MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
- return exports_.emplaceBack(name, maybeFieldName, wasmIndex,
- funcSrcBegin - srcStart_, funcSrcEnd - srcStart_);
- }
- bool addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
- MOZ_ASSERT(!isFinished());
- MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
- MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
- MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
- MOZ_ASSERT(max <= pod.maxHeapLength_);
- MOZ_ASSERT(min <= max);
- pod.heapLengthMask_ = mask;
- pod.minHeapLength_ = min;
- pod.maxHeapLength_ = max;
- pod.hasFixedMinHeapLength_ = true;
- return true;
- }
-
- const GlobalVector& globals() const {
- return globals_;
- }
- const ImportVector& imports() const {
- return imports_;
- }
- const ExportVector& exports() const {
- return exports_;
- }
-
- void setViewsAreShared() {
- if (pod.hasArrayView_)
- pod.isSharedView_ = true;
- }
- bool hasArrayView() const {
- return pod.hasArrayView_;
- }
- bool isSharedView() const {
- return pod.isSharedView_;
- }
- bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
- MOZ_ASSERT(!isFinished());
- if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
- return false;
- if (len > pod.maxHeapLength_)
- return false;
- len = RoundUpToNextValidAsmJSHeapLength(len);
- if (len > pod.minHeapLength_)
- pod.minHeapLength_ = len;
- return true;
- }
-
- /*************************************************************************/
- // A module isFinished() when compilation completes. After being finished,
- // a module must be statically and dynamically linked before execution.
-
- bool isFinished() const {
- return !!wasm_;
- }
- void finish(wasm::Module* wasm, wasm::UniqueStaticLinkData linkData,
- uint32_t endBeforeCurly, uint32_t endAfterCurly);
-
- /*************************************************************************/
- // These accessor functions can only be used after finish():
-
- wasm::Module& wasm() const {
- MOZ_ASSERT(isFinished());
- return *wasm_;
- }
- uint32_t numFFIs() const {
- MOZ_ASSERT(isFinished());
- return pod.numFFIs_;
- }
- uint32_t srcEndBeforeCurly() const {
- MOZ_ASSERT(isFinished());
- return srcStart_ + pod.srcLength_;
- }
- uint32_t srcEndAfterCurly() const {
- MOZ_ASSERT(isFinished());
- return srcStart_ + pod.srcLengthWithRightBrace_;
- }
- bool staticallyLink(ExclusiveContext* cx) {
- return wasm_->staticallyLink(cx, *linkData_);
- }
-
- // See WASM_DECLARE_SERIALIZABLE.
- size_t serializedSize() const;
- uint8_t* serialize(uint8_t* cursor) const;
- const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
- bool clone(JSContext* cx, HandleAsmJSModule moduleObj) const;
- void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
- size_t* asmJSModuleData);
-};
-
-// Store the just-parsed module in the cache using AsmJSCacheOps.
-extern JS::AsmJSCacheResult
-StoreAsmJSModuleInCache(AsmJSParser& parser,
- const AsmJSModule& module,
- ExclusiveContext* cx);
-
-// Attempt to load the asm.js module that is about to be parsed from the cache
-// using AsmJSCacheOps. The return value indicates whether an error was
-// reported. The loadedFromCache outparam indicates whether the module was
-// successfully loaded and stored in moduleObj.extern bool
-extern bool
-LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, HandleAsmJSModule moduleObj,
- bool* loadedFromCache, UniqueChars* compilationTimeReport);
-
-// This function must be called for every detached ArrayBuffer.
-extern bool
-OnDetachAsmJSArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer);
-
-// An AsmJSModuleObject is an internal implementation object (i.e., not exposed
-// directly to user script) which manages the lifetime of an AsmJSModule. A
-// JSObject is necessary since we want LinkAsmJS/CallAsmJS JSFunctions to be
-// able to point to their module via their extended slots.
-class AsmJSModuleObject : public NativeObject
-{
- static const unsigned MODULE_SLOT = 0;
-
- public:
- static const unsigned RESERVED_SLOTS = 1;
-
- static AsmJSModuleObject* create(ExclusiveContext* cx);
-
- bool hasModule() const;
- void setModule(AsmJSModule* module);
- AsmJSModule& module() const;
-
- void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
- size_t* asmJSModuleData) {
- module().addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData);
- }
-
- static const Class class_;
-};
-
-} // namespace js
-
-#endif /* asmjs_AsmJSModule_h */
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -13,17 +13,17 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "asmjs/WasmGenerator.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "asmjs/WasmStubs.h"
#include "jit/MacroAssembler-inl.h"
using namespace js;
using namespace js::jit;
using namespace js::wasm;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -19,29 +19,33 @@
#include "asmjs/WasmModule.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/EnumeratedRange.h"
#include "mozilla/PodOperations.h"
#include "jsprf.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "asmjs/WasmSerialize.h"
#include "builtin/AtomicsObject.h"
+#include "builtin/SIMD.h"
#ifdef JS_ION_PERF
# include "jit/PerfSpewer.h"
#endif
#include "jit/BaselineJIT.h"
#include "jit/ExecutableAllocator.h"
+#include "jit/JitCommon.h"
#include "js/MemoryMetrics.h"
#ifdef MOZ_VTUNE
# include "vtune/VTuneWrapper.h"
#endif
+#include "jsobjinlines.h"
+
#include "jit/MacroAssembler-inl.h"
#include "vm/ArrayBufferObject-inl.h"
#include "vm/TypeInference-inl.h"
using namespace js;
using namespace js::jit;
using namespace js::wasm;
using mozilla::BinarySearch;
@@ -469,16 +473,23 @@ Module::rawHeapPtr() const
}
uint8_t*&
Module::rawHeapPtr()
{
return *(uint8_t**)(globalData() + HeapGlobalDataOffset);
}
+WasmActivation*&
+Module::activation()
+{
+ MOZ_ASSERT(dynamicallyLinked_);
+ return *reinterpret_cast<WasmActivation**>(globalData() + ActivationGlobalDataOffset);
+}
+
void
Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
{
MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
MOZ_ASSERT(!maybeHeap_);
MOZ_ASSERT(!rawHeapPtr());
uint8_t* ptrBase = heap->dataPointerEither().unwrap(/*safe - protected by Module methods*/);
@@ -599,16 +610,71 @@ Module::sendCodeRangesToProfiler(JSConte
method.class_file_name = nullptr;
method.source_file_name = nullptr;
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, (void*)&method);
}
}
#endif
}
+void
+Module::setProfilingEnabled(bool enabled, JSContext* cx)
+{
+ MOZ_ASSERT(dynamicallyLinked_);
+ MOZ_ASSERT(!activation());
+
+ if (profilingEnabled_ == enabled)
+ return;
+
+ // When enabled, generate profiling labels for every name in funcNames_
+ // that is the name of some Function CodeRange. This involves malloc() so
+ // do it now since, once we start sampling, we'll be in a signal-handing
+ // context where we cannot malloc.
+ if (enabled) {
+ funcLabels_.resize(funcNames_.length());
+ for (const CodeRange& codeRange : codeRanges_) {
+ if (!codeRange.isFunction())
+ continue;
+ unsigned lineno = codeRange.funcLineNumber();
+ const char* name = funcNames_[codeRange.funcNameIndex()].get();
+ funcLabels_[codeRange.funcNameIndex()] =
+ UniqueChars(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
+ }
+ } else {
+ funcLabels_.clear();
+ }
+
+ // Patch callsites and returns to execute profiling prologues/epililogues.
+ {
+ AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled");
+
+ for (const CallSite& callSite : callSites_)
+ EnableProfilingPrologue(*this, callSite, enabled);
+
+ for (const CodeRange& codeRange : codeRanges_)
+ EnableProfilingEpilogue(*this, codeRange, enabled);
+ }
+
+ // Update the function-pointer tables to point to profiling prologues.
+ for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
+ auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
+ for (size_t i = 0; i < funcPtrTable.numElems; i++) {
+ const CodeRange* codeRange = lookupCodeRange(array[i]);
+ void* from = code() + codeRange->funcNonProfilingEntry();
+ void* to = code() + codeRange->funcProfilingEntry();
+ if (!enabled)
+ Swap(from, to);
+ MOZ_ASSERT(array[i] == from);
+ array[i] = to;
+ }
+ }
+
+ profilingEnabled_ = enabled;
+}
+
Module::ImportExit&
Module::importToExit(const Import& import)
{
return *reinterpret_cast<ImportExit*>(globalData() + import.exitGlobalDataOffset());
}
/* static */ Module::CacheablePod
Module::zeroPod()
@@ -945,41 +1011,27 @@ Module::maybeHeap() const
size_t
Module::heapLength() const
{
MOZ_ASSERT(dynamicallyLinked_);
return maybeHeap_ ? maybeHeap_->byteLength() : 0;
}
-Module*
-Module::nextLinked() const
-{
- MOZ_ASSERT(dynamicallyLinked_);
- return next_;
-}
-
void
Module::deoptimizeImportExit(uint32_t importIndex)
{
MOZ_ASSERT(dynamicallyLinked_);
const Import& import = imports_[importIndex];
ImportExit& exit = importToExit(import);
exit.code = code() + import.interpExitCodeOffset();
exit.baselineScript = nullptr;
}
bool
-Module::hasDetachedHeap() const
-{
- MOZ_ASSERT(dynamicallyLinked_);
- return pod.usesHeap_ && !maybeHeap_;
-}
-
-bool
Module::changeHeap(Handle<ArrayBufferObject*> newHeap, JSContext* cx)
{
MOZ_ASSERT(dynamicallyLinked_);
MOZ_ASSERT(pod.usesHeap_);
// Content JS should not be able to run (and change heap) from within an
// interrupt callback, but in case it does, fail to change heap. Otherwise,
// the heap can change at every single instruction which would prevent
@@ -1006,43 +1058,166 @@ Module::detachHeap(JSContext* cx)
if (interrupted_) {
JS_ReportError(cx, "attempt to detach from inside interrupt handler");
return false;
}
// Even if this->active(), to reach here, the activation must have called
// out via an import exit stub. FFI stubs check if heapDatum() is null on
// reentry and throw an exception if so.
- MOZ_ASSERT_IF(active(), activation()->exitReason() == ExitReason::ImportJit ||
- activation()->exitReason() == ExitReason::ImportInterp);
+ MOZ_ASSERT_IF(activation(), activation()->exitReason() == ExitReason::ImportJit ||
+ activation()->exitReason() == ExitReason::ImportInterp);
AutoMutateCode amc(cx, *this, "Module::detachHeap");
despecializeFromHeap(maybeHeap_);
return true;
}
void
Module::setInterrupted(bool interrupted)
{
MOZ_ASSERT(dynamicallyLinked_);
interrupted_ = interrupted;
}
-WasmActivation*&
-Module::activation()
+Module*
+Module::nextLinked() const
+{
+ MOZ_ASSERT(dynamicallyLinked_);
+ return next_;
+}
+
+bool
+Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args)
{
MOZ_ASSERT(dynamicallyLinked_);
- return *reinterpret_cast<WasmActivation**>(globalData() + ActivationGlobalDataOffset);
-}
+
+ const Export& exp = exports_[exportIndex];
+
+ // Enable/disable profiling in the Module to match the current global
+ // profiling state. Don't do this if the Module is already active on the
+ // stack since this would leave the Module in a state where profiling is
+ // enabled but the stack isn't unwindable.
+ if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation())
+ setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);
+
+ // The calling convention for an external call into wasm is to pass an
+ // array of 16-byte values where each value contains either a coerced int32
+ // (in the low word), a double value (in the low dword) or a SIMD vector
+ // value, with the coercions specified by the wasm signature. The external
+ // entry point unpacks this array into the system-ABI-specified registers
+ // and stack memory and then calls into the internal entry point. The return
+ // value is stored in the first element of the array (which, therefore, must
+ // have length >= 1).
+ Vector<Module::EntryArg, 8> coercedArgs(cx);
+ if (!coercedArgs.resize(Max<size_t>(1, exp.sig().args().length())))
+ return false;
+
+ RootedValue v(cx);
+ for (unsigned i = 0; i < exp.sig().args().length(); ++i) {
+ v = i < args.length() ? args[i] : UndefinedValue();
+ switch (exp.sig().arg(i)) {
+ case ValType::I32:
+ if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i]))
+ return false;
+ break;
+ case ValType::I64:
+ MOZ_CRASH("int64");
+ case ValType::F32:
+ if (!RoundFloat32(cx, v, (float*)&coercedArgs[i]))
+ return false;
+ break;
+ case ValType::F64:
+ if (!ToNumber(cx, v, (double*)&coercedArgs[i]))
+ return false;
+ break;
+ case ValType::I32x4: {
+ SimdConstant simd;
+ if (!ToSimdConstant<Int32x4>(cx, v, &simd))
+ return false;
+ memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize);
+ break;
+ }
+ case ValType::F32x4: {
+ SimdConstant simd;
+ if (!ToSimdConstant<Float32x4>(cx, v, &simd))
+ return false;
+ memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize);
+ break;
+ }
+ }
+ }
-Module::EntryFuncPtr
-Module::entryTrampoline(const Export& func) const
-{
- MOZ_ASSERT(dynamicallyLinked_);
- return JS_DATA_TO_FUNC_PTR(EntryFuncPtr, code() + func.stubOffset());
+ // The correct way to handle this situation would be to allocate a new range
+ // of PROT_NONE memory and module.changeHeap to this memory. That would
+ // cause every access to take the out-of-bounds signal-handler path which
+ // does the right thing. For now, just throw an out-of-memory exception
+ // since these can technically pop out anywhere and the full fix may
+ // actually OOM when trying to allocate the PROT_NONE memory.
+ if (usesHeap() && !maybeHeap_) {
+ JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
+ return false;
+ }
+
+ {
+ // Push a WasmActivation to describe the wasm frames we're about to push
+ // when running this module. Additionally, push a JitActivation so that
+ // the optimized wasm-to-Ion FFI call path (which we want to be very
+ // fast) can avoid doing so. The JitActivation is marked as inactive so
+ // stack iteration will skip over it.
+ WasmActivation activation(cx, *this);
+ JitActivation jitActivation(cx, /* active */ false);
+
+ // Call the per-exported-function trampoline created by GenerateEntry.
+ auto entry = JS_DATA_TO_FUNC_PTR(EntryFuncPtr, code() + exp.stubOffset());
+ if (!CALL_GENERATED_2(entry, coercedArgs.begin(), globalData()))
+ return false;
+ }
+
+ if (args.isConstructing()) {
+ // By spec, when a function is called as a constructor and this function
+ // returns a primary type, which is the case for all wasm exported
+ // functions, the returned value is discarded and an empty object is
+ // returned instead.
+ PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx);
+ if (!obj)
+ return false;
+ args.rval().set(ObjectValue(*obj));
+ return true;
+ }
+
+ JSObject* simdObj;
+ switch (exp.sig().ret()) {
+ case ExprType::Void:
+ args.rval().set(UndefinedValue());
+ break;
+ case ExprType::I32:
+ args.rval().set(Int32Value(*(int32_t*)&coercedArgs[0]));
+ break;
+ case ExprType::I64:
+ MOZ_CRASH("int64");
+ case ExprType::F32:
+ case ExprType::F64:
+ args.rval().set(NumberValue(*(double*)&coercedArgs[0]));
+ break;
+ case ExprType::I32x4:
+ simdObj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]);
+ if (!simdObj)
+ return false;
+ args.rval().set(ObjectValue(*simdObj));
+ break;
+ case ExprType::F32x4:
+ simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]);
+ if (!simdObj)
+ return false;
+ args.rval().set(ObjectValue(*simdObj));
+ break;
+ }
+
+ return true;
}
bool
Module::callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv,
MutableHandleValue rval)
{
MOZ_ASSERT(dynamicallyLinked_);
@@ -1106,71 +1281,16 @@ Module::callImport(JSContext* cx, uint32
if (!script->baselineScript()->addDependentWasmModule(cx, *this, importIndex))
return false;
exit.code = jitExitCode;
exit.baselineScript = script->baselineScript();
return true;
}
-void
-Module::setProfilingEnabled(bool enabled, JSContext* cx)
-{
- MOZ_ASSERT(dynamicallyLinked_);
- MOZ_ASSERT(!active());
-
- if (profilingEnabled_ == enabled)
- return;
-
- // When enabled, generate profiling labels for every name in funcNames_
- // that is the name of some Function CodeRange. This involves malloc() so
- // do it now since, once we start sampling, we'll be in a signal-handing
- // context where we cannot malloc.
- if (enabled) {
- funcLabels_.resize(funcNames_.length());
- for (const CodeRange& codeRange : codeRanges_) {
- if (!codeRange.isFunction())
- continue;
- unsigned lineno = codeRange.funcLineNumber();
- const char* name = funcNames_[codeRange.funcNameIndex()].get();
- funcLabels_[codeRange.funcNameIndex()] =
- UniqueChars(JS_smprintf("%s (%s:%u)", name, filename_.get(), lineno));
- }
- } else {
- funcLabels_.clear();
- }
-
- // Patch callsites and returns to execute profiling prologues/epililogues.
- {
- AutoMutateCode amc(cx, *this, "Module::setProfilingEnabled");
-
- for (const CallSite& callSite : callSites_)
- EnableProfilingPrologue(*this, callSite, enabled);
-
- for (const CodeRange& codeRange : codeRanges_)
- EnableProfilingEpilogue(*this, codeRange, enabled);
- }
-
- // Update the function-pointer tables to point to profiling prologues.
- for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
- auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
- for (size_t i = 0; i < funcPtrTable.numElems; i++) {
- const CodeRange* codeRange = lookupCodeRange(array[i]);
- void* from = code() + codeRange->funcNonProfilingEntry();
- void* to = code() + codeRange->funcProfilingEntry();
- if (!enabled)
- Swap(from, to);
- MOZ_ASSERT(array[i] == from);
- array[i] = to;
- }
- }
-
- profilingEnabled_ = enabled;
-}
-
const char*
Module::profilingLabel(uint32_t funcIndex) const
{
MOZ_ASSERT(dynamicallyLinked_);
MOZ_ASSERT(profilingEnabled_);
return funcLabels_[funcIndex].get();
}
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -335,29 +335,32 @@ AllocateCode(ExclusiveContext* cx, size_
// a cache file so that the cached code is stored unlinked and ready to be
// statically linked after deserialization.
//
// - Dynamic linking patches code or global data that relies on the address of
// the heap and imports of a module. A module may only be dynamically linked
// once. However, a dynamically-linked module may be cloned so that the clone
// can be independently dynamically linked.
//
-// Once fully dynamically linked, a module can have its exports invoked (via
-// entryTrampoline). While executing, profiling may be enabled/disabled (when
-// the Module is not active()) via setProfilingEnabled(). When profiling is
-// enabled, a module's frames will be visible to wasm::ProfilingFrameIterator.
+// Once fully dynamically linked, a module can have its exports invoked via
+// Module::call.
class Module
{
struct ImportExit {
void* code;
jit::BaselineScript* baselineScript;
HeapPtrFunction fun;
static_assert(sizeof(HeapPtrFunction) == sizeof(void*), "for JIT access");
};
+ struct EntryArg {
+ uint64_t lo;
+ uint64_t hi;
+ };
+ typedef int32_t (*EntryFuncPtr)(EntryArg* args, uint8_t* global);
struct FuncPtrTable {
uint32_t globalDataOffset;
uint32_t numElems;
explicit FuncPtrTable(const StaticLinkData::FuncPtrTable& table)
: globalDataOffset(table.globalDataOffset),
numElems(table.elemOffsets.length())
{}
};
@@ -403,19 +406,21 @@ class Module
FuncLabelVector funcLabels_;
bool interrupted_;
class AutoMutateCode;
uint32_t totalBytes() const;
uint8_t* rawHeapPtr() const;
uint8_t*& rawHeapPtr();
+ WasmActivation*& activation();
void specializeToHeap(ArrayBufferObjectMaybeShared* heap);
void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
void sendCodeRangesToProfiler(JSContext* cx);
+ void setProfilingEnabled(bool enabled, JSContext* cx);
ImportExit& importToExit(const Import& import);
enum CacheBool { NotLoadedFromCache = false, LoadedFromCache = true };
enum ProfilingBool { ProfilingDisabled = false, ProfilingEnabled = true };
static CacheablePod zeroPod();
void init();
Module(const CacheablePod& pod,
@@ -427,20 +432,22 @@ class Module
CallSiteVector&& callSites,
CacheableCharsVector&& funcNames,
CacheableChars filename,
CacheBool loadedFromCache,
ProfilingBool profilingEnabled,
FuncLabelVector&& funcLabels);
template <class> friend struct js::MallocProvider;
+ friend class js::WasmActivation;
public:
static const unsigned SizeOfImportExit = sizeof(ImportExit);
static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
+ static const unsigned SizeOfEntryArg = sizeof(EntryArg);
enum HeapBool { DoesntUseHeap = false, UsesHeap = true };
enum SharedBool { UnsharedHeap = false, SharedHeap = true };
enum MutedBool { DontMuteErrors = false, MuteErrors = true };
Module(CompileArgs args,
uint32_t functionBytes,
uint32_t codeBytes,
@@ -468,17 +475,16 @@ class Module
CompileArgs compileArgs() const;
const ImportVector& imports() const { return imports_; }
const ExportVector& exports() const { return exports_; }
const char* functionName(uint32_t i) const { return funcNames_[i].get(); }
const char* filename() const { return filename_.get(); }
bool loadedFromCache() const { return loadedFromCache_; }
bool staticallyLinked() const { return staticallyLinked_; }
bool dynamicallyLinked() const { return dynamicallyLinked_; }
- bool profilingEnabled() const { return profilingEnabled_; }
// The range [0, functionBytes) is a subrange of [0, codeBytes) that
// contains only function body code, not the stub code. This distinction is
// used by the async interrupt handler to only interrupt when the pc is in
// function code which, in turn, simplifies reasoning about how stubs
// enter/exit.
bool containsFunctionPC(void* pc) const;
@@ -501,40 +507,33 @@ class Module
bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
const AutoVectorRooter<JSFunction*>& imports);
// The wasm heap, established by dynamicallyLink.
ArrayBufferObjectMaybeShared* maybeBuffer() const;
SharedMem<uint8_t*> maybeHeap() const;
size_t heapLength() const;
- Module* nextLinked() const;
// asm.js may detach and change the heap at any time. As an internal detail,
// the heap may not be changed while the module has been asynchronously
// interrupted.
+ //
+ // N.B. These methods and asm.js change-heap support will be removed soon.
- bool hasDetachedHeap() const;
bool changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext* cx);
bool detachHeap(JSContext* cx);
void setInterrupted(bool interrupted);
+ Module* nextLinked() const;
// The exports of a wasm module are called by preparing an array of
// arguments (coerced to the corresponding types of the Export signature)
- // and calling the export's entry trampoline. All such calls must be
- // associated with a containing WasmActivation. The innermost
- // WasmActivation must be maintained in the Module::activation field.
+ // and calling the export's entry trampoline.
- struct EntryArg {
- uint64_t lo;
- uint64_t hi;
- };
- typedef int32_t (*EntryFuncPtr)(EntryArg* args, uint8_t* global);
- EntryFuncPtr entryTrampoline(const Export& func) const;
- WasmActivation*& activation();
+ bool callExport(JSContext* cx, uint32_t exportIndex, CallArgs args);
// Initially, calls to imports in wasm code call out through the generic
// callImport method. If the imported callee gets JIT compiled and the types
// match up, callImport will patch the code to instead call through a thunk
// directly into the JIT code. If the JIT code is released, the Module must
// be notified so it can go back to the generic callImport.
bool callImport(JSContext* cx, uint32_t importIndex, unsigned argc, const Value* argv,
@@ -543,22 +542,22 @@ class Module
// At runtime, when $pc is in wasm function code (containsFunctionPC($pc)),
// $pc may be moved abruptly to interrupt() or outOfBounds() by a signal
// handler or SetContext() from another thread.
uint8_t* interrupt() const { MOZ_ASSERT(staticallyLinked_); return interrupt_; }
uint8_t* outOfBounds() const { MOZ_ASSERT(staticallyLinked_); return outOfBounds_; }
- // When a module is inactive (no live activations), the profiling mode
- // can be toggled. WebAssembly frames only show up in the
- // ProfilingFrameIterator when profilign is enabled.
+ // Each Module has a profilingEnabled state which is updated to match
+ // SPSProfiler::enabled() on the next Modue::call when there are no frames
+ // from the Module on the stack. The ProfilingFrameIterator only shows
+ // frames for Module activations that have profilingEnabled.
- bool active() { return !!activation(); }
- void setProfilingEnabled(bool enabled, JSContext* cx);
+ bool profilingEnabled() const { return profilingEnabled_; }
const char* profilingLabel(uint32_t funcIndex) const;
// See WASM_DECLARE_SERIALIZABLE.
size_t serializedSize() const;
uint8_t* serialize(uint8_t* cursor) const;
typedef UniquePtr<Module, JS::DeletePolicy<Module>> UniqueModule;
static const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor,
UniqueModule* out);
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -16,17 +16,17 @@
* limitations under the License.
*/
#include "asmjs/WasmSignalHandlers.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/PodOperations.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "asmjs/WasmModule.h"
#include "jit/AtomicOperations.h"
#include "jit/Disassembler.h"
#include "vm/Runtime.h"
using namespace js;
using namespace js::jit;
using namespace js::wasm;
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -164,30 +164,30 @@ GenerateEntry(ModuleGenerator& mg, unsig
masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1)));
// Bump the stack for the call.
masm.reserveStack(AlignBytes(StackArgBytes(sig.args()), AsmJSStackAlignment));
// Copy parameters out of argv and into the registers/stack-slots specified by
// the system ABI.
for (ABIArgValTypeIter iter(sig.args()); !iter.done(); iter++) {
- unsigned argOffset = iter.index() * sizeof(Module::EntryArg);
+ unsigned argOffset = iter.index() * Module::SizeOfEntryArg;
Address src(argv, argOffset);
MIRType type = iter.mirType();
switch (iter->kind()) {
case ABIArg::GPR:
masm.load32(src, iter->gpr());
break;
#ifdef JS_CODEGEN_REGISTER_PAIR
case ABIArg::GPR_PAIR:
- MOZ_CRASH("AsmJS uses hardfp for function calls.");
+ MOZ_CRASH("wasm uses hardfp for function calls.");
break;
#endif
case ABIArg::FPU: {
- static_assert(sizeof(Module::EntryArg) >= jit::Simd128DataSize,
+ static_assert(Module::SizeOfEntryArg >= jit::Simd128DataSize,
"EntryArg must be big enough to store SIMD values");
switch (type) {
case MIRType_Int32x4:
masm.loadUnalignedInt32x4(src, iter->fpu());
break;
case MIRType_Float32x4:
masm.loadUnalignedFloat32x4(src, iter->fpu());
break;
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -64,17 +64,16 @@ HandleExecutionInterrupt()
}
} // namespace wasm
} // namespace js
static void
OnDetached()
{
- // See hasDetachedHeap comment in LinkAsmJS.
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
}
static void
OnOutOfBounds()
{
JSContext* cx = JSRuntime::innermostWasmActivation()->cx();
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -12,18 +12,17 @@
#include "jsapi.h"
#include "jscntxt.h"
#include "jsfriendapi.h"
#include "jsgc.h"
#include "jsobj.h"
#include "jsprf.h"
#include "jswrapper.h"
-#include "asmjs/AsmJSLink.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "jit/InlinableNatives.h"
#include "jit/JitFrameIterator.h"
#include "js/Debug.h"
#include "js/HashTable.h"
#include "js/StructuredClone.h"
#include "js/UbiNode.h"
#include "js/UbiNodeBreadthFirst.h"
#include "js/Vector.h"
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -4,17 +4,17 @@
* 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/BytecodeCompiler.h"
#include "jscntxt.h"
#include "jsscript.h"
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
#include "builtin/ModuleObject.h"
#include "frontend/BytecodeEmitter.h"
#include "frontend/FoldConstants.h"
#include "frontend/NameFunctions.h"
#include "frontend/Parser.h"
#include "vm/GlobalObject.h"
#include "vm/TraceLogging.h"
@@ -658,17 +658,17 @@ BytecodeCompiler::compileFunctionBody(Mu
if (!createEmitter(fn->pn_funbox) ||
!emitter->emitFunctionScript(fn->pn_body))
{
return false;
}
} else {
fun.set(fn->pn_funbox->function());
- MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
+ MOZ_ASSERT(IsAsmJSModule(fun));
}
if (!maybeCompleteCompressSource())
return false;
return true;
}
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -24,17 +24,17 @@
#include "jscntxt.h"
#include "jsfun.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsscript.h"
#include "jstypes.h"
#include "jsutil.h"
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
#include "frontend/Parser.h"
#include "frontend/TokenStream.h"
#include "vm/Debugger.h"
#include "vm/GeneratorObject.h"
#include "vm/Stack.h"
#include "jsatominlines.h"
#include "jsobjinlines.h"
@@ -6272,17 +6272,17 @@ BytecodeEmitter::emitFunction(ParseNode*
return false;
if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
script->setUsesArgumentsApplyAndThis();
}
if (outersc->isFunctionBox())
outersc->asFunctionBox()->function()->nonLazyScript()->setHasInnerFunctions(true);
} else {
- MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
+ MOZ_ASSERT(IsAsmJSModule(fun));
}
/* Make the function object a literal in the outer script's pool. */
unsigned index = objectList.add(pn->pn_funbox);
/* Non-hoisted functions simply emit their respective op. */
if (!pn->functionIsHoisted()) {
/* JSOP_LAMBDA_ARROW is always preceded by a new.target */
@@ -6939,18 +6939,17 @@ BytecodeEmitter::emitStatement(ParseNode
// "use strict" should warn for any "use strict" statements seen
// later in the script, because such statements are misleading.
const char* directive = nullptr;
if (atom == cx->names().useStrict) {
if (!sc->strictScript)
directive = js_useStrict_str;
} else if (atom == cx->names().useAsm) {
if (sc->isFunctionBox()) {
- JSFunction* fun = sc->asFunctionBox()->function();
- if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+ if (IsAsmJSModule(sc->asFunctionBox()->function()))
directive = js_useAsm_str;
}
}
if (directive) {
if (!reportStrictWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
return false;
}
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -23,17 +23,17 @@
#include "jsatom.h"
#include "jscntxt.h"
#include "jsfun.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsscript.h"
#include "jstypes.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "builtin/ModuleObject.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FoldConstants.h"
#include "frontend/ParseMaps.h"
#include "frontend/TokenStream.h"
#include "vm/Shape.h"
#include "jsatominlines.h"
@@ -3180,17 +3180,17 @@ Parser<FullParseHandler>::asmJS(Node lis
pc->sc->asFunctionBox()->useAsm = true;
// Attempt to validate and compile this asm.js module. On success, the
// tokenStream has been advanced to the closing }. On failure, the
// tokenStream is in an indeterminate state and we must reparse the
// function from the beginning. Reparsing is triggered by marking that a
// new directive has been encountered and returning 'false'.
bool validated;
- if (!ValidateAsmJS(context, *this, list, &validated))
+ if (!CompileAsmJS(context, *this, list, &validated))
return false;
if (!validated) {
pc->newDirectives->setAsmJS();
return false;
}
return true;
}
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12789,17 +12789,17 @@ IonBuilder::jsop_object(JSObject* obj)
}
bool
IonBuilder::jsop_lambda(JSFunction* fun)
{
MOZ_ASSERT(analysis().usesScopeChain());
MOZ_ASSERT(!fun->isArrow());
- if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+ if (IsAsmJSModule(fun))
return abort("asm.js module function");
MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
current->add(cst);
MLambda* ins = MLambda::New(alloc(), constraints(), current->scopeChain(), cst);
current->add(ins);
current->push(ins);
@@ -12941,17 +12941,17 @@ IonBuilder::jsop_deflexical(uint32_t ind
return resumeAfter(deflex);
}
bool
IonBuilder::jsop_deffun(uint32_t index)
{
JSFunction* fun = script()->getFunction(index);
- if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
+ if (IsAsmJSModule(fun))
return abort("asm.js module function");
MOZ_ASSERT(analysis().usesScopeChain());
MDefFun* deffun = MDefFun::New(alloc(), fun, current->scopeChain());
current->add(deffun);
return resumeAfter(deffun);
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -644,17 +644,17 @@ static inline bool UseHardFpABI()
#endif
}
#endif
// In order to handle SoftFp ABI calls, we need to be able to express that we
// have ABIArg which are represented by pair of general purpose registers.
#define JS_CODEGEN_REGISTER_PAIR 1
-// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
+// See the comments above AsmJSMappedSize in AsmJS.h for more info.
// TODO: Implement this for ARM. Note that it requires Codegen to respect the
// offset field of AsmJSHeapAccess.
static const size_t AsmJSCheckedImmediateRange = 0;
static const size_t AsmJSImmediateRange = 0;
} // namespace jit
} // namespace js
--- a/js/src/jit/x86-shared/Architecture-x86-shared.h
+++ b/js/src/jit/x86-shared/Architecture-x86-shared.h
@@ -442,16 +442,16 @@ hasUnaliasedDouble()
// to a double as a temporary, you need a temporary double register.
inline bool
hasMultiAlias()
{
return false;
}
// Support some constant-offset addressing.
-// See the comments above AsmJSMappedSize in AsmJSValidate.h for more info.
+// See the comments above AsmJSMappedSize in AsmJS.h for more info.
static const size_t AsmJSCheckedImmediateRange = 4096;
static const size_t AsmJSImmediateRange = UINT32_C(0x80000000);
} // namespace jit
} // namespace js
#endif /* jit_x86_shared_Architecture_x86_h */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -38,17 +38,17 @@
#include "jsscript.h"
#include "jsstr.h"
#include "jstypes.h"
#include "jsutil.h"
#include "jswatchpoint.h"
#include "jsweakmap.h"
#include "jswrapper.h"
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
#include "builtin/AtomicsObject.h"
#include "builtin/Eval.h"
#include "builtin/Intl.h"
#include "builtin/MapObject.h"
#include "builtin/RegExp.h"
#include "builtin/SymbolObject.h"
#ifdef ENABLE_BINARYDATA
#include "builtin/SIMD.h"
@@ -3508,17 +3508,17 @@ CloneFunctionObject(JSContext* cx, Handl
return nullptr;
}
if (fun->isBoundFunction()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
- if (fun->isNative() && IsAsmJSModuleNative(fun->native())) {
+ if (IsAsmJSModule(fun)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT);
return nullptr;
}
if (CanReuseScriptForClone(cx->compartment(), fun, dynamicScope)) {
// If the script is to be reused, either the script can already handle
// non-syntactic scopes, or there is only the standard global lexical
// scope.
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -690,16 +690,28 @@ class FunctionExtended : public JSFuncti
public:
static const unsigned NUM_EXTENDED_SLOTS = 2;
/* Arrow functions store their lexical new.target in the first extended slot. */
static const unsigned ARROW_NEWTARGET_SLOT = 0;
static const unsigned METHOD_HOMEOBJECT_SLOT = 0;
+ /*
+ * All asm.js/wasm functions store their compiled module (either
+ * WasmModuleObject or AsmJSModuleObject) in the first extended slot.
+ */
+ static const unsigned ASM_MODULE_SLOT = 0;
+
+ /*
+ * wasm/asm.js exported functions store the index of the export in the
+ * module's export vector in the second slot.
+ */
+ static const unsigned ASM_EXPORT_INDEX_SLOT = 1;
+
static inline size_t offsetOfExtendedSlot(unsigned which) {
MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
}
static inline size_t offsetOfArrowNewTargetSlot() {
return offsetOfExtendedSlot(ARROW_NEWTARGET_SLOT);
}
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -33,17 +33,17 @@
#include "jsscript.h"
#include "jsstr.h"
#include "jstypes.h"
#include "jsutil.h"
#include "jswatchpoint.h"
#include "jswin.h"
#include "jswrapper.h"
-#include "asmjs/AsmJSModule.h"
+#include "asmjs/AsmJS.h"
#include "builtin/Eval.h"
#include "builtin/Object.h"
#include "builtin/SymbolObject.h"
#include "frontend/BytecodeCompiler.h"
#include "gc/Marking.h"
#include "jit/BaselineJIT.h"
#include "js/MemoryMetrics.h"
#include "js/Proxy.h"
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -4,17 +4,17 @@
* 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 jsscriptinlines_h
#define jsscriptinlines_h
#include "jsscript.h"
-#include "asmjs/AsmJSLink.h"
+#include "asmjs/AsmJS.h"
#include "jit/BaselineJIT.h"
#include "jit/IonAnalysis.h"
#include "vm/ScopeObject.h"
#include "jscompartmentinlines.h"
#include "vm/Shape-inl.h"
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -137,19 +137,17 @@ EXPORTS.js += [
'../public/UbiNodePostOrder.h',
'../public/Utility.h',
'../public/Value.h',
'../public/Vector.h',
'../public/WeakMapPtr.h',
]
UNIFIED_SOURCES += [
- 'asmjs/AsmJSLink.cpp',
- 'asmjs/AsmJSModule.cpp',
- 'asmjs/AsmJSValidate.cpp',
+ 'asmjs/AsmJS.cpp',
'asmjs/WasmFrameIterator.cpp',
'asmjs/WasmGenerator.cpp',
'asmjs/WasmIonCompile.cpp',
'asmjs/WasmModule.cpp',
'asmjs/WasmSignalHandlers.cpp',
'asmjs/WasmStubs.cpp',
'asmjs/WasmTypes.cpp',
'builtin/AtomicsObject.cpp',
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -29,18 +29,17 @@
#include "jsobj.h"
#include "jstypes.h"
#include "jsutil.h"
#ifdef XP_WIN
# include "jswin.h"
#endif
#include "jswrapper.h"
-#include "asmjs/AsmJSModule.h"
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "gc/Memory.h"
#include "js/Conversions.h"
#include "js/MemoryMetrics.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/SharedArrayObject.h"
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -17,17 +17,17 @@
#include "jswrapper.h"
#ifndef XP_WIN
# include <sys/mman.h>
#endif
#ifdef MOZ_VALGRIND
# include <valgrind/memcheck.h>
#endif
-#include "asmjs/AsmJSValidate.h"
+#include "asmjs/AsmJS.h"
#include "vm/SharedMem.h"
#include "vm/TypedArrayCommon.h"
#include "jsobjinlines.h"
#include "vm/NativeObject-inl.h"
using namespace js;