qtdeclarative/qmljs_runtime.h

1267 lines
39 KiB
C++

/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the V4VM module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QMLJS_RUNTIME_H
#define QMLJS_RUNTIME_H
#ifndef QMLJS_LLVM_RUNTIME
# include <QtCore/QString>
# include <QtCore/qnumeric.h>
# include <QtCore/QDebug>
#endif
#include <math.h>
#include <cassert>
namespace QQmlJS {
namespace IR {
struct Function;
}
namespace VM {
enum TypeHint {
PREFERREDTYPE_HINT,
NUMBER_HINT,
STRING_HINT
};
enum PropertyAttributes {
NoAttributes = 0,
ValueAttribute = 1,
WritableAttribute = 2,
EnumerableAttribute = 4,
ConfigurableAttribute = 8
};
struct Value;
struct Object;
struct String;
struct Context;
struct FunctionObject;
struct BooleanObject;
struct NumberObject;
struct StringObject;
struct DateObject;
struct ArrayObject;
struct ErrorObject;
struct ActivationObject;
struct ExecutionEngine;
typedef uint Bool;
extern "C" {
// context
Value __qmljs_call_activation_property(Context *, String *name, Value *args, int argc);
Value __qmljs_call_property(Context *context, const Value base, String *name, Value *args, int argc);
Value __qmljs_call_value(Context *context, const Value thisObject, const Value func, Value *args, int argc);
Value __qmljs_construct_activation_property(Context *, String *name, Value *args, int argc);
Value __qmljs_construct_property(Context *context, const Value base, String *name, Value *args, int argc);
Value __qmljs_construct_value(Context *context, const Value func, Value *args, int argc);
Value __qmljs_builtin_typeof(Context *context, Value *args, int argc);
Value __qmljs_builtin_throw(Context *context, Value *args, int argc);
Value __qmljs_builtin_rethrow(Context *context, Value *args, int argc);
// constructors
Value __qmljs_init_string(String *string);
Value __qmljs_init_object(Object *object);
Value __qmljs_init_closure(IR::Function *clos, Context *ctx);
Value __qmljs_init_native_function(void (*code)(Context *), Context *ctx);
Bool __qmljs_is_function(const Value value);
// string literals
Value __qmljs_string_literal_undefined(Context *ctx);
Value __qmljs_string_literal_null(Context *ctx);
Value __qmljs_string_literal_true(Context *ctx);
Value __qmljs_string_literal_false(Context *ctx);
Value __qmljs_string_literal_object(Context *ctx);
Value __qmljs_string_literal_boolean(Context *ctx);
Value __qmljs_string_literal_number(Context *ctx);
Value __qmljs_string_literal_string(Context *ctx);
Value __qmljs_string_literal_function(Context *ctx);
// strings
String *__qmljs_string_from_utf8(Context *ctx, const char *s);
int __qmljs_string_length(Context *ctx, String *string);
double __qmljs_string_to_number(Context *ctx, String *string);
Value __qmljs_string_from_number(Context *ctx, double number);
Bool __qmljs_string_compare(Context *ctx, String *left, String *right);
Bool __qmljs_string_equal(Context *ctx, String *left, String *right);
String *__qmljs_string_concat(Context *ctx, String *first, String *second);
String *__qmljs_identifier_from_utf8(Context *ctx, const char *s);
// objects
Value __qmljs_object_default_value(Context *ctx, const Value object, int typeHint);
Value __qmljs_throw_type_error(Context *ctx);
Value __qmljs_new_object(Context *ctx);
Value __qmljs_new_boolean_object(Context *ctx, bool boolean);
Value __qmljs_new_number_object(Context *ctx, double n);
Value __qmljs_new_string_object(Context *ctx, String *string);
void __qmljs_set_activation_property(Context *ctx, String *name, Value value);
void __qmljs_set_property(Context *ctx, Value object, String *name, Value value);
Value __qmljs_get_property(Context *ctx, Value object, String *name);
Value __qmljs_get_activation_property(Context *ctx, String *name);
Value __qmljs_get_element(Context *ctx, Value object, Value index);
void __qmljs_set_element(Context *ctx, Value object, Value index, Value value);
// context
Value __qmljs_get_thisObject(Context *ctx);
// type conversion and testing
Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint);
Bool __qmljs_to_boolean(const Value value, Context *ctx);
double __qmljs_to_number(const Value value, Context *ctx);
double __qmljs_to_integer(const Value value, Context *ctx);
int __qmljs_to_int32(const Value value, Context *ctx);
unsigned __qmljs_to_uint32(const Value value, Context *ctx);
unsigned short __qmljs_to_uint16(const Value value, Context *ctx);
Value __qmljs_to_string(Context *ctx, const Value value);
Value __qmljs_to_object(Context *ctx, const Value value);
//uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value);
Bool __qmljs_is_callable(Context *ctx, const Value value);
Value __qmljs_default_value(Context *ctx, const Value value, int typeHint);
Value __qmljs_compare(Context *ctx, const Value left, const Value right, bool leftFlag);
Bool __qmljs_equal(Context *ctx, const Value x, const Value y);
Bool __qmljs_strict_equal(Context *ctx, const Value x, const Value y);
// unary operators
Value __qmljs_uplus(const Value value, Context *ctx);
Value __qmljs_uminus(const Value value, Context *ctx);
Value __qmljs_compl(const Value value, Context *ctx);
Value __qmljs_not(const Value value, Context *ctx);
/* ### these 4 methods are apparently unused right now */
Value __qmljs_delete_subscript(Context *ctx, Value base, Value index);
Value __qmljs_delete_member(Context *ctx, Value base, String *name);
Value __qmljs_delete_property(Context *ctx, String *name);
Value __qmljs_delete_value(Context *ctx, Value value);
Value __qmljs_typeof(Context *ctx, const Value value);
void __qmljs_throw(Context *context, Value value);
Value __qmljs_rethrow(Context *context);
// binary operators
Value __qmljs_instanceof(const Value left, const Value right, Context *ctx);
Value __qmljs_in(const Value left, const Value right, Context *ctx);
Value __qmljs_bit_or(const Value left, const Value right, Context *ctx);
Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx);
Value __qmljs_bit_and(const Value left, const Value right, Context *ctx);
Value __qmljs_add(const Value left, const Value right, Context *ctx);
Value __qmljs_sub(const Value left, const Value right, Context *ctx);
Value __qmljs_mul(const Value left, const Value right, Context *ctx);
Value __qmljs_div(const Value left, const Value right, Context *ctx);
Value __qmljs_mod(const Value left, const Value right, Context *ctx);
Value __qmljs_shl(const Value left, const Value right, Context *ctx);
Value __qmljs_shr(const Value left, const Value right, Context *ctx);
Value __qmljs_ushr(const Value left, const Value right, Context *ctx);
Value __qmljs_gt(const Value left, const Value right, Context *ctx);
Value __qmljs_lt(const Value left, const Value right, Context *ctx);
Value __qmljs_ge(const Value left, const Value right, Context *ctx);
Value __qmljs_le(const Value left, const Value right, Context *ctx);
Value __qmljs_eq(const Value left, const Value right, Context *ctx);
Value __qmljs_ne(const Value left, const Value right, Context *ctx);
Value __qmljs_se(const Value left, const Value right, Context *ctx);
Value __qmljs_sne(const Value left, const Value right, Context *ctx);
Value __qmljs_add_helper(const Value left, const Value right, Context *ctx);
void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_add(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_div(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value);
void __qmljs_inplace_bit_and_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_bit_or_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_bit_xor_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_add_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_sub_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_mul_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_div_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_mod_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_shl_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_shr_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_ushr_name(Context *ctx, String *name, Value *value);
void __qmljs_inplace_bit_and_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_bit_or_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_bit_xor_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_add_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_sub_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_mul_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_div_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_mod_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_shl_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_shr_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_ushr_element(Context *ctx, Value *base, Value *index, Value *value);
void __qmljs_inplace_bit_and_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_bit_or_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_bit_xor_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_add_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_sub_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_mul_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_div_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_mod_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_shl_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_shr_member(Context *ctx, Value *base, String *name, Value *value);
void __qmljs_inplace_ushr_member(Context *ctx, Value *base, String *name, Value *value);
Bool __qmljs_cmp_gt(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_lt(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_ge(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_le(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_eq(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right);
Bool __qmljs_cmp_in(Context *ctx, const Value left, const Value right);
} // extern "C"
struct ValueData {
union {
quint64 val;
double dbl;
struct {
#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN
uint tag;
#endif
union {
uint uint_32;
int int_32;
};
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
uint tag;
#endif
};
};
};
struct Value;
template <int> struct ValueBase;
template <> struct ValueBase<4> : public ValueData
{
// we have all 4 bytes on 32 bit to specify the type
enum Masks {
NaN_Mask = 0xfff80000,
Type_Mask = 0xffffffff
};
enum ValueType {
Undefined_Type = NaN_Mask | 0x7ffff, // all 1's
Null_Type = NaN_Mask | 0x0,
Boolean_Type = NaN_Mask | 0x1,
Integer_Type = NaN_Mask | 0x2,
Object_Type = NaN_Mask | 0x2d59b, // give it randomness to avoid accidental collisions (for gc)
String_Type = NaN_Mask | 0x2d5ba,
Double_Type = 0
};
inline bool is(ValueType type) const {
if (type == Double_Type)
return (tag & NaN_Mask) != NaN_Mask;
return tag == type;
}
inline ValueType type() const {
return (ValueType)tag;
}
bool booleanValue() const {
return int_32;
}
double doubleValue() const {
return dbl;
}
void setDouble(double d) {
dbl = d;
}
double asDouble() const {
if (tag == Integer_Type)
return int_32;
return dbl;
}
double integerValue() const {
return int_32;
}
String *stringValue() const {
return (String *)(quintptr) uint_32;
}
Object *objectValue() const {
return (Object *)(quintptr) uint_32;
}
quint64 rawValue() const {
return val;
}
static inline Value undefinedValue();
static inline Value nullValue();
static inline Value fromBoolean(Bool b);
static inline Value fromDouble(double d);
static inline Value fromInt32(int i);
static inline Value fromString(String *s);
static inline Value fromObject(Object *o);
};
template <> struct ValueBase<8> : public ValueData
{
enum Masks {
NaN_Mask = 0x7ff80000,
Type_Mask = 0x7fff0000,
Tag_Shift = 32
};
enum ValueType {
Undefined_Type = NaN_Mask | 0x70000,
Null_Type = NaN_Mask | 0x00000,
Boolean_Type = NaN_Mask | 0x10000,
Integer_Type = NaN_Mask | 0x20000,
Object_Type = NaN_Mask | 0x30000,
String_Type = NaN_Mask | 0x40000,
Double_Type = 0
};
inline bool is(ValueType type) const {
if (type == Double_Type)
return (tag & NaN_Mask) != NaN_Mask;
return (tag & Type_Mask) == type;
}
inline bool isNot(ValueType type) {
return !is(type);
}
inline ValueType type() const {
return (ValueType)(tag & Type_Mask);
}
Bool booleanValue() const {
return int_32;
}
double doubleValue() const {
return dbl;
}
void setDouble(double d) {
dbl = d;
}
double asDouble() const {
if (tag == Integer_Type)
return int_32;
return dbl;
}
double integerValue() const {
return int_32;
}
String *stringValue() const {
return (String *)(val & ~(quint64(Type_Mask) << Tag_Shift));
}
Object *objectValue() const {
return (Object *)(val & ~(quint64(Type_Mask) << Tag_Shift));
}
quint64 rawValue() const {
return val;
}
static Value undefinedValue();
static Value nullValue();
static Value fromBoolean(Bool b);
static Value fromDouble(double d);
static Value fromInt32(int i);
static Value fromString(String *s);
static Value fromObject(Object *o);
};
struct Value : public ValueBase<sizeof(void *)>
{
#ifndef QMLJS_LLVM_RUNTIME
static Value fromString(Context *ctx, const QString &fromString);
using ValueBase<sizeof(void *)>::fromString;
#endif
static int toInteger(double fromNumber);
static int toInt32(double value);
static unsigned int toUInt32(double value);
int toUInt16(Context *ctx);
int toInt32(Context *ctx);
unsigned int toUInt32(Context *ctx);
Bool toBoolean(Context *ctx) const;
double toInteger(Context *ctx) const;
double toNumber(Context *ctx) const;
String *toString(Context *ctx) const;
Value toObject(Context *ctx) const;
inline bool isUndefined() const { return is(Value::Undefined_Type); }
inline bool isNull() const { return is(Value::Null_Type); }
inline bool isString() const { return is(Value::String_Type); }
inline bool isBoolean() const { return type() == Value::Boolean_Type; }
inline bool isNumber() const { return is(Value::Integer_Type) || is(Value::Double_Type); }
inline bool isDouble() const { return is(Value::Double_Type); }
inline bool isInteger() const { return type() == Value::Integer_Type; }
inline bool isObject() const { return type() == Value::Object_Type; }
inline bool isPrimitive() const { return type() != Value::Object_Type; }
bool isFunctionObject() const;
bool isBooleanObject() const;
bool isNumberObject() const;
bool isStringObject() const;
bool isDateObject() const;
bool isArrayObject() const;
bool isErrorObject() const;
bool isArgumentsObject() const;
Object *asObject() const;
FunctionObject *asFunctionObject() const;
BooleanObject *asBooleanObject() const;
NumberObject *asNumberObject() const;
StringObject *asStringObject() const;
DateObject *asDateObject() const;
ArrayObject *asArrayObject() const;
ErrorObject *asErrorObject() const;
ActivationObject *asArgumentsObject() const;
Value property(Context *ctx, String *name) const;
Value *getPropertyDescriptor(Context *ctx, String *name) const;
};
inline Value ValueBase<4>::undefinedValue()
{
Value v;
v.tag = Undefined_Type;
v.uint_32 = 0;
return v;
}
inline Value ValueBase<4>::nullValue()
{
Value v;
v.tag = Null_Type;
v.uint_32 = 0;
return v;
}
inline Value ValueBase<4>::fromBoolean(Bool b)
{
Value v;
v.tag = Boolean_Type;
v.int_32 = (bool)b;
return v;
}
inline Value ValueBase<4>::fromDouble(double d)
{
Value v;
v.dbl = d;
return v;
}
inline Value ValueBase<4>::fromInt32(int i)
{
Value v;
v.tag = Integer_Type;
v.int_32 = i;
return v;
}
inline Value ValueBase<4>::fromString(String *s)
{
Value v;
v.tag = String_Type;
v.uint_32 = (quint32)(quintptr) s;
return v;
}
inline Value ValueBase<4>::fromObject(Object *o)
{
Value v;
v.tag = Object_Type;
v.uint_32 = (quint32)(quintptr) o;
return v;
}
inline Value ValueBase<8>::undefinedValue()
{
Value v;
v.val = quint64(Undefined_Type) << Tag_Shift;
return v;
}
inline Value ValueBase<8>::nullValue()
{
Value v;
v.val = quint64(Null_Type) << Tag_Shift;
return v;
}
inline Value ValueBase<8>::fromBoolean(Bool b)
{
Value v;
v.tag = Boolean_Type;
v.int_32 = (bool)b;
return v;
}
inline Value ValueBase<8>::fromDouble(double d)
{
Value v;
v.dbl = d;
return v;
}
inline Value ValueBase<8>::fromInt32(int i)
{
Value v;
v.tag = Integer_Type;
v.int_32 = i;
return v;
}
inline Value ValueBase<8>::fromString(String *s)
{
Value v;
v.val = (quint64)s;
v.val |= quint64(String_Type) << Tag_Shift;
return v;
}
inline Value ValueBase<8>::fromObject(Object *o)
{
Value v;
v.val = (quint64)o;
v.val |= quint64(Object_Type) << Tag_Shift;
return v;
}
template <Value::ValueType> struct ValueOffsetHelper;
template <> struct ValueOffsetHelper<Value::Boolean_Type>
{
enum { DataOffset = offsetof(ValueData, int_32) };
};
template <> struct ValueOffsetHelper<Value::Undefined_Type>
{
enum { DataOffset = offsetof(ValueData, uint_32) };
};
template <> struct ValueOffsetHelper<Value::Null_Type>
{
enum { DataOffset = offsetof(ValueData, uint_32) };
};
template <> struct ValueOffsetHelper<Value::Integer_Type>
{
enum { DataOffset = offsetof(ValueData, uint_32) };
};
#include <qmljs_math.h>
struct Context {
ExecutionEngine *engine;
Context *parent;
Value activation;
Value thisObject;
Value *arguments;
unsigned int argumentCount;
Value *locals;
Value result;
String **formals;
unsigned int formalCount;
String **vars;
unsigned int varCount;
int calledAsConstructor;
int hasUncaughtException;
Value *lookupPropertyDescriptor(String *name);
inline Value argument(unsigned int index = 0)
{
Value arg;
getArgument(&arg, index);
return arg;
}
inline void getArgument(Value *result, unsigned int index)
{
if (index < argumentCount)
*result = arguments[index];
else
*result = Value::undefinedValue();
}
void init(ExecutionEngine *eng);
void throwError(const Value &value);
void throwTypeError();
void throwReferenceError(const Value &value);
#ifndef QMLJS_LLVM_RUNTIME
void throwError(const QString &message);
void throwUnimplemented(const QString &message);
#endif
void initCallContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc);
void leaveCallContext(FunctionObject *f);
void initConstructorContext(ExecutionEngine *e, const Value *object, FunctionObject *f, Value *args, unsigned argc);
void leaveConstructorContext(FunctionObject *f);
};
extern "C" {
// constructors
inline Value __qmljs_init_string(String *value)
{
return Value::fromString(value);
}
inline Value __qmljs_init_object(Object *object)
{
return Value::fromObject(object);
}
// type conversion and testing
inline Value __qmljs_to_primitive(Context *ctx, const Value value, int typeHint)
{
if (!value.isObject())
return value;
return __qmljs_default_value(ctx, value, typeHint);
}
inline Bool __qmljs_to_boolean(const Value value, Context *ctx)
{
switch (value.type()) {
case Value::Undefined_Type:
case Value::Null_Type:
return false;
case Value::Boolean_Type:
case Value::Integer_Type:
return (bool)value.int_32;
case Value::String_Type:
return __qmljs_string_length(ctx, value.stringValue()) > 0;
case Value::Object_Type:
return true;
default: // double
if (! value.doubleValue() || qIsNaN(value.doubleValue()))
return false;
return true;
}
}
inline double __qmljs_to_number(const Value value, Context *ctx)
{
switch (value.type()) {
case Value::Undefined_Type:
return nan("");
case Value::Null_Type:
return 0;
case Value::Boolean_Type:
return (value.booleanValue() ? 1. : 0.);
case Value::Integer_Type:
return value.int_32;
case Value::String_Type:
return __qmljs_string_to_number(ctx, value.stringValue());
case Value::Object_Type: {
Value prim = __qmljs_to_primitive(ctx, value, NUMBER_HINT);
return __qmljs_to_number(prim, ctx);
}
default: // double
return value.doubleValue();
}
}
inline double __qmljs_to_integer(const Value value, Context *ctx)
{
if (value.isInteger())
return value.int_32;
const double number = __qmljs_to_number(value, ctx);
if (qIsNaN(number))
return +0;
else if (! number || isinf(number))
return number;
const double v = floor(fabs(number));
return signbit(number) ? -v : v;
}
inline int __qmljs_to_int32(const Value value, Context *ctx)
{
if (value.isInteger())
return value.int_32;
double number = __qmljs_to_number(value, ctx);
if ((number >= -2147483648.0 && number < 2147483648.0)) {
return static_cast<int>(number);
}
if (! number || qIsNaN(number) || isinf(number))
return 0;
double D32 = 4294967296.0;
double sign = (number < 0) ? -1.0 : 1.0;
double abs_n = fabs(number);
number = ::fmod(sign * ::floor(abs_n), D32);
const double D31 = D32 / 2.0;
if (sign == -1 && number < -D31)
number += D32;
else if (sign != -1 && number >= D31)
number -= D32;
return int(number);
}
inline unsigned __qmljs_to_uint32(const Value value, Context *ctx)
{
if (value.isInteger())
return (unsigned) value.int_32;
double number = __qmljs_to_number(value, ctx);
if (! number || qIsNaN(number) || isinf(number))
return +0;
double sign = (number < 0) ? -1.0 : 1.0;
double abs_n = ::fabs(number);
const double D32 = 4294967296.0;
number = ::fmod(sign * ::floor(abs_n), D32);
if (number < 0)
number += D32;
return unsigned(number);
}
inline unsigned short __qmljs_to_uint16(const Value value, Context *ctx)
{
double number = __qmljs_to_number(value, ctx);
if (! number || qIsNaN(number) || isinf(number))
return +0;
double sign = (number < 0) ? -1.0 : 1.0;
double abs_n = ::fabs(number);
double D16 = 65536.0;
number = ::fmod(sign * ::floor(abs_n), D16);
if (number < 0)
number += D16;
return (unsigned short)number;
}
inline Value __qmljs_to_string(Context *ctx, const Value value)
{
switch (value.type()) {
case Value::Undefined_Type:
return __qmljs_string_literal_undefined(ctx);
break;
case Value::Null_Type:
return __qmljs_string_literal_null(ctx);
break;
case Value::Boolean_Type:
if (value.booleanValue())
return __qmljs_string_literal_true(ctx);
else
return __qmljs_string_literal_false(ctx);
break;
case Value::String_Type:
return value;
break;
case Value::Object_Type: {
Value prim = __qmljs_to_primitive(ctx, value, STRING_HINT);
if (prim.isPrimitive())
return __qmljs_to_string(ctx, prim);
else
return __qmljs_throw_type_error(ctx);
break;
}
case Value::Integer_Type:
return __qmljs_string_from_number(ctx, value.int_32);
break;
default: // double
return __qmljs_string_from_number(ctx, value.doubleValue());
break;
} // switch
}
inline Value __qmljs_to_object(Context *ctx, const Value value)
{
switch (value.type()) {
case Value::Undefined_Type:
case Value::Null_Type:
return __qmljs_throw_type_error(ctx);
break;
case Value::Boolean_Type:
return __qmljs_new_boolean_object(ctx, value.booleanValue());
break;
case Value::String_Type:
return __qmljs_new_string_object(ctx, value.stringValue());
break;
case Value::Object_Type:
return value;
break;
case Value::Integer_Type:
return __qmljs_new_number_object(ctx, value.int_32);
break;
default: // double
return __qmljs_new_number_object(ctx, value.doubleValue());
break;
}
}
/*
inline uint __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value)
{
switch (value->type()) {
case Value::Undefined_Type:
case Value::Null_Type:
*result = __qmljs_throw_type_error(ctx);
return false;
default:
return true;
}
}
*/
inline Bool __qmljs_is_callable(Context *ctx, const Value value)
{
if (value.isObject())
return __qmljs_is_function(value);
else
return false;
}
inline Value __qmljs_default_value(Context *ctx, const Value value, int typeHint)
{
if (value.isObject())
return __qmljs_object_default_value(ctx, value, typeHint);
return Value::undefinedValue();
}
// unary operators
inline Value __qmljs_typeof(Context *ctx, const Value value)
{
switch (value.type()) {
case Value::Undefined_Type:
return __qmljs_string_literal_undefined(ctx);
break;
case Value::Null_Type:
return __qmljs_string_literal_object(ctx);
break;
case Value::Boolean_Type:
return __qmljs_string_literal_boolean(ctx);
break;
case Value::String_Type:
return __qmljs_string_literal_string(ctx);
break;
case Value::Object_Type:
if (__qmljs_is_callable(ctx, value))
return __qmljs_string_literal_function(ctx);
else
return __qmljs_string_literal_object(ctx); // ### implementation-defined
break;
default:
return __qmljs_string_literal_number(ctx);
break;
}
}
inline Value __qmljs_uplus(const Value value, Context *ctx)
{
if (value.isInteger())
return value;
double n = __qmljs_to_number(value, ctx);
return Value::fromDouble(n);
}
inline Value __qmljs_uminus(const Value value, Context *ctx)
{
if (value.isInteger())
return Value::fromInt32(-value.integerValue());
double n = __qmljs_to_number(value, ctx);
return Value::fromDouble(-n);
}
inline Value __qmljs_compl(const Value value, Context *ctx)
{
int n = __qmljs_to_int32(value, ctx);
return Value::fromInt32(~n);
}
inline Value __qmljs_not(const Value value, Context *ctx)
{
bool b = __qmljs_to_boolean(value, ctx);
return Value::fromBoolean(!b);
}
// binary operators
inline Value __qmljs_bit_or(const Value left, const Value right, Context *ctx)
{
int lval = __qmljs_to_int32(left, ctx);
int rval = __qmljs_to_int32(right, ctx);
// ### changing this to fromInt32() breaks crypto.js
return Value::fromDouble(lval | rval);
}
inline Value __qmljs_bit_xor(const Value left, const Value right, Context *ctx)
{
int lval = __qmljs_to_int32(left, ctx);
int rval = __qmljs_to_int32(right, ctx);
return Value::fromInt32(lval ^ rval);
}
inline Value __qmljs_bit_and(const Value left, const Value right, Context *ctx)
{
int lval = __qmljs_to_int32(left, ctx);
int rval = __qmljs_to_int32(right, ctx);
return Value::fromInt32(lval & rval);
}
inline void __qmljs_inplace_bit_and(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_bit_and(*result, *value, ctx);
}
inline void __qmljs_inplace_bit_or(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_bit_or(*result, *value, ctx);
}
inline void __qmljs_inplace_bit_xor(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_bit_xor(*result, *value, ctx);
}
inline void __qmljs_inplace_add(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_add(*result, *value, ctx);
}
inline void __qmljs_inplace_sub(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_sub(*result, *value, ctx);
}
inline void __qmljs_inplace_mul(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_mul(*result, *value, ctx);
}
inline void __qmljs_inplace_div(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_div(*result, *value, ctx);
}
inline void __qmljs_inplace_mod(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_mod(*result, *value, ctx);
}
inline void __qmljs_inplace_shl(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_shl(*result, *value, ctx);
}
inline void __qmljs_inplace_shr(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_shr(*result, *value, ctx);
}
inline void __qmljs_inplace_ushr(Context *ctx, Value *result, Value *value)
{
*result = __qmljs_ushr(*result, *value, ctx);
}
inline Value __qmljs_add(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return add_int32(left.integerValue(), right.integerValue());
if (left.isNumber() && right.isNumber())
return Value::fromDouble(left.asDouble() + right.asDouble());
else
return __qmljs_add_helper(left, right, ctx);
}
inline Value __qmljs_sub(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return sub_int32(left.integerValue(), right.integerValue());
double lval = __qmljs_to_number(left, ctx);
double rval = __qmljs_to_number(right, ctx);
return Value::fromDouble(lval - rval);
}
inline Value __qmljs_mul(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return mul_int32(left.integerValue(), right.integerValue());
double lval = __qmljs_to_number(left, ctx);
double rval = __qmljs_to_number(right, ctx);
return Value::fromDouble(lval * rval);
}
inline Value __qmljs_div(const Value left, const Value right, Context *ctx)
{
double lval = __qmljs_to_number(left, ctx);
double rval = __qmljs_to_number(right, ctx);
return Value::fromDouble(lval / rval);
}
inline Value __qmljs_mod(const Value left, const Value right, Context *ctx)
{
double lval = __qmljs_to_number(left, ctx);
double rval = __qmljs_to_number(right, ctx);
return Value::fromDouble(fmod(lval, rval));
}
// ### unsigned shl missing?
inline Value __qmljs_shl(const Value left, const Value right, Context *ctx)
{
int lval = __qmljs_to_int32(left, ctx);
unsigned rval = __qmljs_to_uint32(right, ctx);
return Value::fromInt32(lval << rval);
}
inline Value __qmljs_shr(const Value left, const Value right, Context *ctx)
{
int lval = __qmljs_to_int32(left, ctx);
unsigned rval = __qmljs_to_uint32(right, ctx);
return Value::fromInt32(lval >> rval);
}
inline Value __qmljs_ushr(const Value left, const Value right, Context *ctx)
{
unsigned lval = __qmljs_to_uint32(left, ctx);
unsigned rval = __qmljs_to_uint32(right, ctx);
return Value::fromInt32(lval >> rval);
}
inline Value __qmljs_gt(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return Value::fromBoolean(left.integerValue() > right.integerValue());
if (left.isNumber() && right.isNumber()) {
return Value::fromBoolean(left.asDouble() > right.asDouble());
} else {
Value result = __qmljs_compare(ctx, left, right, false);
if (result.isUndefined())
result = Value::fromBoolean(false);
return result;
}
}
inline Value __qmljs_lt(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return Value::fromBoolean(left.integerValue() < right.integerValue());
if (left.isNumber() && right.isNumber()) {
return Value::fromBoolean(left.asDouble() < right.asDouble());
} else {
Value result = __qmljs_compare(ctx, left, right, true);
if (result.isUndefined())
result = Value::fromBoolean(false);
return result;
}
}
inline Value __qmljs_ge(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return Value::fromBoolean(left.integerValue() >= right.integerValue());
if (left.isNumber() && right.isNumber()) {
return Value::fromBoolean(left.asDouble() >= right.asDouble());
} else {
Value result = __qmljs_compare(ctx, right, left, false);
bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue()));
return Value::fromBoolean(r);
}
}
inline Value __qmljs_le(const Value left, const Value right, Context *ctx)
{
if (left.isInteger() && right.isInteger())
return Value::fromBoolean(left.integerValue() <= right.integerValue());
if (left.isNumber() && right.isNumber()) {
return Value::fromBoolean(left.asDouble() <= right.asDouble());
} else {
Value result = __qmljs_compare(ctx, right, left, true);
bool r = ! (result.isUndefined() || (result.isBoolean() && result.booleanValue()));
return Value::fromBoolean(r);
}
}
inline Value __qmljs_eq(const Value left, const Value right, Context *ctx)
{
if (left.val == right.val)
return Value::fromBoolean(true);
if (left.isNumber() && right.isNumber()) {
return Value::fromBoolean(left.asDouble() == right.asDouble());
} else if (left.isString() && right.isString()) {
return Value::fromBoolean(__qmljs_string_equal(ctx, left.stringValue(), right.stringValue()));
} else {
bool r = __qmljs_equal(ctx, left, right);
return Value::fromBoolean(r);
}
}
inline Value __qmljs_ne(const Value left, const Value right, Context *ctx)
{
Value result = __qmljs_eq(left, right, ctx);
result.int_32 = !(bool)result.int_32;
return result;
}
inline Value __qmljs_se(const Value left, const Value right, Context *ctx)
{
bool r = __qmljs_strict_equal(ctx, left, right);
return Value::fromBoolean(r);
}
inline Value __qmljs_sne(const Value left, const Value right, Context *ctx)
{
bool r = ! __qmljs_strict_equal(ctx, left, right);
return Value::fromBoolean(r);
}
inline Bool __qmljs_cmp_gt(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_gt(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_cmp_lt(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_lt(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_cmp_ge(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_ge(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_cmp_le(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_le(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_cmp_eq(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_eq(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_cmp_ne(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_ne(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_cmp_se(Context *ctx, const Value left, const Value right)
{
return __qmljs_strict_equal(ctx, left, right);
}
inline Bool __qmljs_cmp_sne(Context *ctx, const Value left, const Value right)
{
return ! __qmljs_strict_equal(ctx, left, right);
}
inline Bool __qmljs_cmp_instanceof(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_instanceof(left, right, ctx);
return v.booleanValue();
}
inline uint __qmljs_cmp_in(Context *ctx, const Value left, const Value right)
{
Value v = __qmljs_in(left, right, ctx);
return v.booleanValue();
}
inline Bool __qmljs_strict_equal(Context *ctx, const Value x, const Value y)
{
if (x.rawValue() == y.rawValue())
return true;
if (x.isString() && y.isString())
return __qmljs_string_equal(ctx, x.stringValue(), y.stringValue());
return false;
}
} // extern "C"
} // namespace VM
} // namespace QQmlJS
#endif // QMLJS_RUNTIME_H