/* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */ /* vim: set ts=40 sw=4 et tw=99: */ /* ***** 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 Original Code is the Mozilla SpiderMonkey bytecode type inference * * The Initial Developer of the Original Code is * Mozilla Foundation * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brian Hackett * * Alternatively, the contents of this file may be used under the terms of * either of 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 ***** */ /* Definitions related to javascript type inference. */ #ifndef jsinfer_h___ #define jsinfer_h___ #include "jsarena.h" #include "jstl.h" #include "jsprvtd.h" #ifndef _MSC_VER #include #endif /* Define to get detailed output of inference actions. */ namespace js { namespace analyze { struct Bytecode; class Script; } } namespace js { namespace types { /* Forward declarations. */ struct TypeSet; struct VariableSet; struct TypeCallsite; struct TypeObject; struct TypeFunction; struct TypeCompartment; /* * Information about a single concrete type. This is a non-zero value whose * lower 3 bits indicate a particular primitive type below, and if those bits * are zero then a pointer to a type object. */ typedef jsword jstype; /* The primitive types. */ const jstype TYPE_UNDEFINED = 1; const jstype TYPE_NULL = 2; const jstype TYPE_BOOLEAN = 3; const jstype TYPE_INT32 = 4; const jstype TYPE_DOUBLE = 5; const jstype TYPE_STRING = 6; /* * Aggregate unknown type, could be anything. Typically used when a type set * becomes polymorphic, or when accessing an object with unknown properties. */ const jstype TYPE_UNKNOWN = 7; /* Coarse flags for the type of a value. */ enum { TYPE_FLAG_UNDEFINED = 1 << TYPE_UNDEFINED, TYPE_FLAG_NULL = 1 << TYPE_NULL, TYPE_FLAG_BOOLEAN = 1 << TYPE_BOOLEAN, TYPE_FLAG_INT32 = 1 << TYPE_INT32, TYPE_FLAG_DOUBLE = 1 << TYPE_DOUBLE, TYPE_FLAG_STRING = 1 << TYPE_STRING, TYPE_FLAG_UNKNOWN = 1 << TYPE_UNKNOWN, TYPE_FLAG_OBJECT = 0x1000 }; /* Vector of the above flags. */ typedef uint32 TypeFlags; /* * Test whether a type is an primitive or an object. Object types can be * cast into a TypeObject*. */ static inline bool TypeIsPrimitive(jstype type) { JS_ASSERT(type && type != TYPE_UNKNOWN); return type < TYPE_UNKNOWN; } static inline bool TypeIsObject(jstype type) { JS_ASSERT(type && type != TYPE_UNKNOWN); return type > TYPE_UNKNOWN; } /* Get the type of a jsval, or zero for an unknown special value. */ inline jstype GetValueType(JSContext *cx, const Value &val); /* * A constraint which listens to additions to a type set and propagates those * changes to other type sets. */ class TypeConstraint { public: #ifdef DEBUG static unsigned constraintCount; unsigned id_; const char *kind_; unsigned id() const { return id_; } const char *kind() const { return kind_; } #else unsigned id() const { return 0; } const char *kind() const { return NULL; } #endif /* Next constraint listening to the same type set. */ TypeConstraint *next; TypeConstraint(const char *kind) : next(NULL) { #ifdef DEBUG this->id_ = ++constraintCount; this->kind_ = kind; #endif } /* Register a new type for the set this constraint is listening to. */ virtual void newType(JSContext *cx, TypeSet *source, jstype type) = 0; /* * Mark the object containing the set this constraint is listening to * as not a packed array and, possibly, not a dense array. */ virtual void arrayNotPacked(JSContext *cx, bool notDense) {} }; /* * Coarse kinds of a set of objects. These form the following lattice: * * NONE * ___________ / | \______________ * / | \ * PACKED_ARRAY SCRIPTED_FUNCTION NATIVE_FUNCTION * | | | * DENSE_ARRAY | | * \____________ | _____________/ * \ | / * UNKNOWN */ enum ObjectKind { OBJECT_NONE, OBJECT_UNKNOWN, OBJECT_PACKED_ARRAY, OBJECT_DENSE_ARRAY, OBJECT_SCRIPTED_FUNCTION, OBJECT_NATIVE_FUNCTION }; /* Information about the set of types associated with an lvalue. */ struct TypeSet { #ifdef DEBUG static unsigned typesetCount; unsigned id_; /* Pool containing this type set. All constraints must also be in this pool. */ JSArenaPool *pool; unsigned id() const { return id_; } #else unsigned id() const { return 0; } #endif /* Flags for the possible coarse types in this set. */ TypeFlags typeFlags; /* If TYPE_FLAG_OBJECT, the possible objects this this type can represent. */ TypeObject **objectSet; unsigned objectCount; /* Chain of constraints which propagate changes out from this type set. */ TypeConstraint *constraintList; TypeSet(JSArenaPool *pool) : typeFlags(0), objectSet(NULL), objectCount(0), constraintList(NULL) { setPool(pool); } void setPool(JSArenaPool *pool) { #ifdef DEBUG this->id_ = ++typesetCount; this->pool = pool; #endif } void print(JSContext *cx); /* Whether this set contains a specific type. */ inline bool hasType(jstype type); /* * Add a type to this set, calling any constraint handlers if this is a new * possible type. */ inline void addType(JSContext *cx, jstype type); /* Add specific kinds of constraints to this set. */ inline void add(JSContext *cx, TypeConstraint *constraint, bool callExisting = true); void addSubset(JSContext *cx, JSArenaPool &pool, TypeSet *target); void addGetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id); void addSetProperty(JSContext *cx, analyze::Bytecode *code, TypeSet *target, jsid id); void addGetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target); void addSetElem(JSContext *cx, analyze::Bytecode *code, TypeSet *object, TypeSet *target); void addCall(JSContext *cx, TypeCallsite *site); void addArith(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target, TypeSet *other = NULL); void addTransformThis(JSContext *cx, JSArenaPool &pool, TypeSet *target); void addFilterPrimitives(JSContext *cx, JSArenaPool &pool, TypeSet *target, bool onlyNullVoid); void addMonitorRead(JSContext *cx, JSArenaPool &pool, analyze::Bytecode *code, TypeSet *target); /* * Make an intermediate type set with the specified debugging name, * not embedded in another structure. */ static inline TypeSet* make(JSContext *cx, JSArenaPool &pool, const char *name); /* Methods for JIT compilation. */ /* * Get any type tag which all values in this set must have. Should this type * set change in the future so that another type tag is possible, mark script * for recompilation. */ JSValueType getKnownTypeTag(JSContext *cx, JSScript *script); /* Get information about the kinds of objects in this type set. */ ObjectKind getKnownObjectKind(JSContext *cx, JSScript *script); /* Get whether this type set contains any scripted getter or setter. */ bool hasGetterSetter(JSContext *cx, JSScript *script); }; /* * Type information for a value pushed onto the stack at some execution point. * Stack nodes form equivalence classes: if at any time two stack nodes might * be at the same depth in the stack, they are considered equivalent. */ struct TypeStack { /* * Unique node for the equivalence class of this stack node, NULL if this * is the class node itself. These are collected as a union find structure. * If non-NULL the remainder of this structure is empty. */ TypeStack *mergedGroup; /* Identifier for this class within the script. filled in during printing. */ int id; /* Number of nodes beneath this one in the stack. */ unsigned stackDepth; /* Equivalence class for the node beneath this one in the stack. */ TypeStack *innerStack; /* Possible types for values at this stack node. */ TypeSet types; /* Whether any other stack nodes have been merged into this one. */ bool hasMerged; /* Whether the values at this node are bound by a 'with'. */ bool boundWith; /* Whether this node is the iterator for a 'for each' or 'for in' loop. */ bool isForEach; /* * Whether to ignore the type tag of this stack entry downstream; it may not * represent the actual values in this slot. */ bool ignoreTypeTag; /* The name of any 'let' variable stored by this node. */ jsid letVariable; /* Variable set for any scope name binding pushed on this stack node. */ VariableSet *scopeVars; /* Get the representative node for the equivalence class of this node. */ inline TypeStack* group(); /* Set the inner stack of this node. */ inline void setInnerStack(TypeStack *inner); /* Merge the equivalence classes for two stack nodes together. */ static void merge(JSContext *cx, TypeStack *one, TypeStack *two); }; /* * Type information about a callsite. this is separated from the bytecode * information itself so we can handle higher order functions not called * directly via a bytecode. */ struct TypeCallsite { /* Bytecode this call came from. */ analyze::Bytecode *code; /* Whether the bytecode is a 'NEW' operator. */ bool isNew; /* Types of particular arguments to the call. */ TypeSet **argumentTypes; unsigned argumentCount; /* Types of the this variable. */ TypeSet *thisTypes; /* Any definite type for 'this'. */ jstype thisType; /* Type set receiving the return value of this call. pushed by code. */ TypeSet *returnTypes; inline TypeCallsite(analyze::Bytecode *code, bool isNew, unsigned argumentCount); /* Force creation of thisTypes or returnTypes. */ inline void forceThisTypes(JSContext *cx); inline void forceReturnTypes(JSContext *cx); /* Get the new object at this callsite, per Bytecode::getInitObject. */ inline TypeObject* getInitObject(JSContext *cx, bool isArray); /* Pool which handlers on this call site should use. */ inline JSArenaPool & pool(); }; /* Type information about a variable or property. */ struct Variable { /* * Identifier for this variable. Variables representing the aggregate index * property of an object have JSID_VOID, other variables have strings. */ jsid id; /* Next variable in its declared scope. */ Variable *next; /* * Possible types for this variable. This does not account for the initial * undefined value of the variable, though if the variable is explicitly * assigned a possibly-undefined value then this set will contain that type. */ TypeSet types; Variable(JSArenaPool *pool, jsid id) : id(id), next(NULL), types(pool) {} }; /* Type information about a set of variables or properties. */ struct VariableSet { #ifdef DEBUG jsid name_; jsid name() const { return name_; } #else jsid name() const { return JSID_VOID; } #endif /* List of variables in this set which have been accessed explicitly. */ Variable *variables; /* * Other variable sets which should receive all variables added to this set. * For handling prototypes. */ VariableSet **propagateSet; unsigned propagateCount; JSArenaPool *pool; VariableSet(JSArenaPool *pool) : variables(NULL), propagateSet(NULL), propagateCount(NULL), pool(pool) { JS_ASSERT(pool); } /* Get or make the types for the specified id. */ inline TypeSet* getVariable(JSContext *cx, jsid id); /* * Mark target as receiving all variables and type information added to this * set (whether it is currently there or will be added in the future). * If excludePrototype is set the 'prototype' variable is omitted from the * propagation. Returns whether there was already a propagation to target. */ bool addPropagate(JSContext *cx, VariableSet *target, bool excludePrototype); void print(JSContext *cx); }; /* Type information about an object accessed by a script. */ struct TypeObject { /* * Name of this object. This is unique among all objects in the compartment; * if someone tries to make an object with the same name, they will get back * this object instead. */ jsid name; /* Whether this is a function object, and may be cast into TypeFunction. */ bool isFunction; /* * Whether all reads from this object need to be monitored. This includes * all property and element accesses, and for functions all calls to the function. */ bool monitored; /* * Properties of this object. This is filled in lazily for function objects * to avoid unnecessary property and prototype object creation. Don't access * this directly, use properties() below. */ VariableSet propertySet; bool propertiesFilled; /* Link in the list of objects in the property set's pool. */ TypeObject *next; /* * Whether all properties of the Object and/or Array prototype have been * propagated into this object. */ bool hasObjectPropagation; bool hasArrayPropagation; /* * Whether this object is keyed to some allocation site. If a type set only * contains objects allocated at different sites, it is not considered * to be polymorphic. */ bool isInitObject; /* Whether all objects this represents are dense arrays. */ bool isDenseArray; /* Whether all objects this represents are packed arrays (implies isDenseArray). */ bool isPackedArray; /* Make an object with the specified name. */ TypeObject(JSContext *cx, JSArenaPool *pool, jsid id, bool isArray); /* Propagate properties from this object to target. */ bool addPropagate(JSContext *cx, TypeObject *target, bool excludePrototype = true); /* Coerce this object to a function. */ TypeFunction* asFunction() { JS_ASSERT(isFunction); return (TypeFunction *) this; } JSArenaPool & pool() { return *propertySet.pool; } /* Get the properties of this object, filled in lazily. */ inline VariableSet& properties(JSContext *cx); /* Get the type set for all integer index properties of this object. */ inline TypeSet* indexTypes(JSContext *cx); void print(JSContext *cx); }; /* Type information about an interpreted or native function. */ struct TypeFunction : public TypeObject { /* If this function is native, the handler to use at calls to it. */ JSTypeHandler handler; /* If this function is interpreted, the corresponding script. */ JSScript *script; /* * The default prototype object of this function. This may be overridden * for user-defined functions. Created on demand through prototype(). */ TypeObject *prototypeObject; /* The object to use when this function is invoked using 'new'. */ TypeObject *newObject; /* * For interpreted functions and functions with dynamic handlers, the possible * return types of the function. */ TypeSet returnTypes; /* * Whether this is the constructor for a builtin class, whose prototype must * be specified manually. */ bool isBuiltin; /* * Whether this is a generic native handler, and treats its first parameter * the way it normally would its 'this' variable, e.g. Array.reverse(arr) * instead of arr.reverse(). */ bool isGeneric; TypeFunction(JSContext *cx, JSArenaPool *pool, jsid id); /* Get the object created when this function is invoked using 'new'. */ inline TypeObject* getNewObject(JSContext *cx); /* Get the prototype object for this function. */ TypeObject* prototype(JSContext *cx) { /* The prototype is created when the properties are filled in. */ properties(cx); return prototypeObject; } void fillProperties(JSContext *cx); }; inline VariableSet& TypeObject::properties(JSContext *cx) { if (!propertiesFilled) { propertiesFilled = true; if (isFunction) asFunction()->fillProperties(cx); } return propertySet; } /* * Singleton type objects referred to at various points in the system. * At most one of these will exist for each compartment, though many JSObjects * may use them for type information. */ enum FixedTypeObjectName { /* Functions which no propagation is performed for. */ TYPE_OBJECT_OBJECT, TYPE_OBJECT_FUNCTION, TYPE_OBJECT_ARRAY, TYPE_OBJECT_FUNCTION_PROTOTYPE, TYPE_OBJECT_EMPTY_FUNCTION, /* Propagated from Function.prototype */ TYPE_OBJECT_FUNCTION_LAST = TYPE_OBJECT_EMPTY_FUNCTION, /* Objects which no propagation is performed for. */ TYPE_OBJECT_OBJECT_PROTOTYPE, TYPE_OBJECT_ARRAY_PROTOTYPE, TYPE_OBJECT_NEW_BOOLEAN, TYPE_OBJECT_NEW_DATE, TYPE_OBJECT_NEW_ERROR, TYPE_OBJECT_NEW_ITERATOR, TYPE_OBJECT_NEW_NUMBER, TYPE_OBJECT_NEW_STRING, TYPE_OBJECT_NEW_PROXY, TYPE_OBJECT_NEW_REGEXP, TYPE_OBJECT_NEW_ARRAYBUFFER, TYPE_OBJECT_NEW_INT8ARRAY, TYPE_OBJECT_NEW_UINT8ARRAY, TYPE_OBJECT_NEW_INT16ARRAY, TYPE_OBJECT_NEW_UINT16ARRAY, TYPE_OBJECT_NEW_INT32ARRAY, TYPE_OBJECT_NEW_UINT32ARRAY, TYPE_OBJECT_NEW_FLOAT32ARRAY, TYPE_OBJECT_NEW_FLOAT64ARRAY, TYPE_OBJECT_NEW_UINT8CLAMPEDARRAY, TYPE_OBJECT_MAGIC, /* Placeholder for magic values. */ TYPE_OBJECT_GETSET, /* Placeholder for properties with a scripted getter/setter. */ TYPE_OBJECT_BASE_LAST = TYPE_OBJECT_GETSET, /* Objects which no propagation is performed for, and which are monitored. */ TYPE_OBJECT_NEW_XML, TYPE_OBJECT_NEW_QNAME, TYPE_OBJECT_NEW_NAMESPACE, TYPE_OBJECT_ARGUMENTS, TYPE_OBJECT_NOSUCHMETHOD, TYPE_OBJECT_NOSUCHMETHOD_ARGUMENTS, TYPE_OBJECT_PROPERTY_DESCRIPTOR, TYPE_OBJECT_KEY_VALUE_PAIR, TYPE_OBJECT_MONITOR_LAST = TYPE_OBJECT_KEY_VALUE_PAIR, /* Objects which Array.prototype propagation is performed for. */ TYPE_OBJECT_REGEXP_MATCH_ARRAY, TYPE_OBJECT_STRING_SPLIT_ARRAY, TYPE_OBJECT_UNKNOWN_ARRAY, TYPE_OBJECT_CLONE_ARRAY, TYPE_OBJECT_PROPERTY_ARRAY, TYPE_OBJECT_NAMESPACE_ARRAY, TYPE_OBJECT_JSON_ARRAY, TYPE_OBJECT_REFLECT_ARRAY, TYPE_OBJECT_ARRAY_LAST = TYPE_OBJECT_REFLECT_ARRAY, /* Objects which Object.prototype propagation is performed for. */ TYPE_OBJECT_UNKNOWN_OBJECT, TYPE_OBJECT_CLONE_OBJECT, TYPE_OBJECT_JSON_STRINGIFY, TYPE_OBJECT_JSON_REVIVE, TYPE_OBJECT_JSON_OBJECT, TYPE_OBJECT_REFLECT_OBJECT, TYPE_OBJECT_XML_SETTINGS, /* Objects which probably can't escape to scripts. Maybe condense these. */ TYPE_OBJECT_REGEXP_STATICS, TYPE_OBJECT_CALL, TYPE_OBJECT_DECLENV, TYPE_OBJECT_SHARP_ARRAY, TYPE_OBJECT_WITH, TYPE_OBJECT_BLOCK, TYPE_OBJECT_NULL_CLOSURE, TYPE_OBJECT_PROPERTY_ITERATOR, TYPE_OBJECT_SCRIPT, TYPE_OBJECT_FIXED_LIMIT }; extern const char* const fixedTypeObjectNames[]; /* Type information for a compartment. */ struct TypeCompartment { /* * Pool for compartment-wide objects and their variables and constraints. * These aren't collected until the compartment is destroyed. */ JSArenaPool pool; TypeObject *objects; TypeObject *fixedTypeObjects[TYPE_OBJECT_FIXED_LIMIT]; /* Number of scripts in this compartment. */ unsigned scriptCount; /* Whether the interpreter is currently active (we are not inferring types). */ bool interpreting; /* Object containing all global variables. root of all parent chains. */ TypeObject *globalObject; struct IdHasher { typedef jsid Lookup; static uint32 hashByte(uint32 hash, uint8 byte) { hash = (hash << 4) + byte; uint32 x = hash & 0xF0000000L; if (x) hash ^= (x >> 24); return hash & ~x; } static uint32 hash(jsid id) { /* Do an ELF hash of the lower four bytes of the ID. */ uint32 hash = 0, v = uint32(JSID_BITS(id)); hash = hashByte(hash, v & 0xff); v >>= 8; hash = hashByte(hash, v & 0xff); v >>= 8; hash = hashByte(hash, v & 0xff); v >>= 8; return hashByte(hash, v & 0xff); } static bool match(jsid id0, jsid id1) { return id0 == id1; } }; /* Map from object names to the object. */ typedef HashMap ObjectNameTable; ObjectNameTable *objectNameTable; /* Pending recompilations to perform before execution of JIT code can resume. */ Vector *pendingRecompiles; /* Constraint solving worklist structures. */ /* A type that needs to be registered with a constraint. */ struct PendingWork { TypeConstraint *constraint; TypeSet *source; jstype type; }; /* * Worklist of types which need to be propagated to constraints. We use a * worklist to avoid blowing the native stack. */ PendingWork *pendingArray; unsigned pendingCount; unsigned pendingCapacity; /* Whether we are currently resolving the pending worklist. */ bool resolving; /* Logging fields */ /* * Whether any warnings were emitted. These are nonfatal but (generally) * indicate unhandled constructs leading to analysis unsoundness. */ bool warnings; /* * Whether to ignore generated warnings. For handling regressions with * shell functions we don't model. */ bool ignoreWarnings; /* * The total time (in microseconds) spent generating inference structures * and performing analysis. */ uint64_t analysisTime; /* Counts of stack type sets with some number of possible operand types. */ static const unsigned TYPE_COUNT_LIMIT = 4; unsigned typeCounts[TYPE_COUNT_LIMIT]; unsigned typeCountOver; void init(); ~TypeCompartment(); uint64 currentTime() { #ifndef _MSC_VER timeval current; gettimeofday(¤t, NULL); return current.tv_sec * (uint64_t) 1000000 + current.tv_usec; #else /* Timing not available on Windows. */ return 0; #endif } TypeObject *makeFixedTypeObject(JSContext *cx, FixedTypeObjectName which); /* Add a type to register with a list of constraints. */ inline void addPending(JSContext *cx, TypeConstraint *constraint, TypeSet *source, jstype type); void growPendingArray(); /* Resolve pending type registrations, excluding delayed ones. */ inline void resolvePending(JSContext *cx); /* Prints results of this compartment if spew is enabled, checks for warnings. */ void finish(JSContext *cx, JSCompartment *compartment); /* Get a function or non-function object associated with an optional script. */ TypeObject *getTypeObject(JSContext *cx, js::analyze::Script *script, const char *name, bool isArray, bool isFunction); /* * Add the specified type to the specified set, do any necessary reanalysis * stemming from the change and recompile any affected scripts. */ void addDynamicType(JSContext *cx, TypeSet *types, jstype type); void addDynamicPush(JSContext *cx, analyze::Bytecode &code, unsigned index, jstype type); void dynamicAssign(JSContext *cx, JSObject *obj, jsid id, const Value &rval); inline bool hasPendingRecompiles() { return pendingRecompiles != NULL; } void processPendingRecompiles(JSContext *cx); void addPendingRecompile(JSContext *cx, JSScript *script); /* Monitor future effects on a bytecode. */ void monitorBytecode(JSContext *cx, analyze::Bytecode *code); void trace(JSTracer *trc); }; enum SpewChannel { ISpewDynamic, /* dynamic: Dynamic type changes and inference entry points. */ ISpewOps, /* ops: New constraints and types. */ ISpewResult, /* result: Final type sets. */ SPEW_COUNT }; #ifdef DEBUG /* Spew with INFERFLAGS = full or base */ void InferSpew(SpewChannel which, const char *fmt, ...); void InferSpewType(SpewChannel which, JSContext *cx, jstype type, const char *fmt, ...); #else inline void InferSpew(SpewChannel which, const char *fmt, ...) {} inline void InferSpewType(SpewChannel which, JSContext *cx, jstype type, const char *fmt, ...) {} #endif } /* namespace types */ } /* namespace js */ static JS_ALWAYS_INLINE js::types::TypeObject * Valueify(JSTypeObject *jstype) { return (js::types::TypeObject*) jstype; } static JS_ALWAYS_INLINE js::types::TypeFunction * Valueify(JSTypeFunction *jstype) { return (js::types::TypeFunction*) jstype; } static JS_ALWAYS_INLINE js::types::TypeCallsite * Valueify(JSTypeCallsite *jssite) { return (js::types::TypeCallsite*) jssite; } #endif // jsinfer_h___