/// SpiderMonkey 45/52 *.h header port to Delphi
// if defined SM52 condition then SpiderMonkey 52 is used
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SpiderMonkey;
{
    This file is part of Synopse framework.

    Synopse framework. Copyright (c) Arnaud Bouchez
      Synopse Informatique - http://synopse.info

    SyNode for mORMot Copyright (c) Pavel Mashlyakovsky & Vadim Orel
      pavel.mash at gmail.com

  *** BEGIN LICENSE BLOCK *****
  Version: MPL 1.1/GPL 2.0/LGPL 2.1

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (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.mozilla.org/MPL

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License.

  The Initial Developer of the Original Code is
  Pavel Mashlyakovsky & Vadim Orel.
  Portions created by the Initial Developer are Copyright (c)
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  - Arnaud Bouchez
  - Vadim Orel
  - Pavel Mashlyakovsky
  - win2014

  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
  use your version of this file under the terms of the MPL, indicate your
  decision by deleting the provisions above and replace them with the notice
  and other provisions required by the GPL or the LGPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the MPL, the GPL or the LGPL.

  ***** END LICENSE BLOCK *****

  Version 1.18
  - initial release. Use SpiderMonkey 45
}

{$I Synopse.inc} // define HASINLINE CPU32 CPU64 OWNNORMTOUPPER
{$I SyNode.inc}   //define WITHASSERT

interface
uses
  {$ifdef MSWINDOWS}
  Windows,
  {$endif}
  SynCommons,
  SynTable,
  SynLog,
  SysUtils;

type
  JSUnknown = Pointer; //Use this type for developping. In real case comment it and check than you use only known types
{$ifndef UNICODE}
  /// 8 bit signed integer type for C APIs
  int8 = ShortInt;
  /// 8 bit unsigned integer type for C APIs
  uint8 = Byte;

  /// 16 bit signed integer type for C APIs
  int16 = Smallint;
  /// 16 bit unsigned integer type for C APIs
  uint16 = Word;

  /// 32 bit signed integer type for C APIs
  int32 = Integer;
  /// 32 bit unsigned integer type for C APIs
  uint32 = Cardinal;
{$endif}
{$ifndef ISDELPHIXE2}
  uintptr = PtrUInt;
{$endif}
  uintN = PtrUInt;

{.$ifndef FPC}
  /// variable type used to store a buffer size (in bytes) for SMAPI
  size_t = PtrUInt;
{.$endif}
  psize_t = ^size_t;
  CChar = AnsiChar;
  PCChar = PAnsiChar;
  CChar16 = WideChar;
  PCChar16 = PWideChar;
  PPCChar16 = ^PCChar16;

{$Z4}
  JSType = (
    JSTYPE_VOID       = 0, // undefined
    JSTYPE_OBJECT     = 1, // object
    JSTYPE_FUNCTION   = 2, // function
    JSTYPE_STRING     = 3, // string
    JSTYPE_NUMBER     = 4, // number
    JSTYPE_BOOLEAN    = 5, // boolean
    JSTYPE_NULL       = 6, // null
    JSTYPE_SYMBOL     = 7, //symbol
    JSTYPE_LIMIT      = 8
  );

  JSGCParamKey = (
	// Maximum nominal heap before last ditch GC.
	JSGC_MAX_BYTES = 0,
	// Number of JS_malloc bytes before last ditch GC.
	JSGC_MAX_MALLOC_BYTES = 1,
	// Amount of bytes allocated by the GC.
	JSGC_BYTES = 3,
	// Number of times GC has been invoked. Includes both major and minor GC.
	JSGC_NUMBER = 4,
	// Max size of the code cache in bytes.
	JSGC_MAX_CODE_CACHE_BYTES = 5,
	// Select GC mode.
	JSGC_MODE = 6,
	// Number of cached empty GC chunks.
	JSGC_UNUSED_CHUNKS = 7,
	// Total number of allocated GC chunks.
	JSGC_TOTAL_CHUNKS = 8,
	// Max milliseconds to spend in an incremental GC slice.
	JSGC_SLICE_TIME_BUDGET = 9,
	// Maximum size the GC mark stack can grow to.
	JSGC_MARK_STACK_LIMIT = 10,
	// GCs less than this far apart in time will be considered 'high-frequency GCs'.
	// See setGCLastBytes in jsgc.cpp.
	JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
	// Start of dynamic heap growth.
	JSGC_HIGH_FREQUENCY_LOW_LIMIT = 12,
	// End of dynamic heap growth.
	JSGC_HIGH_FREQUENCY_HIGH_LIMIT = 13,
	// Upper bound of heap growth.
	JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX = 14,
	// Lower bound of heap growth.
	JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN = 15,
	// Heap growth for low frequency GCs.
	JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16,
	// If false, the heap growth factor is fixed at 3. If true, it is determined
	// based on whether GCs are high- or low- frequency.
	JSGC_DYNAMIC_HEAP_GROWTH = 17,
	// If true, high-frequency GCs will use a longer mark slice.
	JSGC_DYNAMIC_MARK_SLICE = 18,
	// Lower limit after which we limit the heap growth.
	JSGC_ALLOCATION_THRESHOLD = 19,
{$IFNDEF SM52}
	// We decommit memory lazily. If more than this number of megabytes is
	// available to be decommitted, then JS_MaybeGC will trigger a shrinking GC
	// to decommit it.
	JSGC_DECOMMIT_THRESHOLD = 20,
{$ENDIF}
	// We try to keep at least this many unused chunks in the free chunk pool at
	// all times, even after a shrinking GC.
	JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
	// We never keep more than this many unused chunks in the free chunk pool.
	JSGC_MAX_EMPTY_CHUNK_COUNT = 22,
	// Whether compacting GC is enabled.
	JSGC_COMPACTING_ENABLED = 23
  {$IFDEF SM52}
  // If true, painting can trigger IGC slices.
  ,JSGC_REFRESH_FRAME_SLICES_ENABLED = 24
  {$ENDIF}
  );

  JSGCMode = (
    // Perform only global GCs.
    JSGC_MODE_GLOBAL = 0,
    // Perform per-compartment GCs until too much garbage has accumulated.
    JSGC_MODE_COMPARTMENT = 1,
    // Collect in short time slices rather than all at once. Implies
    // JSGC_MODE_COMPARTMENT.
    JSGC_MODE_INCREMENTAL = 2
  );

  JSVersion = (
  JSVERSION_ECMA_3  = 148,
  /// Run-time version enumeration corresponding to 1.6
  JSVERSION_1_6     = 160,
  /// Run-time version enumeration corresponding to 1.7
  JSVERSION_1_7     = 170,
  /// Run-time version enumeration corresponding to 1.8
  JSVERSION_1_8     = 180,
  /// Run-time version enumeration corresponding to ECMA standard 5, i.e. 1.8.5
  JSVERSION_ECMA_5  = 185,
  /// Run-time version enumeration corresponding to default version
  JSVERSION_DEFAULT = 0,
  /// Run-time version enumeration corresponding to an identified version
  JSVERSION_UNKNOWN = -1
  );

///  This enum is used to select if properties with JSPROP_DEFINE_LATE flag
//  should be defined on the object.
//  Normal JSAPI consumers probably always want DefineAllProperties here.
  JSPropertyDefinitionBehavior = (
  DefineAllProperties,
  OnlyDefineLateProperties,
  DontDefineLateProperties
  );
/// During global creation, we fire notifications to callbacks registered
// via the Debugger API. These callbacks are arbitrary script, and can touch
// the global in arbitrary ways. When that happens, the global should not be
// in a half-baked state. But this creates a problem for consumers that need
// to set slots on the global to put it in a consistent state.
// - This API provides a way for consumers to set slots atomically (immediately
// after the global is created), before any debugger hooks are fired. It's
// unfortunately on the clunky side, but that's the way the cookie crumbles.
// - If callers have no additional state on the global to set up, they may pass
// |FireOnNewGlobalHook| to JS_NewGlobalObject, which causes that function to
// fire the hook as its final act before returning. Otherwise, callers should
// pass |DontFireOnNewGlobalHook|, which means that they are responsible for
// invoking JS_FireOnNewGlobalObject upon successfully creating the global. If
// an error occurs and the operation aborts, callers should skip firing the
// hook. But otherwise, callers must take care to fire the hook exactly once
// before compiling any script in the global's scope (we have assertions in
// place to enforce this). This lets us be sure that debugger clients never miss
// breakpoints.
  OnNewGlobalHookOption = (
    FireOnNewGlobalHook,
    DontFireOnNewGlobalHook
  );
/// Dense index into cached prototypes and class atoms for standard objects.
{$IFDEF SM52}
  JSProtoKey = (
  JSProto_Null = 0,
  JSProto_Object,
  JSProto_Function,
  JSProto_Array,
  JSProto_Boolean,
  JSProto_JSON,
  JSProto_Date,
  JSProto_Math,
  JSProto_Number,
  JSProto_String,
  JSProto_RegExp,
  JSProto_Error,
  JSProto_InternalError,
  JSProto_EvalError,
  JSProto_RangeError,
  JSProto_ReferenceError,
  JSProto_SyntaxError,
  JSProto_TypeError,
  JSProto_URIError,
  JSProto_DebuggeeWouldRun,
  JSProto_CompileError,
  JSProto_RuntimeError,
  JSProto_Iterator,
  JSProto_StopIteration,
  JSProto_ArrayBuffer,
  JSProto_Int8Array,
  JSProto_Uint8Array,
  JSProto_Int16Array,
  JSProto_Uint16Array,
  JSProto_Int32Array,
  JSProto_Uint32Array,
  JSProto_Float32Array,
  JSProto_Float64Array,
  JSProto_Uint8ClampedArray,
  JSProto_Proxy,
  JSProto_WeakMap,
  JSProto_Map,
  JSProto_Set,
  JSProto_DataView,
  JSProto_Symbol,
  JSProto_SharedArrayBuffer,
  JSProto_Intl,
  JSProto_TypedObject,
  JSProto_Reflect,
  JSProto_SIMD,
  JSProto_WeakSet,
  JSProto_TypedArray,
  JSProto_Atomics,
  JSProto_SavedFrame,
  JSProto_WebAssembly,
  JSProto_WasmModule,
  JSProto_WasmInstance,
  JSProto_WasmMemory,
  JSProto_WasmTable,
  JSProto_Promise,
  JSProto_LIMIT
  );
{$ELSE}
  JSProtoKey = (
  JSProto_Null = 0,
  JSProto_Object,
  JSProto_Function,
  JSProto_Array,
  JSProto_Boolean,
  JSProto_JSON,
  JSProto_Date,
  JSProto_Math,
  JSProto_Number,
  JSProto_String,
  JSProto_RegExp,
  JSProto_Error,
  JSProto_InternalError,
  JSProto_EvalError,
  JSProto_RangeError,
  JSProto_ReferenceError,
  JSProto_SyntaxError,
  JSProto_TypeError,
  JSProto_URIError,
  JSProto_Iterator,
  JSProto_StopIteration,
  JSProto_ArrayBuffer,
  JSProto_Int8Array,
  JSProto_Uint8Array,
  JSProto_Int16Array,
  JSProto_Uint16Array,
  JSProto_Int32Array,
  JSProto_Uint32Array,
  JSProto_Float32Array,
  JSProto_Float64Array,
  JSProto_Uint8ClampedArray,
  JSProto_Proxy,
  JSProto_WeakMap,
  JSProto_Map,
  JSProto_Set,
  JSProto_DataView,
  JSProto_Symbol,
  JSProto_SharedArrayBuffer,
  JSProto_Intl,
  JSProto_TypedObject,
  JSProto_Reflect,
  JSProto_SIMD,
  JSProto_WeakSet,
  JSProto_TypedArray,
  JSProto_Atomics,
  JSProto_SavedFrame,
  JSProto_LIMIT
  );
{$ENDIF}
{$Z1}
/// Type of JSValue
  JSValueType = (
    JSVAL_TYPE_DOUBLE   = $00,
    JSVAL_TYPE_INT32    = $01,
    JSVAL_TYPE_UNDEFINED= $02,
    JSVAL_TYPE_BOOLEAN  = $03,
    JSVAL_TYPE_MAGIC    = $04,
    JSVAL_TYPE_STRING   = $05,
    JSVAL_TYPE_SYMBOL   = $06,
    {$IFDEF SM52}
    JSVAL_TYPE_PRIVATE_GCTHING = $07,
    JSVAL_TYPE_NULL     = $08,
    JSVAL_TYPE_OBJECT   = $0C,
    {$ELSE}
    JSVAL_TYPE_NULL     = $07,
    JSVAL_TYPE_OBJECT   = $08,
    {$ENDIF}
    // These never appear in a jsval; they are only provided as an out-of-band value.
    JSVAL_TYPE_UNKNOWN  = $20,
    JSVAL_TYPE_MISSING  = $21
  );

{$ifndef CPU64}
/// first 4 bytes for JSValue
{$MINENUMSIZE 4}
{$WARN COMBINING_SIGNED_UNSIGNED OFF}
{$WARN BOUNDS_ERROR OFF}
  JSValueTag = (
  JSVAL_TAG_CLEAR      = Cardinal($FFFFFF80),
  JSVAL_TAG_INT32      = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_INT32)),
  JSVAL_TAG_UNDEFINED  = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_UNDEFINED)),
  JSVAL_TAG_STRING     = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_STRING)),
  JSVAL_TAG_SYMBOL     = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_SYMBOL)),
  JSVAL_TAG_BOOLEAN    = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_BOOLEAN)),
  JSVAL_TAG_MAGIC      = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_MAGIC)),
  JSVAL_TAG_NULL       = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_NULL)),
  JSVAL_TAG_OBJECT     = Cardinal(JSVAL_TAG_CLEAR or UInt8(JSVAL_TYPE_OBJECT))
  );
{$WARN BOUNDS_ERROR ON}
{$WARN COMBINING_SIGNED_UNSIGNED ON}
{$MINENUMSIZE 1}
{$endif}


{$Z2}
  /// Possible exception types
  // -These types are part of a JSErrorFormatString structure
  // - They define which error to throw in case of a runtime error
  // - JSEXN_NONE marks an unthrowable error
  JSExnType = (
    JSEXN_NONE = -1,
    JSEXN_ERR,
    JSEXN_INTERNALERR,
    JSEXN_EVALERR,
    JSEXN_RANGEERR,
    JSEXN_REFERENCEERR,
    JSEXN_SYNTAXERR,
    JSEXN_TYPEERR,
    JSEXN_URIERR,
    {$IFDEF SM52}
    JSEXN_DEBUGGEEWOULDRUN,
    JSEXN_WASMCOMPILEERROR,
    JSEXN_WASMRUNTIMEERROR,
    JSEXN_WARN,
    {$ENDIF}
    JSEXN_LIMIT
  );
{$Z1}


  Tint8Vector = array[0..(MaxInt div sizeof(int8))-1] of int8;
  Pint8Vector = ^Tint8Vector;
  Tuint8Vector = array[0..(MaxInt div sizeof(uint8))-1] of uint8;
  Puint8Vector = ^Tuint8Vector;
  Tint16Vector = array[0..(MaxInt div sizeof(int16))-1] of int16;
  Pint16Vector = ^Tint16Vector;
  Tuint16Vector = array[0..(MaxInt div sizeof(uint16))-1] of uint16;
  Puint16Vector = ^Tuint16Vector;
  Tint32Vector = array[0..(MaxInt div sizeof(int32))-1] of int32;
  Pint32Vector = ^Tint32Vector;
  Tuint32Vector = array[0..(MaxInt div sizeof(uint32))-1] of uint32;
  Puint32Vector = ^Tuint32Vector;
  Tfloat32Vector = array[0..(MaxInt div sizeof(single))-1] of single;
  Pfloat32Vector = ^Tfloat32Vector;
  Tfloat64Vector = array[0..(MaxInt div sizeof(double))-1] of double;
  Pfloat64Vector = ^Tfloat64Vector;

  /// the available types of elements in a typed array or data view
  // - obj must have passed a JS_IsArrayBufferView/JS_Is*Array test, or somehow
  // be known that it would pass such a test: it is an ArrayBufferView or a
  // wrapper of an ArrayBufferView, and the unwrapping will succeed.
  /// - jsabTYPE_UINT8_CLAMPED is a special type that is a uint8_t, but assignments
  // are clamped to [0,255]: treat the raw data type as a uint8_t.
  // - jsabTYPE_DATAVIEW is the type returned for a DataView. Note that
  // there is no single element type in this case
  JSArrayBufferViewType = (
    jsabTYPE_INT8 = 0,
    jsabTYPE_UINT8,
    jsabTYPE_INT16,
    jsabTYPE_UINT16,
    jsabTYPE_INT32,
    jsabTYPE_UINT32,
    jsabTYPE_FLOAT32,
    jsabTYPE_FLOAT64,
    jsabTYPE_UINT8_CLAMPED,
    jsabTYPE_MAXTYPEDARRAYVIEWTYPE,
    jsabTYPE_FLOAT32x4,
    jsabTYPE_INT32x4

);
const
  nullPtr: pointer = nil;
  /// Run-time version enumeration corresponding to the latest available
  // - that is, ECMA standard 5, i.e. 1.8.5
  JSVERSION_LATEST  = JSVERSION_ECMA_5;
type
// pointers
{$IFDEF SM52}
  JSFreeOp = pointer;
  PJSContextOptions = ^JSContextOptions;
{$ELSE}
  PJSRuntime = ^JSRuntime;
  PJSRuntimeOptions = ^JSRuntimeOptions;
{$ENDIF}
  PJSContext = ^JSContext;
  PJSCompartment = ^JSCompartment;
  PJS_CompartmentOptions = ^JS_CompartmentOptions;
  PJSObject = ^JSObject;
  PJSFunction = PJSObject;
  PJSString = ^JSString;
  PJSClass = ^JSClass;
  PJSCompileOptions = ^JSCompileOptions;
  PJSScript = JSUnknown;
  PJSRootedValue = ^JSRootedValue;
  PJSRootedObject = ^JSRootedObject;
  PJSRootedString = ^JSRootedString;
  PJSPropertySpec = ^JSPropertySpec;
  PJSFunctionSpec = ^JSFunctionSpec;
  PJSErrorReport = ^JSErrorReport;
  PJSErrorFormatString = ^JSErrorFormatString;
  PJSAtomState = JSUnknown;
  PJSPrincipals = JSUnknown;
  PJSAutoCheckCannotGC = JSUnknown;
  PJSStringFinalizer = ^JSStringFinalizer;

// jsid
  JSIdType = (
    JSID_TYPE_STRING = $0,
    JSID_TYPE_INT    = $1,
    JSID_TYPE_VOID   = $2,
    JSID_TYPE_SYMBOL = $4
  );
  jsid = record
    asBits: size_t;
    function isString: Boolean;
    function asJSString: PJSString;
  end;

  TjsidVector = array[0..(MaxInt div sizeof(jsid))-2] of jsid;
  PjsidVector = ^TjsidVector;

  _JSIdArray = record
    cx: PJSContext;
    mBegin: PjsidVector;    //* actually, length jsid words */
    mLength: size_t;
    mCapacity: size_t;
    mStorage: Int64;
  end;
/// internal Spidermonkey structure for storrage jsid
  {$ifdef USERECORDWITHMETHODS}JSIdArray  = record
    {$else}JSIdArray  = object{$endif}
  private
    _internal: _JSIdArray;
    procedure init(cx: PJSContext); {$ifdef HASINLINE}inline;{$endif}
  public
    property Length: size_t read _internal.mLength;
    property vector: PjsidVector read _internal.mBegin;
  end;

// jsvalue
  JSWhyMagic = Cardinal;
  jsval_payload = record
    case Byte of
      0: (i32: int32);
      1: (u32: uint32);
{$IFNDEF CPU64}
      2: (boo: uint32);   // Don't use |bool| -- it must be four bytes.
      3: (str: PJSString);
      4: (obj: PJSObject);
      5: (ptr: pointer);
{$ENDIF}
      6: (why: JSWhyMagic);
{$IFNDEF CPU64}
      7: (word: size_t);
      8: (uintptr: PtrUInt)
{$ENDIF}
  end;
{$ifdef IS_LITTLE_ENDIAN}
  jsval_val_layout = packed record
    payload: jsval_payload;
{$IFNDEF CPU64}
    tag: JSValueTag;
{$ENDIF}
  end;
{$else} //BIG_ENDIAN
  jsval_val_layout = record
    tag: JSValueTag;
    payload: jsval_payload;
  end;
{$endif}

  /// low-level definition of the jsval internals
  // - do not use directly
  jsval_layout = record
    case Byte of
      0: (asBits: QWord);
{$IFNDEF CPU64}
      1: (s: jsval_val_layout);
{$ENDIF}
      2: (asDouble: double);
      3: (asPtr: Pointer);
  end;

  /// used by JS_Stringify() method to incremently write the JSON content
  JSONWriteCallback = function(const buf: PCChar16; len: uint32; data: pointer): Boolean; cdecl;

  /// high-level definition of the JSValue
  {$ifdef USERECORDWITHMETHODS}jsval = record
    {$else}jsval = object{$endif}
  private
    _l: jsval_layout;

    function getIsVoid: Boolean;
    function getIsNull: Boolean;

    function getIsInteger: Boolean;
    function getAsInteger: Integer;
    procedure setAsInteger(const Value: Integer);

    function getAsInt64: Int64;
    procedure setAsInt64(const Value: Int64);

    function getIsDouble: Boolean;
    function getAsDouble: Double;
    procedure setAsDouble(const Value: Double);

    function getIsNumber: Boolean;

    function getIsBoolean: Boolean;
    function getAsBoolean: Boolean;
    procedure setAsBoolean(const Value: Boolean);

    function getIsObject: Boolean;
    function getAsObject: PJSObject;
    procedure setAsObject(const Value: PJSObject);

    function getIsString: Boolean;
    function getJSString: PJSString;
    procedure setJSString(const Value: PJSString);

    function getIsSimpleVariant(cx: PJSContext): Boolean;
    function getSimpleVariant(cx: PJSContext): Variant;
    procedure setSimpleVariant(cx: PJSContext; const Value: Variant);

    function getPrivate: Pointer;
    procedure setPrivate(const Value: Pointer);

    function getAsDate(cx: PJSContext): TDateTime;
    procedure setAsDate(cx: PJSContext; const Value: TDateTime);

    function getAsJson(cx: PJSContext): RawUTF8;
    procedure setAsJson(cx: PJSContext; const Value: RawUTF8);

    function IsPrimitive: Boolean;
    function IsMagic: Boolean;

  public
    /// Is vaule void
    property isVoid: Boolean read getIsVoid;
    /// Set vaule void
    procedure setVoid;

    /// Is vaule null
    property isNull: Boolean read getIsNull;
    /// Set vaule null
    procedure setNull;

    /// Is vaule 32bit integer
    property isInteger: Boolean read getIsInteger;
    /// Get/set vaule as 32bit integer
    property asInteger: Integer read getAsInteger write setAsInteger;

    /// Get/set vaule as 64bit integer (if more than 32 bits than as double)
    property asInt64: Int64 read getAsInt64 write setAsInt64;

    /// Is vaule double
    property isDouble: Boolean read getIsDouble;
    /// Get/set vaule as double
    property asDouble: Double read getAsDouble write setAsDouble;

    /// Is vaule Number (32bit integer or double)
    property isNumber: Boolean read getIsNumber;

    /// Is vaule boolean
    property isBoolean: Boolean read getIsBoolean;
    /// Get/set vaule as boolean
    property asBoolean: Boolean read getAsBoolean write setAsBoolean;

    /// Is vaule object(null is object too)
    property isObject: Boolean read getIsObject;
    /// Get/set vaule as object
    property asObject: PJSObject read getAsObject write setAsObject;

    /// Is vaule string
    property isString: Boolean read getIsString;
    /// Get/set vaule as JSString
    property asJSString: PJSString read getJSString write setJSString;

    /// Is vaule simple(void, null, boolean, number or string)
    property isSimpleVariant[cx: PJSContext]: Boolean read getIsSimpleVariant;
    /// Get/set vaule as simple
    property asSimpleVariant[cx: PJSContext]: Variant read getSimpleVariant write setSimpleVariant;

    /// Get/set vaule as Custom pointer
    property asPrivate: Pointer read getPrivate write setPrivate;

    /// Get/set vaule as DateTime(object Date used)
    property asDate[cx: PJSContext]: TDateTime read getAsDate write setAsDate;

    /// Add JSON representation of value to Writer
    procedure AddJSON(cx: PJSContext; W: TTextWriter);
    /// Get/set vaule as JSON representation
    property asJson[cx: PJSContext]: RawUTF8 read getAsJson write setAsJson;

    /// Get JSON representation of value and launch callback
    function Stringify(cx: PJSContext; var replacer: PJSObject; space: jsval;
      callback: JSONWriteCallback; data: pointer): Boolean;

    /// Get type of value
    function ValType(cx: PJSContext): JSType;

    /// Get source of value
    function toSource(cx: PJSContext): PJSString;

  end;

  /// an abstract array of jsval JavaScript values
  TjsvalVector = array[0..(MaxInt div sizeof(jsval))-3] of jsval;
  /// map an array of jsval JavaScript values
  PjsvalVector = ^TjsvalVector;
  TjsvalDynArray = array of jsval;

  /// low-level definition of arguments of function
  // - do not use it dirrectly
  _JSArgRec = record
    case boolean of
      true: (
        calle: jsval;
        this: jsval;
        argv: jsval;
      );
      false: (rval: jsval);
  end;

  /// hight-level definition of arguments of function
  {$ifdef USERECORDWITHMETHODS}JSArgRec = record
    {$else}JSArgRec = object{$endif}
  private
    rec: _JSArgRec;
    function GetIsConstructing: Boolean; {$ifdef HASINLINE}inline;{$endif}
    function getThis(cx: PJSContext): jsval; {$ifdef HASINLINE}inline;{$endif}
    function getThisObject(cx: PJSContext): PJSObject;
    function getArgv: PjsvalVector; {$ifdef HASINLINE}inline;{$endif}
    function getCalleObject: PJSObject; {$ifdef HASINLINE}inline;{$endif}
  public
  /// is function called as constructor
    property IsConstructing: Boolean read GetIsConstructing;
  /// function, that is called now
    property calle: jsval read rec.calle;
    property calleObject: PJSObject read getCalleObject;
  /// this value of function
    property this[cx: PJSContext]: jsval read getThis;
    property thisObject[cx: PJSContext]: PJSObject read getThisObject;
  /// arguments of function
    property argv: PjsvalVector read getArgv;
  /// return value of function
    property rval: jsval write rec.rval;
  end;

