Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/wasm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 211 kB image not shown  

Quelle  AsmJS.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 *
 * 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 "wasm/AsmJS.h"

#include "mozilla/Attributes.h"
#include "mozilla/Compression.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Maybe.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"  // SprintfLiteral
#include "mozilla/Try.h"      // MOZ_TRY*
#include "mozilla/Utf8.h"     // mozilla::Utf8Unit
#include "mozilla/Variant.h"

#include <algorithm>
#include <new>

#include "jsmath.h"

#include "frontend/BytecodeCompiler.h"    // CompileStandaloneFunction
#include "frontend/FrontendContext.h"     // js::FrontendContext
#include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
#include "frontend/ParseNode.h"
#include "frontend/Parser-macros.h"  // MOZ_TRY_*
#include "frontend/Parser.h"
#include "frontend/ParserAtom.h"     // ParserAtomsTable, TaggedParserAtomIndex
#include "frontend/SharedContext.h"  // TopLevelFunction
#include "frontend/TaggedParserAtomIndexHasher.h"  // TaggedParserAtomIndexHasher
#include "gc/GC.h"
#include "gc/Policy.h"
#include "jit/InlinableNatives.h"
#include "js/BuildId.h"  // JS::BuildIdCharVector
#include "js/experimental/JitInfo.h"
#include "js/friend/ErrorMessages.h"  // JSMSG_*
#include "js/MemoryMetrics.h"
#include "js/Printf.h"
#include "js/ScalarType.h"  // js::Scalar::Type
#include "js/SourceText.h"
#include "js/StableStringChars.h"
#include "js/Wrapper.h"
#include "util/DifferentialTesting.h"
#include "util/StringBuilder.h"
#include "util/Text.h"
#include "vm/ErrorReporting.h"
#include "vm/FunctionFlags.h"          // js::FunctionFlags
#include "vm/GeneratorAndAsyncKind.h"  // js::GeneratorKind, js::FunctionAsyncKind
#include "vm/Interpreter.h"
#include "vm/SelfHosting.h"
#include "vm/Time.h"
#include "vm/TypedArrayObject.h"
#include "vm/Warnings.h"  // js::WarnNumberASCII
#include "wasm/WasmCompile.h"
#include "wasm/WasmFeatures.h"
#include "wasm/WasmGenerator.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmIonCompile.h"
#include "wasm/WasmJS.h"
#include "wasm/WasmModuleTypes.h"
#include "wasm/WasmSerialize.h"
#include "wasm/WasmSignalHandlers.h"
#include "wasm/WasmValidate.h"

#include "frontend/SharedContext-inl.h"
#include "vm/ArrayBufferObject-inl.h"
#include "vm/JSObject-inl.h"
#include "wasm/WasmInstance-inl.h"

using namespace js;
using namespace js::frontend;
using namespace js::jit;
using namespace js::wasm;

using JS::AsmJSOption;
using JS::AutoStableStringChars;
using JS::GenericNaN;
using JS::SourceText;
using mozilla::Abs;
using mozilla::AsVariant;
using mozilla::CeilingLog2;
using mozilla::HashGeneric;
using mozilla::IsNegativeZero;
using mozilla::IsPositiveZero;
using mozilla::IsPowerOfTwo;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::PodZero;
using mozilla::PositiveInfinity;
using mozilla::Some;
using mozilla::Utf8Unit;
using mozilla::Compression::LZ4;

using FunctionVector = JS::GCVector<JSFunction*>;

/*****************************************************************************/

// A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
// shared memory (SharedArrayBuffer).

enum class MemoryUsage { None = false, Unshared = 1, Shared = 2 };

// The asm.js valid heap lengths are precisely the WASM valid heap lengths for
// ARM greater or equal to MinHeapLength
static const size_t MinHeapLength = PageSize;
// An asm.js heap can in principle be up to INT32_MAX bytes but requirements
// on the format restrict it further to the largest pseudo-ARM-immediate.
// See IsValidAsmJSHeapLength().
static const uint64_t MaxHeapLength = 0x7f000000;

static uint64_t RoundUpToNextValidAsmJSHeapLength(uint64_t length) {
  if (length <= MinHeapLength) {
    return MinHeapLength;
  }

  return wasm::RoundUpToNextValidARMImmediate(length);
}

static uint64_t DivideRoundingUp(uint64_t a, uint64_t b) {
  return (a + (b - 1)) / b;
}

/*****************************************************************************/
// 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
};

// LitValPOD is a restricted version of LitVal suitable for asm.js that is
// always POD.

struct LitValPOD {
  PackedTypeCode valType_;
  union U {
    uint32_t u32_;
    uint64_t u64_;
    float f32_;
    double f64_;
  } u;

  LitValPOD() = default;

  explicit LitValPOD(uint32_t u32) : valType_(ValType(ValType::I32).packed()) {
    u.u32_ = u32;
  }
  explicit LitValPOD(uint64_t u64) : valType_(ValType(ValType::I64).packed()) {
    u.u64_ = u64;
  }

  explicit LitValPOD(float f32) : valType_(ValType(ValType::F32).packed()) {
    u.f32_ = f32;
  }
  explicit LitValPOD(double f64) : valType_(ValType(ValType::F64).packed()) {
    u.f64_ = f64;
  }

  LitVal asLitVal() const {
    switch (valType_.typeCode()) {
      case TypeCode::I32:
        return LitVal(u.u32_);
      case TypeCode::I64:
        return LitVal(u.u64_);
      case TypeCode::F32:
        return LitVal(u.f32_);
      case TypeCode::F64:
        return LitVal(u.f64_);
      default:
        MOZ_CRASH("Can't happen");
    }
  }
};

static_assert(std::is_pod_v<LitValPOD>,
              "must be POD to be simply serialized/deserialized");

// An AsmJSGlobal represents a JS global variable in the asm.js module function.
class AsmJSGlobal {
 public:
  enum Which {
    Variable,
    FFI,
    ArrayView,
    ArrayViewCtor,
    MathBuiltinFunction,
    Constant
  };
  enum VarInitKind { InitConstant, InitImport };
  enum ConstantKind { GlobalConstant, MathConstant };

 private:
  struct CacheablePod {
    Which which_;
    union V {
      struct {
        VarInitKind initKind_;
        union U {
          PackedTypeCode importValType_;
          LitValPOD val_;
        } u;
      } var;
      uint32_t ffiIndex_;
      Scalar::Type viewType_;
      AsmJSMathBuiltinFunction mathBuiltinFunc_;
      struct {
        ConstantKind kind_;
        double value_;
      } constant;
    } u;
  } pod;
  CacheableChars field_;

  friend class ModuleValidatorShared;
  template <typename Unit>
  friend class ModuleValidator;

 public:
  AsmJSGlobal() = default;
  AsmJSGlobal(Which which, UniqueChars field) {
    mozilla::PodZero(&pod);  // zero padding for Valgrind
    pod.which_ = which;
    field_ = std::move(field);
  }
  const char* field() const { return field_.get(); }
  Which which() const { return pod.which_; }
  VarInitKind varInitKind() const {
    MOZ_ASSERT(pod.which_ == Variable);
    return pod.u.var.initKind_;
  }
  LitValPOD varInitVal() const {
    MOZ_ASSERT(pod.which_ == Variable);
    MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
    return pod.u.var.u.val_;
  }
  ValType varInitImportType() const {
    MOZ_ASSERT(pod.which_ == Variable);
    MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
    return ValType(pod.u.var.u.importValType_);
  }
  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.
  Scalar::Type viewType() const {
    MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
    return pod.u.viewType_;
  }
  AsmJSMathBuiltinFunction mathBuiltinFunction() const {
    MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
    return pod.u.mathBuiltinFunc_;
  }
  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_;
  }
};

using AsmJSGlobalVector = Vector<AsmJSGlobal, 0, SystemAllocPolicy>;

// An AsmJSImport is slightly different than an asm.js FFI function: a single
// asm.js FFI function can be called with many different signatures. When
// compiled to wasm, each unique FFI function paired with signature generates a
// wasm import.
class AsmJSImport {
  uint32_t ffiIndex_;

 public:
  AsmJSImport() = default;
  explicit AsmJSImport(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
  uint32_t ffiIndex() const { return ffiIndex_; }
};

using AsmJSImportVector = Vector<AsmJSImport, 0, SystemAllocPolicy>;

// An AsmJSExport logically extends Export with the extra information needed for
// an asm.js exported function, viz., the offsets in module's source chars in
// case the function is toString()ed.
class AsmJSExport {
  uint32_t funcIndex_ = 0;