//Todo - set correct codes for some sitautions
  JS_ObjectOpResult = object
    code: UIntPtr;
  end;

// callback
  JSInterruptCallback = function(cx: PJSContext): Boolean; cdecl;
  /// callback prototype for returning an execution error
  JSErrorCallback = function(userRef: Pointer; const errorNumber: uintN): PJSErrorFormatString; cdecl;
  /// callback prototype for reporting error for a given runtime context
{$IFDEF SM52}
  JSWarningReporter = procedure(cx: PJSContext; report: PJSErrorReport); cdecl;
{$ELSE}
  JSErrorReporter = procedure(cx: PJSContext; _message: PCChar; report: PJSErrorReport); cdecl;
{$ENDIF}
  JSStringFinalizerOp = procedure(fin: PJSStringFinalizer; chars: PCChar16);  cdecl;

// Add or get a property named by id in obj.  Note the jsid id type -- id may
// be a string (Unicode property identifier) or an int (element index).  The
// *vp out parameter, on success, is the new property value after the action.
  JSAddPropertyOp = function(cx: PJSContext; var obj: PJSObject; var id: jsid;
    out vp: jsval): Boolean; cdecl;
// Delete a property named by id in obj.
// - If an error occurred, return false as per normal JSAPI error practice.
// - If no error occurred, but the deletion attempt wasn't allowed (perhaps
// because the property was non-configurable), set *succeeded to false and
// return true.  This will cause |delete obj[id]| to evaluate to false in
// non-strict mode code, and to throw a TypeError in strict mode code.
// - If no error occurred and the deletion wasn't disallowed (this is *not* the
// same as saying that a deletion actually occurred -- deleting a non-existent
// property, or an inherited property, is allowed -- it's just pointless),
// set *succeeded to true and return true.
  JSDeletePropertyOp = function(cx: PJSContext; var obj: PJSObject; var id: jsid;
    out res: JS_ObjectOpResult):Boolean; cdecl;
// Get a property named by id in obj.  Note the jsid id type -- id may
// be a string (Unicode property identifier) or an int (element index).  The
// *vp out parameter, on success, is the new property value after the action.
  JSGetterOp = function(cx: PJSContext; var obj: PJSObject; var id: jsid; out vp: jsval):Boolean; cdecl;
// Set a property named by id in obj, treating the assignment as strict
// mode code if strict is true. Note the jsid id type -- id may be a string
// (Unicode property identifier) or an int (element index). The *vp out
// parameter, on success, is the new property value after the
// set.
  JSSetterOp  = function(cx: PJSContext; var obj: PJSObject; var id: jsid;
    var vp: jsval; out res: JS_ObjectOpResult):Boolean; cdecl;
// The old-style JSClass.enumerate op should define all lazy properties not
// yet reflected in obj.
  JSEnumerateOp = function(cx: PJSContext; var obj: PJSObject): Boolean; cdecl;
// Resolve a lazy property named by id in obj by defining it directly in obj.
// Lazy properties are those reflected from some peer native property space
// (e.g., the DOM attributes for a given node reflected as obj) on demand.
// - JS looks for a property in an object, and if not found, tries to resolve
// the given id. *resolvedp should be set to true iff the property was
// was defined on |obj|.
  JSResolveOp = function(cx: PJSContext; var obj: PJSObject; var id: jsid; out resolved: Boolean): Boolean; cdecl;
// A class with a resolve hook can optionally have a mayResolve hook. This hook
// must have no side effects and must return true for a given id if the resolve
// hook may resolve this id. This is useful when we're doing a "pure" lookup: if
// mayResolve returns false, we know we don't have to call the effectful resolve
// hook.
//
// maybeObj, if non-null, is the object on which we're doing the lookup. This
// can be nullptr: during JIT compilation we sometimes know the Class but not
// the object.
  JSMayResolveOp = function(names: PJSAtomState; id: jsid; maybeObj: PJSObject): Boolean; cdecl;
{$IFDEF SM52}
  JSFinalizeOp = procedure(var fop: JSFreeOp; obj: PJSObject); cdecl;
{$ELSE}
  JSFinalizeOp = procedure(var rt: PJSRuntime; obj: PJSObject); cdecl;
{$ENDIF}
// Check whether v is an instance of obj.  Return false on error or exception,
// true on success with true in *bp if v is an instance of obj, false in
// *bp otherwise.
  JSHasInstanceOp = function(cx: PJSContext; var obj: PJSObject; var vp: jsval; out b: Boolean): Boolean; cdecl;