  // All fields are treated as cacheable POD:
  uint32_t startOffsetInModule_ = 0;  // Store module-start-relative offsets
  uint32_t endOffsetInModule_ = 0;    // so preserved by serialization.

 public:
  AsmJSExport() = default;
  AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule,
              uint32_t endOffsetInModule)
      : funcIndex_(funcIndex),
        startOffsetInModule_(startOffsetInModule),
        endOffsetInModule_(endOffsetInModule) {}
  uint32_t funcIndex() const { return funcIndex_; }
  uint32_t startOffsetInModule() const { return startOffsetInModule_; }
  uint32_t endOffsetInModule() const { return endOffsetInModule_; }
};

using AsmJSExportVector = Vector<AsmJSExport, 0, SystemAllocPolicy>;

// Holds the immutable guts of an AsmJSModule.
//
// CodeMetadataForAsmJSImpl is built incrementally by ModuleValidator and then
// shared immutably between AsmJSModules.

struct js::CodeMetadataForAsmJSImpl : CodeMetadataForAsmJS {
  uint32_t numFFIs = 0;
  uint32_t srcLength = 0;
  uint32_t srcLengthWithRightBrace = 0;
  AsmJSGlobalVector asmJSGlobals;
  AsmJSImportVector asmJSImports;
  AsmJSExportVector asmJSExports;
  CacheableCharsVector asmJSFuncNames;
  CacheableChars globalArgumentName;
  CacheableChars importArgumentName;
  CacheableChars bufferArgumentName;

  // These values are not serialized since they are relative to the
  // containing script which can be different between serialization and
  // deserialization contexts. Thus, they must be set explicitly using the
  // ambient Parser/ScriptSource after deserialization.
  //
  // 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 toStringStart;
  uint32_t srcStart;
  bool strict;
  bool alwaysUseFdlibm = false;
  RefPtr<ScriptSource> source;

  uint32_t srcEndBeforeCurly() const { return srcStart + srcLength; }
  uint32_t srcEndAfterCurly() const {
    return srcStart + srcLengthWithRightBrace;
  }

  CodeMetadataForAsmJSImpl() : toStringStart(0), srcStart(0), strict(false) {}
  ~CodeMetadataForAsmJSImpl() = default;

  const CodeMetadataForAsmJSImpl& asAsmJS() const { return *this; }

  const AsmJSExport& lookupAsmJSExport(uint32_t funcIndex) const {
    // The AsmJSExportVector isn't stored in sorted order so do a linear
    // search. This is for the super-cold and already-expensive toString()
    // path and the number of exports is generally small.
    for (const AsmJSExport& exp : asmJSExports) {
      if (exp.funcIndex() == funcIndex) {
        return exp;
      }
    }
    MOZ_CRASH("missing asm.js func export");
  }

  bool mutedErrors() const { return source->mutedErrors(); }
  const char16_t* displayURL() const {
    return source->hasDisplayURL() ? source->displayURL() : nullptr;
  }
  ScriptSource* maybeScriptSource() const { return source.get(); }
  bool getFuncNameForAsmJS(uint32_t funcIndex, UTF8Bytes* name) const {
    const char* p = asmJSFuncNames[funcIndex].get();
    if (!p) {
      return true;
    }
    return name->append(p, strlen(p));
  }

  size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    return asmJSGlobals.sizeOfExcludingThis(mallocSizeOf) +
           asmJSImports.sizeOfExcludingThis(mallocSizeOf) +
           asmJSExports.sizeOfExcludingThis(mallocSizeOf) +
           asmJSFuncNames.sizeOfExcludingThis(mallocSizeOf) +
           globalArgumentName.sizeOfExcludingThis(mallocSizeOf) +
           importArgumentName.sizeOfExcludingThis(mallocSizeOf) +
           bufferArgumentName.sizeOfExcludingThis(mallocSizeOf);
  }
};

using MutableCodeMetadataForAsmJSImpl = RefPtr<CodeMetadataForAsmJSImpl>;

/*****************************************************************************/
// ParseNode utilities

static inline ParseNode* NextNode(ParseNode* pn) { return pn->pn_next; }

static inline ParseNode* UnaryKid(ParseNode* pn) {
  return pn->as<UnaryNode>().kid();
}

static inline ParseNode* BinaryRight(ParseNode* pn) {
  return pn->as<BinaryNode>().right();
}

static inline ParseNode* BinaryLeft(ParseNode* pn) {
  return pn->as<BinaryNode>().left();
}

static inline ParseNode* ReturnExpr(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::ReturnStmt));
  return UnaryKid(pn);
}

static inline ParseNode* TernaryKid1(ParseNode* pn) {
  return pn->as<TernaryNode>().kid1();
}

static inline ParseNode* TernaryKid2(ParseNode* pn) {
  return pn->as<TernaryNode>().kid2();
}

static inline ParseNode* TernaryKid3(ParseNode* pn) {
  return pn->as<TernaryNode>().kid3();
}

static inline ParseNode* ListHead(ParseNode* pn) {
  return pn->as<ListNode>().head();
}

static inline unsigned ListLength(ParseNode* pn) {
  return pn->as<ListNode>().count();
}

static inline ParseNode* CallCallee(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
  return BinaryLeft(pn);
}

static inline unsigned CallArgListLength(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
  return ListLength(BinaryRight(pn));
}

static inline ParseNode* CallArgList(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
  return ListHead(BinaryRight(pn));
}

static inline ParseNode* VarListHead(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::VarStmt) ||
             pn->isKind(ParseNodeKind::ConstDecl));
  return ListHead(pn);
}

static inline bool IsDefaultCase(ParseNode* pn) {
  return pn->as<CaseClause>().isDefault();
}

static inline ParseNode* CaseExpr(ParseNode* pn) {
  return pn->as<CaseClause>().caseExpression();
}

static inline ParseNode* CaseBody(ParseNode* pn) {
  return pn->as<CaseClause>().statementList();
}

static inline ParseNode* BinaryOpLeft(ParseNode* pn) {
  MOZ_ASSERT(pn->isBinaryOperation());
  MOZ_ASSERT(pn->as<ListNode>().count() == 2);
  return ListHead(pn);
}

static inline ParseNode* BinaryOpRight(ParseNode* pn) {
  MOZ_ASSERT(pn->isBinaryOperation());
  MOZ_ASSERT(pn->as<ListNode>().count() == 2);
  return NextNode(ListHead(pn));
}

static inline ParseNode* BitwiseLeft(ParseNode* pn) { return BinaryOpLeft(pn); }

static inline ParseNode* BitwiseRight(ParseNode* pn) {
  return BinaryOpRight(pn);
}

static inline ParseNode* MultiplyLeft(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::MulExpr));
  return BinaryOpLeft(pn);
}

static inline ParseNode* MultiplyRight(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::MulExpr));
  return BinaryOpRight(pn);
}

static inline ParseNode* AddSubLeft(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::AddExpr) ||
             pn->isKind(ParseNodeKind::SubExpr));
  return BinaryOpLeft(pn);
}

static inline ParseNode* AddSubRight(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::AddExpr) ||
             pn->isKind(ParseNodeKind::SubExpr));
  return BinaryOpRight(pn);
}

static inline ParseNode* DivOrModLeft(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::DivExpr) ||
             pn->isKind(ParseNodeKind::ModExpr));
  return BinaryOpLeft(pn);
}

static inline ParseNode* DivOrModRight(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::DivExpr) ||
             pn->isKind(ParseNodeKind::ModExpr));
  return BinaryOpRight(pn);
}

static inline ParseNode* ComparisonLeft(ParseNode* pn) {
  return BinaryOpLeft(pn);
}

static inline ParseNode* ComparisonRight(ParseNode* pn) {
  return BinaryOpRight(pn);
}

static inline bool IsExpressionStatement(ParseNode* pn) {
  return pn->isKind(ParseNodeKind::ExpressionStmt);
}

static inline ParseNode* ExpressionStatementExpr(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::ExpressionStmt));
  return UnaryKid(pn);
}