// Typedef for native functions called by the JS VM.
  JSNative = function(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
// Function type for trace operation of the class called to enumerate all
// traceable things reachable from obj's private data structure. For each such
// thing, a trace implementation must call one of the JS_Call*Tracer variants
// on the thing.
//
// JSTraceOp implementation can assume that no other threads mutates object
// state. It must not change state of the object or corresponding native
// structures. The only exception for this rule is the case when the embedding
// needs a tight integration with GC. In that case the embedding can check if
// the traversal is a part of the marking phase through calling
// JS_IsGCMarkingTracer and apply a special code like emptying caches or
// marking its native structures.
  JSTraceOp = JSUnknown;

{$IFNDEF SM52}
  /// JavaScript execution runtime
  // - this object does not store anything, but just provide some helper methods
  JSRuntime = object
  private
    function GetPrivate: Pointer;
    procedure SetPrivate(const Value: Pointer);
    function GetOptions: PJSRuntimeOptions;
    function GetGCParameter(key: JSGCParamKey): uint32;
    procedure SetGCParameter(key: JSGCParamKey; const Value: uint32);
    function GetErrorReporter: JSErrorReporter;
    procedure SetErrorReporter(er: JSErrorReporter);
    function GetInterruptCallback: JSInterruptCallback;
    procedure SetInterruptCallback(callback: JSInterruptCallback);
    function GetEmptyString: PJSString; {$ifdef HASINLINE}inline;{$endif}
    function GetNowMs: int64; {$ifdef HASINLINE}inline;{$endif}

  public
    /// Initializes the JavaScript runtime.
    class function New(maxbytes: uint32; maxNurseryBytes: uint32; parentRuntime: PJSRuntime): PJSRuntime;
    /// Frees a JavaScript runtime.
    procedure Destroy;
    /// Wrote access a JSRuntime field for application-specific data.
    // Memory management for this private data is the application's responsibility.
    // The JavaScript engine itself never uses it.
    procedure SetNativeStackQuota(systemCodeStackSize: size_t);
    /// Performs garbage collection in the JS memory pool.
    procedure GC;
    /// Request a callback set using JS_SetInterruptCallback
    procedure RequestInterruptCallback;
    /// Create a new JSContext
    function NewContext(stackChunkSize: size_t): PJSContext;
    /// Read/write access a JSRuntime field for application-specific data.
    // Memory management for this private data is the application's responsibility.
    // The JavaScript engine itself never uses it.
    property PrivateData: Pointer read GetPrivate write SetPrivate;
    /// Get options of runtime
    property Options: PJSRuntimeOptions read GetOptions;
    /// Get/Set performance parameters related to garbage collection.
    property GCParameter[key: JSGCParamKey]: uint32 read GetGCParameter write SetGCParameter;
    /// Adjust performance parameters related to garbage collection based on available memory(in megabytes).
    procedure SetGCParametersBasedOnAvailableMemory(availMem: uint32);
    /// Get/Set the error reporting mechanism for an application.
    property ErrorReporter: JSErrorReporter read GetErrorReporter write SetErrorReporter;
    /// Get/Set a callback function that is automatically called periodically while JavaScript code runs.
    property InterruptCallback:JSInterruptCallback read GetInterruptCallback write SetInterruptCallback;

    /// Microseconds since the epoch, midnight, January 1, 1970 UTC.
    property NowMs: int64 read GetNowMs;
    /// Returns the empty string as a JSString object.
    property EmptyString: PJSString read GetEmptyString;
  end;

/// Options of runtime
  JSRuntimeOptions = object
  private
    function getOptions(const Index: Integer): Boolean;
    procedure setOptions(const Index: Integer; const Value: Boolean);
  public
    property Baseline: Boolean index 0 read getOptions write setOptions;
    property Ion: Boolean index 1 read getOptions write setOptions;
    property AsmJS: Boolean index 2 read getOptions write setOptions;
    property ThrowOnAsmJSValidationFailure: Boolean index 3 read getOptions write setOptions;
    property NativeRegExp: Boolean index 4 read getOptions write setOptions;
    property UnboxedArrays: Boolean index 5 read getOptions write setOptions;
    property AsyncStack: Boolean index 6 read getOptions write setOptions;
    property Werror: Boolean index 7 read getOptions write setOptions;
    property StrictMode: Boolean index 8 read getOptions write setOptions;
    property ExtraWarnings: Boolean index 9 read getOptions write setOptions;
  end;
{$ENDIF}
{$IFDEF SM52}
/// Options of context
  JSContextOptions = object
  private
    function getOptions(const Index: Integer): Boolean;
    procedure setOptions(const Index: Integer; const Value: Boolean);
  public
    property Baseline: Boolean index 0 read getOptions write setOptions;
    property Ion: Boolean index 1 read getOptions write setOptions;
    property AsmJS: Boolean index 2 read getOptions write setOptions;
    property Wasm: Boolean index 3 read getOptions write setOptions;
    property WasmAlwaysBaseline: Boolean index 4 read getOptions write setOptions;
    property ThrowOnAsmJSValidationFailure: Boolean index 5 read getOptions write setOptions;
    property NativeRegExp: Boolean index 6 read getOptions write setOptions;
    property UnboxedArrays: Boolean index 7 read getOptions write setOptions;
    property AsyncStack: Boolean index 8 read getOptions write setOptions;
    property ThrowOnDebuggeeWouldRun: Boolean index 9 read getOptions write setOptions;
    property Werror: Boolean index 10 read getOptions write setOptions;
    property StrictMode: Boolean index 11 read getOptions write setOptions;
    property ExtraWarnings: Boolean index 12 read getOptions write setOptions;
  end;
{$ENDIF}
  /// JavaScript execution context
  // - this object does not store anything, but just provide some helper methods
  JSContext = object
  private
    function GetPrivate: Pointer; {$ifdef HASINLINE}inline;{$endif}
    procedure SetPrivate(const Value: Pointer);
{$IFDEF SM52}
    function GetEmptyString: PJSString; {$ifdef HASINLINE}inline;{$endif}
    function GetGCParameter(key: JSGCParamKey): uint32; {$ifdef HASINLINE}inline;{$endif}
    procedure SetGCParameter(key: JSGCParamKey; const Value: uint32); {$ifdef HASINLINE}inline;{$endif}
    function GetNowMs: int64; {$ifdef HASINLINE}inline;{$endif}
    function GetWarningReporter: JSWarningReporter; {$ifdef HASINLINE}inline;{$endif}
    procedure SetWarningReporter(reporter: JSWarningReporter); {$ifdef HASINLINE}inline;{$endif}
    function GetOptions: PJSContextOptions; {$ifdef HASINLINE}inline;{$endif}
{$ELSE}
    function GetRuntime: PJSRuntime; {$ifdef HASINLINE}inline;{$endif}
{$ENDIF}
    function GetIsRunning: boolean; {$ifdef HASINLINE}inline;{$endif}
  protected
    // Return the ArrayBuffer underlying an ArrayBufferView
    // - If the buffer has been neutered, this will still return the neutered buffer.
    // - obj must be an object that would return true for JS_IsArrayBufferViewObject()
    function GetArrayBufferViewBuffer(var obj: PJSObject; out isSharedMemory: Boolean): PJSObject; overload;{$ifdef HASINLINE}inline;{$endif}
    function GetArrayBufferViewBuffer(var obj: PJSObject): PJSObject; overload;{$ifdef HASINLINE}inline;{$endif}
  public
{$IFDEF SM52}
    /// Initializes the JavaScript context.
    class function CreateNew(maxbytes: uint32; maxNurseryBytes: uint32 = 16 * (1 SHL 20); parentContext: PJSContext = nil): PJSContext;
    /// Performs garbage collection in the JS memory pool.
    procedure GC; {$ifdef HASINLINE}inline;{$endif}
    /// Returns the empty string as a JSString object.
    property EmptyString: PJSString read GetEmptyString;
    /// Get/Set performance parameters related to garbage collection.
    property GCParameter[key: JSGCParamKey]: uint32 read GetGCParameter write SetGCParameter;
    /// Adjust performance parameters related to garbage collection based on available memory(in megabytes).
    procedure SetGCParametersBasedOnAvailableMemory(availMem: uint32);
    /// Microseconds since the epoch, midnight, January 1, 1970 UTC.
    property NowMs: int64 read GetNowMs;
    /// Request a callback set using JS_SetInterruptCallback
    procedure RequestInterruptCallback; {$ifdef HASINLINE}inline;{$endif}
    /// Set the size of the native stack that should not be exceed. To disable
    // stack size checking pass 0.
    // - SpiderMonkey allows for a distinction between system code (such as GCs, which
    // may incidentally be triggered by script but are not strictly performed on
    // behalf of such script), trusted script (as determined by JS_SetTrustedPrincipals),
    // and untrusted script. Each kind of code may have a different stack quota,
    // allowing embedders to keep higher-priority machinery running in the face of
    // scripted stack exhaustion by something else.
    // - The stack quotas for each kind of code should be monotonically descending,
    // and may be specified with this function. If 0 is passed for a given kind
    // of code, it defaults to the value of the next-highest-priority kind.
    // - This function may only be called immediately after the runtime is initialized
    // and before any code is executed and/or interrupts requested.
    procedure SetNativeStackQuota(systemCodeStackSize: size_t); {$ifdef HASINLINE}inline;{$endif}
    /// Get options of context
    property Options: PJSContextOptions read GetOptions;
    /// Get/Set the warning reporting mechanism for an application.
    property WarningReporter: JSWarningReporter read GetWarningReporter write SetWarningReporter;
    /// Add callback for interrupt
    procedure AddInterruptCallback(callback: JSInterruptCallback); {$ifdef HASINLINE}inline;{$endif}
    /// Disable interrupt callbacks call
    procedure DisableInterruptCallback; {$ifdef HASINLINE}inline;{$endif}
    /// Disable/enable interrupt callbacks call
    procedure ResetInterruptCallback(disable: boolean); {$ifdef HASINLINE}inline;{$endif}
    /// Call interrupt callback if it is requested
    function CheckForInterrupt: Boolean; {$ifdef HASINLINE}inline;{$endif}
{$ELSE}
    /// Runtime of this context
    property rt: PJSRuntime read GetRuntime;
{$ENDIF}
    /// Read/Write access a JSContext field for application-specific data.
    // Memory management for this private data is the application's responsibility.
    // The JavaScript engine itself never uses it.
    property PrivateData: Pointer read GetPrivate write SetPrivate;
    /// Enter a different compartment on the given context, so that objects in that
    // compartment can be accessed.
    // - NB: This API is infallible; a NULL return value does not indicate error
    function EnterCompartment(target: PJSObject): PJSCompartment;
    /// Leave a the compartment, returning to the compartment active before the
    // corresponding JS_EnterCompartment.
    procedure LeaveCompartment(oldCompartment: PJSCompartment);
    /// indicates to the JS engine that the calling thread is entering a region
    // of code that may call into the JSAPI but does not block
    procedure BeginRequest; {$ifdef HASINLINE}inline;{$endif}
    /// indicates to the JS engine that the calling thread is leaving a region
    // of code that may call into the JSAPI but does not block
    procedure EndRequest; {$ifdef HASINLINE}inline;{$endif}
    /// Create Compile Options
    function NewCompileOptions: PJSCompileOptions;
    /// Free Compile Options
    procedure FreeCompileOptions(opt: PJSCompileOptions);

    /// Create a new JavaScript object for use as a global object.
    function NewGlobalObject(clasp: PJSClass): PJSObject;
    /// Initialize standard JS class constructors, prototypes, and any top-level
    // functions and constants associated with the standard classes (e.g. isNaN
    // for Number).
    // - NB: This sets cx's global object to obj if it was null.
    function InitStandardClasses(var obj: PJSObject): boolean;
    /// Add 'Reflect.parse', a SpiderMonkey extension, to the Reflect object on the
    // given global.
    function InitReflectParse(var obj: PJSObject): boolean;
    /// Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes'
    // object will be sealed.
    function InitCTypesClass(var obj: PJSObject): boolean;
    /// Initialize the 'Debugger' object on a global variable 'obj'. The 'ctypes'
    // object will be sealed.
    function DefineDebuggerObject(var obj: PJSObject): boolean;
    /// This function makes a cross-compartment wrapper for the given JS object.
    // Details see here http://stackoverflow.com/questions/18730477/what-does-js-wrapobject-do
    function WrapObject(var obj: PJSObject): boolean;

    ///// modules support

    /// Initialize modeles classes next 2 functions cannot work without calling this function
    function InitModuleClasses(var obj: PJSObject): boolean;
    /// Compile script as module
    function CompileModule(var obj: PJSObject; opts: PJSCompileOptions;
       chars: PCChar16; length: size_t): PJSObject;
    /// Set handler for module resolving
    procedure SetModuleResolveHook(var hook: PJSFunction);


    /// Invoke a constructor, like the JS expression `new ctor(...args)`. Returns
    // the new object, or null on error.
    function New(var ctor: PJSObject; argc: uintN; argv: PjsvalVector): PJSObject;
    /// Create a new object based on a specified class and root it
    function NewObject(clasp: PJSClass): PJSObject;
    /// Create a new object based on a specified class
    // - Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default
    // proto. If proto is nullptr, the JS object will have `null` as [[Prototype]].
    function NewObjectWithGivenProto(clasp: PJSClass; var proto: PJSObject): PJSObject;

    /// Create/Free rooted value(put value in rooting stack)
    // Garbage collection not performs to rooted values
    // - Warning!!! You must free rooted values in revers order for creating
    // that's why it's a bad idea store rooting values in any place except
    // local variables. Exception is root some values just after creating context
    // and unroot them (in reverse order) just before destroy context.
    // For other cases use reserved slots
    function NewRootedValue(val: jsval): PJSRootedValue; {$ifdef HASINLINE}inline;{$endif}
    procedure FreeRootedValue(str: PJSRootedValue);

    /// Create/Free rooted object(put object in rooting stack)
    // Garbage collection not performs to rooted objects
    // - Warning!!! You must free rooted objects in reverse order for creating
    // that's why it's a bad idea store rooting objects in any place except
    // local variables. Exception is root some objects just after creating context
    // and unroot them (in reverse order) just before destroy context.
    // For other cases use reserved slots
    function NewRootedObject(obj: PJSObject): PJSRootedObject; {$ifdef HASINLINE}inline;{$endif}
    procedure FreeRootedObject(obj: PJSRootedObject);

    /// Create/Free rooted string(put string in rooting stack)
    // Garbage collection not performs to rooted strings
    // - Warning!!! You must free rooted strings in reverse order for creating
    // that's why it's a bad idea store rooting strings in any place except
    // local variables. Exception is root some strings just after creating context
    // and unroot them (in reverse order) just before destroy context.
    // For other cases use reserved slots
    function NewRootedString(obj: PJSString): PJSRootedString; {$ifdef HASINLINE}inline;{$endif}
    procedure FreeRootedString(str: PJSRootedString);

    /// create a new JavaScript string instance
    function NewJSString(const Value: SynUnicode): PJSString; overload; {$ifdef HASINLINE}inline;{$endif}
    function NewJSString(const Value: RawUTF8): PJSString; overload; {$ifdef HASINLINE}inline;{$endif}
    function NewJSString(TextWide: PWideChar; TextLen: integer): PJSString; overload; {$ifdef HASINLINE}inline;{$endif}
    function NewJSString(TextAnsi: PAnsiChar; TextLen, CodePage: integer): PJSString; overload;
    function NewExternalString(const Value: SynUnicode): PJSString; {$ifdef HASINLINE}inline;{$endif}

    /// create a new JavaScript Date object instance
    function NewDateObject(year, mon, mday, hour, min, sec: int32): PJSObject; {$ifdef HASINLINE}inline;{$endif}
    function NewDateObjectMsec(msec: double): PJSObject; {$ifdef HASINLINE}inline;{$endif}

    /// create a new JavaScript Array object instance
    function NewArrayObject(length: size_t): PJSObject; overload; {$ifdef HASINLINE}inline;{$endif}
    function NewArrayObject(length: size_t; vector: PjsvalVector): PJSObject; overload; {$ifdef HASINLINE}inline;{$endif}

    /// create a new JavaScript Function object instance
    function NewFunction(call: JSNative; nargs: uintN; flags: uintN; name: PCChar): PJSObject; {$ifdef HASINLINE}inline;{$endif}

    /// Reports a memory allocation error
    // - Call JS_ReportOutOfMemory to report that an operation failed because the
    // system is out of memory
    // - When the JavaScript engine tries to allocate memory and allocation fails,
    // it reports an error as though by calling this function
    procedure ReportOutOfMemory;
    /// Create a new JavaScript Error object and set it to be the pending exception on cx.
    // The callback must then return JS_FALSE to cause the exception to be propagated
    // to the calling script.
    procedure ReportError(format: PCChar);
    /// Report an error with an application-defined error code.
    // - varargs is Additional arguments for the error message.
    //- These arguments must be of type jschar*
    // - The number of additional arguments required depends on the error
    // message, which is determined by the errorCallback
    procedure ReportErrorNumberUC(errorCallback: JSErrorCallback; userRef: pointer; const erroNubmer: uintN);
    /// Offer the JavaScript engine an opportunity to perform garbage collection if needed.
    procedure MaybeGC;

    /// Get the current pending exception for a given JSContext.
    function GetPendingException(out rv: jsval): boolean; {$ifdef HASINLINE}inline;{$endif}
    /// Clear the currently pending exception in a context.
    procedure ClearPendingException; {$ifdef HASINLINE}inline;{$endif}

    /// Convert a jsid to type JS::Value.
    function IdToValue(id: jsid; out v: jsval): Boolean; //~~~ write delphi realization
    /// Convert a JS::Value to type jsid.
    function ValueToId(var v: jsval; out id: jsid): Boolean; //~~~ write delphi realization

    /// Determines the JS data type of a JS value.
    function TypeOfValue(v: jsval): JSType; //~~~ write delphi realization

    /// Compile and execute a script in the scope of the current global of cx.
    function EvaluateScript(opts: PJSCompileOptions;
       bytes: PCChar; length: size_t;
       out rval: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function EvaluateUCScript(opts: PJSCompileOptions;
       chars: PCChar16; length: size_t;
       out rval: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Compile a script, source, for execution.
    function CompileScript(bytes: PCChar; length: size_t; opts: PJSCompileOptions;
      out script: PJSScript): boolean; {$ifdef HASINLINE}inline;{$endif}
    function CompileUCScript(chars: PCChar16; length: size_t; opts: PJSCompileOptions;
      out script: PJSScript): boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Evaluate a script in the scope of the current global of cx.
    function ExecuteScript(var script: PJSScript; out rval: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Return the global object for the active function on the context.
    // The global object is specific for a compartment
    function CurrentGlobalOrNull: PJSObject; {$ifdef HASINLINE}inline;{$endif}

    //ArrayBuffer support

    /// Create a new signed 8 bit integer typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewInt8Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new unsigned 8 bit integer (byte) typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewUint8Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit integer typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewUint8ClampedArray(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new signed 16 bit integer typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewInt16Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new unsigned 16 bit integer typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewUint16Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new signed 32 bit integer typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewInt32Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new unsigned 32 bit integer typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewUint32Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new signed 32 bit float (single) typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewFloat32Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new signed 64 bit float (double) typed array with nelements elements
    // - will fill the newly created array with zeros
    function NewFloat64Array(nelements: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit signed integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewInt8ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit unsigned integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewUint8ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit unsigned integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewUint8ClampedArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 16 bit signed integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewInt16ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 16 bit unsigned integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewUint16ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 32 bit signed integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewInt32ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 32 bit unsigned integer typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewUint32ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 32 bit float (single) typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewFloat32ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 64 bit float (double) typed array and copy in values
    // from a given object
    // - The object is used as if it was an array; that is, the new array (if
    // successfully created) will have length given by array.length, and its
    // elements will be those specified by array[0], array[1], and so on, after
    // conversion to the typed array element type.
    function NewFloat64ArrayFromArray(var arr: PJSObject): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit signed integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewInt8ArrayWithBuffer(var arrayBuffer: PJSObject;
      byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit unsigned integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewUint8ArrayWithBuffer(var arrayBuffer: PJSObject;
      byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 8 bit unsigned integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewUint8ClampedArrayWithBuffer(var arrayBuffer: PJSObject;
      byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 16 bit signed integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewInt16ArrayWithBuffer(var arrayBuffer: PJSObject;
     byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 16 bit unsigned integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewUint16ArrayWithBuffer(var arrayBuffer: PJSObject;
     byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 32 bit signed integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewInt32ArrayWithBuffer(var arrayBuffer: PJSObject;
     byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 32 bit unsigned integer typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewUint32ArrayWithBuffer(var arrayBuffer: PJSObject;
     byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 32 bit float (single) typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewFloat32ArrayWithBuffer(var arrayBuffer: PJSObject;
     byteOffset: uint32; length: int32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new 64 bit float (double) typed array using the given
    // ArrayBuffer for storage
    // - The length value is optional; if -1 is passed, enough elements to use up the
    // remainder of the byte array is used as the default value
    function NewFloat64ArrayWithBuffer(var arrayBuffer: PJSObject;
     byteOffset: uint32; length: int32): PJSObject; {$ifdef HASINLINE}inline;{$endif}

    /// Create a new SharedArrayBuffer with the given byte length.
    function NewSharedArrayBuffer(nbytes: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Create a new ArrayBuffer with the given byte length.
    function NewArrayBuffer(nbytes: uint32): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Indicates whether or not a script or function is currently executing in a given context.
    property IsRunning: boolean read GetIsRunning;
    /// Destroy a JSContext.
    procedure Destroy;
  end;

  /// JavaScript execution compartment
  // - this object does not store anything, but just provide some helper methods
  JSCompartment = object
  end;
  /// options of compartment
  JS_CompartmentOptions = record
    version: JSVersion;
    invisibleToDebugger: Boolean;
    mergeable: Boolean;
    discardSource: Boolean;
    disableLazyParsing_: Boolean;
    cloneSingletons: Boolean;
    extraWarningsOverride: JSUnknown;
    zone_: JSUnknown;
    traceGlobal: JSTraceOp;
    singletonsAsTemplates: Boolean;
    addonId: JSUnknown;
    preserveJitCode: Boolean;
  end;

  /// JSObject is the type of JavaScript objects in the JSAPI
  // - this object does not store anything, but just provide some helper methods
  // to access a PJSObject value via low-level API functions
  {$ifdef USERECORDWITHMETHODS}JSObject = record
    {$else}JSObject = object{$endif}
  private
    function GetPrivate: Pointer; {$ifdef HASINLINE}inline;{$endif}
    procedure SetPrivate(data: Pointer); cdecl; {$ifdef HASINLINE}inline;{$endif}
    function GetReservedSlot(index: uint32): jsval; {$ifdef HASINLINE}inline;{$endif}
    procedure SetReservedSlot(index: uint32; v: jsval); {$ifdef HASINLINE}inline;{$endif}
    function GetClass: PJSClass; {$ifdef HASINLINE}inline;{$endif}
    function GetConstructor(cx: PJSContext): PJSObject; {$ifdef HASINLINE}inline;{$endif}
    // Return the available byte length of an array buffer
    // - obj must have passed a JS_IsArrayBufferObject test, or somehow be known
    // that it would pass such a test: it is an ArrayBuffer or a wrapper of an
    // ArrayBuffer, and the unwrapping will succeed
    function GetArrayBufferByteLength: uint32;{$ifdef HASINLINE}inline;{$endif}
    function GetSharedArrayBufferByteLength: uint32;{$ifdef HASINLINE}inline;{$endif}
    // Return a pointer to the start of the data referenced by any typed array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    // - Prefer the type-specific versions when possible
    function GetArrayBufferViewData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pointer;{$ifdef HASINLINE}inline;{$endif}
  public
    /// get a jsval corresponding to this object
    function ToJSValue: jsval; {$ifdef HASINLINE}inline;{$endif}

    /// Access the private data field of an object.
    property PrivateData: Pointer read GetPrivate write SetPrivate;

    property Ctor[cx: PJSContext]: PJSObject read GetConstructor;
    /// Read access an object's reserved slots.
    property ReservedSlot[index: uint32]: jsval read GetReservedSlot write SetReservedSlot;
    /// Retrieves the class associated with an object.
    property Class_: PJSClass read GetClass;

    /// JSAPI method equivalent to the instanceof operator in JavaScript.
    function HasInstance(cx: PJSContext; var val: jsval): Boolean;

    /// is object is Date object
    function isDate(cx: PJSContext): Boolean; {$ifdef HASINLINE}inline;{$endif}
    /// is object is Function object
    function isFunction(cx: PJSContext): Boolean; {$ifdef HASINLINE}inline;{$endif}
    /// is object is Array object
    function isArray(cx: PJSContext): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Retrieve the private data associated with an object, if that object is an
    // instance of a specified class.
    function GetInstancePrivate(cx: PJSContext; clasp: PJSClass): Pointer; {$ifdef HASINLINE}inline;{$endif}

    /// Create a new property on an object.
    function DefineProperty(cx: PJSContext; const name: PCChar;
        const value: jsval; attrs: uint32 = 0;
        getter: JSNative = nil; setter: JSNative = nil): boolean; {$ifdef HASINLINE}inline;{$endif}
    function DefineUCProperty(cx: PJSContext; const name: SynUnicode;
        const value: jsval; attrs: uint32 = 0;
        getter: JSNative = nil; setter: JSNative = nil): Boolean; overload; {$ifdef HASINLINE}inline;{$endif}
    function DefineUCProperty(cx: PJSContext; const name: PCChar16; namelen: size_t;
        const value: jsval; attrs: uint32 = 0;
        getter: JSNative = nil; setter: JSNative = nil): Boolean; overload; {$ifdef HASINLINE}inline;{$endif}
    function DefinePropertyById(cx: PJSContext; var id: jsid;
        const value: jsval; attrs: uint32 = 0;
        getter: JSNative = nil; setter: JSNative = nil): boolean; {$ifdef HASINLINE}inline;{$endif}
    /// Define multiple properties for a single object.
    // PJSPropertySpec must be null-terminated
    function DefineProperties(cx: PJSContext; ps: PJSPropertySpec): boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Find a specified property and retrieve its value.
    function GetPropValue(cx: PJSContext; const name: SynUnicode): jsval;
    function GetProperty(cx: PJSContext; const name: PCChar; out vp: jsval): boolean; {$ifdef HASINLINE}inline;{$endif}
    function GetUCProperty(cx: PJSContext; const name: PCChar16; namelen: size_t; out vp: jsval): boolean; {$ifdef HASINLINE}inline;{$endif}
    function GetPropertyById(cx: PJSContext; const id: jsid; out vp: jsval): boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Assign a value to a property of an object.
    function SetProperty(cx: PJSContext; const name: PCChar; const vp: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function SetUCProperty(cx: PJSContext; const name: PCChar16; namelen: size_t; const vp: jsval): boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Removes a specified property from an object.
    function DeletePropertyById(cx: PJSContext; const id: jsid;
      out res: JS_ObjectOpResult): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Determine whether a JavaScript object has a specified property.
    function HasProperty(cx: PJSContext; const name: PCChar): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function HasUCProperty(cx: PJSContext; const name: PCChar16; namelen: size_t; out found: Boolean): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Determine whether a property is already physically present on a JSObject.
    function AlreadyHasOwnUCProperty(cx: PJSContext; const name: PCChar16; namelen: size_t): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Create a native function and assign it as a property to a specified JS object
    function DefineFunction(cx: PJSContext; name: PCChar;
        call: JSNative; nargs: uintN; attrs: uintN = 0): PJSFunction; {$ifdef HASINLINE}inline;{$endif}
    function DefineUCFunction(cx: PJSContext; name: PCChar16;
        namelen: size_t; call: JSNative; nargs: uintN; attrs: uintN = 0): PJSFunction; {$ifdef HASINLINE}inline;{$endif}

    /// Create zero or more functions and makes them properties (methods)
    // of a specified object, obj, as if by calling JS_DefineFunction repeatedly
    function DefineFunctions(cx: PJSContext; fs: PJSFunctionSpec;
        behavior: JSPropertyDefinitionBehavior = DefineAllProperties): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Calls a specified JS function.
    // - Perform the method call `rval = obj[name](args)`.
    function RunMethod(cx: PJSContext; const name: PCChar;
      args: TjsvalDynArray; out rval: jsval): Boolean; overload; {$ifdef HASINLINE}inline;{$endif}
    function RunMethod(cx: PJSContext; const name: PCChar;
      arg: jsval; out rval: jsval): Boolean; overload; {$ifdef HASINLINE}inline;{$endif}
    function RunMethod(cx: PJSContext; const name: PCChar;
      out rval: jsval): Boolean; overload; {$ifdef HASINLINE}inline;{$endif}
    function CallFunction(cx: PJSContext; var fun: PJSFunction;
      argc: size_t; argv: PjsvalVector; out rval: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function CallFunctionValue(cx: PJSContext; val: jsval;
      argc: size_t; argv: PjsvalVector; out rval: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function CallFunctionName(cx: PJSContext; const name: PCChar;
      argc: size_t; argv: PjsvalVector; out rval: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Make a JSClass accessible to JavaScript code by creating its prototype,
    // constructor, properties, and functions.
    function InitClass(cx: PJSContext; var parent_proto: PJSObject;
      clasp: PJSClass; _constructor: JSNative; nargs: Cardinal;
      ps: PJSPropertySpec; fs: PJSFunctionSpec;
      static_ps: PJSPropertySpec; static_fs: PJSFunctionSpec): PJSObject; {$ifdef HASINLINE}inline;{$endif}

    /// Get the prototype of obj, storing it in result.
    // - Implements: ES6 [[GetPrototypeOf]] internal method.
    function GetPrototype(cx: PJSContext; out protop: PJSObject): Boolean; {$ifdef HASINLINE}inline;{$endif}
    /// Change the prototype of obj.
    // - Implements: ES6 [[SetPrototypeOf]] internal method.
    // - In cases where ES6 [[SetPrototypeOf]] returns false without an exception,
    // JS_SetPrototype throws a TypeError and returns false.
    // - Performance warning: JS_SetPrototype is very bad for performance. It may
    // cause compiled jit-code to be invalidated. It also causes not only obj but
    // all other objects in the same "group" as obj to be permanently deoptimized.
    // It's better to create the object with the right prototype from the start.
    function SetPrototype(cx: PJSContext; var proto: PJSObject): Boolean; {$ifdef HASINLINE}inline;{$endif}

    /// Get an array of the non-symbol enumerable properties of obj.
    // This function is roughly equivalent to:
    //
    //     var result = [];
    //     for (key in obj)
    //         result.push(key);
    //     return result;
    //
    // This is the closest thing we currently have to the ES6 [[Enumerate]]
    // internal method.
    function Enumerate(cx: PJSContext): JSIdArray;

    //array methods
    function GetArrayLength(cx: PJSContext; var length: uint32): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function GetElement(cx: PJSContext; index: uint32; out vp: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function SetElement(cx: PJSContext; index: uint32; const vp: jsval): Boolean; {$ifdef HASINLINE}inline;{$endif}
    function DeleteElement(cx: PJSContext; index: uint32; out res: JS_ObjectOpResult): Boolean; {$ifdef HASINLINE}inline;{$endif}

    //function methods
    function GetFunctionId: PJSString; {$ifdef HASINLINE}inline;{$endif}
    property FunctionId: PJSString read GetFunctionId;
    function DecompileFunction(cx: PJSContext; PrettyPrint: Boolean = true): PJSString;

    //arrayBuffer methods

    /// Check whether obj supports JS_GetTypedArray* APIs
    // - Note that this may return false if a security wrapper is encountered that
    // denies the unwrapping.
    // - if this test or one of the JS_Is*Array tests succeeds, then it is safe to call
    // the dedicated accessor JSAPI calls
    function IsTypedArrayObject: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Check whether obj supports JS_GetArrayBufferView* APIs
    // - Note that this may return false if a security wrapper is encountered that
    // denies the unwrapping.
    // - if this test or one of the JS_Is*Array tests succeeds, then it is safe to call
    // the dedicated ArrayBufferView accessor JSAPI calls
    function IsArrayBufferViewObject: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 8 bit signed integer typed array types (ArrayBufferView subtypes)
    function IsInt8Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 8 bit unsigned integer typed array types (ArrayBufferView subtypes)
    function IsUint8Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 8 bit unsigned integer typed array types (ArrayBufferView subtypes)
    function IsUint8ClampedArray: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 16 bit signed integer typed array types (ArrayBufferView subtypes)
    function IsInt16Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 16 bit unsigned integer typed array types (ArrayBufferView subtypes)
    function IsUint16Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 32 bit signed integer typed array types (ArrayBufferView subtypes)
    function IsInt32Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 32 bit unsigned integer typed array types (ArrayBufferView subtypes)
    function IsUint32Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 32 bit float (single) typed array types (ArrayBufferView subtypes)
    function IsFloat32Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Test for specific 64 bit float (double) typed array types (ArrayBufferView subtypes)
    function IsFloat64Array: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Return the isShared flag of a typed array, which denotes whether
    // the underlying buffer is a SharedArrayBuffer.
    //
    // |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
    // be known that it would pass such a test: it is a typed array or a wrapper of
    // a typed array, and the unwrapping will succeed.
    function GetTypedArraySharedness: Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 8 bit signed integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsInt8Array(out length: uint32; out isSharedMemory:Boolean; out Data: Pint8Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 8 bit unsigned integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsUint8Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Puint8Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 8 bit unsigned integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsUint8ClampedArray(out length: uint32; out isSharedMemory:Boolean; out  Data: Puint8Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 16 bit signed integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsInt16Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Pint16Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 16 bit unsigned integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsUint16Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Puint16Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 32 bit signed integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsInt32Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Pint32Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 32 bit unsigned integer typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsUint32Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Puint32Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 32 bit float (single) typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsFloat32Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Pfloat32Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap 64 bit float (double) typed array into direct memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsFloat64Array(out length: uint32; out isSharedMemory:Boolean; out  Data: Pfloat64Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap an object as its raw binary memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsArrayBufferView(out length: uint32; out isSharedMemory:Boolean; out  Data: Puint8Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Unwrap an object as its raw binary memory buffer
    // - Return nil without throwing any exception if the object cannot be viewed as the
    // correct typed array, or the typed array object on success, filling both out parameters
    function GetObjectAsArrayBuffer(out length: uint32; out Data: Puint8Vector): PJSObject;{$ifdef HASINLINE}inline;{$endif}

    /// Get the type of elements in a typed array, or jsabTYPE_DATAVIEW if a DataView
    function GetArrayBufferViewType: JSArrayBufferViewType;{$ifdef HASINLINE}inline;{$endif}

//    function GetSharedArrayBufferViewType: JSArrayBufferViewType;{$ifdef HASINLINE}inline;{$endif}

    /// Check whether obj supports the JS_GetArrayBuffer* APIs
    // - Note that this may return false if a security wrapper is encountered that denies the
    // unwrapping
    // - If this test succeeds, then it is safe to call the various accessor JSAPI calls
    function IsArrayBufferObject: Boolean;{$ifdef HASINLINE}inline;{$endif}

    function IsSharedArrayBufferObject: Boolean;

    /// Return true if the arrayBuffer contains any data. This will return false for
    // ArrayBuffer.prototype and neutered ArrayBuffers.
    //
    // |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
    // that it would pass such a test: it is an ArrayBuffer or a wrapper of an
    // ArrayBuffer, and the unwrapping will succeed.
    function ArrayBufferHasData: Boolean;

    /// Return a pointer to an array buffer's data
    // - The buffer is still owned by the array buffer object, and should not
    // be modified on another thread. The returned pointer is stable across GCs
    // - obj must have passed a JS_IsArrayBufferObject test, or somehow be known
    // that it would pass such a test: it is an ArrayBuffer or a wrapper of an
    // ArrayBuffer, and the unwrapping will succeed.
    function GetArrayBufferData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint8Vector; overload;{$ifdef HASINLINE}inline;{$endif}
    function GetArrayBufferData: Puint8Vector; overload;{$ifdef HASINLINE}inline;{$endif}

    /// Check whether the obj is ArrayBufferObject and memory mapped. Note that this
    // may return false if a security wrapper is encountered that denies the
    // unwrapping.
    function IsMappedArrayBufferObject(obj: PJSObject): Boolean;{$ifdef HASINLINE}inline;{$endif}

    /// Return the number of elements in a typed array
    // - obj must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
    // be known that it would pass such a test: it is a typed array or a wrapper of
    // a typed array, and the unwrapping will succeed.
    function GetTypedArrayLength: uint32;{$ifdef HASINLINE}inline;{$endif}

    /// Return the byte offset from the start of an array buffer to the start of a
    // typed array view
    // - obj must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
    // be known that it would pass such a test: it is a typed array or a wrapper of
    // a typed array, and the unwrapping will succeed.
    function GetTypedArrayByteOffset: uint32;

    /// Return the byte length of a typed array
    // - obj must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
    // be known that it would pass such a test: it is a typed array or a wrapper of
    // a typed array, and the unwrapping will succeed
    function GetTypedArrayByteLength: uint32;{$ifdef HASINLINE}inline;{$endif}

    /// More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
    function GetArrayBufferViewByteLength: uint32;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 8 bit signed integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetInt8ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pint8Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 8 bit unsigned integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetUint8ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint8Vector; overload;{$ifdef HASINLINE}inline;{$endif}
    function GetUInt8ArrayData: Puint8Vector; overload;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 8 bit unsigned integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetUint8ClampedArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint8Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 16 bit signed integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetInt16ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pint16Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 16 bit unsigned integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetUint16ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint16Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 32 bit signed integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetInt32ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pint32Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 32 bit unsigned integer array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetUint32ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint32Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 32 bit float (single) array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetFloat32ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pfloat32Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by a typed 64 bit float (double) array
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - obj must have passed a JS_Is*Array test, or somehow be known that it would
    // pass such a test: it is a typed array or a wrapper of a typed array, and the
    // unwrapping will succeed
    function GetFloat64ArrayData(out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pfloat64Vector;{$ifdef HASINLINE}inline;{$endif}

    /// Return a pointer to the start of the data referenced by any typed array
    // and it's length. For ArrayBufferView return a pointer and length of slice.
    // - The data is still owned by the typed array, and should not be modified on
    // another thread
    // - If JSObject is not a typed array or arrayBufferView return false
    function GetBufferDataAndLength(out data: Puint8Vector; out len: uint32): boolean;{$ifdef HASINLINE}inline;{$endif}
  end;


  JSString = object
    /// get the UTF-8 text corresponding to this string, for a given
    // runtime execution context
    function ToUTF8(cx: PJSContext): RawUTF8; overload;
    /// get the UTF-8 text corresponding to this string, for a given
    // runtime execution context
    // - slightly faster overloaded method (avoid string assignment)
    procedure ToUTF8(cx: PJSContext; var result: RawUTF8); overload;
    /// Add UTF-8 text corresponding to this string to writer,
    // without escaping
    procedure ToUTF8(cx: PJSContext; W: TTextWriter); overload;

    /// get the Ansi text corresponding to this string
    // if source string containt non-ascii chars = return ''
    function ToAnsi(cx: PJSContext): AnsiString;

    /// get the UTF-16 text corresponding to this string, for a given
    // runtime execution context
    function ToSynUnicode(cx: PJSContext): SynUnicode;
    function ToString(cx: PJSContext): string;
    procedure ToVariant(cx: PJSContext; var Value: Variant);
    procedure ToJSONString(cx: PJSContext; W: TTextWriter);
    /// get a jsval corresponding to this string
    function ToJSVal: jsval;

    function HasLatin1Chars: Boolean;
    function Length: size_t;

    function GetLatin1StringCharsAndLength(cx: PJSContext; out len: size_t):PCChar;
    function GetTwoByteStringCharsAndLength(cx: PJSContext; out len: size_t):PCChar16;
  end;
{$IFDEF SM52}
  JSClassOps = record
    addProperty:        JSAddPropertyOp;
    delProperty:        JSDeletePropertyOp;
    getProperty:        JSGetterOp;
    setProperty:        JSSetterOp;
    enumerate:          JSEnumerateOp;
    resolve:            JSResolveOp;
    mayResolve:         JSMayResolveOp;
    finalize:           JSFinalizeOp;
    call:               JSNative;
    hasInstance:        JSHasInstanceOp;
    construct:          JSNative;
    trace:              JSTraceOp;
  end;
  PJSClassOps = ^JSClassOps;
{$ENDIF}
  JSClass =  record
    name:               PCChar;
    flags:              uint32;
{$IFDEF SM52}
    cOps:               PJSClassOps;
    reserved:           array [0..2] of pointer;
{$ELSE}
    addProperty:        JSAddPropertyOp;
    delProperty:        JSDeletePropertyOp;
    getProperty:        JSGetterOp;
    setProperty:        JSSetterOp;
    enumerate:          JSEnumerateOp;
    resolve:            JSResolveOp;
    mayResolve:         JSMayResolveOp;
    finalize:           JSFinalizeOp;
    call:               JSNative;
    hasInstance:        JSHasInstanceOp;
    construct:          JSNative;
    trace:              JSTraceOp;
    reserved:           array [0..22] of pointer;
{$ENDIF}
  end;

  JSCompileOptions = record
    reserved: array[0..1] of Pointer;
    filename: PCChar;
    reserved1: array[3..24] of Pointer;
  end;

  JSRootedValue = record
    Stack: JSUnknown;
    prev: JSUnknown;
    ptr: jsval;
  end;
  JSRootedObjectStack = record
    Last: PJSRootedObject;
  end;
  PJSRootedObjectStack = ^JSRootedObjectStack;
  JSRootedObject = record
    Stack: PJSRootedObjectStack;
    prev: PJSRootedObject;
    ptr: PJSObject;
  end;

  JSRootedString = record
    Stack: JSUnknown;
    prev: JSUnknown;
    ptr: PJSString;
  end;

  PJSJitInfo = JSUnknown;
  JSNativeWrapper = record
    op: JSNative;
    info: PJSJitInfo
  end;
  SelfHostedWrapper = record
    unused: Pointer;
    funname: PCChar;
  end;
  JSPropertySpecGetSetRec = record
    case Boolean of
    true: (native: JSNativeWrapper);
    false: (selfHosted: SelfHostedWrapper);
  end;
  JSPropertySpec = record
    name: PCChar;
    flags: uint8;
    getter: JSPropertySpecGetSetRec;
    setter: JSPropertySpecGetSetRec;
  end;
  TJSPropertySpecDynArray = array of JSPropertySpec;

  JSFunctionSpec = record
    name: PCChar;
    call: JSNativeWrapper;
    nargs: uint16;
    flags: uint16;
    selfHostedName: PCChar;
  end;
  TJSFunctionSpecArray = array of JSFunctionSpec;

  /// internal structure used to report JavaScript errors
  JSErrorReport = record
{$IFDEF SM52}
    /// The (default) error message.
    // If ownsMessage is true, the it is freed in destructor.
    message_: PUTF8Char;
    /// offending source line without final #13
    // If ownsLinebuf is true, the buffer is freed in destructor.
    linebuf: PCChar16;
    /// number of chars in linebuf_. Does not include trailing '\0'
    linebufLength: size_t;
    /// The 0-based offset of error token in linebuf_.
    tokenOffset: size_t;
    /// source file name, URL, etc., or null
    filename: PCChar;
    /// source line number
    lineno: uint32;
    /// zero-based column index in line
    column: uint32;
    /// error/warning, etc.
    flags: uint32;
    /// the error number, e.g. see js.msg
    errorNumber: uint32;
    /// One of the JSExnType constants
    exnType: JSExnType;
    /// See the comment in ReadOnlyCompileOptions.
    isMuted: Boolean;
    ownsLinebuf: Boolean;
    ownsMessage: Boolean;
{$ELSE}
    /// offending source line without final #13
    linebuf: PCChar16;
    /// number of chars in linebuf_. Does not include trailing '\0'
    linebufLength: size_t;
    /// the 0-based offset of error token in linebuf_
    tokenOffset:size_t;
    /// source file name, URL, etc., or null
    filename: PCChar;
    /// source line number
    lineno: uint32;
    /// zero-based column index in line
    column: uint32;
    /// See the comment in ReadOnlyCompileOptions.
    isMuted: Boolean;
    /// error/warning, etc.
    flags: uint32;
    /// the error number, e.g. see js.msg
    errorNumber: uint32;
    /// the (default) error message
    ucmessage: PCChar16;
    /// arguments for the error message
    messageArgs: PPCChar16;
    /// One of the JSExnType constants
    exnType: JSExnType;
{$ENDIF}
  end;

  /// used by JSErrorCallback() callback
  JSErrorFormatString = record
{$IFDEF SM52}
    /// The error message name in ASCII.
    name: PCChar;
{$ENDIF}
    /// The error format string (UTF-8 if js_CStringsAreUTF8)
    format: PCChar;
    /// The number of arguments to expand in the formatted error message
    argCount: uint16;
    /// One of the JSExnType constants above
    exnType: JSExnType;
  end;

/// * Finalizes external strings created by JS_NewExternalString.
  JSStringFinalizer = record
    finalize: JSStringFinalizerOp;
  end;

const
  /// JSClass instance objects have private slot
  JSCLASS_HAS_PRIVATE             =   1 shl 0;
  /// JSClass instance class's initialization code will call
  // SetNewObjectMetadata itself
  JSCLASS_DELAY_METADATA_CALLBACK =   1 shl 1;
  /// JSClass instance private is (nsISupports*)
  JSCLASS_PRIVATE_IS_NSISUPPORTS =   1 shl 3;
  /// JSClass instance objects are DOM
  JSCLASS_IS_DOMJSCLASS          =   1 shl 4;
  /// JSClass instance objects of this class act like the value undefined,
  // in some contexts
  JSCLASS_EMULATES_UNDEFINED     =   1 shl 6;
  /// Reserved for embeddings.
  JSCLASS_USERBIT1               =   1 shl 7;
  /// JSClass instance room for 8 flags below
  JSCLASS_RESERVED_SLOTS_SHIFT   = 8;
  /// JSClass instance and 16 above this field
  JSCLASS_RESERVED_SLOTS_WIDTH   = 8;

  JSCLASS_RESERVED_SLOTS_MASK    = (uint32(1) shl JSCLASS_RESERVED_SLOTS_WIDTH)-1;
  JSCLASS_HIGH_FLAGS_SHIFT       = (JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH);

  JSCLASS_IS_ANONYMOUS           = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+0));
  JSCLASS_IS_GLOBAL              = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+1));
  JSCLASS_INTERNAL_FLAG2         = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+2));
  JSCLASS_INTERNAL_FLAG3         = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+3));

  JSCLASS_IS_PROXY               = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+4));

  JSCLASS_SKIP_NURSERY_FINALIZE  = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+5));
  // Reserved for embeddings.
  JSCLASS_USERBIT2               = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+6));
  JSCLASS_USERBIT3               = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+7));

  JSCLASS_BACKGROUND_FINALIZE    = (1 shl (JSCLASS_HIGH_FLAGS_SHIFT+8));
// Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
// below.

  JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;

//  JSProto_LIMIT = 46;
{$IFDEF SM52}
  JSCLASS_GLOBAL_SLOT_COUNT         = (JSCLASS_GLOBAL_APPLICATION_SLOTS + ord(JSProto_LIMIT) * 2 + 39);
{$ELSE}
  JSCLASS_GLOBAL_SLOT_COUNT         = (JSCLASS_GLOBAL_APPLICATION_SLOTS + ord(JSProto_LIMIT) * 3 + 36);
{$ENDIF}

  JSCLASS_GLOBAL_FLAGS              = JSCLASS_IS_GLOBAL or
                                      (((JSCLASS_GLOBAL_SLOT_COUNT) and JSCLASS_RESERVED_SLOTS_MASK) shl JSCLASS_RESERVED_SLOTS_SHIFT);


/// Property attributes, set in JSPropertySpec and passed to API functions.
// NB: The data structure in which some of these values are stored only uses
//     a uint8_t to store the relevant information. Proceed with caution if
//     trying to reorder or change the the first byte worth of flags.

  JSPROP_ENUMERATE        = $01; // property is visible to for/in loop
  JSPROP_READONLY         = $02; // not settable: assignment is no-op.
                                  //  This flag is only valid when neither
                                  //  JSPROP_GETTER nor JSPROP_SETTER is
                                  //  set.
  JSPROP_PERMANENT        = $04; // property cannot be deleted
  JSPROP_PROPOP_ACCESSORS = $08; // Passed to JS_Define(UC)Property* and
                                  //  JS_DefineElement if getters/setters
                                  //  are JSPropertyOp/JSStrictPropertyOp
  JSPROP_GETTER           = $10; // property holds getter function
  JSPROP_SETTER           = $20; // property holds setter function
  JSPROP_SHARED           = $40; // don't allocate a value slot for this
                                  // property; don't copy the property on
                                  // set of the same-named property in an
                                  // object that delegates to a prototype
                                  // containing this property
  JSPROP_INTERNAL_USE_BIT= $80; // name is actually (int) index
  JSPROP_DEFINE_LATE     = $100; // Don't define property when initially creating
                                  // the constructor. Some objects like Function/Object
                                  // have self-hosted functions that can only be defined
                                  // after the initialization is already finished.
  JSFUN_STUB_GSOPS       = $200; // use JS_PropertyStub getter/setter
                                  // instead of defaulting to class gsops
                                  // for property holding function

  JSFUN_CONSTRUCTOR      = $400; // native that can be called as a ctor

{$IFDEF SM52}
// unused
{$ELSE}
// Specify a generic native prototype methods, i.e., methods of a class
// prototype that are exposed as static methods taking an extra leading
// argument: the generic |this| parameter.
// If you set this flag in a JSFunctionSpec struct's flags initializer, then
// that struct must live at least as long as the native static method object
// created due to this flag by JS_DefineFunctions or JS_InitClass.  Typically
// JSFunctionSpec structs are allocated in static arrays.
//  JSFUN_GENERIC_NATIVE   = $800;

  JSFUN_GENERIC_NATIVE = $800;
{$ENDIF}
{$IFDEF SM52}
  JSFUN_HAS_REST = $1000; // function has ...rest parameter.
{$ENDIF}

  JSPROP_REDEFINE_NONCONFIGURABLE = $1000; // If set, will allow redefining a
                                           // non-configurable property, but
                                           // only on a non-DOM global.  This
                                           // is a temporary hack that will
                                           // need to go away in bug
                                           // 1105518

// Resolve hooks and enumerate hooks must pass this flag when calling
// JS_Define* APIs to reify lazily-defined properties.
// JSPROP_RESOLVING is used only with property-defining APIs. It tells the
// engine to skip the resolve hook when performing the lookup at the beginning
// of property definition. This keeps the resolve hook from accidentally
// triggering itself: unchecked recursion.
// For enumerate hooks, triggering the resolve hook would be merely silly, not
// fatal, except in some cases involving non-configurable properties.
  JSPROP_RESOLVING        = $2000;

  JSPROP_IGNORE_ENUMERATE = $4000;  // ignore the value in JSPROP_ENUMERATE.
                                    // This flag only valid when defining over
                                    // an existing property.
  JSPROP_IGNORE_READONLY  = $8000;  // ignore the value in JSPROP_READONLY.
                                    // This flag only valid when defining over
                                    // an existing property.
  JSPROP_IGNORE_PERMANENT = $10000; // ignore the value in JSPROP_PERMANENT.
                                    // This flag only valid when defining over
                                    // an existing property.
  JSPROP_IGNORE_VALUE     = $20000; // ignore the Value in the descriptor. Nothing was
                                    // specified when passed to Object.defineProperty
                                    // from script.

type
  /// available options for JS Objects Properties
  TJSPropertyAttr = (
    jspEnumerate, jspReadOnly, jspPermanent, jspPropAccessors, jspGetter,
    jspSetter, jspShared, jspInternal, jspDefineLate,
    jspFunStubGSOps, jspFunConstructor, jspFunGenericNative,
    jspRedefineNonConfigurable, jspResolving,
    jspIgnoreEnumerate, jspIgnoreReadOnly, jspIgnorePermanent, jspIgnoreValue);
  /// set of available options for JS Objects Properties
  TJSPropertyAttrs = set of TJSPropertyAttr;

  ESMException = class(ESynException)
  private
    FJSErrorNum: integer;
    FFileName: RawUTF8;
    FLineNum: integer;
    FJSStackTrace: SynUnicode;
  public
    /// constructor which will create JavaScript exception with JS stack trace
    constructor CreateWithTrace(const AFileName: RawUTF8; AJSErrorNum, ALineNum: integer;
       AMessage: string; const AStackTrace: SynUnicode);
    /// Format a JS exception as text
    // If SM_DEBUG is defined will write full JS stack, including SyNode core_modules calls
    //  if not - core_modules is cutched from stack trace for simplicity
    procedure WriteFormatted(WR: TTextWriter);

    {$ifndef NOEXCEPTIONINTERCEPT}
    /// Custmize SM exception log output
    function CustomLog(WR: TTextWriter; const Context: TSynLogExceptionContext): boolean; override;
    {$endif}
  published
    property ErrorNum: integer read FJSErrorNum;
    property Stack: SynUnicode read FJSStackTrace;
    property FileName: RawUTF8 read FFileName;
    property Line: integer read FLineNum;
  end;

/// pass exception of this type to JSError for raising JS RangeError exception
  ESMRangeException = class(ESMException);
/// pass exception of this type to JSError for raising JS TypeError exception
  ESMTypeException = class(ESMException);

/// to be used to catch Delphi exceptions inside JSNative function implementation
// - usage example:
// ! try
// !   doSomething()
// !   Result := True;
// ! except
// ! on E: Exception do begin
// !   vp.rval := JSVAL_VOID;
// !   JSError(cx, E);
// !   Result := False;
// ! end;

procedure JSError(cx: PJSContext; aException: Exception);
procedure JSErrorUC(cx: PJSContext; aMessage: WideString);
procedure JSRangeErrorUC(cx: PJSContext; aMessage: WideString);
procedure JSTypeErrorUC(cx: PJSContext; aMessage: WideString);

// must be called ONCE per process before any interaction with JavaScript
function InitJS: Boolean;
// must be called ONCE per process after all engine managers are destroyed
procedure ShutDownJS;

var
    nullObj: PJSObject = nil;
const
{$ifdef CPU64}
  JSVAL_TAG_SHIFT              = 47;
  JSVAL_PAYLOAD_MASK           = $00007FFFFFFFFFFF;
  JSVAL_TAG_MASK               = $FFFF800000000000;

  JSVAL_TAG_MAX_DOUBLE                = UInt32($1FFF0);
  JSVAL_TAG_INT32                     = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_INT32));
  JSVAL_TAG_UNDEFINED                 = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_UNDEFINED));
  JSVAL_TAG_STRING                    = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_STRING));
  JSVAL_TAG_SYMBOL                    = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_SYMBOL));
  JSVAL_TAG_BOOLEAN                   = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_BOOLEAN));
  JSVAL_TAG_MAGIC                     = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_MAGIC));
  JSVAL_TAG_NULL                      = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_NULL));
  JSVAL_TAG_OBJECT                    = UInt32(JSVAL_TAG_MAX_DOUBLE or UInt8(JSVAL_TYPE_OBJECT));

  JSVAL_SHIFTED_TAG_MAX_DOUBLE        = (uint64(JSVAL_TAG_MAX_DOUBLE) shl JSVAL_TAG_SHIFT) or $FFFFFFFF;
  JSVAL_SHIFTED_TAG_INT32             = uint64(JSVAL_TAG_INT32) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_UNDEFINED         = uint64(JSVAL_TAG_UNDEFINED) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_STRING            = uint64(JSVAL_TAG_STRING) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_SYMBOL            = uint64(JSVAL_TAG_SYMBOL) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_BOOLEAN           = uint64(JSVAL_TAG_BOOLEAN) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_MAGIC             = uint64(JSVAL_TAG_MAGIC) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_NULL              = uint64(JSVAL_TAG_NULL) shl JSVAL_TAG_SHIFT;
  JSVAL_SHIFTED_TAG_OBJECT            = uint64(JSVAL_TAG_OBJECT) shl JSVAL_TAG_SHIFT;
{$endif}