static inline TaggedParserAtomIndex LoopControlMaybeLabel(ParseNode* pn) {
  MOZ_ASSERT(pn->isKind(ParseNodeKind::BreakStmt) ||
             pn->isKind(ParseNodeKind::ContinueStmt));
  return pn->as<LoopControlStatement>().label();
}

static inline TaggedParserAtomIndex LabeledStatementLabel(ParseNode* pn) {
  return pn->as<LabeledStatement>().label();
}

static inline ParseNode* LabeledStatementStatement(ParseNode* pn) {
  return pn->as<LabeledStatement>().statement();
}

static double NumberNodeValue(ParseNode* pn) {
  return pn->as<NumericLiteral>().value();
}

static bool NumberNodeHasFrac(ParseNode* pn) {
  return pn->as<NumericLiteral>().decimalPoint() == HasDecimal;
}

static ParseNode* DotBase(ParseNode* pn) {
  return &pn->as<PropertyAccess>().expression();
}

static TaggedParserAtomIndex DotMember(ParseNode* pn) {
  return pn->as<PropertyAccess>().name();
}

static ParseNode* ElemBase(ParseNode* pn) {
  return &pn->as<PropertyByValue>().expression();
}

static ParseNode* ElemIndex(ParseNode* pn) {
  return &pn->as<PropertyByValue>().key();
}

static inline TaggedParserAtomIndex FunctionName(FunctionNode* funNode) {
  if (auto name = funNode->funbox()->explicitName()) {
    return name;
  }
  return TaggedParserAtomIndex::null();
}

static inline ParseNode* FunctionFormalParametersList(FunctionNode* fn,
                                                      unsigned* numFormals) {
  ParamsBodyNode* argsBody = fn->body();

  // The number of formals is equal to the number of parameters (excluding the
  // trailing lexical scope). There are no destructuring or rest parameters for
  // asm.js functions.
  *numFormals = argsBody->count();

  // If the function has been fully parsed, the trailing function body node is a
  // lexical scope. If we've only parsed the function parameters, the last node
  // is the last parameter.
  if (*numFormals > 0 && argsBody->last()->is<LexicalScopeNode>()) {
    MOZ_ASSERT(argsBody->last()->as<LexicalScopeNode>().scopeBody()->isKind(
        ParseNodeKind::StatementList));
    (*numFormals)--;
  }

  return argsBody->head();
}

static inline ParseNode* FunctionStatementList(FunctionNode* funNode) {
  LexicalScopeNode* last = funNode->body()->body();
  MOZ_ASSERT(last->isEmptyScope());
  ParseNode* body = last->scopeBody();
  MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
  return body;
}

static inline bool IsNormalObjectField(ParseNode* pn) {
  return pn->isKind(ParseNodeKind::PropertyDefinition) &&
         pn->as<PropertyDefinition>().accessorType() == AccessorType::None &&
         BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName);
}

static inline TaggedParserAtomIndex ObjectNormalFieldName(ParseNode* pn) {
  MOZ_ASSERT(IsNormalObjectField(pn));
  MOZ_ASSERT(BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName));
  return BinaryLeft(pn)->as<NameNode>().atom();
}

static inline ParseNode* ObjectNormalFieldInitializer(ParseNode* pn) {
  MOZ_ASSERT(IsNormalObjectField(pn));
  return BinaryRight(pn);
}

static inline bool IsUseOfName(ParseNode* pn, TaggedParserAtomIndex name) {
  return pn->isName(name);
}

static inline bool IsIgnoredDirectiveName(TaggedParserAtomIndex atom) {
  return atom != TaggedParserAtomIndex::WellKnown::use_strict_();
}

static inline bool IsIgnoredDirective(ParseNode* pn) {
  return pn->isKind(ParseNodeKind::ExpressionStmt) &&
         UnaryKid(pn)->isKind(ParseNodeKind::StringExpr) &&
         IsIgnoredDirectiveName(UnaryKid(pn)->as<NameNode>().atom());
}

static inline bool IsEmptyStatement(ParseNode* pn) {
  return pn->isKind(ParseNodeKind::EmptyStmt);
}

static inline ParseNode* SkipEmptyStatements(ParseNode* pn) {
  while (pn && IsEmptyStatement(pn)) {
    pn = pn->pn_next;
  }
  return pn;
}

static inline ParseNode* NextNonEmptyStatement(ParseNode* pn) {
  return SkipEmptyStatements(pn->pn_next);
}

template <typename Unit>
static bool GetToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
  auto& ts = parser.tokenStream;
  TokenKind tk;
  while (true) {
    if (!ts.getToken(&tk, TokenStreamShared::SlashIsRegExp)) {
      return false;
    }
    if (tk != TokenKind::Semi) {
      break;
    }
  }
  *tkp = tk;
  return true;
}

template <typename Unit>
static bool PeekToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
  auto& ts = parser.tokenStream;
  TokenKind tk;
  while (true) {
    if (!ts.peekToken(&tk, TokenStream::SlashIsRegExp)) {
      return false;
    }
    if (tk != TokenKind::Semi) {
      break;
    }
    ts.consumeKnownToken(TokenKind::Semi, TokenStreamShared::SlashIsRegExp);
  }
  *tkp = tk;
  return true;
}

template <typename Unit>
static bool ParseVarOrConstStatement(AsmJSParser<Unit>& parser,
                                     ParseNode** var) {
  TokenKind tk;
  if (!PeekToken(parser, &tk)) {
    return false;
  }
  if (tk != TokenKind::Var && tk != TokenKind::Const) {
    *var = nullptr;
    return true;
  }

  MOZ_TRY_VAR_OR_RETURN(*var, parser.statementListItem(YieldIsName), false);

  MOZ_ASSERT((*var)->isKind(ParseNodeKind::VarStmt) ||
             (*var)->isKind(ParseNodeKind::ConstDecl));
  return true;
}

/*****************************************************************************/

// Represents the type and value of an asm.js numeric literal.
//
// A literal is a double iff the literal contains a decimal point (even if the
// fractional part is 0). Otherwise, integers may be classified:
//  fixnum: [0, 2^31)
//  negative int: [-2^31, 0)
//  big unsigned: [2^31, 2^32)
//  out of range: otherwise
// Lastly, a literal may be a float literal which is any double or integer
// literal coerced with Math.fround.
class NumLit {
 public:
  enum Which {
    Fixnum,
    NegativeInt,
    BigUnsigned,
    Double,
    Float,
    OutOfRangeInt = -1
  };

 private:
  Which which_;
  JS::Value value_;

 public:
  NumLit() = default;

  NumLit(Which w, const Value& v) : which_(w), value_(v) {}

  Which which() const { return which_; }

  int32_t toInt32() const {
    MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt ||
               which_ == BigUnsigned);
    return value_.toInt32();
  }

  uint32_t toUint32() const { return (uint32_t)toInt32(); }

  double toDouble() const {
    MOZ_ASSERT(which_ == Double);
    return value_.toDouble();
  }

  float toFloat() const {
    MOZ_ASSERT(which_ == Float);
    return float(value_.toDouble());
  }

  Value scalarValue() const {
    MOZ_ASSERT(which_ != OutOfRangeInt);
    return value_;
  }

  bool valid() const { return which_ != OutOfRangeInt; }

  bool isZeroBits() const {
    MOZ_ASSERT(valid());
    switch (which()) {
      case NumLit::Fixnum:
      case NumLit::NegativeInt:
      case NumLit::BigUnsigned:
        return toInt32() == 0;
      case NumLit::Double:
        return IsPositiveZero(toDouble());
      case NumLit::Float:
        return IsPositiveZero(toFloat());
      case NumLit::OutOfRangeInt:
        MOZ_CRASH("can't be here because of valid() check above");
    }
    return false;
  }

  LitValPOD value() const {
    switch (which_) {
      case NumLit::Fixnum:
      case NumLit::NegativeInt:
      case NumLit::BigUnsigned:
        return LitValPOD(toUint32());
      case NumLit::Float:
        return LitValPOD(toFloat());
      case NumLit::Double:
        return LitValPOD(toDouble());
      case NumLit::OutOfRangeInt:;
    }
    MOZ_CRASH("bad literal");
  }
};

// Represents the type of a general asm.js expression.
//
// A canonical subset of types representing the coercion targets: Int, Float,
// Double.
//
// Void is also part of the canonical subset.

class Type {
 public:
  enum Which {
    Fixnum = NumLit::Fixnum,
    Signed = NumLit::NegativeInt,
    Unsigned = NumLit::BigUnsigned,
    DoubleLit = NumLit::Double,
    Float = NumLit::Float,
    Double,
    MaybeDouble,
    MaybeFloat,
    Floatish,
    Int,
    Intish,
    Void
  };

 private:
  Which which_;

 public:
  Type() = default;
  MOZ_IMPLICIT Type(Which w) : which_(w) {}

  // Map an already canonicalized Type to the return type of a function call.
  static Type ret(Type t) {
    MOZ_ASSERT(t.isCanonical());
    // The 32-bit external type is Signed, not Int.
    return t.isInt() ? Signed : t;
  }

  static Type lit(const NumLit& lit) {
    MOZ_ASSERT(lit.valid());
    Which which = Type::Which(lit.which());
    MOZ_ASSERT(which >= Fixnum && which <= Float);
    Type t;
    t.which_ = which;
    return t;
  }

  // Map |t| to one of the canonical vartype representations of a
  // wasm::ValType.
  static Type canonicalize(Type t) {
    switch (t.which()) {
      case Fixnum:
      case Signed:
      case Unsigned:
      case Int:
        return Int;

      case Float:
        return Float;

      case DoubleLit:
      case Double:
        return Double;

      case Void:
        return Void;

      case MaybeDouble:
      case MaybeFloat:
      case Floatish:
      case Intish:
        // These types need some kind of coercion, they can't be mapped
        // to an VarType.
        break;
    }
    MOZ_CRASH("Invalid vartype");
  }

  Which which() const { return which_; }

  bool operator==(Type rhs) const { return which_ == rhs.which_; }
  bool operator!=(Type rhs) const { return which_ != rhs.which_; }

  bool operator<=(Type rhs) const {
    switch (rhs.which_) {
      case Signed:
        return isSigned();
      case Unsigned:
        return isUnsigned();
      case DoubleLit:
        return isDoubleLit();
      case Double:
        return isDouble();
      case Float:
        return isFloat();
      case MaybeDouble:
        return isMaybeDouble();
      case MaybeFloat:
        return isMaybeFloat();
      case Floatish:
        return isFloatish();
      case Int:
        return isInt();
      case Intish:
        return isIntish();
      case Fixnum:
        return isFixnum();
      case Void:
        return isVoid();
    }
    MOZ_CRASH("unexpected rhs type");
  }

  bool isFixnum() const { return which_ == Fixnum; }

  bool isSigned() const { return which_ == Signed || which_ == Fixnum; }

  bool isUnsigned() const { return which_ == Unsigned || which_ == Fixnum; }

  bool isInt() const { return isSigned() || isUnsigned() || which_ == Int; }

  bool isIntish() const { return isInt() || which_ == Intish; }

  bool isDoubleLit() const { return which_ == DoubleLit; }

  bool isDouble() const { return isDoubleLit() || which_ == Double; }

  bool isMaybeDouble() const { return isDouble() || which_ == MaybeDouble; }

  bool isFloat() const { return which_ == Float; }

  bool isMaybeFloat() const { return isFloat() || which_ == MaybeFloat; }

  bool isFloatish() const { return isMaybeFloat() || which_ == Floatish; }

  bool isVoid() const { return which_ == Void; }

  bool isExtern() const { return isDouble() || isSigned(); }

  // Check if this is one of the valid types for a function argument.
  bool isArgType() const { return isInt() || isFloat() || isDouble(); }

  // Check if this is one of the valid types for a function return value.
  bool isReturnType() const {
    return isSigned() || isFloat() || isDouble() || isVoid();
  }

  // Check if this is one of the valid types for a global variable.
  bool isGlobalVarType() const { return isArgType(); }

  // Check if this is one of the canonical vartype representations of a
  // wasm::ValType, or is void. See Type::canonicalize().
  bool isCanonical() const {
    switch (which()) {
      case Int:
      case Float:
      case Double:
      case Void:
        return true;
      default:
        return false;
    }
  }

  // Check if this is a canonical representation of a wasm::ValType.
  bool isCanonicalValType() const { return !isVoid() && isCanonical(); }

  // Convert this canonical type to a wasm::ValType.
  ValType canonicalToValType() const {
    switch (which()) {
      case Int:
        return ValType::I32;
      case Float:
        return ValType::F32;
      case Double:
        return ValType::F64;
      default:
        MOZ_CRASH("Need canonical type");
    }
  }

  Maybe<ValType> canonicalToReturnType() const {
    return isVoid() ? Nothing() : Some(canonicalToValType());
  }

  // Convert this type to a wasm::TypeCode for use in a wasm
  // block signature. This works for all types, including non-canonical
  // ones. Consequently, the type isn't valid for subsequent asm.js
  // validation; it's only valid for use in producing wasm.
  TypeCode toWasmBlockSignatureType() const {
    switch (which()) {
      case Fixnum:
      case Signed:
      case Unsigned:
      case Int:
      case Intish:
        return TypeCode::I32;

      case Float:
      case MaybeFloat:
      case Floatish:
        return TypeCode::F32;

      case DoubleLit:
      case Double:
      case MaybeDouble:
        return TypeCode::F64;

      case Void:
        return TypeCode::BlockVoid;
    }
    MOZ_CRASH("Invalid Type");
  }

  const char* toChars() const {
    switch (which_) {
      case Double:
        return "double";
      case DoubleLit:
        return "doublelit";
      case MaybeDouble:
        return "double?";
      case Float:
        return "float";
      case Floatish:
        return "floatish";
      case MaybeFloat:
        return "float?";
      case Fixnum:
        return "fixnum";
      case Int:
        return "int";
      case Signed:
        return "signed";
      case Unsigned:
        return "unsigned";
      case Intish:
        return "intish";
      case Void:
        return "void";
    }
    MOZ_CRASH("Invalid Type");
  }
};

static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;

class MOZ_STACK_CLASS ModuleValidatorShared {
 public:
  struct Memory {
    MemoryUsage usage;
    uint64_t minLength;

    uint64_t minPages() const { return DivideRoundingUp(minLength, PageSize); }

    Memory() = default;
  };

  class Func {
    TaggedParserAtomIndex name_;
    uint32_t sigIndex_;
    uint32_t firstUse_;
    uint32_t funcDefIndex_;

    bool defined_;

    // Available when defined:
    uint32_t srcBegin_;
    uint32_t srcEnd_;
    uint32_t line_;
    Bytes bytes_;
    Uint32Vector callSiteLineNums_;

   public:
    Func(TaggedParserAtomIndex name, uint32_t sigIndex, uint32_t firstUse,
         uint32_t funcDefIndex)
        : name_(name),
          sigIndex_(sigIndex),
          firstUse_(firstUse),
          funcDefIndex_(funcDefIndex),
          defined_(false),
          srcBegin_(0),
          srcEnd_(0),
          line_(0) {}

    TaggedParserAtomIndex name() const { return name_; }
    uint32_t sigIndex() const { return sigIndex_; }
    uint32_t firstUse() const { return firstUse_; }
    bool defined() const { return defined_; }
    uint32_t funcDefIndex() const { return funcDefIndex_; }

    void define(ParseNode* fn, uint32_t line, Bytes&& bytes,
                Uint32Vector&& callSiteLineNums) {
      MOZ_ASSERT(!defined_);
      defined_ = true;
      srcBegin_ = fn->pn_pos.begin;
      srcEnd_ = fn->pn_pos.end;
      line_ = line;
      bytes_ = std::move(bytes);
      callSiteLineNums_ = std::move(callSiteLineNums);
    }

    uint32_t srcBegin() const {
      MOZ_ASSERT(defined_);
      return srcBegin_;
    }
    uint32_t srcEnd() const {
      MOZ_ASSERT(defined_);
      return srcEnd_;
    }
    uint32_t line() const {
      MOZ_ASSERT(defined_);
      return line_;
    }
    const Bytes& bytes() const {
      MOZ_ASSERT(defined_);
      return bytes_;
    }
    Uint32Vector& callSiteLineNums() {
      MOZ_ASSERT(defined_);
      return callSiteLineNums_;
    }
  };

  using ConstFuncVector = Vector<const Func*>;
  using FuncVector = Vector<Func>;

  class Table {
    uint32_t sigIndex_;
    TaggedParserAtomIndex name_;
    uint32_t firstUse_;
    uint32_t mask_;
    bool defined_;