{$ifdef CPU64}
  JSVAL_NULL_impl =  QWord(JSVAL_SHIFTED_TAG_NULL);
  JSVAL_VOID_impl =  QWord(JSVAL_SHIFTED_TAG_UNDEFINED);
{$ELSE}
  JSVAL_NULL_impl =  QWord(QWord(JSVAL_TAG_NULL     ) shl 32) or 0;
  JSVAL_VOID_impl =  QWord(QWord(JSVAL_TAG_UNDEFINED) shl 32) or 0;
{$endif}

  JSVAL_NAN_impl  =  $7FF8000000000000;

const
  JSVAL_NULL: jsval = (_l:(asBits: JSVAL_NULL_impl));
  JSVAL_VOID: jsval = (_l:(asBits: JSVAL_VOID_impl));
  JSVAL_NAN:  jsval = (_l:(asBits: JSVAL_NAN_impl));

const
  JSREPORT_ERROR = 0;
  JSREPORT_WARNING = 1;
  JSREPORT_EXCEPTION = 2;
  JSREPORT_STRICT = 4;
  JSREPORT_STRICT_MODE_ERROR = 8;

function SimpleVariantToJSval(cx: PJSContext; val: Variant): jsval;

const
{$IFDEF SM52}
  SpiderMonkeyLib = 'synmozjs52'{$IFDEF MSWINDOWS} + '.dll'{$ENDIF};
{$ELSE}
  SpiderMonkeyLib = 'mozjs-45'{$IFDEF MSWINDOWS} + '.dll'{$ENDIF};
{$ENDIF}

 /// Initialize SpiderMonkey, returning true only if initialization succeeded.
 // Once this method has succeeded, it is safe to call JS_NewRuntime and other
 // JSAPI methods.
 // - This method must be called before any other JSAPI method is used on any
 // thread.  Once it has been used, it is safe to call any JSAPI method, and it
 // remains safe to do so until JS_ShutDown is correctly called.
 // - It is currently not possible to initialize SpiderMonkey multiple times (that
 // is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
 // again).  This restriction may eventually be lifted.
function JS_Init: Boolean; cdecl; external SpiderMonkeyLib {$IFDEF SM52}name 'JS_Initialize'{$ENDIF};
 /// Destroy free-standing resources allocated by SpiderMonkey, not associated
 // with any runtime, context, or other structure.
 // - This method should be called after all other JSAPI data has been properly
 // cleaned up: every new runtime must have been destroyed, every new context
 // must have been destroyed, and so on.  Calling this method before all other
 // resources have been destroyed has undefined behavior.
 // - Failure to call this method, at present, has no adverse effects other than
 // leaking memory.  This may not always be the case; it's recommended that all
 // embedders call this method when all other JSAPI operations have completed.
 // - It is currently not possible to initialize SpiderMonkey multiple times (that
 // is, calling JS_Init/JSAPI methods/JS_ShutDown in that order, then doing so
 // again).  This restriction may eventually be lifted.
procedure JS_ShutDown; cdecl; external SpiderMonkeyLib;

/// Microseconds since the epoch, midnight, January 1, 1970 UTC.
function JS_Now: int64; cdecl; external SpiderMonkeyLib;

/// Returns the empty string as a JSString object.
{$IFDEF SM52}
function JS_GetEmptyString(cx: PJSContext): PJSString; cdecl; external SpiderMonkeyLib;
{$ELSE}
function JS_GetEmptyString(rt: PJSRuntime): PJSString; cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// Determines the JS data type of a JS value.
function JS_TypeOfValue(cx: PJSContext; var v: jsval): JSType; cdecl; external SpiderMonkeyLib;

{$IFNDEF SM52}
/// Initializes the JavaScript runtime.
function JS_NewRuntime(maxbytes: uint32; maxNurseryBytes: uint32; parentRuntime: PJSRuntime): PJSRuntime;
  cdecl; external SpiderMonkeyLib;

/// Frees a JavaScript runtime.
procedure JS_DestroyRuntime(runtime: PJSRuntime); cdecl; external SpiderMonkeyLib;

/// Read access a JSRuntime field for application-specific data.
// Memory management for this private data is the application's responsibility.
// The JavaScript engine itself never uses it.
function JS_GetRuntimePrivate(rt: PJSRuntime): pointer; cdecl; external SpiderMonkeyLib;

/// Wrote access a JSRuntime field for application-specific data.
// Memory management for this private data is the application's responsibility.
// The JavaScript engine itself never uses it.
procedure JS_SetRuntimePrivate(rt: PJSRuntime; data: pointer); cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// indicates to the JS engine that the calling thread is entering a region
// of code that may call into the JSAPI but does not block
procedure JS_BeginRequest(cx: PJSContext); cdecl; external SpiderMonkeyLib;

/// indicates to the JS engine that the calling thread is leaving a region
// of code that may call into the JSAPI but does not block
procedure JS_EndRequest(cx: PJSContext); cdecl; external SpiderMonkeyLib;

/// Create a new JSContext
{$IFDEF SM52}
function JS_NewContext(maxbytes: uint32; maxNurseryBytes: uint32 = 16 * (1 SHL 20); parentContext: PJSContext = nil): PJSContext;
  cdecl; external SpiderMonkeyLib;
function InitSelfHostedCode(cx: PJSContext): boolean; cdecl; external SpiderMonkeyLib;
{$ELSE}
function JS_NewContext(rt: PJSRuntime; stackChunkSize: size_t): PJSContext;
  cdecl; external SpiderMonkeyLib;
{$ENDIF}
/// Destroy a JSContext.
procedure JS_DestroyContext(cx: PJSContext); cdecl; external SpiderMonkeyLib;

/// Read access a JSContext field for application-specific data.
// Memory management for this private data is the application's responsibility.
// The JavaScript engine itself never uses it.
function JS_GetContextPrivate(cx: PJSContext): Pointer; cdecl; external SpiderMonkeyLib;
/// Write access a JSContext field for application-specific data.
// Memory management for this private data is the application's responsibility.
// The JavaScript engine itself never uses it.
procedure JS_SetContextPrivate(cx: PJSContext; data: Pointer); cdecl; external SpiderMonkeyLib;