   public:
    Table(uint32_t sigIndex, TaggedParserAtomIndex name, uint32_t firstUse,
          uint32_t mask)
        : sigIndex_(sigIndex),
          name_(name),
          firstUse_(firstUse),
          mask_(mask),
          defined_(false) {}

    Table(Table&& rhs) = delete;

    uint32_t sigIndex() const { return sigIndex_; }
    TaggedParserAtomIndex name() const { return name_; }
    uint32_t firstUse() const { return firstUse_; }
    unsigned mask() const { return mask_; }
    bool defined() const { return defined_; }
    void define() {
      MOZ_ASSERT(!defined_);
      defined_ = true;
    }
  };

  using TableVector = Vector<Table*>;

  class Global {
   public:
    enum Which {
      Variable,
      ConstantLiteral,
      ConstantImport,
      Function,
      Table,
      FFI,
      ArrayView,
      ArrayViewCtor,
      MathBuiltinFunction
    };

   private:
    Which which_;
    union U {
      struct VarOrConst {
        Type::Which type_;
        unsigned index_;
        NumLit literalValue_;

        VarOrConst(unsigned index, const NumLit& lit)
            : type_(Type::lit(lit).which()),
              index_(index),
              literalValue_(lit)  // copies |lit|
        {}

        VarOrConst(unsigned index, Type::Which which)
            : type_(which), index_(index) {
          // The |literalValue_| field remains unused and
          // uninitialized for non-constant variables.
        }

        explicit VarOrConst(double constant)
            : type_(Type::Double),
              literalValue_(NumLit::Double, DoubleValue(constant)) {
          // The index_ field is unused and uninitialized for
          // constant doubles.
        }
      } varOrConst;
      uint32_t funcDefIndex_;
      uint32_t tableIndex_;
      uint32_t ffiIndex_;
      Scalar::Type viewType_;
      AsmJSMathBuiltinFunction mathBuiltinFunc_;

      // |varOrConst|, through |varOrConst.literalValue_|, has a
      // non-trivial constructor and therefore MUST be placement-new'd
      // into existence.
      MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
      U() : funcDefIndex_(0) {}
      MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
    } u;

    friend class ModuleValidatorShared;
    template <typename Unit>
    friend class ModuleValidator;
    friend class js::LifoAlloc;

    explicit Global(Which which) : which_(which) {}