{$IFNDEF SM52}
/// Retrieves a pointer to the JSRuntime with which a specified JSContext, cx, is associated
function JS_GetRuntime(cx: PJSContext): PJSRuntime; cdecl; external SpiderMonkeyLib;
{$ENDIF}
/// This function makes a cross-compartment wrapper for the given JS object.
// Details see here http://stackoverflow.com/questions/18730477/what-does-js-wrapobject-do
function JS_WrapObject(cx: PJSContext; var obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;

/// Enter a different compartment on the given context, so that objects in that
// compartment can be accessed.
// - NB: This API is infallible; a NULL return value does not indicate error
function JS_EnterCompartment(cx: PJSContext; target: PJSObject): PJSCompartment;
  cdecl; external SpiderMonkeyLib;

/// Leave a the compartment, returning to the compartment active before the
// corresponding JS_EnterCompartment.
procedure JS_LeaveCompartment(cx: PJSContext; oldCompartment: PJSCompartment);
  cdecl; external SpiderMonkeyLib;

/// Initialize standard JS class constructors, prototypes, and any top-level
// functions and constants associated with the standard classes (e.g. isNaN
// for Number).
// - NB: This sets cx's global object to obj if it was null.
function JS_InitStandardClasses(cx: PJSContext; var obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;

///Return the global object for the active function on the context.
function JS_CurrentGlobalOrNull(cx: PJSContext):PJSObject; cdecl; external SpiderMonkeyLib name 'CurrentGlobalOrNull';

/// Add 'Reflect.parse', a SpiderMonkey extension, to the Reflect object on the
// given global.
function JS_InitReflectParse(cx: PJSContext; var obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;

/// Initialize the 'ctypes' object on a global variable 'obj'. The 'ctypes'
// object will be sealed.
function JS_InitCTypesClass(cx: PJSContext; var obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;

/// Initialize the 'Debugger' object on a global variable 'obj'. The 'ctypes'
// object will be sealed.
function JS_DefineDebuggerObject(cx: PJSContext; var obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;

/// Performs garbage collection in the JS memory pool.
{$IFDEF SM52}
procedure JS_GC(cx: PJSContext); cdecl; external SpiderMonkeyLib;
{$ELSE}
procedure JS_GC(rt: PJSRuntime); cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// Offer the JavaScript engine an opportunity to perform garbage collection if needed.
procedure JS_MaybeGC(cx: PJSContext); cdecl; external SpiderMonkeyLib;

///Set performance parameters related to garbage collection.
{$IFDEF SM52}
procedure JS_SetGCParameter(cx: PJSContext; key: JSGCParamKey; value: uint32);
  cdecl; external SpiderMonkeyLib;
{$ELSE}
procedure JS_SetGCParameter(rt: PJSRuntime; key: JSGCParamKey; value: uint32);
  cdecl; external SpiderMonkeyLib;
{$ENDIF}

///Get performance parameters related to garbage collection.
{$IFDEF SM52}
function JS_GetGCParameter(cx: PJSContext; key: JSGCParamKey): uint32;
  cdecl; external SpiderMonkeyLib;
{$ELSE}
function JS_GetGCParameter(rt: PJSRuntime; key: JSGCParamKey): uint32;
  cdecl; external SpiderMonkeyLib;
{$ENDIF}

///Adjust performance parameters related to garbage collection based on available memory(in megabytes).
{$IFDEF SM52}
procedure JS_SetGCParametersBasedOnAvailableMemory(cx: PJSContext; availMem: uint32);
  cdecl; external SpiderMonkeyLib;
{$ELSE}
procedure JS_SetGCParametersBasedOnAvailableMemory(rt: PJSRuntime; availMem: uint32);
  cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// Creates a new JSString whose characters are stored in external memory, i.e.,
//  memory allocated by the application, not the JavaScript engine
// - Since the program allocated the memory, it will need to free it;
//  this happens in an external string finalizer indicated by the type parameter.
// - chars is Pointer to the first element of an array of jschars.
//  This array is used as the character buffer of the JSString to be created.
//  The array must be populated with the desired character data before JS_NewExternalString
//  is called, and the array must remain in memory, with its contents unchanged,
//  for as long as the JavaScript engine needs to hold on to it.
// (Ultimately, the string will be garbage collected, and the JavaScript engine will
// call the string finalizer callback, allowing the application to free the array)
//  - The text buffer array does not need to be zero-terminated.
function JS_NewExternalString(cx: PJSContext; chars: PCChar16; length: size_t;
  fin: PJSStringFinalizer): PJSString; cdecl; external SpiderMonkeyLib;

/// Set the size of the native stack that should not be exceed. To disable
// stack size checking pass 0.
// - SpiderMonkey allows for a distinction between system code (such as GCs, which
// may incidentally be triggered by script but are not strictly performed on
// behalf of such script), trusted script (as determined by JS_SetTrustedPrincipals),
// and untrusted script. Each kind of code may have a different stack quota,
// allowing embedders to keep higher-priority machinery running in the face of
// scripted stack exhaustion by something else.
// - The stack quotas for each kind of code should be monotonically descending,
// and may be specified with this function. If 0 is passed for a given kind
// of code, it defaults to the value of the next-highest-priority kind.
// - This function may only be called immediately after the runtime is initialized
// and before any code is executed and/or interrupts requested.
{$IFDEF SM52}
procedure JS_SetNativeStackQuota(cx: PJSContext; systemCodeStackSize: size_t;
  trustedScriptStackSize: size_t = 0; untrustedScriptStackSize: size_t = 0); cdecl; external SpiderMonkeyLib;
{$ELSE}
procedure JS_SetNativeStackQuota(runtime: PJSRuntime; systemCodeStackSize: size_t;
  trustedScriptStackSize: size_t = 0; untrustedScriptStackSize: size_t = 0); cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// Convert a JS::Value to type jsid.
function JS_ValueToId(cx: PJSContext; var v: jsval; out id: jsid): Boolean; cdecl; external SpiderMonkeyLib;
/// Convert a jsid to type JS::Value.
function JS_IdToValue(cx: PJSContext; id: jsid; out v: jsval): Boolean; cdecl; external SpiderMonkeyLib;

function JS_ValueToSource(cx: PJSContext; var v: jsval): PJSString; cdecl; external SpiderMonkeyLib;


/// Make a JSClass accessible to JavaScript code by creating its prototype,
// constructor, properties, and functions.
function JS_InitClass(cx: PJSContext; var obj: PJSObject; var parent_proto: PJSObject;
    clasp: PJSClass; _constructor: JSNative; nargs: uintN;
    ps: PJSPropertySpec; fs: PJSFunctionSpec;
    static_ps: PJSPropertySpec; static_fs: PJSFunctionSpec): PJSObject;
  cdecl; external SpiderMonkeyLib ;

/// Retrieves the class associated with an object.
function JS_GetClass(obj: PJSObject): PJSClass; cdecl; external SpiderMonkeyLib;

/// JSAPI method equivalent to the instanceof operator in JavaScript.
function JS_HasInstance(cx: PJSContext; var obj: PJSObject; var val: jsval; out res: Boolean): Boolean; cdecl; external SpiderMonkeyLib;

/// Access the private data field of an object.
function JS_GetPrivate(obj: PJSObject): Pointer; cdecl; external SpiderMonkeyLib;

/// Sets the private data field of an object.
procedure JS_SetPrivate(obj: PJSObject; data: Pointer); cdecl; external SpiderMonkeyLib;

/// Retrieves the constructor for an object.
function JS_GetConstructor(cx: PJSContext; var proto: PJSObject): PJSObject; cdecl; external SpiderMonkeyLib;

/// Retrieve the private data associated with an object, if that object is an
// instance of a specified class.
function JS_GetInstancePrivate(cx: PJSContext; var obj: PJSObject; clasp: PJSClass; args: JSUnknown): Pointer;
  cdecl; external SpiderMonkeyLib;

/// Create a new JavaScript object for use as a global object.
function JS_NewGlobalObject(cx: PJSContext; clasp: PJSClass; principals: PJSPrincipals;
  hookOption: OnNewGlobalHookOption; options: PJS_CompartmentOptions): PJSObject; cdecl; external SpiderMonkeyLib;

/// Spidermonkey does not have a good way of keeping track of what compartments should be marked on
/// their own. We can mark the roots unconditionally, but marking GC things only relevant in live
/// compartments is hard. To mitigate this, we create a static trace hook, installed on each global
/// object, from which we can be sure the compartment is relevant, and mark it.
///
/// It is still possible to specify custom trace hooks for global object classes. They can be
/// provided via the CompartmentOptions passed to JS_NewGlobalObject.
procedure JS_GlobalObjectTraceHook(trc: Pointer{ JSTracer }; global: PJSObject); cdecl; external SpiderMonkeyLib;

/// Create a new object based on a specified class
function JS_NewObject(cx: PJSContext; clasp: PJSClass): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new object based on a specified class
// - Unlike JS_NewObject, JS_NewObjectWithGivenProto does not compute a default
// proto. If proto is nullptr, the JS object will have `null` as [[Prototype]].
function JS_NewObjectWithGivenProto(cx: PJSContext; clasp: PJSClass; var proto: PJSObject): PJSObject; cdecl; external SpiderMonkeyLib;

/// Get the prototype of obj, storing it in result.
// - Implements: ES6 [[GetPrototypeOf]] internal method.
function JS_GetPrototype(cx: PJSContext; var obj: PJSObject; out result: PJSObject):Boolean; cdecl; external SpiderMonkeyLib;

/// Change the prototype of obj.
// - Implements: ES6 [[SetPrototypeOf]] internal method.
// - In cases where ES6 [[SetPrototypeOf]] returns false without an exception,
// JS_SetPrototype throws a TypeError and returns false.
// - Performance warning: JS_SetPrototype is very bad for performance. It may
// cause compiled jit-code to be invalidated. It also causes not only obj but
// all other objects in the same "group" as obj to be permanently deoptimized.
// It's better to create the object with the right prototype from the start.
function JS_SetPrototype(cx: PJSContext; var obj: PJSObject; var proto: PJSObject):Boolean; cdecl; external SpiderMonkeyLib;

/// Create a new property on an object.
// Name indentifies by ID
function JS_DefinePropertyById(cx: PJSContext; var obj: PJSObject; var id: jsid;
  var value: jsval; attrs: uint32; getter: JSNative; setter: JSNative): boolean;
    cdecl; external SpiderMonkeyLib;
/// Create a new property on an object.
// Name indentifies by ansi string
function JS_DefineProperty(cx: PJSContext; var obj: PJSObject; const name: PCChar;
    var value: jsval; attrs: uint32; getter: JSNative; setter: JSNative): boolean;
    cdecl; external SpiderMonkeyLib;
/// Create a new property on an object.
// Name indentifies by unicode string
function JS_DefineUCProperty(cx: PJSContext; var obj: PJSObject; const name: PCChar16;
    namelen: size_t; var value: jsval; attrs: uint32; getter: JSNative; setter: JSNative): Boolean;
    cdecl; external SpiderMonkeyLib;

/// Determine whether a JavaScript object has a specified property.
// Name indentifies by ansi string
function JS_HasProperty(cx: PJSContext; var obj: PJSObject;
    const name: PCChar; var found: Boolean): Boolean;
  cdecl; external SpiderMonkeyLib;
/// Determine whether a JavaScript object has a specified property.
// Name indentifies by unicode string
function JS_HasUCProperty(cx: PJSContext; var obj: PJSObject;
    const name: PCChar16; namelen: size_t; var found: Boolean): Boolean;
  cdecl; external SpiderMonkeyLib;

/// Find a specified property and retrieve its value.
// Name indentifies by ID
function JS_GetPropertyById(cx: PJSContext; var obj: PJSObject; var id: jsid;
    out vp: jsval): boolean; cdecl; external SpiderMonkeyLib;
/// Find a specified property and retrieve its value.
// Name indentifies by ansi string
function JS_GetProperty(cx: PJSContext; var obj: PJSObject; const name: PCChar;
    out vp: jsval): boolean; cdecl; external SpiderMonkeyLib;
/// Find a specified property and retrieve its value.
// Name indentifies by unicode string
function JS_GetUCProperty(cx: PJSContext; var obj: PJSObject; const name: PCChar16; namelen: size_t;
    out vp: jsval): boolean; cdecl; external SpiderMonkeyLib;
/// Find a specified numeric property of an object and return its current value.
function JS_GetElement(cx: PJSContext; var obj: PJSObject; index: uint32;
  out vp: jsval): Boolean; cdecl; external SpiderMonkeyLib;

/// Assign a value to a property of an object.
// Name indentifies by ansi string
function JS_SetProperty(cx: PJSContext; var obj: PJSObject; const name: PCChar;
    var vp: jsval): Boolean; cdecl; external SpiderMonkeyLib;
/// Assign a value to a property of an object.
// Name indentifies by unicode string
function JS_SetUCProperty(cx: PJSContext; var obj: PJSObject; const name: PCChar16; namelen: size_t;
    var vp: jsval): boolean; cdecl; external SpiderMonkeyLib;
/// Assign a value to a numeric property of an object.
function JS_SetElement(cx: PJSContext; var obj: PJSObject; index: uint32;
    var vp: jsval): Boolean; cdecl; external SpiderMonkeyLib;

/// Removes a specified property from an object.
// Name indentifies by ID
function JS_DeletePropertyById(cx: PJSContext; var obj: PJSObject; var id: jsid; out res: JS_ObjectOpResult): Boolean;
  cdecl; external SpiderMonkeyLib;
/// Removes a specified element or numeric property from an object.
function JS_DeleteElement(cx: PJSContext; var obj: PJSObject; index: uint32; out res: JS_ObjectOpResult): Boolean;
  cdecl; external SpiderMonkeyLib;

/// Get an array of the non-symbol enumerable properties of obj.
// This function is roughly equivalent to:
//
//     var result = [];
//     for (key in obj)
//         result.push(key);
//     return result;
//
// This is the closest thing we currently have to the ES6 [[Enumerate]]
// internal method.
//
// The JSIdArray returned by JS_Enumerate must be rooted to protect its
// contents from garbage collection. Use JS::AutoIdArray.
function JS_Enumerate(cx: PJSContext; var obj: PJSObject; out props: JSIdArray): Boolean;
  cdecl; external SpiderMonkeyLib;

type
  JSHandleValueArray = record
    length: size_t;
    elements_: PjsvalVector;
  end;
/// Calls a specified JS function.
// Function identifies by jsvalue
// - equivalent of `rval = Reflect.apply(fun, obj, args)`.
function JS_CallFunctionValue(cx: PJSContext; var obj: PJSObject; var val: jsval;
  var args: JSHandleValueArray; out rval: jsval): Boolean; cdecl; external SpiderMonkeyLib;
/// Calls a specified JS function.
// Function identifies by PJSFunction
function JS_CallFunction(cx: PJSContext; var obj: PJSObject; var fun: PJSFunction;
  var args: JSHandleValueArray; out rval: jsval): Boolean; cdecl; external SpiderMonkeyLib;
/// Calls a specified JS function.
// Function identifies by ansi string
// - Perform the method call `rval = obj[name](args)`.
function JS_CallFunctionName(cx: PJSContext; var obj: PJSObject; const name: PCChar;
  var args: JSHandleValueArray; out rval: jsval): Boolean; cdecl; external SpiderMonkeyLib;

/// Invoke a constructor, like the JS expression `new ctor(...args)`. Returns
// the new object, or null on error.
function JS_New(cx: PJSContext; var ctor: PJSObject; var args: JSHandleValueArray): PJSObject; cdecl; external SpiderMonkeyLib;

/// Define multiple properties for a single object.
function JS_DefineProperties(cx: PJSContext; var obj: PJSObject; ps: PJSPropertySpec): boolean; cdecl; external SpiderMonkeyLib ;

/// Determine whether a property is already physically present on a JSObject.
// Name indentifies by unicode string
function JS_AlreadyHasOwnUCProperty(cx: PJSContext; var obj: PJSObject;
    const name: PCChar16; namelen: size_t; var foundp: Boolean): Boolean;
  cdecl; external SpiderMonkeyLib;

/// Create a new Array object.
// Only length passed
function JS_NewArrayObject(cx: PJSContext; length: size_t): PJSObject; cdecl; external SpiderMonkeyLib;
/// Create a new Array object.
// Content passed
function JS_NewArrayObject2(cx: PJSContext; const contents: JSHandleValueArray): PJSObject; cdecl; external SpiderMonkeyLib;

/// Returns true and sets |*isArray| indicating whether |obj| is an Array object
// or a wrapper around one, otherwise returns false on failure.
// - This method returns true with |*isArray == false| when passed a proxy whose
// target is an Array, or when passed a revoked proxy.
function JS_IsArrayObject(cx: PJSContext; var obj: PJSObject; out isArray: Boolean): boolean; cdecl; external SpiderMonkeyLib;
/// JS_GetArrayLength gets the .length property of obj as though by calling JS_GetProperty
// and converts it to a 32-bit unsigned integer. If obj is an array (see JS_IsArrayObject),
// this is guaranteed to succeed, because the .length property of an array is always a number
// and can't be deleted or redefined.
// - On success, JS_GetArrayLength stores the length in *lengthp and returns true.
// On failure, it reports an error and returns false, and the value left in *lengthp
// is undefined.
function JS_GetArrayLength(cx: PJSContext; var obj: PJSObject;
  out length: uint32): Boolean; cdecl; external SpiderMonkeyLib;

/// Read access an object's reserved slots.
{$IFDEF SM52}
function JS_GetReservedSlot(obj: PJSObject; index: uint32): Int64; cdecl; external SpiderMonkeyLib name 'JS_GetReservedSlot1';
{$ELSE}
function JS_GetReservedSlot(obj: PJSObject; index: uint32): Int64; cdecl; external SpiderMonkeyLib;
{$ENDIF}
/// Write access an object's reserved slots
{$IFDEF SM52}
procedure JS_SetReservedSlot(obj: PJSObject; index: uint32; var v: jsval); cdecl; external SpiderMonkeyLib;
{$ELSE}
procedure JS_SetReservedSlot(obj: PJSObject; index: uint32; v: Int64); cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// Create a new JavaScript function that is implemented as a JSNative.
function JS_NewFunction(cx: PJSContext; call: JSNative; nargs: uintN; flags: uintN; name: PCChar): PJSObject; cdecl; external SpiderMonkeyLib;

/// Return the function's identifier as a JSString, or null if fun is unnamed.
// The returned string lives as long as fun, so you don't need to root a saved
// reference to it if fun is well-connected or rooted, and provided you bound
// the use of the saved reference by fun's lifetime.
function JS_GetFunctionId(fun: PJSFunction): PJSString; cdecl; external SpiderMonkeyLib;

/// Infallible predicate to test whether obj is a function object (faster than
// comparing obj's class name to "Function", but equivalent unless someone has
// overwritten the "Function" identifier with a different constructor and then
// created instances using that constructor that might be passed in as obj).
function JS_ObjectIsFunction(cx: PJSContext; obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;

/// Create zero or more functions and makes them properties (methods)
// of a specified object, obj, as if by calling JS_DefineFunction repeatedly
function JS_DefineFunctions(cx: PJSContext; var obj: PJSObject; fs: PJSFunctionSpec;
    behavior: JSPropertyDefinitionBehavior): Boolean;
  cdecl; external SpiderMonkeyLib;
/// Create a native function and assign it as a property to a specified JS object
function JS_DefineFunction(cx: PJSContext; var obj: PJSObject; name: PCChar;
    call: JSNative; nargs: uintN; attrs: uintN): PJSFunction;
  cdecl; external SpiderMonkeyLib;
/// Unicode version to create a native function
function JS_DefineUCFunction(cx: PJSContext; var obj: PJSObject; name: PCChar16;
    namelen: size_t; call: JSNative; nargs: uintN; attrs: uintN): PJSFunction;
  cdecl; external SpiderMonkeyLib;

/// Compile a script, source, for execution.
// Ansi version
function JS_CompileScript(cx: PJSContext; bytes: PCChar;
    length: size_t; options: PJSCompileOptions; out script: PJSScript): boolean;
  cdecl; external SpiderMonkeyLib;
/// Compile a script, source, for execution.
// Unicode version
function JS_CompileUCScript(cx: PJSContext;
    chars: PCChar16; length: size_t; options: PJSCompileOptions; out script: PJSScript): boolean;
  cdecl; external SpiderMonkeyLib;

/// Generate the complete source code of a function declaration from a compiled function
function JS_DecompileFunction(cx: PJSContext; var fun: PJSFunction; indent: uintN): PJSString;
  cdecl; external SpiderMonkeyLib;

/// Evaluate a script in the scope of the current global of cx.
function JS_ExecuteScript(cx: PJSContext; var script: PJSScript;
  out rval: jsval): Boolean; cdecl; external SpiderMonkeyLib;

{$IFDEF SM52}
/// These functions allow setting an interrupt callback that will be called
// from the JS thread some time after any thread triggered the callback using
// JS_RequestInterruptCallback(cx).
// - To schedule the GC and for other activities the engine internally triggers
// interrupt callbacks. The embedding should thus not rely on callbacks being
//triggered through the external API only.
// - Important note: Additional callbacks can occur inside the callback handler
// if it re-enters the JS engine. The embedding must ensure that the callback
// is disconnected before attempting such re-entry.
function JS_CheckForInterrupt(cx: PJSContext): Boolean; cdecl; external SpiderMonkeyLib;
function JS_AddInterruptCallback(cx: PJSContext; callback: JSInterruptCallback):
  Boolean; cdecl; external SpiderMonkeyLib;
function JS_DisableInterruptCallback(cx: PJSContext):Boolean; cdecl; external SpiderMonkeyLib;
procedure JS_ResetInterruptCallback(cx: PJSContext; enable: Boolean); cdecl; external SpiderMonkeyLib;
/// Request a callback set using JS_SetInterruptCallback
procedure JS_RequestInterruptCallback(cx: PJSContext); cdecl; external SpiderMonkeyLib;
{$ELSE}
/// Set a callback function that is automatically called periodically while JavaScript code runs.
function JS_SetInterruptCallback(rt: PJSRuntime; callback: JSInterruptCallback):
  JSInterruptCallback; cdecl; external SpiderMonkeyLib;
/// Return the currently installed interrupt callback, or NULL if none is currently installed
function JS_GetInterruptCallback(rt: PJSRuntime): JSInterruptCallback;
  cdecl; external SpiderMonkeyLib;
/// Request a callback set using JS_SetInterruptCallback
procedure JS_RequestInterruptCallback(rt: PJSRuntime); cdecl; external SpiderMonkeyLib;
{$ENDIF}
/// Indicates whether or not a script or function is currently executing in a given context.
function JS_IsRunning(cx: PJSContext): Boolean; cdecl; external SpiderMonkeyLib;

/// Allocate space for a JavaScript string and its underlying storage,
// and copy n characters from a character array, s, into the new JSString
// Ansi version
function JS_NewStringCopyN(cx: PJSContext; s: PCChar; n: size_t): PJSString; cdecl; external SpiderMonkeyLib;
/// Allocate space for a JavaScript string and its underlying storage,
// and copy n characters from a character array, s, into the new JSString
// Unicode version
function JS_NewUCStringCopyN(cx: PJSContext; s: PCChar16; n: size_t): PJSString; cdecl; external SpiderMonkeyLib;

/// Return the length of a JavaScript string.
function JS_GetStringLength(str: PJSString): size_t; cdecl; external SpiderMonkeyLib;

/// Return true if the string's characters are stored as Latin1.
function JS_StringHasLatin1Chars(str: PJSString): boolean; cdecl; external SpiderMonkeyLib;
/// Return a pointer to the string, and store the length to *length
// Use it when characters are stored as Latin1.
function JS_GetLatin1StringCharsAndLength(cx: PJSContext; nogc: PJSAutoCheckCannotGC; str: PJSString; plength: psize_t):PCChar; cdecl; external SpiderMonkeyLib;
/// Return a pointer to the string, and store the length to *length
// Use it when characters are stored as Unicode
function JS_GetTwoByteStringCharsAndLength(cx: PJSContext; nogc: PJSAutoCheckCannotGC; str: PJSString; plength: psize_t):PCChar16; cdecl; external SpiderMonkeyLib;

/// converts a value to JSON, optionally replacing values if a replacer
// function is specified, or optionally including only the specified properties
// if a replacer array is specified
function JS_Stringify(cx: PJSContext; var vp: jsval; var replacer: PJSObject;
    var space: jsval; callback: JSONWriteCallback; data: pointer): Boolean;
  cdecl; external SpiderMonkeyLib;

/// parse a string using the JSON syntax described in ECMAScript 5 and
// return the corresponding value into vp
function JS_ParseJSON(cx: PJSContext; const chars: PCChar16;
    len: uint32; out vp: jsval): Boolean;  cdecl; external SpiderMonkeyLib;

/// Create a new JavaScript Error object and set it to be the pending exception on cx.
// The callback must then return JS_FALSE to cause the exception to be propagated
// to the calling script.
procedure JS_ReportError(cx: PJSContext; const format: PCChar);
  cdecl; varargs; external SpiderMonkeyLib{$IFDEF SM52} name 'JS_ReportErrorASCII'{$ENDIF};
/// Report an error with an application-defined error code.
// - varargs is Additional arguments for the error message.
//- These arguments must be of type jschar*
// - The number of additional arguments required depends on the error
// message, which is determined by the errorCallback
procedure JS_ReportErrorNumberUC(cx: PJSContext; errorCallback: JSErrorCallback;
  userRef: pointer; const erroNubmer: uintN); cdecl; varargs; external SpiderMonkeyLib;
/// Reports a memory allocation error
// - Call JS_ReportOutOfMemory to report that an operation failed because the
// system is out of memory
// - When the JavaScript engine tries to allocate memory and allocation fails,
// it reports an error as though by calling this function
procedure JS_ReportOutOfMemory(cx: PJSContext); cdecl; external SpiderMonkeyLib;

{$IFDEF SM52}
/// Get the warning reporting mechanism for an application. It is not working for errors.
function JS_GetWarningReporter(cx: PJSContext): JSWarningReporter;
  cdecl; external SpiderMonkeyLib name 'GetWarningReporter';
/// Specify the warning reporting mechanism for an application.  It is not working for errors.
function JS_SetWarningReporter(cx: PJSContext; reporter: JSWarningReporter): JSWarningReporter;
  cdecl; external SpiderMonkeyLib name 'SetWarningReporter';
{$ELSE}
/// Get the error reporting mechanism for an application.
function JS_GetErrorReporter(rt: PJSRuntime): JSErrorReporter; cdecl; external SpiderMonkeyLib;
/// Specify the error reporting mechanism for an application.
function JS_SetErrorReporter(rt: PJSRuntime; er: JSErrorReporter): JSErrorReporter;
  cdecl; external SpiderMonkeyLib;
{$ENDIF}

/// Create a new JavaScript date object
function JS_NewDateObject(cx: PJSContext; year, mon, mday, hour, min, sec: int32): PJSObject;
  cdecl; external SpiderMonkeyLib;
/// Create a new JavaScript date object from the Unix millisecond elapsed since EPOC
function JS_NewDateObjectMsec(cx: PJSContext; msec: double): PJSObject;
  cdecl; external SpiderMonkeyLib;
// Returns true and sets |*isDate| indicating whether |obj| is a Date object or
// a wrapper around one, otherwise returns false on failure.
// - This method returns true with |*isDate == false| when passed a proxy whose
// target is a Date, or when passed a revoked proxy.
function JS_ObjectIsDate(cx: PJSContext; var obj: PJSObject; out isDate: boolean): boolean; cdecl; external SpiderMonkeyLib;

/// Determine whether an exception is pending in the JS engine.
function JS_IsExceptionPending(cx: PJSContext): Boolean; cdecl; external SpiderMonkeyLib;
/// Get the current pending exception for a given JSContext.
function JS_GetPendingException(cx: PJSContext; out vp: jsval): Boolean; cdecl; external SpiderMonkeyLib;
/// Sets the current exception being thrown within a context.
procedure JS_SetPendingException(cx: PJSContext; var vp: jsval); cdecl; external SpiderMonkeyLib;
/// Clear the currently pending exception in a context.
procedure JS_ClearPendingException(cx: PJSContext); cdecl; external SpiderMonkeyLib;
{$IFDEF SM52}
/// If the given object is an exception object, the exception will have (or be
// able to lazily create) an error report struct, and this function will return
// the address of that struct.  Otherwise, it returns nullptr. The lifetime
// of the error report struct that might be returned is the same as the
// lifetime of the exception object.
function JS_ErrorFromException(cx: PJSContext; var obj: PJSObject): PJSErrorReport;
  cdecl; external SpiderMonkeyLib;
{$ELSE}
/// Forward the current pending exception in a given JSContext
// to the current JSErrorReporter callback.
function JS_ReportPendingException(cx: PJSContext): Boolean; cdecl; external SpiderMonkeyLib;
{$ENDIF}

{$IFDEF SM52}
/// Get options of context
function JS_GetContextOptions(cx: PJSContext): PJSContextOptions; cdecl; external SpiderMonkeyLib;
{$ELSE}
/// Get options of runtime
function JS_GetRuntimeOptions(runtime: PJSRuntime): PJSRuntimeOptions; cdecl; external SpiderMonkeyLib;
{$ENDIF}

//function JS_NewRootedValue(cx: PJSContext; val: jsval): PJSRootedValue; cdecl; external SpiderMonkeyLib;
function JS_NewRootedValue(cx: PJSContext; val: Int64): PJSRootedValue; cdecl; external SpiderMonkeyLib;
procedure JS_FreeRootedValue(val: PJSRootedValue); cdecl; external SpiderMonkeyLib name 'JS_FreeRooteValue';

function JS_NewRootedObject(cx: PJSContext; obj: PJSObject): PJSRootedObject; cdecl; external SpiderMonkeyLib;
procedure JS_FreeRootedObject(obj: PJSRootedObject); cdecl; external SpiderMonkeyLib;

function JS_NewRootedString(cx: PJSContext; obj: PJSString): PJSRootedString; cdecl; external SpiderMonkeyLib;
procedure JS_FreeRootedString(str: PJSRootedString); cdecl; external SpiderMonkeyLib;

/// Create Compile Options
function JS_NewCompileOptions(cx: PJSContext): PJSCompileOptions; cdecl; external SpiderMonkeyLib;
/// Free Compile Options
procedure JS_FreeCompileOptions(opt: PJSCompileOptions); cdecl; external SpiderMonkeyLib;
///////////////////

function JS_EvaluateScript(cx: PJSContext;
   options: PJSCompileOptions;
   bytes: PCChar; length: size_t;
   out rval: jsval): Boolean; cdecl; external SpiderMonkeyLib;

function JS_EvaluateUCScript(cx: PJSContext;
   options: PJSCompileOptions;
   chars: PCChar16; length: size_t;
   out rval: jsval): Boolean; cdecl; external SpiderMonkeyLib;

/// Compute |this| for the |vp| inside a JSNative, either boxing primitives or
// replacing with the global object as necessary.
// - This method will go away at some point: instead use |args.thisv()|.  If the
// value is an object, no further work is required.  If that value is |null| or
// |undefined|, use |JS_GetGlobalForObject| to compute the global object.  If
// the value is some other primitive, use |JS_ValueToObject| to box it.
// - low-level API used by JS_THIS() macro.
//function JS_ComputeThis(cx: PJSContext; var vp: jsval): jsval; cdecl; external SpiderMonkeyLib;
function JS_ComputeThis(cx: PJSContext; var vp: jsval): Int64; cdecl; external SpiderMonkeyLib;

procedure strFinalizeOp(fin: PJSStringFinalizer; chars: PCChar16);  cdecl;

const
  strFinalizer: JSStringFinalizer = (
    finalize: strFinalizeOp;
  );

{ ArrayBuffer support from jsfriendapi.h}

/// Create a new signed 8 bit integer typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewInt8Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new unsigned 8 bit integer (byte) typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewUint8Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit integer typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewUint8ClampedArray(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new signed 16 bit integer typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewInt16Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new unsigned 16 bit integer typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewUint16Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new signed 32 bit integer typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewInt32Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new unsigned 32 bit integer typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewUint32Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new signed 32 bit float (single) typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewFloat32Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new signed 64 bit float (double) typed array with nelements elements
// - will fill the newly created array with zeros
function JS_NewFloat64Array(cx: PJSContext; nelements: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit signed integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewInt8ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit unsigned integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewUint8ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit unsigned integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewUint8ClampedArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 16 bit signed integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewInt16ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 16 bit unsigned integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewUint16ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 32 bit signed integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewInt32ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 32 bit unsigned integer typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewUint32ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 32 bit float (single) typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewFloat32ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 64 bit float (double) typed array and copy in values
// from a given object
// - The object is used as if it was an array; that is, the new array (if
// successfully created) will have length given by array.length, and its
// elements will be those specified by array[0], array[1], and so on, after
// conversion to the typed array element type.
function JS_NewFloat64ArrayFromArray(cx: PJSContext; var arr: PJSObject): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit signed integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewInt8ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit unsigned integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewUint8ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 8 bit unsigned integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewUint8ClampedArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 16 bit signed integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewInt16ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 16 bit unsigned integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewUint16ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 32 bit signed integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewInt32ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 32 bit unsigned integer typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewUint32ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 32 bit float (single) typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewFloat32ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new 64 bit float (double) typed array using the given
// ArrayBuffer for storage
// - The length value is optional; if -1 is passed, enough elements to use up the
// remainder of the byte array is used as the default value
function JS_NewFloat64ArrayWithBuffer(cx: PJSContext; var arrayBuffer: PJSObject;
 byteOffset: uint32; length: int32): PJSObject; cdecl; external SpiderMonkeyLib;

/// Create a new SharedArrayBuffer with the given byte length.
function JS_NewSharedArrayBuffer(cx: PJSContext; nbytes: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Create a new ArrayBuffer with the given byte length.
function JS_NewArrayBuffer(cx: PJSContext; nbytes: uint32): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Check whether obj supports JS_GetTypedArray* APIs
// - Note that this may return false if a security wrapper is encountered that
// denies the unwrapping.
// - if this test or one of the JS_Is*Array tests succeeds, then it is safe to call
// the dedicated accessor JSAPI calls
function JS_IsTypedArrayObject(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Check whether obj supports JS_GetArrayBufferView* APIs
// - Note that this may return false if a security wrapper is encountered that
// denies the unwrapping.
// - if this test or one of the JS_Is*Array tests succeeds, then it is safe to call
// the dedicated ArrayBufferView accessor JSAPI calls
function JS_IsArrayBufferViewObject(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 8 bit signed integer typed array types (ArrayBufferView subtypes)
function JS_IsInt8Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 8 bit unsigned integer typed array types (ArrayBufferView subtypes)
function JS_IsUint8Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 8 bit unsigned integer typed array types (ArrayBufferView subtypes)
function JS_IsUint8ClampedArray(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 16 bit signed integer typed array types (ArrayBufferView subtypes)
function JS_IsInt16Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 16 bit unsigned integer typed array types (ArrayBufferView subtypes)
function JS_IsUint16Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 32 bit signed integer typed array types (ArrayBufferView subtypes)
function JS_IsInt32Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 32 bit unsigned integer typed array types (ArrayBufferView subtypes)
function JS_IsUint32Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 32 bit float (single) typed array types (ArrayBufferView subtypes)
function JS_IsFloat32Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Test for specific 64 bit float (double) typed array types (ArrayBufferView subtypes)
function JS_IsFloat64Array(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Return the isShared flag of a typed array, which denotes whether
// the underlying buffer is a SharedArrayBuffer.
//
// |obj| must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
// be known that it would pass such a test: it is a typed array or a wrapper of
// a typed array, and the unwrapping will succeed.
function JS_GetTypedArraySharedness(obj: PJSObject): Boolean; cdecl; external SpiderMonkeyLib;

/// Unwrap 8 bit signed integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsInt8Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out Data: Pint8Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 8 bit unsigned integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsUint8Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Puint8Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 8 bit unsigned integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsUint8ClampedArray(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Puint8Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 16 bit signed integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsInt16Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Pint16Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 16 bit unsigned integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsUint16Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Puint16Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 32 bit signed integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsInt32Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Pint32Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 32 bit unsigned integer typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsUint32Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Puint32Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 32 bit float (single) typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsFloat32Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Pfloat32Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap 64 bit float (double) typed array into direct memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsFloat64Array(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Pfloat64Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap an object as its raw binary memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsArrayBufferView(obj: PJSObject; out length: uint32; out isSharedMemory:Boolean; out  Data: Puint8Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;

/// Unwrap an object as its raw binary memory buffer
// - Return nil without throwing any exception if the object cannot be viewed as the
// correct typed array, or the typed array object on success, filling both out parameters
function JS_GetObjectAsArrayBuffer(obj: PJSObject; out length: uint32; out Data: Puint8Vector): PJSObject;
  cdecl; external SpiderMonkeyLib;


  /// Get the type of elements in a typed array, or jsabTYPE_DATAVIEW if a DataView
function JS_GetArrayBufferViewType(obj: PJSObject): JSArrayBufferViewType;
  cdecl; external SpiderMonkeyLib;

//function JS_GetSharedArrayBufferViewType(obj: PJSObject): JSArrayBufferViewType;
//  cdecl; external SpiderMonkeyLib;

/// Check whether obj supports the JS_GetArrayBuffer* APIs
// - Note that this may return false if a security wrapper is encountered that denies the
// unwrapping
// - If this test succeeds, then it is safe to call the various accessor JSAPI calls
function JS_IsArrayBufferObject(obj: PJSObject): Boolean;
  cdecl; external SpiderMonkeyLib;

function JS_IsSharedArrayBufferObject(obj: PJSObject): Boolean;
  cdecl; external SpiderMonkeyLib;

/// Return the available byte length of an array buffer
// - obj must have passed a JS_IsArrayBufferObject test, or somehow be known
// that it would pass such a test: it is an ArrayBuffer or a wrapper of an
// ArrayBuffer, and the unwrapping will succeed
function JS_GetArrayBufferByteLength(obj: PJSObject): uint32;
  cdecl; external SpiderMonkeyLib;

function JS_GetSharedArrayBufferByteLength(obj: PJSObject): uint32;
  cdecl; external SpiderMonkeyLib;

/// Return true if the arrayBuffer contains any data. This will return false for
// ArrayBuffer.prototype and neutered ArrayBuffers.
//
// |obj| must have passed a JS_IsArrayBufferObject test, or somehow be known
// that it would pass such a test: it is an ArrayBuffer or a wrapper of an
// ArrayBuffer, and the unwrapping will succeed.
function JS_ArrayBufferHasData(obj: PJSObject): Boolean;
  cdecl; external SpiderMonkeyLib;

/// Return a pointer to an array buffer's data
// - The buffer is still owned by the array buffer object, and should not
// be modified on another thread. The returned pointer is stable across GCs
// - obj must have passed a JS_IsArrayBufferObject test, or somehow be known
// that it would pass such a test: it is an ArrayBuffer or a wrapper of an
// ArrayBuffer, and the unwrapping will succeed.
function JS_GetArrayBufferData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint8Vector; cdecl; external SpiderMonkeyLib;

/// Check whether the obj is ArrayBufferObject and memory mapped. Note that this
// may return false if a security wrapper is encountered that denies the
// unwrapping.
function JS_IsMappedArrayBufferObject(obj: PJSObject): Boolean;
  cdecl; external SpiderMonkeyLib;

/// Return the number of elements in a typed array
// - obj must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
// be known that it would pass such a test: it is a typed array or a wrapper of
// a typed array, and the unwrapping will succeed.
function JS_GetTypedArrayLength(obj: PJSObject): uint32; cdecl; external SpiderMonkeyLib;

/// Return the byte offset from the start of an array buffer to the start of a
// typed array view
// - obj must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
// be known that it would pass such a test: it is a typed array or a wrapper of
// a typed array, and the unwrapping will succeed.
function JS_GetTypedArrayByteOffset(obj: PJSObject): uint32; cdecl; external SpiderMonkeyLib;

/// Return the byte length of a typed array
// - obj must have passed a JS_IsTypedArrayObject/JS_Is*Array test, or somehow
// be known that it would pass such a test: it is a typed array or a wrapper of
// a typed array, and the unwrapping will succeed
function JS_GetTypedArrayByteLength(obj: PJSObject): uint32; cdecl; external SpiderMonkeyLib;

/// More generic name for JS_GetTypedArrayByteLength to cover DataViews as well
function JS_GetArrayBufferViewByteLength(obj: PJSObject): uint32; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 8 bit signed integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetInt8ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pint8Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 8 bit unsigned integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetUint8ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint8Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 8 bit unsigned integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetUint8ClampedArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint8Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 16 bit signed integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetInt16ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pint16Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 16 bit unsigned integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetUint16ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint16Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 32 bit signed integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetInt32ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pint32Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 32 bit unsigned integer array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetUint32ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Puint32Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 32 bit float (single) array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetFloat32ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pfloat32Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by a typed 64 bit float (double) array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
function JS_GetFloat64ArrayData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pfloat64Vector; cdecl; external SpiderMonkeyLib;

/// Return a pointer to the start of the data referenced by any typed array
// - The data is still owned by the typed array, and should not be modified on
// another thread
// - obj must have passed a JS_Is*Array test, or somehow be known that it would
// pass such a test: it is a typed array or a wrapper of a typed array, and the
// unwrapping will succeed
// - Prefer the type-specific versions when possible
function JS_GetArrayBufferViewData(obj: PJSObject; out isSharedMemory: Boolean; nogc: PJSAutoCheckCannotGC): Pointer; cdecl; external SpiderMonkeyLib;

/// Return the ArrayBuffer underlying an ArrayBufferView
// - If the buffer has been neutered, this will still return the neutered buffer.
// - obj must be an object that would return true for JS_IsArrayBufferViewObject()
function JS_GetArrayBufferViewBuffer(cx: PJSContext; var obj: PJSObject; out isSharedMemory: Boolean): PJSObject; cdecl; external SpiderMonkeyLib;


////modules

/// Initialize modeles classes next 2 functions cannot work without calling this function
function JS_InitModuleClasses(cx: PJSContext; var obj: PJSObject): boolean; cdecl; external SpiderMonkeyLib;
/// Compile script as module
function JS_CompileModule(cx: PJSContext;
   var obj: PJSObject;
   options: PJSCompileOptions;
   chars: PCChar16; length: size_t): PJSObject; cdecl; external SpiderMonkeyLib;
/// Set handler for module resolving
procedure JS_SetModuleResolveHook(cx: PJSContext; var hook: PJSFunction); cdecl; external SpiderMonkeyLib;

type
  pjsval = ^jsval;

var
  /// global TSynAnsiConvert instance to handle LATIN1(ISO/IEC 8859-1) encoding
  // - this instance is global and instantied during the whole program life time
  // - Spidermonkey internal encoding is LATIN1 or UTF-16
  Latin1AnsiConvert: TSynAnsiConvert;

implementation

uses
  Variants;

const
  JSVAL_INT_MAX = int32($7fffffff);

procedure JSError(cx: PJSContext; aException: Exception);
var
  ws: WideString;
begin
  if not JS_IsExceptionPending(cx) then
    // raise only if this is the first exception in chain
    if aException is EOutOfMemory then
      JS_ReportOutOfMemory(cx)
    else if aException is ESMRangeException then begin
      ws := StringToSynUnicode(aException.Message);
      JSRangeErrorUC(cx, ws);
    end else if aException is ESMTypeException then begin
      ws := StringToSynUnicode(aException.Message);
      JSTypeErrorUC(cx, ws);
    end else begin
      ws := StringToSynUnicode(aException.Message);
      JSErrorUC(cx, ws);
    end;
end;

const
 ErrorUCFormatString: JSErrorFormatString =
  (
    format: '{0}';
    argCount: 1;
    exnType: JSEXN_ERR;
  );
 RangeErrorUCFormatString: JSErrorFormatString =
  (
    format: '{0}';
    argCount: 1;
    exnType: JSEXN_RANGEERR;
  );
 TypeErrorUCFormatString: JSErrorFormatString =
  (
    format: '{0}';
    argCount: 1;
    exnType: JSEXN_TYPEERR;
  );
  SMExceptionNumber = 500;//from 0 to JSErr_Limit(421 for SM 45 ) Error numbers are reserved

function ReportErrorUC(userRef: Pointer; const errorNumber: uintN): PJSErrorFormatString; cdecl;
begin
  result := @ErrorUCFormatString;
end;

function ReportRangeErrorUC(userRef: Pointer; const errorNumber: uintN): PJSErrorFormatString; cdecl;
begin
  result := @RangeErrorUCFormatString;
end;

function TypeRangeErrorUC(userRef: Pointer; const errorNumber: uintN): PJSErrorFormatString; cdecl;
begin
  result := @TypeErrorUCFormatString;
end;

procedure JSErrorUC(cx: PJSContext; aMessage: WideString);
begin
  if not JS_IsExceptionPending(cx) then
    JS_ReportErrorNumberUC(cx, ReportErrorUC, nil, SMExceptionNumber ,Pointer(aMessage));
end;

procedure JSRangeErrorUC(cx: PJSContext; aMessage: WideString);
begin
  if not JS_IsExceptionPending(cx) then
    JS_ReportErrorNumberUC(cx, ReportRangeErrorUC, nil, SMExceptionNumber ,Pointer(aMessage));
end;

procedure JSTypeErrorUC(cx: PJSContext; aMessage: WideString);
begin
  if not JS_IsExceptionPending(cx) then
    JS_ReportErrorNumberUC(cx, TypeRangeErrorUC, nil, SMExceptionNumber ,Pointer(aMessage));
end;

function InitJS: Boolean;
begin
  Result := JS_Init;
end;

procedure ShutDownJS;
begin
  JS_ShutDown;
end;


{ JSString }

procedure JSString.ToJSONString(cx: PJSContext; W: TTextWriter);
var
  str8: PCChar;
  str16: PCChar16;
  strL: size_t;
begin
  W.Add('"');
  if JS_StringHasLatin1Chars(@self) then begin
    str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);
    W.AddJSONEscape(pointer(str8),strL);
  end else begin
    str16 := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @self, @strL);
    W.AddJSONEscapeW(pointer(str16),strL);
  end;
  W.Add('"');
end;

function JSString.ToJSVal: jsval;
begin
  Result.asJSString := @self
end;

function JSString.ToString(cx: PJSContext): string;
var
  str8: PCChar;
  str16: PCChar16;
  strL: size_t;
begin
  if JS_StringHasLatin1Chars(@self) then begin
    str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);
    {$ifdef UNICODE}
      SetString(Result, str8, strL);
    {$else}
      Result := Latin1AnsiConvert.AnsiToUnicodeString(str8, strL);
    {$endif}
  end else begin
    str16 := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @self, @strL);
    RawUnicodeToString(PWideChar(str16),strL,result)
  end;
end;

function JSString.ToSynUnicode(cx: PJSContext): SynUnicode;
var
  str8: PCChar;
  str16: PCChar16;
  strL: size_t;
begin
  if JS_StringHasLatin1Chars(@self) then begin
    str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);
    Result := Latin1AnsiConvert.AnsiToUnicodeString(str8, strL);
  end else begin
    str16 := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @self, @strL);
    SetString(Result, str16, strL);
  end;
end;

function JSString.ToUTF8(cx: PJSContext): RawUTF8;
begin
  ToUTF8(cx,result);
end;

procedure JSString.ToUTF8(cx: PJSContext; var result: RawUTF8);
var
  str8: PCChar;
  str16: PCChar16;
  strL: size_t;
begin
  if JS_StringHasLatin1Chars(@self) then begin
    str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);
    result := Latin1AnsiConvert.AnsiBufferToRawUTF8(str8, strL);
  end else begin
    str16 := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @self, @strL);
    RawUnicodeToUTF8(str16,strL,result, [ccfNoTrailingZero, ccfReplacementCharacterForUnmatchedSurrogate]);
  end;
end;

function JSString.ToAnsi(cx: PJSContext): AnsiString;
var
  str8: PCChar;
  strL: size_t;
begin
  if JS_StringHasLatin1Chars(@self) then begin
    str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);
    SetLength(Result, strL);
    MoveFast(Pointer(str8)^,Result[1],strL);
  end else
    result := '';
end;

procedure JSString.ToVariant(cx: PJSContext; var Value: Variant);
var
  str8: PCChar;
  str16: PCChar16;
  strL: size_t;
begin
  VarClear(Value);
  with TVarData(Value) do begin
    VType := varSynUnicode;
    VAny := nil; // avoid GPF below
    if JS_StringHasLatin1Chars(@self) then begin
      str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);
      SynUnicode(VAny) := Latin1AnsiConvert.AnsiToUnicodeString(str8, strL);
    end else begin
      str16 := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @self, @strL);
      SetString(SynUnicode(VAny), str16, strL);
    end;
  end;
end;

procedure JSString.ToUTF8(cx: PJSContext; W: TTextWriter);
var
  str8: PCChar;
  str16: PCChar16;
  strL: size_t;
  tmpU8: array[0..256*3] of AnsiChar;
  U8: PUTF8Char;
begin
  if JS_StringHasLatin1Chars(@self) then begin
    str8 := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @self, @strL);

    if strL>=SizeOf(tmpU8)div 3 then
      Getmem(U8,strL*3+1) else
      U8 := @tmpU8;
    strL := Latin1AnsiConvert.AnsiBufferToUTF8(U8,pointer(str8),strL)-U8;
    W.AddNoJSONEscape(pointer(U8), strL);
    if U8<>@tmpU8 then
      FreeMem(U8);
  end else begin
    str16 := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @self, @strL);
    W.AddNoJSONEscapeW(pointer(str16),strL);
  end;
end;

function JSString.HasLatin1Chars: Boolean;
begin
  result := JS_StringHasLatin1Chars(@self);
end;

function JSString.GetLatin1StringCharsAndLength(cx: PJSContext;
  out len: size_t): PCChar;
begin
  Result := JS_GetLatin1StringCharsAndLength(cx, @nullPtr, @Self, @len);
end;

function JSString.Length: size_t;
begin
  Result := JS_GetStringLength(@self);
end;

function JSString.GetTwoByteStringCharsAndLength(cx: PJSContext;
  out len: size_t): PCChar16;
begin
  Result := JS_GetTwoByteStringCharsAndLength(cx, @nullPtr, @Self, @len);
end;

{ JSContext }
{$IFDEF SM52}
function JSContext.CheckForInterrupt: Boolean;
begin
  result := JS_CheckForInterrupt(@Self);
end;

procedure JSContext.DisableInterruptCallback;
begin
  JS_DisableInterruptCallback(@self);
end;

procedure JSContext.AddInterruptCallback(callback: JSInterruptCallback);
begin
  JS_AddInterruptCallback(@self, callback);
end;

procedure JSContext.ResetInterruptCallback(disable: boolean);
begin
  JS_ResetInterruptCallback(@self, disable);
end;

{$ENDIF}

procedure JSContext.ClearPendingException;
begin
  JS_ClearPendingException(@self);
end;

function JSContext.CurrentGlobalOrNull: PJSObject;
begin
  result := JS_CurrentGlobalOrNull(@self);
end;

procedure JSContext.Destroy;
begin
  JS_DestroyContext(@self);
end;

function JSContext.EnterCompartment(target: PJSObject): PJSCompartment;
begin
  Result := JS_EnterCompartment(@self, target);
end;

function JSContext.GetPendingException(out rv: jsval): boolean;
begin
  Result := JS_GetPendingException(@self, rv);
end;

function JSContext.GetPrivate: Pointer;
begin
  result := JS_GetContextPrivate(@self);
end;

function JSContext.IdToValue(id: jsid; out v: jsval): Boolean;
begin
  Result := JS_IdToValue(@Self, id, v);
end;

function JSContext.InitStandardClasses(var obj: PJSObject): boolean;
begin
  Result := JS_InitStandardClasses(@Self, obj);
end;

procedure JSContext.SetModuleResolveHook(var hook: PJSFunction);
begin
  JS_SetModuleResolveHook(@Self, hook);
end;

function JSContext.NewDateObjectMsec(msec: double): PJSObject;
begin
  Result := JS_NewDateObjectMsec(@Self, msec);
end;

procedure JSContext.LeaveCompartment(oldCompartment: PJSCompartment);
begin
  JS_LeaveCompartment(@Self, oldCompartment);
end;

procedure JSContext.MaybeGC;
begin
  JS_MaybeGC(@self);
end;

function JSContext.NewDateObject(year, mon, mday, hour, min, sec: int32): PJSObject;
begin
  Result := JS_NewDateObject(@Self, year, mon, mday, hour, min, sec);
end;

function JS_NewCompartmentOptions(): PJS_CompartmentOptions; cdecl; external SpiderMonkeyLib;
procedure JS_FreeCompartmentOptions(opt: PJS_CompartmentOptions); cdecl; external SpiderMonkeyLib;

function JSContext.NewGlobalObject(clasp: PJSClass): PJSObject;
var
  Opt: PJS_CompartmentOptions;
begin
  Opt := JS_NewCompartmentOptions;
  {$IFNDEF SM52}
  Opt.version := JSVERSION_LATEST;
  {$ENDIF}
  Result := JS_NewGlobalObject(@Self, clasp, nil, DontFireOnNewGlobalHook, Opt);
  JS_FreeCompartmentOptions(Opt);
end;

function JSContext.NewInt16Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewInt16Array(@Self, nelements);
end;

function JSContext.NewInt16ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewInt16ArrayFromArray(@Self, arr);
end;

function JSContext.NewInt16ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewInt16ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewInt32Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewInt32Array(@Self, nelements);
end;

function JSContext.NewInt32ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewInt32ArrayFromArray(@Self, arr);
end;

function JSContext.NewInt32ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewInt32ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewInt8Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewInt8Array(@Self, nelements);
end;

function JSContext.NewInt8ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewInt8ArrayFromArray(@Self, arr);
end;

function JSContext.NewInt8ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewInt8ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewJSString(TextWide: PWideChar;
  TextLen: integer): PJSString;
begin
  result := JS_NewUCStringCopyN(@Self, pointer(TextWide), TextLen);
end;

function JSContext.NewJSString(TextAnsi: PAnsiChar; TextLen,
  CodePage: integer): PJSString;
var short: array[byte] of CChar16; // to avoid temp allocation on heap
    buf: PCChar16;
begin
  if TextLen<(sizeof(short) div 3) then
    buf := @short else
    GetMem(buf,TextLen*3+2);
  result := JS_NewUCStringCopyN(@Self, buf,
    TSynAnsiConvert.Engine(CodePage).AnsiBufferToUnicode(PWideChar(buf),TextAnsi,TextLen)-buf);
  if buf<>@short then
    FreeMem(buf);
end;

function JSContext.NewJSString(const Value: SynUnicode): PJSString;
begin
  result := JS_NewUCStringCopyN(@Self, pointer(Value), Length(Value));
end;

function JSContext.NewObject(clasp: PJSClass): PJSObject;
begin
  Result := JS_NewObject(@Self, clasp);
end;

function JSContext.NewObjectWithGivenProto(clasp: PJSClass; var proto: PJSObject): PJSObject;
begin
  Result := JS_NewObjectWithGivenProto(@Self, clasp, proto);
end;

procedure JSContext.ReportError(format: PCChar);
begin
  JS_ReportError(@Self, format);
end;

procedure JSContext.ReportErrorNumberUC(errorCallback: JSErrorCallback; userRef: pointer; const erroNubmer: uintN);
begin
  JS_ReportErrorNumberUC(@Self, errorCallback, userRef, erroNubmer);
end;

procedure JSContext.ReportOutOfMemory;
begin
  JS_ReportOutOfMemory(@self);
end;

procedure JSContext.SetPrivate(const Value: Pointer);
begin
  JS_SetContextPrivate(@self,Value);
end;

function JSContext.TypeOfValue(v: jsval): JSType;
begin
  result := JS_TypeOfValue(@Self, v);
end;

function JSContext.ValueToId(var v: jsval; out id: jsid): Boolean;
begin
  Result := JS_ValueToId(@Self, v, id);
end;

function JSContext.WrapObject(var obj: PJSObject): boolean;
begin
  Result := JS_WrapObject(@Self, obj);
end;

procedure JSContext.BeginRequest;
begin
  JS_BeginRequest(@self);
end;

procedure JSContext.EndRequest;
begin
  JS_EndRequest(@self);
end;

function JSContext.GetArrayBufferViewBuffer(var obj: PJSObject;
  out isSharedMemory: Boolean): PJSObject;
begin
  Result := JS_GetArrayBufferViewBuffer(@self, obj, isSharedMemory);
end;

function JSContext.GetArrayBufferViewBuffer(var obj: PJSObject): PJSObject;
var
  isSharedMemory: Boolean;
begin
  Result := JS_GetArrayBufferViewBuffer(@self, obj, isSharedMemory);
end;

function JSContext.GetIsRunning: boolean;
begin
  result := JS_IsRunning(@self);
end;

procedure JSContext.FreeCompileOptions(opt: PJSCompileOptions);
begin
  JS_FreeCompileOptions(opt);
end;

procedure JSContext.FreeRootedObject(obj: PJSRootedObject);
var
  curr: PJSRootedObject;
begin
  curr := obj.stack.Last;
  while curr.ptr = nil do curr := curr.prev;
  if curr <> obj then
    raise ESMException.Create('FreeRootedObject Stack error');
  JS_FreeRootedObject(obj);
end;

procedure JSContext.FreeRootedString(str: PJSRootedString);
begin
  if ppointer(str.stack)^ <> str then
    raise ESMException.Create('FreeRootedString Stack error');
  JS_FreeRootedString(str);
end;

procedure JSContext.FreeRootedValue(str: PJSRootedValue);
begin
  if ppointer(str.stack)^ <> str then
    raise ESMException.Create('FreeRootedValue Stack error');
  JS_FreeRootedValue(str);
end;

function JSContext.NewRootedObject(obj: PJSObject): PJSRootedObject;
begin
  Result := JS_NewRootedObject(@Self, obj);
end;

function JSContext.NewRootedString(obj: PJSString): PJSRootedString;
begin
  Result := JS_NewRootedString(@Self, obj)
end;

function JSContext.NewRootedValue(val: jsval): PJSRootedValue;
begin
  Result := JS_NewRootedValue(@Self, val._l.asBits)
end;

function JSContext.NewSharedArrayBuffer(nbytes: uint32): PJSObject;
begin
  Result := JS_NewSharedArrayBuffer(@Self, nbytes);
end;

function JSContext.NewUint16Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewUint16Array(@Self, nelements);
end;

function JSContext.NewUint16ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewUInt16ArrayFromArray(@Self, arr);
end;

function JSContext.NewUint16ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewUInt16ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewUint32Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewUInt32Array(@Self, nelements);
end;

function JSContext.NewUint32ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewUInt32ArrayFromArray(@Self, arr);
end;