   public:
    Which which() const { return which_; }
    Type varOrConstType() const {
      MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral ||
                 which_ == ConstantImport);
      return u.varOrConst.type_;
    }
    unsigned varOrConstIndex() const {
      MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
      return u.varOrConst.index_;
    }
    bool isConst() const {
      return which_ == ConstantLiteral || which_ == ConstantImport;
    }
    NumLit constLiteralValue() const {
      MOZ_ASSERT(which_ == ConstantLiteral);
      return u.varOrConst.literalValue_;
    }
    uint32_t funcDefIndex() const {
      MOZ_ASSERT(which_ == Function);
      return u.funcDefIndex_;
    }
    uint32_t tableIndex() const {
      MOZ_ASSERT(which_ == Table);
      return u.tableIndex_;
    }
    unsigned ffiIndex() const {
      MOZ_ASSERT(which_ == FFI);
      return u.ffiIndex_;
    }
    Scalar::Type viewType() const {
      MOZ_ASSERT(which_ == ArrayView || which_ == ArrayViewCtor);
      return u.viewType_;
    }
    bool isMathFunction() const { return which_ == MathBuiltinFunction; }
    AsmJSMathBuiltinFunction mathBuiltinFunction() const {
      MOZ_ASSERT(which_ == MathBuiltinFunction);
      return u.mathBuiltinFunc_;
    }
  };

  struct MathBuiltin {
    enum Kind { Function, Constant };
    Kind kind;

    union {
      double cst;
      AsmJSMathBuiltinFunction func;
    } u;

    MathBuiltin() : kind(Kind(-1)), u{} {}
    explicit MathBuiltin(double cst) : kind(Constant) { u.cst = cst; }
    explicit MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
      u.func = func;
    }
  };

  struct ArrayView {
    ArrayView(TaggedParserAtomIndex name, Scalar::Type type)
        : name(name), type(type) {}

    TaggedParserAtomIndex name;
    Scalar::Type type;
  };

 protected:
  class HashableSig {
    uint32_t sigIndex_;
    const TypeContext& types_;

   public:
    HashableSig(uint32_t sigIndex, const TypeContext& types)
        : sigIndex_(sigIndex), types_(types) {}
    uint32_t sigIndex() const { return sigIndex_; }
    const FuncType& funcType() const { return types_[sigIndex_].funcType(); }

    // Implement HashPolicy:
    using Lookup = const FuncType&;
    static HashNumber hash(Lookup l) { return l.hash(nullptr); }
    static bool match(HashableSig lhs, Lookup rhs) {
      return FuncType::strictlyEquals(lhs.funcType(), rhs);
    }
  };

  class NamedSig : public HashableSig {
    TaggedParserAtomIndex name_;

   public:
    NamedSig(TaggedParserAtomIndex name, uint32_t sigIndex,
             const TypeContext& types)
        : HashableSig(sigIndex, types), name_(name) {}
    TaggedParserAtomIndex name() const { return name_; }

    // Implement HashPolicy:
    struct Lookup {
      TaggedParserAtomIndex name;
      const FuncType& funcType;
      Lookup(TaggedParserAtomIndex name, const FuncType& funcType)
          : name(name), funcType(funcType) {}
    };
    static HashNumber hash(Lookup l) {
      return HashGeneric(TaggedParserAtomIndexHasher::hash(l.name),
                         l.funcType.hash(nullptr));
    }
    static bool match(NamedSig lhs, Lookup rhs) {
      return lhs.name() == rhs.name &&
             FuncType::strictlyEquals(lhs.funcType(), rhs.funcType);
    }
  };

  using SigSet = HashSet<HashableSig, HashableSig>;
  using FuncImportMap = HashMap<NamedSig, uint32_t, NamedSig>;
  using GlobalMap =
      HashMap<TaggedParserAtomIndex, Global*, TaggedParserAtomIndexHasher>;
  using MathNameMap =
      HashMap<TaggedParserAtomIndex, MathBuiltin, TaggedParserAtomIndexHasher>;
  using ArrayViewVector = Vector<ArrayView>;

 protected:
  FrontendContext* fc_;
  ParserAtomsTable& parserAtoms_;
  FunctionNode* moduleFunctionNode_;
  TaggedParserAtomIndex moduleFunctionName_;
  TaggedParserAtomIndex globalArgumentName_;
  TaggedParserAtomIndex importArgumentName_;
  TaggedParserAtomIndex bufferArgumentName_;
  MathNameMap standardLibraryMathNames_;

  // Validation-internal state:
  LifoAlloc validationLifo_;
  Memory memory_;
  FuncVector funcDefs_;
  TableVector tables_;
  GlobalMap globalMap_;
  SigSet sigSet_;
  FuncImportMap funcImportMap_;
  ArrayViewVector arrayViews_;

  // State used to build the AsmJSModule in finish():
  CompilerEnvironment compilerEnv_;
  MutableModuleMetadata moduleMeta_;
  MutableCodeMetadata codeMeta_;
  MutableCodeMetadataForAsmJSImpl codeMetaForAsmJS_;

  // Error reporting:
  UniqueChars errorString_ = nullptr;
  uint32_t errorOffset_ = UINT32_MAX;
  bool errorOverRecursed_ = false;

 protected:
  ModuleValidatorShared(FrontendContext* fc, ParserAtomsTable& parserAtoms,
                        MutableModuleMetadata moduleMeta,
                        MutableCodeMetadata codeMeta,
                        FunctionNode* moduleFunctionNode)
      : fc_(fc),
        parserAtoms_(parserAtoms),
        moduleFunctionNode_(moduleFunctionNode),
        moduleFunctionName_(FunctionName(moduleFunctionNode)),
        standardLibraryMathNames_(fc),
        validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE, js::MallocArena),
        funcDefs_(fc),
        tables_(fc),
        globalMap_(fc),
        sigSet_(fc),
        funcImportMap_(fc),
        arrayViews_(fc),
        compilerEnv_(CompileMode::Once, Tier::Optimized, DebugEnabled::False),
        moduleMeta_(moduleMeta),
        codeMeta_(codeMeta) {
    compilerEnv_.computeParameters();
    memory_.minLength = RoundUpToNextValidAsmJSHeapLength(0);
  }

 protected:
  [[nodiscard]] bool addStandardLibraryMathInfo() {
    static constexpr struct {
      const char* name;
      AsmJSMathBuiltinFunction func;
    } functions[] = {
        {"sin", AsmJSMathBuiltin_sin},       {"cos", AsmJSMathBuiltin_cos},
        {"tan", AsmJSMathBuiltin_tan},       {"asin", AsmJSMathBuiltin_asin},
        {"acos", AsmJSMathBuiltin_acos},     {"atan", AsmJSMathBuiltin_atan},
        {"ceil", AsmJSMathBuiltin_ceil},     {"floor", AsmJSMathBuiltin_floor},
        {"exp", AsmJSMathBuiltin_exp},       {"log", AsmJSMathBuiltin_log},
        {"pow", AsmJSMathBuiltin_pow},       {"sqrt", AsmJSMathBuiltin_sqrt},
        {"abs", AsmJSMathBuiltin_abs},       {"atan2", AsmJSMathBuiltin_atan2},
        {"imul", AsmJSMathBuiltin_imul},     {"clz32", AsmJSMathBuiltin_clz32},
        {"fround", AsmJSMathBuiltin_fround}, {"min", AsmJSMathBuiltin_min},
        {"max", AsmJSMathBuiltin_max},
    };

    auto AddMathFunction = [this](const char* name,
                                  AsmJSMathBuiltinFunction func) {
      auto atom = parserAtoms_.internAscii(fc_, name, strlen(name));
      if (!atom) {
        return false;
      }
      MathBuiltin builtin(func);
      return this->standardLibraryMathNames_.putNew(atom, builtin);
    };

    for (const auto& info : functions) {
      if (!AddMathFunction(info.name, info.func)) {
        return false;
      }
    }

    static constexpr struct {
      const char* name;
      double value;
    } constants[] = {
        {"E", M_E},
        {"LN10", M_LN10},
        {"LN2", M_LN2},
        {"LOG2E", M_LOG2E},
        {"LOG10E", M_LOG10E},
        {"PI", M_PI},
        {"SQRT1_2", M_SQRT1_2},
        {"SQRT2", M_SQRT2},
    };

    auto AddMathConstant = [this](const char* name, double cst) {
      auto atom = parserAtoms_.internAscii(fc_, name, strlen(name));
      if (!atom) {
        return false;
      }
      MathBuiltin builtin(cst);
      return this->standardLibraryMathNames_.putNew(atom, builtin);
    };

    for (const auto& info : constants) {
      if (!AddMathConstant(info.name, info.value)) {
        return false;
      }
    }

    return true;
  }

 public:
  FrontendContext* fc() const { return fc_; }
  TaggedParserAtomIndex moduleFunctionName() const {
    return moduleFunctionName_;
  }
  TaggedParserAtomIndex globalArgumentName() const {
    return globalArgumentName_;
  }
  TaggedParserAtomIndex importArgumentName() const {
    return importArgumentName_;
  }
  TaggedParserAtomIndex bufferArgumentName() const {
    return bufferArgumentName_;
  }
  const CodeMetadata* codeMeta() { return codeMeta_; }
  const ModuleMetadata* moduleMeta() { return moduleMeta_; }

  void initModuleFunctionName(TaggedParserAtomIndex name) {
    MOZ_ASSERT(!moduleFunctionName_);
    moduleFunctionName_ = name;
  }
  [[nodiscard]] bool initGlobalArgumentName(TaggedParserAtomIndex n) {
    globalArgumentName_ = n;
    if (n) {
      codeMetaForAsmJS_->globalArgumentName =
          parserAtoms_.toNewUTF8CharsZ(fc_, n);
      if (!codeMetaForAsmJS_->globalArgumentName) {
        return false;
      }
    }
    return true;
  }
  [[nodiscard]] bool initImportArgumentName(TaggedParserAtomIndex n) {
    importArgumentName_ = n;
    if (n) {
      codeMetaForAsmJS_->importArgumentName =
          parserAtoms_.toNewUTF8CharsZ(fc_, n);
      if (!codeMetaForAsmJS_->importArgumentName) {
        return false;
      }
    }
    return true;
  }
  [[nodiscard]] bool initBufferArgumentName(TaggedParserAtomIndex n) {
    bufferArgumentName_ = n;
    if (n) {
      codeMetaForAsmJS_->bufferArgumentName =
          parserAtoms_.toNewUTF8CharsZ(fc_, n);
      if (!codeMetaForAsmJS_->bufferArgumentName) {
        return false;
      }
    }
    return true;
  }
  bool addGlobalVarInit(TaggedParserAtomIndex var, const NumLit& lit, Type type,
                        bool isConst) {
    MOZ_ASSERT(type.isGlobalVarType());
    MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));

    uint32_t index = codeMeta_->globals.length();
    if (!codeMeta_->globals.emplaceBack(type.canonicalToValType(), !isConst,
                                        index, ModuleKind::AsmJS)) {
      return false;
    }

    Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
    Global* global = validationLifo_.new_<Global>(which);
    if (!global) {
      return false;
    }
    if (isConst) {
      new (&global->u.varOrConst) Global::U::VarOrConst(index, lit);
    } else {
      new (&global->u.varOrConst) Global::U::VarOrConst(index, type.which());
    }
    if (!globalMap_.putNew(var, global)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
    g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
    g.pod.u.var.u.val_ = lit.value();
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addGlobalVarImport(TaggedParserAtomIndex var,
                          TaggedParserAtomIndex field, Type type,
                          bool isConst) {
    MOZ_ASSERT(type.isGlobalVarType());

    UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, field);
    if (!fieldChars) {
      return false;
    }

    uint32_t index = codeMeta_->globals.length();
    ValType valType = type.canonicalToValType();
    if (!codeMeta_->globals.emplaceBack(valType, !isConst, index,
                                        ModuleKind::AsmJS)) {
      return false;
    }

    Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
    Global* global = validationLifo_.new_<Global>(which);
    if (!global) {
      return false;
    }
    new (&global->u.varOrConst) Global::U::VarOrConst(index, type.which());
    if (!globalMap_.putNew(var, global)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::Variable, std::move(fieldChars));
    g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
    g.pod.u.var.u.importValType_ = valType.packed();
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addArrayView(TaggedParserAtomIndex var, Scalar::Type vt,
                    TaggedParserAtomIndex maybeField) {
    UniqueChars fieldChars;
    if (maybeField) {
      fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, maybeField);
      if (!fieldChars) {
        return false;
      }
    }

    if (!arrayViews_.append(ArrayView(var, vt))) {
      return false;
    }

    Global* global = validationLifo_.new_<Global>(Global::ArrayView);
    if (!global) {
      return false;
    }
    new (&global->u.viewType_) Scalar::Type(vt);
    if (!globalMap_.putNew(var, global)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::ArrayView, std::move(fieldChars));
    g.pod.u.viewType_ = vt;
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addMathBuiltinFunction(TaggedParserAtomIndex var,
                              AsmJSMathBuiltinFunction func,
                              TaggedParserAtomIndex field) {
    UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, field);
    if (!fieldChars) {
      return false;
    }

    Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
    if (!global) {
      return false;
    }
    new (&global->u.mathBuiltinFunc_) AsmJSMathBuiltinFunction(func);
    if (!globalMap_.putNew(var, global)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, std::move(fieldChars));
    g.pod.u.mathBuiltinFunc_ = func;
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }

 private:
  bool addGlobalDoubleConstant(TaggedParserAtomIndex var, double constant) {
    Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
    if (!global) {
      return false;
    }
    new (&global->u.varOrConst) Global::U::VarOrConst(constant);
    return globalMap_.putNew(var, global);
  }

 public:
  bool addMathBuiltinConstant(TaggedParserAtomIndex var, double constant,
                              TaggedParserAtomIndex field) {
    UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, field);
    if (!fieldChars) {
      return false;
    }

    if (!addGlobalDoubleConstant(var, constant)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::Constant, std::move(fieldChars));
    g.pod.u.constant.value_ = constant;
    g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant;
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addGlobalConstant(TaggedParserAtomIndex var, double constant,
                         TaggedParserAtomIndex field) {
    UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, field);
    if (!fieldChars) {
      return false;
    }

    if (!addGlobalDoubleConstant(var, constant)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::Constant, std::move(fieldChars));
    g.pod.u.constant.value_ = constant;
    g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addArrayViewCtor(TaggedParserAtomIndex var, Scalar::Type vt,
                        TaggedParserAtomIndex field) {
    UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, field);
    if (!fieldChars) {
      return false;
    }

    Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
    if (!global) {
      return false;
    }
    new (&global->u.viewType_) Scalar::Type(vt);
    if (!globalMap_.putNew(var, global)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, std::move(fieldChars));
    g.pod.u.viewType_ = vt;
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addFFI(TaggedParserAtomIndex var, TaggedParserAtomIndex field) {
    UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, field);
    if (!fieldChars) {
      return false;
    }

    if (codeMetaForAsmJS_->numFFIs == UINT32_MAX) {
      return false;
    }
    uint32_t ffiIndex = codeMetaForAsmJS_->numFFIs++;

    Global* global = validationLifo_.new_<Global>(Global::FFI);
    if (!global) {
      return false;
    }
    new (&global->u.ffiIndex_) uint32_t(ffiIndex);
    if (!globalMap_.putNew(var, global)) {
      return false;
    }

    AsmJSGlobal g(AsmJSGlobal::FFI, std::move(fieldChars));
    g.pod.u.ffiIndex_ = ffiIndex;
    return codeMetaForAsmJS_->asmJSGlobals.append(std::move(g));
  }
  bool addExportField(const Func& func, TaggedParserAtomIndex maybeField) {
    // Record the field name of this export.
    CacheableName fieldName;
    if (maybeField) {
      UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(fc_, maybeField);
      if (!fieldChars) {
        return false;
      }
      fieldName = CacheableName::fromUTF8Chars(std::move(fieldChars));
    }

    // Declare which function is exported which gives us an index into the
    // module ExportVector.
    uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
    if (!moduleMeta_->exports.emplaceBack(std::move(fieldName), funcIndex,
                                          DefinitionKind::Function)) {
      return false;
    }

    // The exported function might have already been exported in which case
    // the index will refer into the range of AsmJSExports.
    return codeMetaForAsmJS_->asmJSExports.emplaceBack(
        funcIndex, func.srcBegin() - codeMetaForAsmJS_->srcStart,
        func.srcEnd() - codeMetaForAsmJS_->srcStart);
  }

  bool defineFuncPtrTable(uint32_t tableIndex, Uint32Vector&& elems) {
    Table& table = *tables_[tableIndex];
    if (table.defined()) {
      return false;
    }

    table.define();

    for (uint32_t& index : elems) {
      index += funcImportMap_.count();
    }

    ModuleElemSegment seg = ModuleElemSegment();
    seg.elemType = RefType::func();
    seg.tableIndex = tableIndex;
    seg.offsetIfActive = Some(InitExpr(LitVal(uint32_t(0))));
    seg.encoding = ModuleElemSegment::Encoding::Indices;
    seg.elemIndices = std::move(elems);
    bool ok = codeMeta_->elemSegmentTypes.append(seg.elemType) &&
              moduleMeta_->elemSegments.append(std::move(seg));
    MOZ_ASSERT_IF(ok, codeMeta_->elemSegmentTypes.length() ==
                          moduleMeta_->elemSegments.length());
    return ok;
  }

  bool tryConstantAccess(uint64_t start, uint64_t width) {
    MOZ_ASSERT(UINT64_MAX - start > width);
    uint64_t len = start + width;
    if (len > uint64_t(INT32_MAX) + 1) {
      return false;
    }
    len = RoundUpToNextValidAsmJSHeapLength(len);
    if (len > memory_.minLength) {
      memory_.minLength = len;
    }
    return true;
  }

  // Error handling.
  bool hasAlreadyFailed() const { return !!errorString_; }

  bool failOffset(uint32_t offset, const char* str) {
    MOZ_ASSERT(!hasAlreadyFailed());
    MOZ_ASSERT(errorOffset_ == UINT32_MAX);
    MOZ_ASSERT(str);
    errorOffset_ = offset;
    errorString_ = DuplicateString(str);
    return false;
  }

  bool fail(ParseNode* pn, const char* str) {
    return failOffset(pn->pn_pos.begin, str);
  }

  bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap)
      MOZ_FORMAT_PRINTF(3, 0) {
    MOZ_ASSERT(!hasAlreadyFailed());
    MOZ_ASSERT(errorOffset_ == UINT32_MAX);
    MOZ_ASSERT(fmt);
    errorOffset_ = offset;
    errorString_ = JS_vsmprintf(fmt, ap);
    return false;
  }

  bool failfOffset(uint32_t offset, const char* fmt, ...)
      MOZ_FORMAT_PRINTF(3, 4) {
    va_list ap;
    va_start(ap, fmt);
    failfVAOffset(offset, fmt, ap);
    va_end(ap);
    return false;
  }

  bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
    va_list ap;
    va_start(ap, fmt);
    failfVAOffset(pn->pn_pos.begin, fmt, ap);
    va_end(ap);
    return false;
  }

  bool failNameOffset(uint32_t offset, const char* fmt,
                      TaggedParserAtomIndex name) {
    // This function is invoked without the caller properly rooting its locals.
    if (UniqueChars bytes = parserAtoms_.toPrintableString(name)) {
      failfOffset(offset, fmt, bytes.get());
    } else {
      ReportOutOfMemory(fc_);
    }
    return false;
  }

  bool failName(ParseNode* pn, const char* fmt, TaggedParserAtomIndex name) {
    return failNameOffset(pn->pn_pos.begin, fmt, name);
  }

  bool failOverRecursed() {
    errorOverRecursed_ = true;
    return false;
  }

  unsigned numArrayViews() const { return arrayViews_.length(); }
  const ArrayView& arrayView(unsigned i) const { return arrayViews_[i]; }
  unsigned numFuncDefs() const { return funcDefs_.length(); }
  const Func& funcDef(unsigned i) const { return funcDefs_[i]; }
  unsigned numFuncPtrTables() const { return tables_.length(); }
  Table& table(unsigned i) const { return *tables_[i]; }

  const Global* lookupGlobal(TaggedParserAtomIndex name) const {
    if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
      return p->value();
    }
    return nullptr;
  }

  Func* lookupFuncDef(TaggedParserAtomIndex name) {
    if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
      Global* value = p->value();
      if (value->which() == Global::Function) {
        return &funcDefs_[value->funcDefIndex()];
      }
    }
    return nullptr;
  }

  bool lookupStandardLibraryMathName(TaggedParserAtomIndex name,
                                     MathBuiltin* mathBuiltin) const {
    if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
      *mathBuiltin = p->value();
      return true;
    }
    return false;
  }

  bool startFunctionBodies() {
    if (!arrayViews_.empty()) {
      memory_.usage = MemoryUsage::Unshared;
    } else {
      memory_.usage = MemoryUsage::None;
    }
    return true;
  }
};