function JSContext.NewUint32ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewUInt32ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewUint8Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewuInt8Array(@Self, nelements);
end;

function JSContext.NewUint8ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewUInt8ArrayFromArray(@Self, arr);
end;

function JSContext.NewUint8ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewUInt8ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewUint8ClampedArray(nelements: uint32): PJSObject;
begin
  Result := JS_NewUint8ClampedArray(@Self, nelements);
end;

function JSContext.NewUint8ClampedArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewUint8ClampedArrayFromArray(@Self, arr);
end;

function JSContext.NewUint8ClampedArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewUint8ClampedArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

procedure strFinalizeOp(fin: PJSStringFinalizer; chars: PCChar16);  cdecl;
begin

end;

function JSContext.NewExternalString(const Value: SynUnicode): PJSString;
begin
  Result := JS_NewExternalString(@Self,  pointer(Value), length(Value), @strFinalizer);
end;

function JSContext.NewFloat32Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewFloat32Array(@Self, nelements);
end;

function JSContext.NewFloat32ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewFloat32ArrayFromArray(@Self, arr);
end;

function JSContext.NewFloat32ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewFloat32ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewFloat64Array(nelements: uint32): PJSObject;
begin
  Result := JS_NewFloat64Array(@Self, nelements);
end;

function JSContext.NewFloat64ArrayFromArray(var arr: PJSObject): PJSObject;
begin
  Result := JS_NewFloat64ArrayFromArray(@Self, arr);
end;

function JSContext.NewFloat64ArrayWithBuffer(var arrayBuffer: PJSObject;
  byteOffset: uint32; length: int32): PJSObject;
begin
  Result := JS_NewFloat64ArrayWithBuffer(@Self, arrayBuffer, byteOffset, length);
end;

function JSContext.NewFunction(call: JSNative; nargs, flags: uintN;
  name: PCChar): PJSObject;
begin
  Result := JS_NewFunction(@Self, call, nargs, flags, name);
end;

function JSContext.DefineDebuggerObject(var obj: PJSObject): boolean;
begin
  Result := JS_DefineDebuggerObject(@Self, obj);
end;

function JSContext.NewCompileOptions: PJSCompileOptions;
begin
  result := JS_NewCompileOptions(@self);
end;

function JSContext.CompileModule(var obj: PJSObject; opts: PJSCompileOptions;
  chars: PCChar16; length: size_t): PJSObject;
begin
  Result := JS_CompileModule(@Self, obj, opts, chars, length);
end;

function JSContext.CompileScript(bytes: PCChar;
  length: size_t; opts: PJSCompileOptions; out script: PJSScript): boolean;
begin
  Result := JS_CompileScript(@Self, bytes, length, opts, script);
end;

function JSContext.CompileUCScript(chars: PCChar16; length: size_t;
  opts: PJSCompileOptions; out script: PJSScript): boolean;
begin
  Result := JS_CompileUCScript(@Self, chars, length, opts, script);
end;

function JSContext.EvaluateScript(opts: PJSCompileOptions; bytes: PCChar; length: size_t;
  out rval: jsval): Boolean;
begin
  Result := JS_EvaluateScript(@Self, opts, bytes, length, rval);
end;

function JSContext.EvaluateUCScript(opts: PJSCompileOptions; chars: PCChar16; length: size_t;
  out rval: jsval): Boolean;
begin
  Result := JS_EvaluateUCScript(@Self, opts, chars, length, rval);
end;

function JSContext.ExecuteScript(var script: PJSScript; out rval: jsval): Boolean;
begin
  Result := JS_ExecuteScript(@Self, script, rval);
end;

function JSContext.New(var ctor: PJSObject; argc: uintN; argv: PjsvalVector): PJSObject;
var
    args: JSHandleValueArray;
begin
  args.length := argc;
  args.elements_ := argv;
  Result := JS_New(@self, ctor, args);
end;

function JSContext.NewArrayBuffer(nbytes: uint32): PJSObject;
begin
  Result := JS_NewArrayBuffer(@Self, nbytes);
end;

function JSContext.NewArrayObject(length: size_t;
  vector: PjsvalVector): PJSObject;
var
    contents: JSHandleValueArray;
begin
  contents.length := length;
  contents.elements_ := vector;
  Result := JS_NewArrayObject2(@Self, contents);
end;

function JSContext.NewArrayObject(length: size_t): PJSObject;
begin
  Result := JS_NewArrayObject(@Self, length);
end;

{$IFNDEF SM52}
function JSContext.GetRuntime: PJSRuntime;
begin
  result := JS_GetRuntime(@self);
end;
{$ENDIF}

function JSContext.InitCTypesClass(var obj: PJSObject): boolean;
begin
  Result := JS_InitCTypesClass(@Self, obj);
end;

function JSContext.InitReflectParse(var obj: PJSObject): boolean;
begin
  Result := JS_InitReflectParse(@Self, obj);
end;

function JSContext.InitModuleClasses(var obj: PJSObject): boolean;
begin
  Result := JS_InitModuleClasses(@Self, obj);
end;

function JSContext.NewJSString(const Value: RawUTF8): PJSString;
begin
  result := NewJSString(pointer(Value),length(Value),CP_UTF8);
end;

{$IFDEF SM52}
function JSContextOptions.getOptions(const Index: Integer): Boolean;
{$ELSE}
function JSRuntimeOptions.getOptions(const Index: Integer): Boolean;
{$ENDIF}
{ JSRuntimeOptions }
begin
   Result := (pword(@self)^ and (1 shl Index)) <> 0;
end;

{$IFDEF SM52}
procedure JSContextOptions.setOptions(const Index: Integer;
{$ELSE}
procedure JSRuntimeOptions.setOptions(const Index: Integer;
{$ENDIF}
  const Value: Boolean);
var
  val: uint16;
begin
  val := 1 shl Index;
  if Value then
    pword(@self)^ := pword(@self)^ or val
  else
    pword(@self)^ := pword(@self)^ and (not val);
end;

{$IFNDEF SM52}
{ JSRuntime }
procedure JSRuntime.Destroy;
begin
  JS_DestroyRuntime(@self);
end;
{$ENDIF}

{$IFDEF SM52}
procedure JSContext.GC;
{$ELSE}
procedure JSRuntime.GC;
{$ENDIF}
begin
  JS_GC(@self);
end;

{$IFDEF SM52}
function JSContext.GetEmptyString: PJSString;
{$ELSE}
function JSRuntime.GetEmptyString: PJSString;
{$ENDIF}
begin
  Result := JS_GetEmptyString(@self);
end;

{$IFDEF SM52}
function JSContext.GetWarningReporter: JSWarningReporter;
begin
  Result := JS_GetWarningReporter(@self);
end;
{$ELSE}
function JSRuntime.GetErrorReporter: JSErrorReporter;
begin
  Result := JS_GetErrorReporter(@self);
end;
{$ENDIF}

{$IFDEF SM52}
function JSContext.GetGCParameter(key: JSGCParamKey): uint32;
{$ELSE}
function JSRuntime.GetGCParameter(key: JSGCParamKey): uint32;
{$ENDIF}
begin
  Result := JS_GetGCParameter(@Self, key);
end;

{$IFDEF SM52}
{$ELSE}
function JSRuntime.GetInterruptCallback: JSInterruptCallback;
begin
  Result := JS_GetInterruptCallback(@self);
end;
{$ENDIF}

{$IFDEF SM52}
function JSContext.GetNowMs: int64;
{$ELSE}
function JSRuntime.GetNowMs: int64;
{$ENDIF}
begin
  Result := JS_Now;
end;

{$IFDEF SM52}
class function JSContext.CreateNew(maxbytes: uint32; maxNurseryBytes: uint32; parentContext: PJSContext): PJSContext;
begin
  with TSynFPUException.ForLibraryCode do begin
    Result := JS_NewContext(maxbytes, maxNurseryBytes, parentContext);
    InitSelfHostedCode(Result);
  end;
end;
{$ELSE}

function JSRuntime.GetPrivate: Pointer;
begin
  Result := JS_GetRuntimePrivate(@self);
end;

class function JSRuntime.new(maxbytes: uint32; maxNurseryBytes: uint32; parentRuntime: PJSRuntime): PJSRuntime;
begin
  Result := JS_NewRuntime(maxbytes, maxNurseryBytes, parentRuntime);
end;

function JSRuntime.NewContext(stackChunkSize: size_t): PJSContext;
begin
  Result := JS_NewContext(@Self, stackChunkSize)
end;
{$ENDIF}

{$IFDEF SM52}
function JSContext.GetOptions: PJSContextOptions;
begin
  Result := JS_GetContextOptions(@self);
end;
{$ELSE}
function JSRuntime.GetOptions: PJSRuntimeOptions;
begin
  Result := JS_GetRuntimeOptions(@self);
end;
{$ENDIF}


{$IFDEF SM52}
procedure JSContext.RequestInterruptCallback;
{$ELSE}
procedure JSRuntime.RequestInterruptCallback;
{$ENDIF}
begin
  JS_RequestInterruptCallback(@self);
end;

{$IFDEF SM52}
procedure JSContext.SetWarningReporter(reporter: JSWarningReporter);
begin
  JS_SetWarningReporter(@self, reporter);
end;
{$ELSE}
procedure JSRuntime.SetErrorReporter(er: JSErrorReporter);
begin
  JS_SetErrorReporter(@self, er);
end;
{$ENDIF}

{$IFDEF SM52}
procedure JSContext.SetGCParameter(key: JSGCParamKey; const Value: uint32);
{$ELSE}
procedure JSRuntime.SetGCParameter(key: JSGCParamKey; const Value: uint32);
{$ENDIF}
begin
  JS_SetGCParameter(@Self, key, Value);
end;

{$IFDEF SM52}
procedure JSContext.SetGCParametersBasedOnAvailableMemory(availMem: uint32);
{$ELSE}
procedure JSRuntime.SetGCParametersBasedOnAvailableMemory(availMem: uint32);
{$ENDIF}
begin
  JS_SetGCParametersBasedOnAvailableMemory(@Self, availMem);
end;

{$IFDEF SM52}
{$ELSE}
procedure JSRuntime.SetInterruptCallback(callback: JSInterruptCallback);
begin
  JS_SetInterruptCallback(@Self, callback);
end;
{$ENDIF}

{$IFDEF SM52}
procedure JSContext.SetNativeStackQuota(systemCodeStackSize: size_t);
{$ELSE}
procedure JSRuntime.SetNativeStackQuota(systemCodeStackSize: size_t);
{$ENDIF}
begin
  JS_SetNativeStackQuota(@Self, systemCodeStackSize);
end;

{$IFDEF SM52}

{$ELSE}
procedure JSRuntime.SetPrivate(const Value: Pointer);
begin
  JS_SetRuntimePrivate(@Self, Value);
end;
{$ENDIF}

{ JSObject }

function JSObject.isArray(cx: PJSContext): Boolean;
var
  _isArray: Boolean;
  obj: PJSObject;
begin
  obj := @Self;
  Result := JS_IsArrayObject(cx, obj, _isArray) and _isArray;
end;

function JSObject.IsArrayBufferObject: Boolean;
begin
  result := JS_IsArrayBufferObject(@self)
end;

function JSObject.IsArrayBufferViewObject: Boolean;
begin
  result := JS_IsArrayBufferViewObject(@self)
end;

function JSObject.isDate(cx: PJSContext): Boolean;
var
  _isDate: Boolean;
  obj: PJSObject;
begin
  obj := @Self;
  Result := JS_ObjectIsDate(cx, obj, _isDate) and _isDate;
end;

function JSObject.IsFloat32Array: Boolean;
begin
  result := JS_IsFloat32Array(@self);
end;

function JSObject.IsFloat64Array: Boolean;
begin
  result := JS_IsFloat64Array(@self);
end;

function JSObject.isFunction(cx: PJSContext): Boolean;
begin
  Result := JS_ObjectIsFunction(cx, @Self);
end;

function JSObject.IsInt16Array: Boolean;
begin
  result := JS_IsInt16Array(@self)
end;

function JSObject.IsInt32Array: Boolean;
begin
  result := JS_IsInt32Array(@self)
end;

function JSObject.IsInt8Array: Boolean;
begin
  result := JS_IsInt8Array(@self)
end;

function JSObject.IsMappedArrayBufferObject(obj: PJSObject): Boolean;
begin
  result := JS_IsMappedArrayBufferObject(@self);
end;

function JSObject.IsSharedArrayBufferObject: Boolean;
begin
  result := JS_IsSharedArrayBufferObject(@self)
end;

function JSObject.IsTypedArrayObject: Boolean;
begin
  result := JS_IsTypedArrayObject(@self)
end;

function JSObject.IsUint16Array: Boolean;
begin
  result := JS_IsUInt16Array(@self)
end;

function JSObject.IsUint32Array: Boolean;
begin
  result := JS_IsUInt32Array(@self);
end;

function JSObject.IsUint8Array: Boolean;
begin
  result := JS_IsUint8Array(@self)
end;

function JSObject.IsUint8ClampedArray: Boolean;
begin
  result := JS_IsUint8ClampedArray(@self)
end;

function JSObject.GetArrayBufferViewData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Pointer;
begin
  result := JS_GetArrayBufferViewData(@self, isSharedMemory, nogc);
end;

function JSObject.RunMethod(cx: PJSContext; const name: PCChar;
  out rval: jsval): Boolean;
begin
  Result := CallFunctionName(cx, name, 0, nil, rval);
end;

function JSObject.RunMethod(cx: PJSContext; const name: PCChar; arg: jsval;
  out rval: jsval): Boolean;
begin
  Result := CallFunctionName(cx, name, 1, @arg, rval);
end;

function JSObject.RunMethod(cx: PJSContext; const name: PCChar;
  args: TjsvalDynArray; out rval: jsval): Boolean;
begin
  Result := CallFunctionName(cx, name, Length(args), @args[0], rval);
end;

function JSObject.GetInstancePrivate(cx: PJSContext; clasp: PJSClass): Pointer;
var
  obj: PJSObject;
begin
  obj := @Self;
  result := JS_GetInstancePrivate(cx, obj, clasp, nil);
end;

function JSObject.GetInt16ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Pint16Vector;
begin
  result := JS_GetInt16ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetInt32ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Pint32Vector;
begin
  result := JS_GetInt32ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetInt8ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Pint8Vector;
begin
  result := JS_GetInt8ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetObjectAsArrayBuffer(out length: uint32;
  out Data: Puint8Vector): PJSObject;
begin
  result := JS_GetObjectAsArrayBuffer(@self, length, data);
end;

function JSObject.GetObjectAsArrayBufferView(out length: uint32;
  out isSharedMemory: Boolean; out Data: Puint8Vector): PJSObject;
begin
  result := JS_GetObjectAsArrayBufferView(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsFloat32Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Pfloat32Vector): PJSObject;
begin
  result := JS_GetObjectAsFloat32Array(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsFloat64Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Pfloat64Vector): PJSObject;
begin
  result := JS_GetObjectAsFloat64Array(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsInt16Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Pint16Vector): PJSObject;
begin
  result := JS_GetObjectAsInt16Array(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsInt32Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Pint32Vector): PJSObject;
begin
  result := JS_GetObjectAsInt32Array(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsInt8Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Pint8Vector): PJSObject;
begin
  result := JS_GetObjectAsInt8Array(@self, length, isSharedMemory, data)
end;

function JSObject.GetObjectAsUint16Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Puint16Vector): PJSObject;
begin
  result := JS_GetObjectAsUInt16Array(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsUint32Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Puint32Vector): PJSObject;
begin
  result := JS_GetObjectAsUint32Array(@self, length, isSharedMemory, data);
end;

function JSObject.GetObjectAsUint8Array(out length: uint32;
  out isSharedMemory: Boolean; out Data: Puint8Vector): PJSObject;
begin
  result := JS_GetObjectAsUInt8Array(@self, length, isSharedMemory, data)
end;

function JSObject.GetObjectAsUint8ClampedArray(out length: uint32;
  out isSharedMemory: Boolean; out Data: Puint8Vector): PJSObject;
begin
  result := JS_GetObjectAsUint8ClampedArray(@self, length, isSharedMemory, data)
end;

function JSObject.GetPrivate: Pointer;
begin
  Result := JS_GetPrivate(@self);
end;

function JSObject.GetProperty(cx: PJSContext; const name: PCChar;
  out vp: jsval): boolean;
var
  obj: PJSObject;
begin
  obj := @Self;
  Result := JS_GetProperty(cx, obj, name, vp);
end;

function JSObject.GetPropertyById(cx: PJSContext; const id: jsid;
  out vp: jsval): boolean;
var
  obj: PJSObject;
  _id: jsid;
begin
  obj := @Self;
  _id := id;
  Result := JS_GetPropertyById(cx, obj, _id, vp);
end;

function JSObject.GetPropValue(cx: PJSContext; const name: SynUnicode): jsval;
begin
  {$ifdef WITHASSERT}
  Assert(
  {$ENDIF}
  GetUCProperty(cx, Pointer(name), Length(name), Result)
  {$ifdef WITHASSERT}
  );
  {$ENDIF}
end;

function JSObject.GetPrototype(cx: PJSContext; out protop: PJSObject): Boolean;
var
  obj: PJSObject;
begin
  obj := @Self;
  Result := JS_GetPrototype(cx, obj, protop);
end;

function JSObject.GetReservedSlot(index: uint32): jsval;
begin
{$IFDEF SM52}
  result._l.asBits := JS_GetReservedSlot(@Self, index)
{$ELSE}
  result._l.asBits := JS_GetReservedSlot(@Self, index)
{$ENDIF}
end;

function JSObject.GetSharedArrayBufferByteLength: uint32;
begin
  result := JS_GetSharedArrayBufferByteLength(@self);
end;

//function JSObject.GetSharedArrayBufferViewType: JSArrayBufferViewType;
//begin
//  result := JS_GetSharedArrayBufferViewType(@self);
//end;

function JSObject.GetTypedArrayByteLength: uint32;
begin
  result := JS_GetTypedArrayLength(@self);
end;

function JSObject.GetTypedArrayByteOffset: uint32;
begin
  result := JS_GetTypedArrayByteOffset(@self);
end;

function JSObject.GetTypedArrayLength: uint32;
begin
  result := JS_GetTypedArrayLength(@self);
end;

function JSObject.GetTypedArraySharedness: Boolean;
begin
  result := JS_GetTypedArraySharedness(@self)
end;

function JSObject.AlreadyHasOwnUCProperty(cx: PJSContext; const name: PCChar16;
  namelen: size_t): Boolean;
var
  obj: PJSObject;
  foundp: Boolean;
begin
  obj := @Self;
  Result := JS_AlreadyHasOwnUCProperty(cx, obj, name, namelen, foundp) and foundp;
end;

function JSObject.ArrayBufferHasData: Boolean;
begin
  result := JS_ArrayBufferHasData(@self);
end;

function JSObject.CallFunction(cx: PJSContext; var fun: PJSFunction;
  argc: uintN; argv: PjsvalVector; out rval: jsval): Boolean;
var obj: PJSObject;
    args: JSHandleValueArray;
begin
  obj := @Self;
  args.length := argc;
  args.elements_ := argv;
  Result := JS_CallFunction(cx, obj, fun, args, rval);
end;

function JSObject.CallFunctionName(cx: PJSContext; const name: PCChar;
  argc: uintN; argv: PjsvalVector; out rval: jsval): Boolean;
var obj: PJSObject;
    args: JSHandleValueArray;
begin
  obj := @Self;
  args.length := argc;
  args.elements_ := argv;
  Result := JS_CallFunctionName(cx, obj, name, args, rval);
end;

function JSObject.CallFunctionValue(cx: PJSContext; val: jsval; argc: uintN;
  argv: PjsvalVector; out rval: jsval): Boolean;
var obj: PJSObject;
    args: JSHandleValueArray;
begin
  obj := @Self;
  args.length := argc;
  args.elements_ := argv;
  Result := JS_CallFunctionValue(cx, obj, val, args, rval);
end;

const
  /// API extension: OR this into indent to avoid pretty-printing the decompiled
  // source resulting from JS_DecompileFunction{,Body}.
  JS_DONT_PRETTY_PRINT = $8000;
  prettyPrintAr: array[boolean] of uintN = (JS_DONT_PRETTY_PRINT, 0);
function JSObject.DecompileFunction(cx: PJSContext;
  PrettyPrint: Boolean): PJSString;
var
  fun: PJSFunction;
begin
  fun := @self;
  Result := JS_DecompileFunction(cx, fun, prettyPrintAr[PrettyPrint]);
end;

function JSObject.DefineFunction(cx: PJSContext; name: PCChar; call: JSNative;
  nargs, attrs: uintN): PJSFunction;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_DefineFunction(cx, obj, name, call, nargs, attrs);
end;

function JSObject.DefineFunctions(cx: PJSContext; fs: PJSFunctionSpec;
  behavior: JSPropertyDefinitionBehavior): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_DefineFunctions(cx, obj, fs, behavior);
end;

function JSObject.DefineProperties(cx: PJSContext;
  ps: PJSPropertySpec): boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_DefineProperties(cx, obj, ps)
end;

function JSObject.DefineProperty(cx: PJSContext; const name: PCChar;
  const value: jsval; attrs: uint32; getter, setter: JSNative): boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_DefineProperty(cx, obj, name, pjsval(@value)^, attrs, getter, setter)
end;

function JSObject.DefinePropertyById(cx: PJSContext; var id: jsid;
  const value: jsval; attrs: uint32; getter, setter: JSNative): boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_DefinePropertyById(cx, obj, id, pjsval(@value)^, attrs, getter, setter)
end;

function JSObject.DefineUCFunction(cx: PJSContext; name: PCChar16;
  namelen: size_t; call: JSNative; nargs, attrs: uintN): PJSFunction;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_DefineUCFunction(cx, obj, name, namelen, call, nargs, attrs);
end;

function JSObject.DefineUCProperty(cx: PJSContext; const name: SynUnicode;
  const value: jsval; attrs: uint32; getter, setter: JSNative): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_DefineUCProperty(cx, obj, Pointer(name), Length(name), pjsval(@value)^, attrs, getter, setter)
end;

function JSObject.DefineUCProperty(cx: PJSContext; const name: PCChar16;
  namelen: size_t; const value: jsval; attrs: uint32; getter,
  setter: JSNative): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_DefineUCProperty(cx, obj, name, namelen, pjsval(@value)^, attrs, getter, setter)
end;

function JSObject.DeleteElement(cx: PJSContext; index: uint32;
  out res: JS_ObjectOpResult): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_DeleteElement(cx, obj, index, res);
end;

function JSObject.DeletePropertyById(cx: PJSContext; const id: jsid;
  out res: JS_ObjectOpResult): Boolean;
var obj: PJSObject;
    _id: jsid;
begin
  obj := @Self;
  _id := id;
  Result := JS_DeletePropertyById(cx, obj, _id, res);
end;

function JSObject.Enumerate(cx: PJSContext): JSIdArray;
var obj: PJSObject;
begin
  obj := @Self;
  Result.init(cx);
  {$ifdef WITHASSERT}
  Assert(
  {$ENDIF}
  JS_Enumerate(cx, obj, Result)
  {$ifdef WITHASSERT}
  );
  {$ENDIF}
end;

function JSObject.GetUCProperty(cx: PJSContext; const name: PCChar16;
  namelen: size_t; out vp: jsval): boolean;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_GetUCProperty(cx, obj, name, namelen, vp);
end;

function JSObject.GetUint16ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Puint16Vector;
begin
  result := JS_GetUInt16ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetUint32ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Puint32Vector;
begin
  result := JS_GetUint32ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetUint8ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Puint8Vector;
begin
  result := JS_GetUInt8ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetUint8ArrayData: Puint8Vector;
var isShared: Boolean;
begin
  result := JS_GetUInt8ArrayData(@self, isShared, nil);
end;

function JSObject.GetUint8ClampedArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Puint8Vector;
begin
  result := JS_GetUint8ClampedArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.HasInstance(cx: PJSContext; var val: jsval): Boolean;
var obj: PJSObject;
    res: Boolean;
begin
  obj := @Self;
  Result := JS_HasInstance(cx, obj, val, res) and res;
end;

function JSObject.HasProperty(cx: PJSContext; const name: PCChar): Boolean;
var obj: PJSObject;
    found: Boolean;
begin
  obj := @Self;
  Result := JS_HasProperty(cx, obj, name, found) and found;
end;

function JSObject.HasUCProperty(cx: PJSContext; const name: PCChar16;
  namelen: size_t; out found: Boolean): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_HasUCProperty(cx, obj, name, namelen, found);
end;

function JSObject.InitClass(cx: PJSContext; var parent_proto: PJSObject;
  clasp: PJSClass; _constructor: JSNative; nargs: Cardinal; ps: PJSPropertySpec;
  fs: PJSFunctionSpec; static_ps: PJSPropertySpec;
  static_fs: PJSFunctionSpec): PJSObject;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_InitClass(cx, obj, parent_proto, clasp, _constructor, nargs, ps, fs, static_ps, static_fs);
end;

function JSObject.GetArrayBufferByteLength: uint32;
begin
  result := JS_GetArrayBufferByteLength(@self);
end;

function JSObject.GetArrayBufferData: Puint8Vector;
var isShared: Boolean;
begin
  result := JS_GetArrayBufferData(@self, isShared, nil);
end;

function JSObject.GetArrayBufferData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Puint8Vector;
begin
  result := JS_GetArrayBufferData(@self, isSharedMemory, nogc);
end;

function JSObject.GetArrayBufferViewByteLength: uint32;
begin
  result := JS_GetArrayBufferViewByteLength(@self);
end;

function JSObject.GetArrayBufferViewType: JSArrayBufferViewType;
begin
  result := JS_GetArrayBufferViewType(@self);
end;

function JSObject.GetArrayLength(cx: PJSContext; var length: uint32): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_GetArrayLength(cx, obj, length);
end;

function JSObject.GetClass: PJSClass;
begin
  result := JS_GetClass(@self);
end;

function JSObject.GetConstructor(cx: PJSContext): PJSObject;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_GetConstructor(cx, obj);
end;

function JSObject.GetElement(cx: PJSContext; index: uint32;
  out vp: jsval): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_GetElement(cx, obj, index, vp);
end;

function JSObject.GetFloat32ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Pfloat32Vector;
begin
  result := JS_GetFloat32ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetFloat64ArrayData(out isSharedMemory: Boolean;
  nogc: PJSAutoCheckCannotGC): Pfloat64Vector;
begin
  result := JS_GetFloat64ArrayData(@self, isSharedMemory, nogc);
end;

function JSObject.GetFunctionId: PJSString;
begin
  Result := JS_GetFunctionId(@self);
end;

function JSObject.SetElement(cx: PJSContext; index: uint32;
  const vp: jsval): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  result := JS_SetElement(cx, obj, index, pjsval(@vp)^);
end;

procedure JSObject.SetPrivate(data: Pointer);
begin
  JS_SetPrivate(@self, data);
end;

function JSObject.SetProperty(cx: PJSContext; const name: PCChar;
  const vp: jsval): Boolean;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_SetProperty(cx, obj, name, pjsval(@vp)^);
end;

function JSObject.SetPrototype(cx: PJSContext; var proto: PJSObject): Boolean;
var
  obj: PJSObject;
begin
  obj := @Self;
  Result := JS_SetPrototype(cx, obj, proto);
end;

procedure JSObject.SetReservedSlot(index: uint32; v: jsval);
begin
{$IFDEF SM52}
  JS_SetReservedSlot(@Self, index, v);
{$ELSE}
  JS_SetReservedSlot(@Self, index, v._l.asBits);
{$ENDIF}
end;

function JSObject.SetUCProperty(cx: PJSContext; const name: PCChar16;
  namelen: size_t; const vp: jsval): boolean;
var obj: PJSObject;
begin
  obj := @Self;
  Result := JS_SetUCProperty(cx, obj, name, namelen, pjsval(@vp)^);
end;

function JSObject.ToJSValue: jsval;
begin
  Result.asObject := @self;
end;

function JSObject.GetBufferDataAndLength(out data: Puint8Vector; out len: uint32): boolean;
var
  isShared: boolean;
begin
  if Self.IsArrayBufferViewObject then begin
    Result := Self.GetObjectAsArrayBufferView(len, isShared, data) <> nil;
  end else if Self.IsArrayBufferObject then begin
    data := Self.GetArrayBufferData;
    len := Self.GetArrayBufferByteLength;
    Result := True
  end else
    Result := False;
end;

{ ESMException }

constructor ESMException.CreateWithTrace(const AFileName: RawUTF8; AJSErrorNum, ALineNum: integer; AMessage: string; const AStackTrace: SynUnicode);
begin
  Create(AMessage);
  FJSErrorNum := AJSErrorNum;
  if AFileName='' then
    FFileName := '<>'
  else
    FFileName := AFileName;
  FLineNum := ALineNum;
  FJSStackTrace := AStackTrace;
end;

procedure ESMException.WriteFormatted(WR: TTextWriter);
{$IFNDEF SM_DEBUG}
var
  P, Pb: PWord;
{$ENDIF}
begin
  WR.AddJSONEscape(pointer(FileName), Length(fileName));
    WR.Add(':'); WR.Add(Line);
  WR.AddShort('\n\nError: ');
    WR.AddJSONEscapeString(Message); WR.AddShort('\n');
  {$IFDEF SM_DEBUG}
    WR.AddJSONEscapeString(Stack);
  {$ELSE}
    // any stack line what don't start with `@` is internal (core_modules) calls
    // remove it to simplify domain logic debugging
    if length(Stack) > 0 then begin
      P := PWord(pointer(Stack));
      while P^ <> 0 do begin
        if (P^ = Ord('@')) then begin
          Pb := P;
          while (P^ <> 10) and (P^ <> 0) do Inc(P);
          if (P^ = 10) then Inc(P);
          WR.AddJSONEscapeW(Pb, (PtrUInt(P)-PtrUInt(Pb)) div 2);
        end else
          while (P^ <> 10) and (P^ <> 0) do
            Inc(P);
          if (P^ = 10) then Inc(P);
      end;
    end;
  {$ENDIF}
end;

{$ifndef NOEXCEPTIONINTERCEPT}
function ESMException.CustomLog(
  WR: TTextWriter; const Context: TSynLogExceptionContext): boolean;
begin
  (Context.EInstance as ESMException).WriteFormatted(WR);
  result := true; // do not append a address
end;
{$endif}

{ JSArgRec }

function JSArgRec.getArgv: PjsvalVector;
begin
  Result := @rec.argv;
end;

function JSArgRec.getCalleObject: PJSObject;
begin
  Result := rec.calle.asObject;
end;

function JSArgRec.GetIsConstructing: Boolean;
begin
  Result := rec.this.IsMagic;
end;

function JSArgRec.getThis(cx: PJSContext): jsval;
begin
  if rec.this.IsPrimitive then
    result._l.asBits := JS_ComputeThis(cx, rec.calle) else
    result := rec.this;
end;

function JSArgRec.getThisObject(cx: PJSContext): PJSObject;
begin
  Result := this[cx].asObject;
end;

{ jsid }

const
  JSID_TYPE_MASK = $7;

function jsid.isString: Boolean;
begin
  Result := JSIdType(asBits and JSID_TYPE_MASK) = JSID_TYPE_STRING;
end;

function jsid.asJSString: PJSString;
begin
{$ifdef WITHASSERT}
  Assert(isString);
{$endif}
  Result := PJSString(asBits);
end;

{ JSIdArray }

procedure JSIdArray.init(cx: PJSContext);
begin
  _internal.cx := cx;
  _internal.mBegin := @_internal.mStorage;
  _internal.mLength := 0;
  _internal.mCapacity := 1;
end;


{ jsval }

const
{$ifdef CPU64}
  JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET         = JSVAL_TAG_NULL;
  JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET           = JSVAL_TAG_OBJECT;

  JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET      = JSVAL_SHIFTED_TAG_UNDEFINED;
  JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET   = JSVAL_SHIFTED_TAG_OBJECT;
{$else}
  JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET         = JSVAL_TAG_NULL;
  JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET           = JSVAL_TAG_OBJECT;
  JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET              = JSVAL_TAG_INT32;
  JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET             = JSVAL_TAG_STRING;
{$endif}

function jsval.getAsBoolean: Boolean;
begin
  {$ifdef WITHASSERT}
  assert(isBoolean);
  {$endif}
  {$ifdef  CPU64}
  Result := Boolean(_l.asBits and JSVAL_PAYLOAD_MASK)
  {$else}
  Result := Boolean(_l.s.payload.boo);
  {$endif}
end;

function jsval.getAsDate(cx: PJSContext): TDateTime;
var oDate: PJSObject;
{$ifdef CONSIDER_TIME_IN_Z} // as defined in SyNode.inc
    ms: double;
    ms64: Int64;
    fval: jsval;
{$else}
    d, m, Y, h, mn, s, ml: Integer;
    v, fval: jsval;
  function GetIntFuncPropVal(funcName: PWideChar): Integer;
  begin
    Result := 0;
    if oDate.GetUCProperty(cx, pointer(funcName), Length(funcName), fval) then
      if oDate.CallFunctionValue(cx, fval, 0, nil, v)   then
        Result := v.asInteger;
  end;
{$endif}
begin
  oDate := getAsObject;
  if not oDate.isDate(cx) then
    raise ESMException.create('not a DateTime object');
{$ifdef CONSIDER_TIME_IN_Z}
  ms := 0;
  if oDate.CallFunctionName(cx, PCChar('getTime'), 0, nil, fval) then
    ms := fval.asDouble;
  if ms = 0 then
    raise ESMException.Create('JSDateToDateTime: no getTime() in Date object');
  ms64 := Trunc(ms);
  // W/O millisec: Result := IncMilliSecond(UnixDateDelta, ms64);
  Result := UnixMSTimeToDateTime(ms64);
{$else}
  d := GetIntFuncPropVal('getDate');
  m := GetIntFuncPropVal('getMonth') + 1; //WTF months start from 0
  Y := GetIntFuncPropVal('getFullYear');
  h := GetIntFuncPropVal('getHours');
  mn := GetIntFuncPropVal('getMinutes');
  s := GetIntFuncPropVal('getSeconds');
  ml := GetIntFuncPropVal('getMilliseconds');
  Result := EncodeDateTime(Y, m, d, h, mn, s, ml);
{$endif}
end;

function jsval.getAsDouble: Double;
begin
  {$ifdef WITHASSERT}
  assert(isDouble);
  {$endif}
  Result := _l.asDouble;
end;

function jsval.getAsInteger: Integer;
begin
  {$ifdef WITHASSERT}
  assert(isInteger);
  {$endif}
  {$ifdef CPU64}
  Result := int32(_l.asBits);
  {$else}
  Result := _l.s.payload.i32;
  {$endif}
end;

function writeCallback(const buf: PCChar16; len: uint32; data: pointer): Boolean; cdecl;
begin
  TTextWriter(data).AddNoJSONEscapeW(pointer(buf),len);
  result := true;
end;

procedure jsval.AddJSON(cx: PJSContext; W: TTextWriter);
var
//  voidVal: jsval;
  T: JSType;
begin
  if @self=nil then
    W.AddShort('null')
  else begin
    T := cx.TypeOfValue(self);
    case T of
      JSTYPE_VOID,
      JSTYPE_NULL:
        W.AddShort('null');
      JSTYPE_STRING:
        asJSString.ToJSONString(cx,W);
      JSTYPE_NUMBER:
        if isInteger then
          W.Add(asInteger) else
          W.AddDouble(asDouble);
      JSTYPE_BOOLEAN:
        W.Add(asBoolean);
      JSTYPE_OBJECT,
      JSTYPE_FUNCTION: begin
        if not Stringify(cx, nullObj, JSVAL_VOID, writeCallback, pointer(W)) then
          ;
      end
      else
        raise ESMException.CreateFmt('Unhandled AddJSON(%d)',[ord(T)]);
    end;
  end;
end;

function jsval.getAsJson(cx: PJSContext): RawUTF8;
var W: TJSONWriter;
begin
  W := TJSONWriter.CreateOwnedStream;
  try
    AddJSON(cx,W);
    W.SetText(result);
  finally
    W.Free;
  end;
end;

function jsval.getAsInt64: Int64;
begin
  if isInteger then
    result := asInteger else
  {$ifdef WITHASSERT}
  if not isDouble then
    raise ESMException.Create('jsval.getAsInt64!') else
  {$endif}
    result := trunc(asDouble);
end;

function jsval.getAsObject: PJSObject;
{$ifdef  CPU64}
var  ptrBits: UInt64 absolute Result;
{$endif}
begin
  {$ifdef WITHASSERT}
  assert(isObject);
  {$endif}
  {$ifdef CPU64}
  ptrBits := _l.asBits and JSVAL_PAYLOAD_MASK;
  {$ifdef WITHASSERT}
  assert(ptrBits and 7 = 0);
  {$endif}
  {$else}
  Result := _l.s.payload.obj;
  {$endif}
end;

function jsval.getIsBoolean: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits shr JSVAL_TAG_SHIFT = JSVAL_TAG_BOOLEAN;
  {$else}
  Result := _l.s.tag = JSVAL_TAG_BOOLEAN;
  {$endif}
end;

function jsval.getIsDouble: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits <= JSVAL_SHIFTED_TAG_MAX_DOUBLE
  {$else}
  Result := UInt32(_l.s.tag) <= UInt32(JSVAL_TAG_CLEAR)
  {$endif}
end;

function jsval.getIsInteger: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits shr JSVAL_TAG_SHIFT = JSVAL_TAG_INT32;
  {$else}
  Result := _l.s.tag = JSVAL_TAG_INT32;
  {$endif}
end;

function jsval.getIsNull: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits  = JSVAL_SHIFTED_TAG_NULL;
  {$else}
  Result := _l.s.tag = JSVAL_TAG_NULL;
  {$endif}
end;

function jsval.getIsNumber: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits  < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET;
  {$else}
  {$ifdef WITHASSERT}
  assert(_l.s.tag <> JSVAL_TAG_CLEAR);
  {$endif}
  Result := (UInt32(_l.s.tag) <= UInt32(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET));
  {$endif}
end;

function jsval.getIsObject: Boolean;
begin
  {$ifdef CPU64}
  {$ifdef WITHASSERT}
  Assert(_l.asBits shr JSVAL_TAG_SHIFT <= JSVAL_TAG_OBJECT);
  {$endif}
  Result := _l.asBits >= JSVAL_SHIFTED_TAG_OBJECT;
  {$else}
  {$ifdef WITHASSERT}
  Assert(_l.s.tag <= JSVAL_TAG_OBJECT);
  {$endif}
  Result := (UInt32(_l.s.tag) >= UInt32(JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET));
  {$endif}
end;

function jsval.getIsSimpleVariant(cx: PJSContext): Boolean;
var
  t: JSType;
begin
  t := ValType(cx);
  Result := (t = JSTYPE_VOID) or (t = JSTYPE_NULL) or (t = JSTYPE_STRING)
      or (t = JSTYPE_NUMBER) or (t = JSTYPE_BOOLEAN);
end;

function jsval.getIsString: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits shr JSVAL_TAG_SHIFT = JSVAL_TAG_STRING;
  {$else}
  Result := _l.s.tag = JSVAL_TAG_STRING;
  {$endif}
end;

function jsval.getIsVoid: Boolean;
begin
  {$ifdef CPU64}
  Result := _l.asBits = JSVAL_SHIFTED_TAG_UNDEFINED;
  {$else}
  Result := _l.s.tag = JSVAL_TAG_UNDEFINED;
  {$endif}
end;

function jsval.getJSString: PJSString;
begin
  {$ifdef CPU64}
  Result := PJSString(_l.asBits and JSVAL_PAYLOAD_MASK);
  {$else}
  {$ifdef WITHASSERT}
  Assert(isString);
  {$endif}
  Result := _l.s.payload.str;
  {$endif}
end;

function jsval.getPrivate: Pointer;
begin
  {$ifdef CPU64}
  {$ifdef WITHASSERT}
  Assert(_l.asBits and $8000000000000000 = 0);
  {$endif}
  Result := Pointer(_l.asBits shl 1);
  {$else}
  {$ifdef WITHASSERT}
  Assert(isDouble);
  {$endif}
  Result := _l.s.payload.ptr;
  {$endif}
end;

function jsval.getSimpleVariant(cx: PJSContext): Variant;
var
  t: JSType;
begin
  t := ValType(cx);
  case t of
    JSTYPE_VOID:
      VarClear(result);
    JSTYPE_NULL:
      SetVariantNull(result);
//    JSTYPE_OBJECT:
// todo
    JSTYPE_STRING:
       getJSString.ToVariant(cx,result);
    JSTYPE_NUMBER:
      if getIsInteger then
        result := getAsInteger else
        result := getAsDouble;
    JSTYPE_BOOLEAN:
      result := getAsBoolean;
//    JSTYPE_FUNCTION:
// todo
    else
      raise ESMException.CreateFmt('Unhandled ToVariant(%d)',[ord(t)]);
  end;

end;

function jsval.IsMagic: Boolean;
begin
  {$IFDEF CPU64}
  Result := _l.asBits shr JSVAL_TAG_SHIFT = JSVAL_TAG_MAGIC;
  {$ELSE}
  Result := _l.s.tag = JSVAL_TAG_MAGIC;
  {$ENDIF}
end;

function jsval.IsPrimitive: Boolean;
begin
  {$IFDEF CPU64}
  Result := _l.asBits < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET;
  {$ELSE}
  Result := (UInt32(_l.s.tag) < UInt32(JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET));
  {$ENDIF}
end;

function doubleIsInt(Value: Double):boolean; {$ifdef HASINLINE}inline;{$endif}
var Value_int64:Int64 absolute Value;
    len: Int16;
begin
  len := (Value_int64 shr 52) and $7FF - $3FF;
  result := ((len >= 0) and (len < 31) and ((Value_int64 and (QWORD(1) shl (52 - len)-1)) = 0))
  or (Value_int64 = 0) or (Value_int64 = $C1E0000000000000);
end;

function doubleToInt(Value: Double):Integer; {$ifdef HASINLINE}inline;{$endif}
var Value_int64:Int64 absolute Value;
    len: Smallint;
begin
  if Value_int64 = 0 then
    result := 0
  else begin
    len := (Value_int64 shr 52) and $7FF - $3FF;
    Result := (Value_int64 and $000FFFFFFFFFFFFF or $0010000000000000) shr (52-len);
    if (Value_int64 and $8000000000000000)<>0 then
      Result := -Result;
  end
end;

procedure jsval.setAsBoolean(const Value: Boolean);
begin
  {$ifdef CPU64}
  _l.asBits := uint64(ord(Value)) or JSVAL_SHIFTED_TAG_BOOLEAN;
  {$else}
  _l.s.tag := JSVAL_TAG_BOOLEAN;
  _l.s.payload.boo := ord(Value);
  {$endif}
end;

procedure jsval.setAsDate(cx: PJSContext; const Value: TDateTime);
var dmsec: double;
    unixTime: Int64;
  {$ifdef CONSIDER_TIME_IN_Z} // as defined in SyNode.inc
    oDate: PJSObject;
{$else}
    // this realisation is buggy - it ignores timezone rules change history
    // for server-side realisation the best solution is to use GMT time here
    ms: Word;
    STLocal, STUtc: TSystemTime;
    TZ: TTimeZoneInformation;
    AUTCDateTime: TDateTime;
{$endif}
begin
{$ifdef CONSIDER_TIME_IN_Z}
  unixTime := DateTimeToUnixMSTime(Value);
  dmsec := unixTime-(unixTime mod 1000);
  oDate := cx.NewDateObjectMsec(dmsec);
  if not oDate.IsDate(cx) then
    raise ESMException.CreateFmt('SetDateTime(%g): not a valid date',[Value]);
  setAsObject(oDate);
{$else}
  DateTimeToSystemTime(Value, STLocal);
  GetTimeZoneInformation(TZ);
  // use TzSpecificLocalTimeToSystemTime?
  TZ.Bias := -TZ.Bias;
  TZ.StandardBias := -TZ.StandardBias;
  TZ.DaylightBias := -TZ.DaylightBias;
  SystemTimeToTzSpecificLocalTime(@TZ, STLocal, STUtc);
  ms := STUtc.wMilliseconds;
  AUTCDateTime := SystemTimeToDateTime(STUtc);
  dmSec := DateTimeToUnixMSTime(AUTCDateTime) + ms;
  setAsObject(cx.NewDateObjectMsec(dmsec));
{$endif}
end;

procedure jsval.setAsDouble(const Value: Double);
begin
  if doubleIsInt(Value) then
    asInteger := doubleToInt(Value)
  else begin
    _l.asDouble := Value;
    if ((_l.asBits and $7FF0000000000000) = $7FF0000000000000) and
       ((_l.asBits and $000FFFFFFFFFFFFF) <> $0000000000000000) then
      _l.asBits := JSVAL_NAN_impl; // canonize NaN
  end;
end;

procedure jsval.setAsInteger(const Value: Integer);

begin
  {$ifdef CPU64}
  _l.asBits := Value;
  _l.asBits := uint32(Value) or JSVAL_SHIFTED_TAG_INT32;
  {$else}
  _l.s.tag := JSVAL_TAG_INT32;
  _l.s.payload.i32 := Value;
  {$endif}
end;

procedure jsval.setAsJson(cx: PJSContext; const Value: RawUTF8);
var tmp: RawUnicode;
    len: integer;
begin
  if Value='' then begin
    SetVoid;
  end else begin
    len := Utf8DecodeToRawUnicodeUI(Value,tmp);
    if not JS_ParseJSON(cx, pointer(tmp), len shr 1, self) then
    {$ifdef WITHASSERT}
      raise ESMException.Create('jsval.setAsJson!') else
    {$ELSE}
      SetVoid;
    {$endif}
  end;
end;

procedure jsval.setAsInt64(const Value: Int64);
begin
  if (Value>=Low(integer)) and (Value<=High(integer)) then
    setAsInteger(Value) else
    setAsDouble(Value);
end;

procedure jsval.setAsObject(const Value: PJSObject);
{$ifdef CPU64}
var objBits: UInt64 absolute Value;
{$endif}
begin
  {$ifdef CPU64}
  {$ifdef WITHASSERT}
  Assert(objBits shr JSVAL_TAG_SHIFT = 0);
  {$endif}
  _l.asBits := QWord(objBits or JSVAL_SHIFTED_TAG_OBJECT);
  {$else}
  if Value<>nil then begin
    _l.s.tag := JSVAL_TAG_OBJECT;
    _l.s.payload.obj := Value;
  end else
    _l.asBits := JSVAL_NULL_impl;
  {$endif}
end;

procedure jsval.setJSString(const Value: PJSString);
{$ifdef CPU64}
var strBits: UInt64 absolute Value;
{$endif}
begin
  {$ifdef CPU64}
  {$ifdef WITHASSERT}
  Assert(strBits shr JSVAL_TAG_SHIFT = 0);
  {$endif}
  _l.asBits := strBits or JSVAL_SHIFTED_TAG_STRING;
  {$else}
  {$ifdef WITHASSERT}
  assert(str<>nil) ;
  {$endif}
  _l.s.tag := JSVAL_TAG_STRING;
  _l.s.payload.str := Value;
  {$endif}
end;

procedure jsval.setNull;
begin
  _l.asBits := JSVAL_NULL_impl;
end;

procedure jsval.setPrivate(const Value: Pointer);
{$ifdef CPU64}
var ptrBits: UInt64 absolute Value;
{$endif}
begin
  {$ifdef CPU64}
  {$ifdef WITHASSERT}
  Assert(ptrBits and 1 = 0);
  {$endif}
  _l.asBits := ptrBits shr 1;
  {$ifdef WITHASSERT}
  assert(isDouble);
  {$endif}
  {$else}
  {$ifdef WITHASSERT}
  assert((uint32(ptr) and 1) = 0);
  {$endif}
  _l.s.tag := JSValueTag(0);
  _l.s.payload.ptr := Value;
  {$ifdef WITHASSERT}
  assert(isDouble);
  {$endif}
  {$endif}
end;

procedure jsval.setSimpleVariant(cx: PJSContext; const Value: Variant);
begin
  with TVarData(Value) do
  case VType of
    varNull: setNull;
    varEmpty: setVoid;
    varBoolean: setAsBoolean(VBoolean);
    varSmallint: setAsInteger(VSmallInt);
    {$ifndef DELPHI5OROLDER}
    varShortInt: setAsInteger(VShortInt);
    varWord: setAsInteger(VWord);
    varLongWord: setAsInt64(VLongWord);
    {$endif}
    varByte: setAsInteger(VByte);
    varInteger: setAsInteger(VInteger);
    {$ifdef FPC}varQword,{$endif}
    varInt64: setAsInt64(VInt64);

    varSingle: setAsDouble(VSingle);
    varDouble: setAsDouble(VDouble);
    varCurrency: setAsDouble(VCurrency);
    varDate: setAsDate(cx, VDate);
    varOleStr: setJSString(cx.NewJSString(WideString(VAny)));
    varString: setJSString(cx.NewJSString(VAny,length(RawByteString(VAny)),
    {$ifndef UNICODE}   CP_UTF8));
    {$else}             StringCodePage(RawByteString(VAny))));
    varUString: setJSString(cx.NewJSString(UnicodeString(VAny)));
    {$endif}
  else
    if VType=varByRef or varVariant then
      setSimpleVariant(cx,PVariant(VPointer)^) else
    if VType=varByRef or varOleStr then
      setJSString(cx.NewJSString(PWideString(VAny)^)) else
    {$ifdef UNICODE}
    if VType=varByRef or varUString then
      setJSString(cx.NewJSString(PUnicodeString(VAny)^)) else
    {$endif}
      raise ESMException.CreateFmt('Unhandled variant type %d',[VType]);
  end;
end;

procedure jsval.setVoid;
begin
  _l.asBits := JSVAL_VOID_impl;
end;

function jsval.Stringify(cx: PJSContext; var replacer: PJSObject;
  space: jsval; callback: JSONWriteCallback; data: pointer): Boolean;
begin
  with TSynFPUException.ForLibraryCode do
    Result := JS_Stringify(cx, Self, replacer, space, callback, data);
end;

function jsval.toSource(cx: PJSContext): PJSString;
begin
  Result := JS_ValueToSource(cx, self);
end;

function jsval.ValType(cx: PJSContext): JSType;
begin
  Result := cx.TypeOfValue(self);
end;

function SimpleVariantToJSval(cx: PJSContext; val: Variant): jsval;
begin
  Result.asSimpleVariant[cx] := val;
end;

initialization
  Latin1AnsiConvert := TSynAnsiConvert.Engine(CODEPAGE_LATIN1);
end.