// The ModuleValidator encapsulates the entire validation of an asm.js module.
// Its lifetime goes from the validation of the top components of an asm.js
// module (all the globals), the emission of bytecode for all the functions in
// the module and the validation of function's pointer tables. It also finishes
// the compilation of all the module's stubs.
template <typename Unit>
class MOZ_STACK_CLASS ModuleValidator : public ModuleValidatorShared {
 private:
  AsmJSParser<Unit>& parser_;

 public:
  ModuleValidator(FrontendContext* fc, ParserAtomsTable& parserAtoms,
                  MutableModuleMetadata moduleMeta,
                  MutableCodeMetadata codeMeta, AsmJSParser<Unit>& parser,
                  FunctionNode* moduleFunctionNode)
      : ModuleValidatorShared(fc, parserAtoms, moduleMeta, codeMeta,
                              moduleFunctionNode),
        parser_(parser) {}

  ~ModuleValidator() {
    if (errorString_) {
      MOZ_ASSERT(errorOffset_ != UINT32_MAX);
      typeFailure(errorOffset_, errorString_.get());
    }
    if (errorOverRecursed_) {
      ReportOverRecursed(fc_);
    }
  }

 private:
  // Helpers:
  bool newSig(FuncType&& sig, uint32_t* sigIndex) {
    if (codeMeta_->types->length() >= MaxTypes) {
      return failCurrentOffset("too many signatures");
    }

    *sigIndex = codeMeta_->types->length();
    return codeMeta_->types->addType(std::move(sig));
  }
  bool declareSig(FuncType&& sig, uint32_t* sigIndex) {
    SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
    if (p) {
      *sigIndex = p->sigIndex();
      MOZ_ASSERT(FuncType::strictlyEquals(
          codeMeta_->types->type(*sigIndex).funcType(), sig));
      return true;
    }

    return newSig(std::move(sig), sigIndex) &&
           sigSet_.add(p, HashableSig(*sigIndex, *codeMeta_->types));
  }

 private:
  void typeFailure(uint32_t offset, ...) {
    va_list args;
    va_start(args, offset);

    auto& ts = tokenStream();
    ErrorMetadata metadata;
    if (ts.computeErrorMetadata(&metadata, AsVariant(offset))) {
      if (ts.anyCharsAccess().options().throwOnAsmJSValidationFailure()) {
        ReportCompileErrorLatin1VA(fc_, std::move(metadata), nullptr,
                                   JSMSG_USE_ASM_TYPE_FAIL, &args);
      } else {
        // asm.js type failure is indicated by calling one of the fail*
        // functions below.  These functions always return false to
        // halt asm.js parsing.  Whether normal parsing is attempted as
        // fallback, depends whether an exception is also set.
        //
        // If warning succeeds, no exception is set.  If warning fails,
        // an exception is set and execution will halt.  Thus it's safe
        // and correct to ignore the return value here.
        (void)ts.compileWarning(std::move(metadata), nullptr,
                                JSMSG_USE_ASM_TYPE_FAIL, &args);
      }
    }

    va_end(args);
  }

 public:
  bool init() {
    codeMetaForAsmJS_ = js_new<CodeMetadataForAsmJSImpl>();
    if (!codeMetaForAsmJS_) {
      ReportOutOfMemory(fc_);
      return false;
    }

    codeMetaForAsmJS_->toStringStart =
        moduleFunctionNode_->funbox()->extent().toStringStart;
    codeMetaForAsmJS_->srcStart = moduleFunctionNode_->body()->pn_pos.begin;
    codeMetaForAsmJS_->strict = parser_.pc_->sc()->strict() &&
                                !parser_.pc_->sc()->hasExplicitUseStrict();
    codeMetaForAsmJS_->alwaysUseFdlibm = parser_.options().alwaysUseFdlibm();
    codeMetaForAsmJS_->source = do_AddRef(parser_.ss);

    return addStandardLibraryMathInfo();
  }

  AsmJSParser<Unit>& parser() const { return parser_; }

  auto& tokenStream() const { return parser_.tokenStream; }

  bool alwaysUseFdlibm() const { return codeMetaForAsmJS_->alwaysUseFdlibm; }

 public:
  bool addFuncDef(TaggedParserAtomIndex name, uint32_t firstUse, FuncType&& sig,
                  Func** func) {
    uint32_t sigIndex;
    if (!declareSig(std::move(sig), &sigIndex)) {
      return false;
    }

    uint32_t funcDefIndex = funcDefs_.length();
    if (funcDefIndex >= MaxFuncs) {
      return failCurrentOffset("too many functions");
    }

    Global* global = validationLifo_.new_<Global>(Global::Function);
    if (!global) {
      return false;
    }
    new (&global->u.funcDefIndex_) uint32_t(funcDefIndex);
    if (!globalMap_.putNew(name, global)) {
      return false;
    }
    if (!funcDefs_.emplaceBack(name, sigIndex, firstUse, funcDefIndex)) {
      return false;
    }
    *func = &funcDefs_.back();
    return true;
  }
  bool declareFuncPtrTable(FuncType&& sig, TaggedParserAtomIndex name,
                           uint32_t firstUse, uint32_t mask,
                           uint32_t* tableIndex) {
    if (mask > MaxTableElemsRuntime) {
      return failCurrentOffset("function pointer table too big");
    }

    MOZ_ASSERT(codeMeta_->tables.length() == tables_.length());
    *tableIndex = codeMeta_->tables.length();

    uint32_t sigIndex;
    if (!newSig(std::move(sig), &sigIndex)) {
      return false;
    }

    MOZ_ASSERT(sigIndex >= codeMeta_->asmJSSigToTableIndex.length());
    if (!codeMeta_->asmJSSigToTableIndex.resize(sigIndex + 1)) {
      return false;
    }

    Limits limits = Limits(mask + 1, Nothing(), Shareable::False);
    codeMeta_->asmJSSigToTableIndex[sigIndex] = codeMeta_->tables.length();
    if (!codeMeta_->tables.emplaceBack(limits, RefType::func(),
                                       /* initExpr */ Nothing(),
                                       /*isAsmJS*/ true)) {
      return false;
    }

    Global* global = validationLifo_.new_<Global>(Global::Table);
    if (!global) {
      return false;
    }

    new (&global->u.tableIndex_) uint32_t(*tableIndex);
    if (!globalMap_.putNew(name, global)) {
      return false;
    }

    Table* t = validationLifo_.new_<Table>(sigIndex, name, firstUse, mask);
    return t && tables_.append(t);
  }
  bool declareImport(TaggedParserAtomIndex name, FuncType&& sig,
                     unsigned ffiIndex, uint32_t* importIndex) {
    if (sig.args().length() > MaxParams) {
      return failCurrentOffset("too many parameters");
    }

    FuncImportMap::AddPtr p =
        funcImportMap_.lookupForAdd(NamedSig::Lookup(name, sig));
    if (p) {
      *importIndex = p->value();
      return true;
    }

    *importIndex = funcImportMap_.count();
    MOZ_ASSERT(*importIndex == codeMetaForAsmJS_->asmJSImports.length());

    if (*importIndex >= MaxImports) {
      return failCurrentOffset("too many imports");
    }

    if (!codeMetaForAsmJS_->asmJSImports.emplaceBack(ffiIndex)) {
      return false;
    }

    uint32_t sigIndex;
    if (!declareSig(std::move(sig), &sigIndex)) {
      return false;
    }

    return funcImportMap_.add(p, NamedSig(name, sigIndex, *codeMeta_->types),
                              *importIndex);
  }

  // Error handling.
  bool failCurrentOffset(const char* str) {
    return failOffset(tokenStream().anyCharsAccess().currentToken().pos.begin,
                      str);
  }

  SharedModule finish() {
    MOZ_ASSERT(codeMeta_->numMemories() == 0);
    if (memory_.usage != MemoryUsage::None) {
      Limits limits;
      limits.shared = memory_.usage == MemoryUsage::Shared ? Shareable::True
                                                           : Shareable::False;
      limits.initial = memory_.minPages();
      limits.maximum = Nothing();
      limits.addressType = AddressType::I32;
      if (!codeMeta_->memories.append(MemoryDesc(limits))) {
        return nullptr;
      }
    }
    MOZ_ASSERT(codeMeta_->funcs.empty());
    if (!codeMeta_->funcs.resize(funcImportMap_.count() + funcDefs_.length())) {
      return nullptr;
    }
    for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty();
         r.popFront()) {
      uint32_t funcIndex = r.front().value();
      uint32_t funcTypeIndex = r.front().key().sigIndex();
      codeMeta_->funcs[funcIndex] = FuncDesc(funcTypeIndex);
    }
    for (const Func& func : funcDefs_) {
      uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
      uint32_t funcTypeIndex = func.sigIndex();
      codeMeta_->funcs[funcIndex] = FuncDesc(funcTypeIndex);
    }
    for (const Export& exp : moduleMeta_->exports) {
      if (exp.kind() != DefinitionKind::Function) {
        continue;
      }
      uint32_t funcIndex = exp.funcIndex();
      codeMeta_->funcs[funcIndex].declareFuncExported(/* eager */ true,
                                                      /* canRefFunc */ false);
    }

    codeMeta_->numFuncImports = funcImportMap_.count();

    // All globals (inits and imports) are imports from Wasm point of view.
    codeMeta_->numGlobalImports = codeMeta_->globals.length();

    MOZ_ASSERT(codeMetaForAsmJS_->asmJSFuncNames.empty());
    if (!codeMetaForAsmJS_->asmJSFuncNames.resize(funcImportMap_.count())) {
      return nullptr;
    }
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=98 G=95

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.