Initial import
This commit is contained in:
commit
47062ff244
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,50 @@
|
|||
|
||||
#include "qmljs_objects.h"
|
||||
#include "qv4codegen_p.h"
|
||||
|
||||
#include <QtCore>
|
||||
#include <private/qqmljsengine_p.h>
|
||||
#include <private/qqmljslexer_p.h>
|
||||
#include <private/qqmljsparser_p.h>
|
||||
#include <private/qqmljsast_p.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
using namespace QQmlJS;
|
||||
|
||||
GC_INIT();
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
QStringList args = app.arguments();
|
||||
args.removeFirst();
|
||||
|
||||
Engine engine;
|
||||
foreach (const QString &fn, args) {
|
||||
QFile file(fn);
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
const QString code = QString::fromUtf8(file.readAll());
|
||||
file.close();
|
||||
|
||||
Lexer lexer(&engine);
|
||||
lexer.setCode(code, 1, false);
|
||||
Parser parser(&engine);
|
||||
|
||||
const bool parsed = parser.parseProgram();
|
||||
|
||||
foreach (const DiagnosticMessage &m, parser.diagnosticMessages()) {
|
||||
std::cerr << qPrintable(fn) << ':' << m.loc.startLine << ':' << m.loc.startColumn
|
||||
<< ": error: " << qPrintable(m.message) << std::endl;
|
||||
}
|
||||
|
||||
if (parsed) {
|
||||
using namespace AST;
|
||||
Program *program = AST::cast<Program *>(parser.rootNode());
|
||||
|
||||
Codegen cg;
|
||||
cg(program);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
#include "qmljs_objects.h"
|
||||
#include <cassert>
|
||||
|
||||
bool Object::get(String *name, Value *result)
|
||||
{
|
||||
if (Property *prop = getProperty(name)) {
|
||||
*result = prop->value;
|
||||
return true;
|
||||
}
|
||||
|
||||
__qmljs_init_undefined(0, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
Property *Object::getOwnProperty(String *name)
|
||||
{
|
||||
if (members) {
|
||||
if (Property *prop = members->find(name)) {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Property *Object::getProperty(String *name)
|
||||
{
|
||||
if (Property *prop = getOwnProperty(name))
|
||||
return prop;
|
||||
else if (prototype)
|
||||
return prototype->getProperty(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Object::put(String *name, const Value &value, bool flag)
|
||||
{
|
||||
if (! members)
|
||||
members = new (GC) Table();
|
||||
|
||||
members->insert(name, value);
|
||||
}
|
||||
|
||||
bool Object::canPut(String *name)
|
||||
{
|
||||
if (Property *prop = getOwnProperty(name))
|
||||
return true;
|
||||
if (! prototype)
|
||||
return extensible;
|
||||
if (Property *inherited = prototype->getProperty(name)) {
|
||||
return inherited->isWritable();
|
||||
} else {
|
||||
return extensible;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::hasProperty(String *name) const
|
||||
{
|
||||
if (members)
|
||||
return members->find(name) != 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Object::deleteProperty(String *name, bool flag)
|
||||
{
|
||||
if (members)
|
||||
return members->remove(name);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Object::defaultValue(Value *result, int typeHint)
|
||||
{
|
||||
Context *ctx = 0; // ###
|
||||
|
||||
if (typeHint == STRING_HINT) {
|
||||
if (asFunctionObject() != 0)
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("function")));
|
||||
else
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("object")));
|
||||
} else {
|
||||
__qmljs_init_undefined(ctx, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool FunctionObject::hasInstance(const Value &value) const
|
||||
{
|
||||
Q_UNUSED(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
Value FunctionObject::call(const Value &thisObject, const Value args[], unsigned argc)
|
||||
{
|
||||
(void) thisObject;
|
||||
|
||||
Value v;
|
||||
__qmljs_init_undefined(0, &v);
|
||||
return v;
|
||||
}
|
||||
|
||||
Value FunctionObject::construct(const Value args[], unsigned argc)
|
||||
{
|
||||
Value thisObject;
|
||||
__qmljs_init_object(0, &thisObject, new (GC) Object);
|
||||
call(thisObject, args, argc);
|
||||
return thisObject;
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
#ifndef QMLJS_OBJECTS_H
|
||||
#define QMLJS_OBJECTS_H
|
||||
|
||||
#include "qmljs_runtime.h"
|
||||
|
||||
#include <gc/gc_cpp.h>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QHash>
|
||||
|
||||
struct Value;
|
||||
struct Object;
|
||||
struct BooleanObject;
|
||||
struct NumberObject;
|
||||
struct StringObject;
|
||||
struct ArrayObject;
|
||||
struct FunctionObject;
|
||||
struct ErrorObject;
|
||||
|
||||
struct String: gc_cleanup {
|
||||
String(const QString &text)
|
||||
: _text(text), _hashValue(0) {}
|
||||
|
||||
inline const QString &text() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
inline unsigned hashValue() const {
|
||||
if (! _hashValue)
|
||||
_hashValue = qHash(_text);
|
||||
|
||||
return _hashValue;
|
||||
}
|
||||
|
||||
static String *get(Context *ctx, const QString &s) {
|
||||
return new (GC) String(s);
|
||||
}
|
||||
|
||||
private:
|
||||
QString _text;
|
||||
mutable unsigned _hashValue;
|
||||
};
|
||||
|
||||
struct Property: gc {
|
||||
String *name;
|
||||
Value value;
|
||||
PropertyAttributes flags;
|
||||
Property *next;
|
||||
|
||||
Property(String *name, const Value &value, PropertyAttributes flags = NoAttributes)
|
||||
: name(name), value(value), flags(flags), next(0) {}
|
||||
|
||||
inline bool isWritable() const { return flags & WritableAttribute; }
|
||||
inline bool isEnumerable() const { return flags & EnumerableAttribute; }
|
||||
inline bool isConfigurable() const { return flags & ConfigurableAttribute; }
|
||||
|
||||
inline bool hasName(String *n) const {
|
||||
if (name == n || (name->hashValue() == n->hashValue() && name->text() == n->text()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline unsigned hashValue() const {
|
||||
return name->hashValue();
|
||||
}
|
||||
};
|
||||
|
||||
class Table: public gc
|
||||
{
|
||||
Property **_properties;
|
||||
Property **_buckets;
|
||||
int _propertyCount;
|
||||
int _bucketCount;
|
||||
int _allocated;
|
||||
|
||||
public:
|
||||
Table()
|
||||
: _properties(0)
|
||||
, _buckets(0)
|
||||
, _propertyCount(-1)
|
||||
, _bucketCount(11)
|
||||
, _allocated(0) {}
|
||||
|
||||
bool empty() const { return _propertyCount == -1; }
|
||||
unsigned size() const { return _propertyCount + 1; }
|
||||
|
||||
typedef Property **iterator;
|
||||
iterator begin() const { return _properties; }
|
||||
iterator end() const { return _properties + (_propertyCount + 1); }
|
||||
|
||||
bool remove(String *name)
|
||||
{
|
||||
if (_properties) {
|
||||
const unsigned hash = name->hashValue() % _bucketCount;
|
||||
|
||||
if (Property *prop = _buckets[hash]) {
|
||||
if (prop->hasName(name)) {
|
||||
_buckets[hash] = prop->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
do {
|
||||
Property *next = prop->next;
|
||||
|
||||
if (next && next->hasName(name)) {
|
||||
prop->next = next->next;
|
||||
return true;
|
||||
}
|
||||
prop = next;
|
||||
} while (prop);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Property *find(String *name) const
|
||||
{
|
||||
if (! _properties)
|
||||
return 0;
|
||||
|
||||
for (Property *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
|
||||
if (prop->hasName(name))
|
||||
return prop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Property *insert(String *name, const Value &value)
|
||||
{
|
||||
if (Property *prop = find(name)) {
|
||||
prop->value = value;
|
||||
return prop;
|
||||
}
|
||||
|
||||
if (++_propertyCount == _allocated) {
|
||||
if (! _allocated)
|
||||
_allocated = 4;
|
||||
else
|
||||
_allocated *= 2;
|
||||
|
||||
Property **properties = new (GC) Property*[_allocated];
|
||||
std::copy(_properties, _properties + _propertyCount, properties);
|
||||
_properties = properties;
|
||||
}
|
||||
|
||||
Property *prop = new (GC) Property(name, value);
|
||||
_properties[_propertyCount] = prop;
|
||||
|
||||
if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) {
|
||||
rehash();
|
||||
} else {
|
||||
Property *&bucket = _buckets[prop->hashValue() % _bucketCount];
|
||||
prop->next = bucket;
|
||||
bucket = prop;
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
private:
|
||||
void rehash()
|
||||
{
|
||||
if (_bucketCount)
|
||||
_bucketCount *= 2; // ### next prime
|
||||
|
||||
_buckets = new (GC) Property *[_bucketCount];
|
||||
std::fill(_buckets, _buckets + _bucketCount, (Property *) 0);
|
||||
|
||||
for (int i = 0; i <= _propertyCount; ++i) {
|
||||
Property *prop = _properties[i];
|
||||
Property *&bucket = _buckets[prop->name->hashValue() % _bucketCount];
|
||||
prop->next = bucket;
|
||||
bucket = prop;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct Object: gc_cleanup {
|
||||
Object *prototype;
|
||||
String *klass;
|
||||
Table *members;
|
||||
bool extensible;
|
||||
|
||||
Object()
|
||||
: prototype(0)
|
||||
, klass(0)
|
||||
, members(0)
|
||||
, extensible(true) {}
|
||||
|
||||
virtual ~Object() {}
|
||||
|
||||
virtual FunctionObject *asFunctionObject() { return 0; }
|
||||
|
||||
virtual bool get(String *name, Value *result);
|
||||
virtual Property *getOwnProperty(String *name);
|
||||
virtual Property *getProperty(String *name);
|
||||
virtual void put(String *name, const Value &value, bool flag = 0);
|
||||
virtual bool canPut(String *name);
|
||||
virtual bool hasProperty(String *name) const;
|
||||
virtual bool deleteProperty(String *name, bool flag);
|
||||
virtual void defaultValue(Value *result, int typeHint);
|
||||
// ### TODO: defineOwnProperty(name, descriptor, boolean) -> boolean
|
||||
};
|
||||
|
||||
struct BooleanObject: Object {
|
||||
Value value;
|
||||
BooleanObject(const Value &value): value(value) {}
|
||||
virtual void defaultValue(Value *result, int typehint) { *result = value; }
|
||||
};
|
||||
|
||||
struct NumberObject: Object {
|
||||
Value value;
|
||||
NumberObject(const Value &value): value(value) {}
|
||||
virtual void defaultValue(Value *result, int typehint) { *result = value; }
|
||||
};
|
||||
|
||||
struct StringObject: Object {
|
||||
Value value;
|
||||
StringObject(const Value &value): value(value) {}
|
||||
virtual void defaultValue(Value *result, int typehint) { *result = value; }
|
||||
};
|
||||
|
||||
struct ArrayObject: Object {
|
||||
};
|
||||
|
||||
struct FunctionObject: Object {
|
||||
Object *scope;
|
||||
String **formalParameterList;
|
||||
|
||||
FunctionObject(Object *scope = 0): scope(scope), formalParameterList(0) {}
|
||||
virtual FunctionObject *asFunctionObject() { return this; }
|
||||
|
||||
virtual bool hasInstance(const Value &value) const;
|
||||
virtual Value call(const Value &thisObject, const Value args[], unsigned argc);
|
||||
virtual Value construct(const Value args[], unsigned argc);
|
||||
};
|
||||
|
||||
struct ErrorObject: Object {
|
||||
String *message;
|
||||
ErrorObject(String *message): message(message) {}
|
||||
};
|
||||
|
||||
struct ArgumentsObject: Object {
|
||||
};
|
||||
|
||||
struct Context: gc {
|
||||
Value activation;
|
||||
Value thisObject;
|
||||
Object *scope;
|
||||
Value *arguments;
|
||||
unsigned argumentCount;
|
||||
|
||||
Context()
|
||||
: scope(0)
|
||||
, arguments(0)
|
||||
, argumentCount(0)
|
||||
{
|
||||
activation.type = NULL_TYPE;
|
||||
thisObject.type = NULL_TYPE;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QMLJS_OBJECTS_H
|
|
@ -0,0 +1,339 @@
|
|||
|
||||
#include "qmljs_runtime.h"
|
||||
#include "qmljs_objects.h"
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
Value Value::string(Context *ctx, const QString &s)
|
||||
{
|
||||
return string(ctx, String::get(ctx, s));
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void __qmljs_string_literal_undefined(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("undefined")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_null(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("null")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_true(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("true")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_false(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("false")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_object(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("object")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_boolean(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("boolean")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_number(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("number")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_string(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("string")));
|
||||
}
|
||||
|
||||
void __qmljs_string_literal_function(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_string(ctx, result, String::get(ctx, QLatin1String("function")));
|
||||
}
|
||||
|
||||
void __qmljs_delete(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
(void) ctx;
|
||||
(void) result;
|
||||
(void) value;
|
||||
}
|
||||
|
||||
void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
if (right->type == OBJECT_TYPE) {
|
||||
if (FunctionObject *function = right->objectValue->asFunctionObject()) {
|
||||
bool r = function->hasInstance(*left);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
}
|
||||
|
||||
void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
if (right->type == OBJECT_TYPE) {
|
||||
Value s;
|
||||
__qmljs_to_string(ctx, &s, left);
|
||||
bool r = right->objectValue->hasProperty(s.stringValue);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
} else {
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
}
|
||||
}
|
||||
|
||||
int __qmljs_string_length(Context *, String *string)
|
||||
{
|
||||
return string->text().length();
|
||||
}
|
||||
|
||||
double __qmljs_string_to_number(Context *, String *string)
|
||||
{
|
||||
bool ok;
|
||||
return string->text().toDouble(&ok); // ### TODO
|
||||
}
|
||||
|
||||
void __qmljs_string_from_number(Context *ctx, Value *result, double number)
|
||||
{
|
||||
String *string = String::get(ctx, QString::number(number, 'g', 16));
|
||||
__qmljs_init_string(ctx, result, string);
|
||||
}
|
||||
|
||||
bool __qmljs_string_compare(Context *, String *left, String *right)
|
||||
{
|
||||
return left->text() < right->text();
|
||||
}
|
||||
|
||||
bool __qmljs_string_equal(Context *, String *left, String *right)
|
||||
{
|
||||
return left == right ||
|
||||
(left->hashValue() == right->hashValue() &&
|
||||
left->text() == right->text());
|
||||
}
|
||||
|
||||
String *__qmljs_string_concat(Context *ctx, String *first, String *second)
|
||||
{
|
||||
return String::get(ctx, first->text() + second->text());
|
||||
}
|
||||
|
||||
bool __qmljs_is_function(Context *, const Value *value)
|
||||
{
|
||||
return value->objectValue->asFunctionObject() != 0;
|
||||
}
|
||||
|
||||
void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint)
|
||||
{
|
||||
object->defaultValue(result, typeHint);
|
||||
}
|
||||
|
||||
void __qmljs_throw_type_error(Context *ctx, Value *result)
|
||||
{
|
||||
__qmljs_init_object(ctx, result, new (GC) ErrorObject(String::get(ctx, QLatin1String("type error"))));
|
||||
}
|
||||
|
||||
void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean)
|
||||
{
|
||||
Value value;
|
||||
__qmljs_init_boolean(ctx, &value, boolean);
|
||||
__qmljs_init_object(ctx, result, new (GC) BooleanObject(value));
|
||||
}
|
||||
|
||||
void __qmljs_new_number_object(Context *ctx, Value *result, double number)
|
||||
{
|
||||
Value value;
|
||||
__qmljs_init_number(ctx, &value, number);
|
||||
__qmljs_init_object(ctx, result, new (GC) NumberObject(value));
|
||||
}
|
||||
|
||||
void __qmljs_new_string_object(Context *ctx, Value *result, String *string)
|
||||
{
|
||||
Value value;
|
||||
__qmljs_init_string(ctx, &value, string);
|
||||
__qmljs_init_object(ctx, result, new (GC) StringObject(value));
|
||||
}
|
||||
|
||||
void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value)
|
||||
{
|
||||
object->objectValue->put(name, *value, /*flags*/ 0);
|
||||
}
|
||||
|
||||
void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double number)
|
||||
{
|
||||
Value value;
|
||||
__qmljs_init_number(ctx, &value, number);
|
||||
object->objectValue->put(name, value, /*flag*/ 0);
|
||||
}
|
||||
|
||||
void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *s)
|
||||
{
|
||||
Value value;
|
||||
__qmljs_init_string(ctx, &value, s);
|
||||
object->objectValue->put(name, value, /*flag*/ 0);
|
||||
}
|
||||
|
||||
void __qmljs_set_activation_property(Context *ctx, String *name, Value *value)
|
||||
{
|
||||
__qmljs_set_property(ctx, &ctx->activation, name, value);
|
||||
}
|
||||
|
||||
void __qmljs_set_activation_property_number(Context *ctx, String *name, double value)
|
||||
{
|
||||
__qmljs_set_property_number(ctx, &ctx->activation, name, value);
|
||||
}
|
||||
|
||||
void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value)
|
||||
{
|
||||
__qmljs_set_property_string(ctx, &ctx->activation, name, value);
|
||||
}
|
||||
|
||||
void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name)
|
||||
{
|
||||
Q_ASSERT(object->type == OBJECT_TYPE);
|
||||
object->objectValue->get(name, result);
|
||||
}
|
||||
|
||||
void __qmljs_get_activation_property(Context *ctx, Value *result, String *name)
|
||||
{
|
||||
__qmljs_get_property(ctx, result, &ctx->activation, name);
|
||||
}
|
||||
|
||||
void __qmljs_get_activation(Context *ctx, Value *result)
|
||||
{
|
||||
*result = ctx->activation;
|
||||
}
|
||||
|
||||
void __qmljs_get_thisObject(Context *ctx, Value *result)
|
||||
{
|
||||
*result = ctx->thisObject;
|
||||
}
|
||||
|
||||
void __qmljs_compare(Context *ctx, Value *result, const Value *x, const Value *y, bool leftFirst)
|
||||
{
|
||||
Value px, py;
|
||||
|
||||
if (leftFirst) {
|
||||
__qmljs_to_primitive(ctx, &px, x, NUMBER_HINT);
|
||||
__qmljs_to_primitive(ctx, &py, y, NUMBER_HINT);
|
||||
} else {
|
||||
__qmljs_to_primitive(ctx, &py, x, NUMBER_HINT);
|
||||
__qmljs_to_primitive(ctx, &px, y, NUMBER_HINT);
|
||||
}
|
||||
|
||||
if (px.type == STRING_TYPE && py.type == STRING_TYPE) {
|
||||
bool r = __qmljs_string_compare(ctx, px.stringValue, py.stringValue);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
} else {
|
||||
double nx = __qmljs_to_number(ctx, &px);
|
||||
double ny = __qmljs_to_number(ctx, &py);
|
||||
if (isnan(nx) || isnan(ny)) {
|
||||
__qmljs_init_undefined(ctx, result);
|
||||
} else {
|
||||
const int sx = signbit(nx);
|
||||
const int sy = signbit(ny);
|
||||
const bool ix = isinf(nx);
|
||||
const bool iy = isinf(ny);
|
||||
bool r = false;
|
||||
|
||||
if (ix && !sx) {
|
||||
r = false;
|
||||
} else if (iy && !sy) {
|
||||
r = true;
|
||||
} else if (iy && sy) {
|
||||
r = false;
|
||||
} else if (ix && sx) {
|
||||
r = true;
|
||||
} else if (nx == ny) {
|
||||
r = false;
|
||||
} else {
|
||||
r = nx < ny;
|
||||
}
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool __qmljs_equal(Context *ctx, const Value *x, const Value *y)
|
||||
{
|
||||
if (x->type == y->type) {
|
||||
switch ((ValueType) x->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
return true;
|
||||
case NULL_TYPE:
|
||||
return true;
|
||||
case BOOLEAN_TYPE:
|
||||
return x->booleanValue == y->booleanValue;
|
||||
break;
|
||||
case NUMBER_TYPE:
|
||||
return x->numberValue == y->numberValue;
|
||||
case STRING_TYPE:
|
||||
return __qmljs_string_equal(ctx, x->stringValue, y->stringValue);
|
||||
case OBJECT_TYPE:
|
||||
return x->objectValue == y->objectValue;
|
||||
}
|
||||
// unreachable
|
||||
} else {
|
||||
if (x->type == NULL_TYPE && y->type == UNDEFINED_TYPE) {
|
||||
return true;
|
||||
} else if (x->type == UNDEFINED_TYPE && y->type == NULL_TYPE) {
|
||||
return true;
|
||||
} else if (x->type == NUMBER_TYPE && y->type == STRING_TYPE) {
|
||||
Value ny;
|
||||
__qmljs_init_number(ctx, &ny, __qmljs_to_number(ctx, y));
|
||||
return __qmljs_equal(ctx, x, &ny);
|
||||
} else if (x->type == STRING_TYPE && y->type == NUMBER_TYPE) {
|
||||
Value nx;
|
||||
__qmljs_init_number(ctx, &nx, __qmljs_to_number(ctx, x));
|
||||
return __qmljs_equal(ctx, &nx, y);
|
||||
} else if (x->type == BOOLEAN_TYPE) {
|
||||
Value nx;
|
||||
__qmljs_init_number(ctx, &nx, (double) x->booleanValue);
|
||||
return __qmljs_equal(ctx, &nx, y);
|
||||
} else if (y->type == BOOLEAN_TYPE) {
|
||||
Value ny;
|
||||
__qmljs_init_number(ctx, &ny, (double) y->booleanValue);
|
||||
return __qmljs_equal(ctx, x, &ny);
|
||||
} else if ((x->type == NUMBER_TYPE || x->type == STRING_TYPE) && y->type == OBJECT_TYPE) {
|
||||
Value py;
|
||||
__qmljs_to_primitive(ctx, &py, y, PREFERREDTYPE_HINT);
|
||||
return __qmljs_equal(ctx, x, &py);
|
||||
} else if (x->type == OBJECT_TYPE && (y->type == NUMBER_TYPE || y->type == STRING_TYPE)) {
|
||||
Value px;
|
||||
__qmljs_to_primitive(ctx, &px, x, PREFERREDTYPE_HINT);
|
||||
return __qmljs_equal(ctx, &px, y);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void __qmljs_call(Context *ctx, Value *result, const Value *function,
|
||||
const Value *thisObject, const Value *arguments, int argc)
|
||||
{
|
||||
if (function->type != OBJECT_TYPE) {
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
} else if (FunctionObject *f = function->objectValue->asFunctionObject()) {
|
||||
*result = f->call(*thisObject, arguments, argc);
|
||||
} else {
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
}
|
||||
}
|
||||
|
||||
void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc)
|
||||
{
|
||||
if (function->type != OBJECT_TYPE) {
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
} else if (FunctionObject *f = function->objectValue->asFunctionObject()) {
|
||||
*result = f->construct(arguments, argc);
|
||||
} else {
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // extern "C"
|
|
@ -0,0 +1,623 @@
|
|||
#ifndef QMLJS_RUNTIME_H
|
||||
#define QMLJS_RUNTIME_H
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QDebug>
|
||||
#include <math.h>
|
||||
|
||||
enum ValueType {
|
||||
UNDEFINED_TYPE,
|
||||
NULL_TYPE,
|
||||
BOOLEAN_TYPE,
|
||||
NUMBER_TYPE,
|
||||
STRING_TYPE,
|
||||
OBJECT_TYPE
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
extern "C" {
|
||||
|
||||
// constructors
|
||||
void __qmljs_init_undefined(Context *ctx, Value *result);
|
||||
void __qmljs_init_null(Context *ctx, Value *result);
|
||||
void __qmljs_init_boolean(Context *ctx, Value *result, bool value);
|
||||
void __qmljs_init_number(Context *ctx, Value *result, double number);
|
||||
void __qmljs_init_string(Context *ctx, Value *result, String *string);
|
||||
void __qmljs_init_object(Context *ctx, Value *result, Object *object);
|
||||
|
||||
bool __qmljs_is_function(Context *ctx, const Value *value);
|
||||
|
||||
// string literals
|
||||
void __qmljs_string_literal_undefined(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_null(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_true(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_false(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_object(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_boolean(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_number(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_string(Context *ctx, Value *result);
|
||||
void __qmljs_string_literal_function(Context *ctx, Value *result);
|
||||
|
||||
// strings
|
||||
int __qmljs_string_length(Context *ctx, String *string);
|
||||
double __qmljs_string_to_number(Context *ctx, String *string);
|
||||
void __qmljs_string_from_number(Context *ctx, Value *result, 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);
|
||||
|
||||
// objects
|
||||
void __qmljs_object_default_value(Context *ctx, Value *result, Object *object, int typeHint);
|
||||
void __qmljs_throw_type_error(Context *ctx, Value *result);
|
||||
void __qmljs_new_boolean_object(Context *ctx, Value *result, bool boolean);
|
||||
void __qmljs_new_number_object(Context *ctx, Value *result, double n);
|
||||
void __qmljs_new_string_object(Context *ctx, Value *result, String *string);
|
||||
void __qmljs_set_property(Context *ctx, Value *object, String *name, Value *value);
|
||||
void __qmljs_set_property_number(Context *ctx, Value *object, String *name, double value);
|
||||
void __qmljs_set_property_string(Context *ctx, Value *object, String *name, String *value);
|
||||
void __qmljs_set_activation_property(Context *ctx, String *name, Value *value);
|
||||
void __qmljs_set_activation_property_number(Context *ctx, String *name, double value);
|
||||
void __qmljs_set_activation_property_string(Context *ctx, String *name, String *value);
|
||||
void __qmljs_get_property(Context *ctx, Value *result, Value *object, String *name);
|
||||
void __qmljs_get_activation_property(Context *ctx, Value *result, String *name);
|
||||
|
||||
// context
|
||||
void __qmljs_get_activation(Context *ctx, Value *result);
|
||||
void __qmljs_get_thisObject(Context *ctx, Value *result);
|
||||
|
||||
// type conversion and testing
|
||||
void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint);
|
||||
bool __qmljs_to_boolean(Context *ctx, const Value *value);
|
||||
double __qmljs_to_number(Context *ctx, const Value *value);
|
||||
double __qmljs_to_integer(Context *ctx, const Value *value);
|
||||
int __qmljs_to_int32(Context *ctx, const Value *value);
|
||||
unsigned __qmljs_to_uint32(Context *ctx, const Value *value);
|
||||
unsigned short __qmljs_to_uint16(Context *ctx, const Value *value);
|
||||
void __qmljs_to_string(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_to_object(Context *ctx, Value *result, const Value *value);
|
||||
bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value);
|
||||
bool __qmljs_is_callable(Context *ctx, const Value *value);
|
||||
void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint);
|
||||
|
||||
void __qmljs_compare(Context *ctx, Value *result, 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
|
||||
void __qmljs_delete(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_typeof(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_postincr(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_postdecr(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_uplus(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_uminus(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_compl(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_not(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_preincr(Context *ctx, Value *result, const Value *value);
|
||||
void __qmljs_predecr(Context *ctx, Value *result, const Value *value);
|
||||
|
||||
// binary operators
|
||||
void __qmljs_instanceof(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_in(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_bit_and(Context *ctx, Value *result, const Value *left,const Value *right);
|
||||
void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_mul(Context *ctx, Value *result, const Value *left,const Value *right);
|
||||
void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right);
|
||||
|
||||
void __qmljs_call(Context *ctx, Value *result, const Value *function, const Value *thisObject, const Value *arguments, int argc);
|
||||
void __qmjs_construct(Context *ctx, Value *result, const Value *function, const Value *arguments, int argc);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
struct Value {
|
||||
int type;
|
||||
union {
|
||||
bool booleanValue;
|
||||
double numberValue;
|
||||
Object *objectValue;
|
||||
String *stringValue;
|
||||
};
|
||||
|
||||
static inline Value boolean(Context *ctx, bool value) {
|
||||
Value v;
|
||||
__qmljs_init_boolean(ctx, &v, value);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Value number(Context *ctx, double value) {
|
||||
Value v;
|
||||
__qmljs_init_number(ctx, &v, value);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Value object(Context *ctx, Object *value) {
|
||||
Value v;
|
||||
__qmljs_init_object(ctx, &v, value);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline Value string(Context *ctx, String *value) {
|
||||
Value v;
|
||||
__qmljs_init_string(ctx, &v, value);
|
||||
return v;
|
||||
}
|
||||
|
||||
static Value string(Context *ctx, const QString &string);
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
// constructors
|
||||
inline void __qmljs_init_undefined(Context *, Value *result)
|
||||
{
|
||||
result->type = UNDEFINED_TYPE;
|
||||
}
|
||||
|
||||
inline void __qmljs_init_null(Context *, Value *result)
|
||||
{
|
||||
result->type = NULL_TYPE;
|
||||
}
|
||||
|
||||
inline void __qmljs_init_boolean(Context *, Value *result, bool value)
|
||||
{
|
||||
result->type = BOOLEAN_TYPE;
|
||||
result->booleanValue = value;
|
||||
}
|
||||
|
||||
inline void __qmljs_init_number(Context *, Value *result, double value)
|
||||
{
|
||||
result->type = NUMBER_TYPE;
|
||||
result->numberValue = value;
|
||||
}
|
||||
|
||||
inline void __qmljs_init_string(Context *, Value *result, String *value)
|
||||
{
|
||||
result->type = STRING_TYPE;
|
||||
result->stringValue = value;
|
||||
}
|
||||
|
||||
inline void __qmljs_init_object(Context *, Value *result, Object *object)
|
||||
{
|
||||
result->type = OBJECT_TYPE;
|
||||
result->objectValue = object;
|
||||
}
|
||||
|
||||
|
||||
// type conversion and testing
|
||||
inline void __qmljs_to_primitive(Context *ctx, Value *result, const Value *value, int typeHint)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
case NULL_TYPE:
|
||||
case BOOLEAN_TYPE:
|
||||
case NUMBER_TYPE:
|
||||
case STRING_TYPE:
|
||||
*result = *value;
|
||||
break;
|
||||
case OBJECT_TYPE:
|
||||
__qmljs_default_value(ctx, result, value, typeHint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool __qmljs_to_boolean(Context *ctx, const Value *value)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
case NULL_TYPE:
|
||||
return false;
|
||||
case BOOLEAN_TYPE:
|
||||
return value->booleanValue;
|
||||
case NUMBER_TYPE:
|
||||
if (! value->numberValue || isnan(value->numberValue))
|
||||
return false;
|
||||
return true;
|
||||
case STRING_TYPE:
|
||||
return __qmljs_string_length(ctx, value->stringValue) > 0;
|
||||
case OBJECT_TYPE:
|
||||
return true;
|
||||
}
|
||||
Q_ASSERT(!"unreachable");
|
||||
return false;
|
||||
}
|
||||
|
||||
inline double __qmljs_to_number(Context *ctx, const Value *value)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
return nan("");
|
||||
case NULL_TYPE:
|
||||
return 0;
|
||||
case BOOLEAN_TYPE:
|
||||
return (double) value->booleanValue;
|
||||
case NUMBER_TYPE:
|
||||
return value->numberValue;
|
||||
case STRING_TYPE:
|
||||
return __qmljs_string_to_number(ctx, value->stringValue);
|
||||
case OBJECT_TYPE: {
|
||||
Value prim;
|
||||
__qmljs_to_primitive(ctx, &prim, value, NUMBER_HINT);
|
||||
return __qmljs_to_number(ctx, &prim);
|
||||
}
|
||||
} // switch
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
inline double __qmljs_to_integer(Context *ctx, const Value *value)
|
||||
{
|
||||
const double number = __qmljs_to_number(ctx, value);
|
||||
if (isnan(number))
|
||||
return +0;
|
||||
else if (! number || isinf(number))
|
||||
return number;
|
||||
return signbit(number) * floor(fabs(number));
|
||||
}
|
||||
|
||||
inline int __qmljs_to_int32(Context *ctx, const Value *value)
|
||||
{
|
||||
const double number = __qmljs_to_number(ctx, value);
|
||||
if (! number || isnan(number) || isinf(number))
|
||||
return +0;
|
||||
return (int) trunc(number); // ###
|
||||
}
|
||||
|
||||
inline unsigned __qmljs_to_uint32(Context *ctx, const Value *value)
|
||||
{
|
||||
const double number = __qmljs_to_number(ctx, value);
|
||||
if (! number || isnan(number) || isinf(number))
|
||||
return +0;
|
||||
return (unsigned) trunc(number); // ###
|
||||
}
|
||||
|
||||
inline unsigned short __qmljs_to_uint16(Context *ctx, const Value *value)
|
||||
{
|
||||
const double number = __qmljs_to_number(ctx, value);
|
||||
if (! number || isnan(number) || isinf(number))
|
||||
return +0;
|
||||
return (unsigned short) trunc(number); // ###
|
||||
}
|
||||
|
||||
inline void __qmljs_to_string(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
__qmljs_string_literal_undefined(ctx, result);
|
||||
break;
|
||||
case NULL_TYPE:
|
||||
__qmljs_string_literal_null(ctx, result);
|
||||
break;
|
||||
case BOOLEAN_TYPE:
|
||||
if (value->booleanValue)
|
||||
__qmljs_string_literal_true(ctx, result);
|
||||
else
|
||||
__qmljs_string_literal_false(ctx, result);
|
||||
break;
|
||||
case NUMBER_TYPE:
|
||||
__qmljs_string_from_number(ctx, result, value->numberValue);
|
||||
break;
|
||||
case STRING_TYPE:
|
||||
*result = *value;
|
||||
break;
|
||||
case OBJECT_TYPE: {
|
||||
Value prim;
|
||||
__qmljs_to_primitive(ctx, &prim, value, STRING_HINT);
|
||||
__qmljs_to_string(ctx, result, &prim);
|
||||
break;
|
||||
}
|
||||
|
||||
} // switch
|
||||
}
|
||||
|
||||
inline void __qmljs_to_object(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
case NULL_TYPE:
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
break;
|
||||
case BOOLEAN_TYPE:
|
||||
__qmljs_new_boolean_object(ctx, result, value->booleanValue);
|
||||
break;
|
||||
case NUMBER_TYPE:
|
||||
__qmljs_new_number_object(ctx, result, value->numberValue);
|
||||
break;
|
||||
case STRING_TYPE:
|
||||
__qmljs_new_string_object(ctx, result, value->stringValue);
|
||||
break;
|
||||
case OBJECT_TYPE:
|
||||
*result = *value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool __qmljs_check_object_coercible(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
case NULL_TYPE:
|
||||
__qmljs_throw_type_error(ctx, result);
|
||||
return false;
|
||||
case BOOLEAN_TYPE:
|
||||
case NUMBER_TYPE:
|
||||
case STRING_TYPE:
|
||||
case OBJECT_TYPE:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool __qmljs_is_callable(Context *ctx, const Value *value)
|
||||
{
|
||||
if (value->type == OBJECT_TYPE)
|
||||
return __qmljs_is_function(ctx, value);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void __qmljs_default_value(Context *ctx, Value *result, const Value *value, int typeHint)
|
||||
{
|
||||
if (value->type == OBJECT_TYPE)
|
||||
__qmljs_object_default_value(ctx, result, value->objectValue, typeHint);
|
||||
else
|
||||
__qmljs_init_undefined(ctx, result);
|
||||
}
|
||||
|
||||
|
||||
// unary operators
|
||||
inline void __qmljs_typeof(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
switch ((ValueType) value->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
__qmljs_string_literal_undefined(ctx, result);
|
||||
break;
|
||||
case NULL_TYPE:
|
||||
__qmljs_string_literal_object(ctx, result);
|
||||
break;
|
||||
case BOOLEAN_TYPE:
|
||||
__qmljs_string_literal_boolean(ctx, result);
|
||||
break;
|
||||
case NUMBER_TYPE:
|
||||
__qmljs_string_literal_number(ctx, result);
|
||||
break;
|
||||
case STRING_TYPE:
|
||||
__qmljs_string_literal_string(ctx, result);
|
||||
break;
|
||||
case OBJECT_TYPE:
|
||||
if (__qmljs_is_callable(ctx, value))
|
||||
__qmljs_string_literal_function(ctx, result);
|
||||
else
|
||||
__qmljs_string_literal_object(ctx, result); // ### implementation-defined
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void __qmljs_uplus(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
double n = __qmljs_to_number(ctx, value);
|
||||
__qmljs_init_number(ctx, result, n);
|
||||
}
|
||||
|
||||
inline void __qmljs_uminus(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
double n = __qmljs_to_number(ctx, value);
|
||||
__qmljs_init_number(ctx, result, -n);
|
||||
}
|
||||
|
||||
inline void __qmljs_compl(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
int n = __qmljs_to_int32(ctx, value);
|
||||
__qmljs_init_number(ctx, result, ~n);
|
||||
}
|
||||
|
||||
inline void __qmljs_not(Context *ctx, Value *result, const Value *value)
|
||||
{
|
||||
bool b = __qmljs_to_boolean(ctx, value);
|
||||
__qmljs_init_number(ctx, result, !b);
|
||||
}
|
||||
|
||||
// binary operators
|
||||
inline void __qmljs_bit_or(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
int lval = __qmljs_to_int32(ctx, left);
|
||||
int rval = __qmljs_to_int32(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval | rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_bit_xor(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
int lval = __qmljs_to_int32(ctx, left);
|
||||
int rval = __qmljs_to_int32(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval ^ rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_bit_and(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
int lval = __qmljs_to_int32(ctx, left);
|
||||
int rval = __qmljs_to_int32(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval & rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_add(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
Value pleft, pright;
|
||||
__qmljs_to_primitive(ctx, &pleft, left, PREFERREDTYPE_HINT);
|
||||
__qmljs_to_primitive(ctx, &pright, right, PREFERREDTYPE_HINT);
|
||||
if (pleft.type == STRING_TYPE || pright.type == STRING_TYPE) {
|
||||
if (pleft.type != STRING_TYPE)
|
||||
__qmljs_to_string(ctx, &pleft, &pleft);
|
||||
if (pright.type != STRING_TYPE)
|
||||
__qmljs_to_string(ctx, &pright, &pright);
|
||||
String *string = __qmljs_string_concat(ctx, pleft.stringValue, pright.stringValue);
|
||||
__qmljs_init_string(ctx, result, string);
|
||||
} else {
|
||||
double x = __qmljs_to_number(ctx, &pleft);
|
||||
double y = __qmljs_to_number(ctx, &pright);
|
||||
__qmljs_init_number(ctx, result, x + y);
|
||||
}
|
||||
}
|
||||
|
||||
inline void __qmljs_sub(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
double lval = __qmljs_to_number(ctx, left);
|
||||
double rval = __qmljs_to_number(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval - rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_mul(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
double lval = __qmljs_to_number(ctx, left);
|
||||
double rval = __qmljs_to_number(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval * rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_div(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
double lval = __qmljs_to_number(ctx, left);
|
||||
double rval = __qmljs_to_number(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval * rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_mod(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
double lval = __qmljs_to_number(ctx, left);
|
||||
double rval = __qmljs_to_number(ctx, right);
|
||||
__qmljs_init_number(ctx, result, fmod(lval, rval));
|
||||
}
|
||||
|
||||
inline void __qmljs_shl(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
int lval = __qmljs_to_int32(ctx, left);
|
||||
unsigned rval = __qmljs_to_uint32(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval << rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_shr(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
int lval = __qmljs_to_int32(ctx, left);
|
||||
unsigned rval = __qmljs_to_uint32(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval >> rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_ushr(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
unsigned lval = __qmljs_to_uint32(ctx, left);
|
||||
unsigned rval = __qmljs_to_uint32(ctx, right);
|
||||
__qmljs_init_number(ctx, result, lval << rval);
|
||||
}
|
||||
|
||||
inline void __qmljs_gt(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
__qmljs_compare(ctx, result, right, left, false);
|
||||
|
||||
if (result->type == UNDEFINED_TYPE)
|
||||
__qmljs_init_boolean(ctx, result, false);
|
||||
}
|
||||
|
||||
inline void __qmljs_lt(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
__qmljs_compare(ctx, result, left, right, true);
|
||||
|
||||
if (result->type == UNDEFINED_TYPE)
|
||||
__qmljs_init_boolean(ctx, result,false);
|
||||
}
|
||||
|
||||
inline void __qmljs_ge(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
__qmljs_compare(ctx, result, right, left, false);
|
||||
|
||||
bool r = ! (result->type == UNDEFINED_TYPE ||
|
||||
(result->type == BOOLEAN_TYPE && result->booleanValue == true));
|
||||
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
|
||||
inline void __qmljs_le(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
__qmljs_compare(ctx, result, left, right, true);
|
||||
|
||||
bool r = ! (result->type == UNDEFINED_TYPE ||
|
||||
(result->type == BOOLEAN_TYPE && result->booleanValue == true));
|
||||
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
|
||||
inline void __qmljs_eq(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
bool r = __qmljs_equal(ctx, left, right);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
|
||||
inline void __qmljs_ne(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
bool r = ! __qmljs_equal(ctx, left, right);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
|
||||
inline void __qmljs_se(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
bool r = __qmljs_strict_equal(ctx, left, right);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
|
||||
inline void __qmljs_sne(Context *ctx, Value *result, const Value *left, const Value *right)
|
||||
{
|
||||
bool r = ! __qmljs_strict_equal(ctx, left, right);
|
||||
__qmljs_init_boolean(ctx, result, r);
|
||||
}
|
||||
|
||||
inline bool __qmljs_strict_equal(Context *ctx, const Value *x, const Value *y)
|
||||
{
|
||||
if (x->type != y->type)
|
||||
return false;
|
||||
|
||||
switch ((ValueType) x->type) {
|
||||
case UNDEFINED_TYPE:
|
||||
case NULL_TYPE:
|
||||
return true;
|
||||
case BOOLEAN_TYPE:
|
||||
return x->booleanValue == y->booleanValue;
|
||||
case NUMBER_TYPE:
|
||||
return x->numberValue == y->numberValue;
|
||||
case STRING_TYPE:
|
||||
return __qmljs_string_equal(ctx, x->stringValue, y->stringValue);
|
||||
case OBJECT_TYPE:
|
||||
return x->objectValue == y->objectValue;
|
||||
}
|
||||
Q_ASSERT(!"unreachable");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // QMLJS_RUNTIME_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,228 @@
|
|||
#ifndef QV4CODEGEN_P_H
|
||||
#define QV4CODEGEN_P_H
|
||||
|
||||
#include "qv4ir_p.h"
|
||||
#include <private/qqmljsastvisitor_p.h>
|
||||
|
||||
namespace QQmlJS {
|
||||
|
||||
namespace AST {
|
||||
class UiParameterList;
|
||||
}
|
||||
|
||||
class Codegen: protected AST::Visitor
|
||||
{
|
||||
public:
|
||||
Codegen();
|
||||
|
||||
void operator()(AST::Program *ast);
|
||||
|
||||
protected:
|
||||
enum Format { ex, cx, nx };
|
||||
struct Result {
|
||||
IR::Expr *code;
|
||||
IR::BasicBlock *iftrue;
|
||||
IR::BasicBlock *iffalse;
|
||||
Format format;
|
||||
Format requested;
|
||||
|
||||
explicit Result(Format requested = ex)
|
||||
: code(0)
|
||||
, iftrue(0)
|
||||
, iffalse(0)
|
||||
, format(ex)
|
||||
, requested(requested) {}
|
||||
|
||||
explicit Result(IR::BasicBlock *iftrue, IR::BasicBlock *iffalse)
|
||||
: code(0)
|
||||
, iftrue(iftrue)
|
||||
, iffalse(iffalse)
|
||||
, format(ex)
|
||||
, requested(cx) {}
|
||||
|
||||
inline IR::Expr *operator*() const { Q_ASSERT(format == ex); return code; }
|
||||
inline IR::Expr *operator->() const { Q_ASSERT(format == ex); return code; }
|
||||
|
||||
bool accept(Format f)
|
||||
{
|
||||
if (requested == f) {
|
||||
format = f;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct UiMember {
|
||||
};
|
||||
|
||||
struct Loop {
|
||||
IR::BasicBlock *breakBlock;
|
||||
IR::BasicBlock *continueBlock;
|
||||
|
||||
Loop()
|
||||
: breakBlock(0), continueBlock(0) {}
|
||||
|
||||
Loop(IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock)
|
||||
: breakBlock(breakBlock), continueBlock(continueBlock) {}
|
||||
};
|
||||
|
||||
IR::Expr *member(IR::Expr *base, const QString *name);
|
||||
IR::Expr *subscript(IR::Expr *base, IR::Expr *index);
|
||||
IR::Expr *argument(IR::Expr *expr);
|
||||
IR::Expr *binop(IR::AluOp op, IR::Expr *left, IR::Expr *right);
|
||||
|
||||
void linearize(IR::Function *function);
|
||||
void defineFunction(AST::FunctionExpression *ast, bool isDeclaration = false);
|
||||
|
||||
void statement(AST::Statement *ast);
|
||||
void statement(AST::ExpressionNode *ast);
|
||||
void condition(AST::ExpressionNode *ast, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse);
|
||||
Result expression(AST::ExpressionNode *ast);
|
||||
QString propertyName(AST::PropertyName *ast);
|
||||
Result sourceElement(AST::SourceElement *ast);
|
||||
UiMember uiObjectMember(AST::UiObjectMember *ast);
|
||||
|
||||
void accept(AST::Node *node);
|
||||
|
||||
void argumentList(AST::ArgumentList *ast);
|
||||
void caseBlock(AST::CaseBlock *ast);
|
||||
void caseClause(AST::CaseClause *ast);
|
||||
void caseClauses(AST::CaseClauses *ast);
|
||||
void catchNode(AST::Catch *ast);
|
||||
void defaultClause(AST::DefaultClause *ast);
|
||||
void elementList(AST::ElementList *ast);
|
||||
void elision(AST::Elision *ast);
|
||||
void finallyNode(AST::Finally *ast);
|
||||
void formalParameterList(AST::FormalParameterList *ast);
|
||||
void functionBody(AST::FunctionBody *ast);
|
||||
void program(AST::Program *ast);
|
||||
void propertyNameAndValueList(AST::PropertyNameAndValueList *ast);
|
||||
void sourceElements(AST::SourceElements *ast);
|
||||
void statementList(AST::StatementList *ast);
|
||||
void uiArrayMemberList(AST::UiArrayMemberList *ast);
|
||||
void uiImport(AST::UiImport *ast);
|
||||
void uiImportList(AST::UiImportList *ast);
|
||||
void uiObjectInitializer(AST::UiObjectInitializer *ast);
|
||||
void uiObjectMemberList(AST::UiObjectMemberList *ast);
|
||||
void uiParameterList(AST::UiParameterList *ast);
|
||||
void uiProgram(AST::UiProgram *ast);
|
||||
void uiQualifiedId(AST::UiQualifiedId *ast);
|
||||
void variableDeclaration(AST::VariableDeclaration *ast);
|
||||
void variableDeclarationList(AST::VariableDeclarationList *ast);
|
||||
|
||||
// nodes
|
||||
virtual bool visit(AST::ArgumentList *ast);
|
||||
virtual bool visit(AST::CaseBlock *ast);
|
||||
virtual bool visit(AST::CaseClause *ast);
|
||||
virtual bool visit(AST::CaseClauses *ast);
|
||||
virtual bool visit(AST::Catch *ast);
|
||||
virtual bool visit(AST::DefaultClause *ast);
|
||||
virtual bool visit(AST::ElementList *ast);
|
||||
virtual bool visit(AST::Elision *ast);
|
||||
virtual bool visit(AST::Finally *ast);
|
||||
virtual bool visit(AST::FormalParameterList *ast);
|
||||
virtual bool visit(AST::FunctionBody *ast);
|
||||
virtual bool visit(AST::Program *ast);
|
||||
virtual bool visit(AST::PropertyNameAndValueList *ast);
|
||||
virtual bool visit(AST::SourceElements *ast);
|
||||
virtual bool visit(AST::StatementList *ast);
|
||||
virtual bool visit(AST::UiArrayMemberList *ast);
|
||||
virtual bool visit(AST::UiImport *ast);
|
||||
virtual bool visit(AST::UiImportList *ast);
|
||||
virtual bool visit(AST::UiObjectInitializer *ast);
|
||||
virtual bool visit(AST::UiObjectMemberList *ast);
|
||||
virtual bool visit(AST::UiParameterList *ast);
|
||||
virtual bool visit(AST::UiProgram *ast);
|
||||
virtual bool visit(AST::UiQualifiedId *ast);
|
||||
virtual bool visit(AST::VariableDeclaration *ast);
|
||||
virtual bool visit(AST::VariableDeclarationList *ast);
|
||||
|
||||
// expressions
|
||||
virtual bool visit(AST::Expression *ast);
|
||||
virtual bool visit(AST::ArrayLiteral *ast);
|
||||
virtual bool visit(AST::ArrayMemberExpression *ast);
|
||||
virtual bool visit(AST::BinaryExpression *ast);
|
||||
virtual bool visit(AST::CallExpression *ast);
|
||||
virtual bool visit(AST::ConditionalExpression *ast);
|
||||
virtual bool visit(AST::DeleteExpression *ast);
|
||||
virtual bool visit(AST::FalseLiteral *ast);
|
||||
virtual bool visit(AST::FieldMemberExpression *ast);
|
||||
virtual bool visit(AST::FunctionExpression *ast);
|
||||
virtual bool visit(AST::IdentifierExpression *ast);
|
||||
virtual bool visit(AST::NestedExpression *ast);
|
||||
virtual bool visit(AST::NewExpression *ast);
|
||||
virtual bool visit(AST::NewMemberExpression *ast);
|
||||
virtual bool visit(AST::NotExpression *ast);
|
||||
virtual bool visit(AST::NullExpression *ast);
|
||||
virtual bool visit(AST::NumericLiteral *ast);
|
||||
virtual bool visit(AST::ObjectLiteral *ast);
|
||||
virtual bool visit(AST::PostDecrementExpression *ast);
|
||||
virtual bool visit(AST::PostIncrementExpression *ast);
|
||||
virtual bool visit(AST::PreDecrementExpression *ast);
|
||||
virtual bool visit(AST::PreIncrementExpression *ast);
|
||||
virtual bool visit(AST::RegExpLiteral *ast);
|
||||
virtual bool visit(AST::StringLiteral *ast);
|
||||
virtual bool visit(AST::ThisExpression *ast);
|
||||
virtual bool visit(AST::TildeExpression *ast);
|
||||
virtual bool visit(AST::TrueLiteral *ast);
|
||||
virtual bool visit(AST::TypeOfExpression *ast);
|
||||
virtual bool visit(AST::UnaryMinusExpression *ast);
|
||||
virtual bool visit(AST::UnaryPlusExpression *ast);
|
||||
virtual bool visit(AST::VoidExpression *ast);
|
||||
virtual bool visit(AST::FunctionDeclaration *ast);
|
||||
|
||||
// property names
|
||||
virtual bool visit(AST::IdentifierPropertyName *ast);
|
||||
virtual bool visit(AST::NumericLiteralPropertyName *ast);
|
||||
virtual bool visit(AST::StringLiteralPropertyName *ast);
|
||||
|
||||
// source elements
|
||||
virtual bool visit(AST::FunctionSourceElement *ast);
|
||||
virtual bool visit(AST::StatementSourceElement *ast);
|
||||
|
||||
// statements
|
||||
virtual bool visit(AST::Block *ast);
|
||||
virtual bool visit(AST::BreakStatement *ast);
|
||||
virtual bool visit(AST::ContinueStatement *ast);
|
||||
virtual bool visit(AST::DebuggerStatement *ast);
|
||||
virtual bool visit(AST::DoWhileStatement *ast);
|
||||
virtual bool visit(AST::EmptyStatement *ast);
|
||||
virtual bool visit(AST::ExpressionStatement *ast);
|
||||
virtual bool visit(AST::ForEachStatement *ast);
|
||||
virtual bool visit(AST::ForStatement *ast);
|
||||
virtual bool visit(AST::IfStatement *ast);
|
||||
virtual bool visit(AST::LabelledStatement *ast);
|
||||
virtual bool visit(AST::LocalForEachStatement *ast);
|
||||
virtual bool visit(AST::LocalForStatement *ast);
|
||||
virtual bool visit(AST::ReturnStatement *ast);
|
||||
virtual bool visit(AST::SwitchStatement *ast);
|
||||
virtual bool visit(AST::ThrowStatement *ast);
|
||||
virtual bool visit(AST::TryStatement *ast);
|
||||
virtual bool visit(AST::VariableStatement *ast);
|
||||
virtual bool visit(AST::WhileStatement *ast);
|
||||
virtual bool visit(AST::WithStatement *ast);
|
||||
|
||||
// ui object members
|
||||
virtual bool visit(AST::UiArrayBinding *ast);
|
||||
virtual bool visit(AST::UiObjectBinding *ast);
|
||||
virtual bool visit(AST::UiObjectDefinition *ast);
|
||||
virtual bool visit(AST::UiPublicMember *ast);
|
||||
virtual bool visit(AST::UiScriptBinding *ast);
|
||||
virtual bool visit(AST::UiSourceElement *ast);
|
||||
|
||||
private:
|
||||
Result _expr;
|
||||
QString _property;
|
||||
UiMember _uiMember;
|
||||
Loop _loop;
|
||||
IR::Module *_module;
|
||||
IR::Function *_function;
|
||||
IR::BasicBlock *_block;
|
||||
IR::BasicBlock *_exitBlock;
|
||||
unsigned _returnAddress;
|
||||
};
|
||||
|
||||
} // end of namespace QQmlJS
|
||||
|
||||
#endif // QV4CODEGEN_P_H
|
|
@ -0,0 +1,697 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtQml module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia 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.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qv4ir_p.h"
|
||||
#include <private/qqmljsast_p.h>
|
||||
|
||||
#include <QtCore/qtextstream.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <math.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QQmlJS {
|
||||
namespace IR {
|
||||
|
||||
const char *typeName(Type t)
|
||||
{
|
||||
switch (t) {
|
||||
case InvalidType: return "invalid";
|
||||
case UndefinedType: return "undefined";
|
||||
case NullType: return "null";
|
||||
case VoidType: return "void";
|
||||
case StringType: return "string";
|
||||
case UrlType: return "QUrl";
|
||||
case ColorType: return "QColor";
|
||||
case SGAnchorLineType: return "SGAnchorLine";
|
||||
case AttachType: return "AttachType";
|
||||
case ObjectType: return "object";
|
||||
case VariantType: return "variant";
|
||||
case VarType: return "var";
|
||||
case BoolType: return "bool";
|
||||
case IntType: return "int";
|
||||
case FloatType: return "float";
|
||||
case NumberType: return "number";
|
||||
default: return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isNumberType(IR::Type ty)
|
||||
{
|
||||
return ty >= IR::FirstNumberType;
|
||||
}
|
||||
|
||||
inline bool isStringType(IR::Type ty)
|
||||
{
|
||||
return ty == IR::StringType || ty == IR::UrlType || ty == IR::ColorType;
|
||||
}
|
||||
|
||||
IR::Type maxType(IR::Type left, IR::Type right)
|
||||
{
|
||||
if (isStringType(left) && isStringType(right)) {
|
||||
// String promotions (url to string) are more specific than
|
||||
// identity conversions (AKA left == right). That's because
|
||||
// we want to ensure we convert urls to strings in binary
|
||||
// expressions.
|
||||
return IR::StringType;
|
||||
} else if (left == right)
|
||||
return left;
|
||||
else if (isNumberType(left) && isNumberType(right)) {
|
||||
IR::Type ty = qMax(left, right);
|
||||
return ty == FloatType ? NumberType : ty; // promote floats
|
||||
} else if ((isNumberType(left) && isStringType(right)) ||
|
||||
(isNumberType(right) && isStringType(left)))
|
||||
return IR::StringType;
|
||||
else
|
||||
return IR::InvalidType;
|
||||
}
|
||||
|
||||
bool isRealType(IR::Type type)
|
||||
{
|
||||
return type == IR::NumberType || type == IR::FloatType;
|
||||
}
|
||||
|
||||
const char *opname(AluOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case OpInvalid: return "?";
|
||||
|
||||
case OpIfTrue: return "(bool)";
|
||||
case OpNot: return "!";
|
||||
case OpUMinus: return "-";
|
||||
case OpUPlus: return "+";
|
||||
case OpCompl: return "~";
|
||||
|
||||
case OpBitAnd: return "&";
|
||||
case OpBitOr: return "|";
|
||||
case OpBitXor: return "^";
|
||||
|
||||
case OpAdd: return "+";
|
||||
case OpSub: return "-";
|
||||
case OpMul: return "*";
|
||||
case OpDiv: return "/";
|
||||
case OpMod: return "%";
|
||||
|
||||
case OpLShift: return "<<";
|
||||
case OpRShift: return ">>";
|
||||
case OpURShift: return ">>>";
|
||||
|
||||
case OpGt: return ">";
|
||||
case OpLt: return "<";
|
||||
case OpGe: return ">=";
|
||||
case OpLe: return "<=";
|
||||
case OpEqual: return "==";
|
||||
case OpNotEqual: return "!=";
|
||||
case OpStrictEqual: return "===";
|
||||
case OpStrictNotEqual: return "!==";
|
||||
|
||||
case OpAnd: return "&&";
|
||||
case OpOr: return "||";
|
||||
|
||||
default: return "?";
|
||||
|
||||
} // switch
|
||||
}
|
||||
|
||||
AluOp binaryOperator(int op)
|
||||
{
|
||||
switch (static_cast<QSOperator::Op>(op)) {
|
||||
case QSOperator::Add: return OpAdd;
|
||||
case QSOperator::And: return OpAnd;
|
||||
case QSOperator::BitAnd: return OpBitAnd;
|
||||
case QSOperator::BitOr: return OpBitOr;
|
||||
case QSOperator::BitXor: return OpBitXor;
|
||||
case QSOperator::Div: return OpDiv;
|
||||
case QSOperator::Equal: return OpEqual;
|
||||
case QSOperator::Ge: return OpGe;
|
||||
case QSOperator::Gt: return OpGt;
|
||||
case QSOperator::Le: return OpLe;
|
||||
case QSOperator::LShift: return OpLShift;
|
||||
case QSOperator::Lt: return OpLt;
|
||||
case QSOperator::Mod: return OpMod;
|
||||
case QSOperator::Mul: return OpMul;
|
||||
case QSOperator::NotEqual: return OpNotEqual;
|
||||
case QSOperator::Or: return OpOr;
|
||||
case QSOperator::RShift: return OpRShift;
|
||||
case QSOperator::StrictEqual: return OpStrictEqual;
|
||||
case QSOperator::StrictNotEqual: return OpStrictNotEqual;
|
||||
case QSOperator::Sub: return OpSub;
|
||||
case QSOperator::URShift: return OpURShift;
|
||||
default: return OpInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
void Const::dump(QTextStream &out)
|
||||
{
|
||||
switch (type) {
|
||||
case QQmlJS::IR::UndefinedType:
|
||||
out << "undefined";
|
||||
break;
|
||||
case QQmlJS::IR::NullType:
|
||||
out << "null";
|
||||
break;
|
||||
case QQmlJS::IR::VoidType:
|
||||
out << "void";
|
||||
break;
|
||||
case QQmlJS::IR::BoolType:
|
||||
out << (value ? "true" : "false");
|
||||
break;
|
||||
default:
|
||||
out << QString::number(value, 'g', 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void String::dump(QTextStream &out)
|
||||
{
|
||||
out << '"' << escape(*value) << '"';
|
||||
}
|
||||
|
||||
QString String::escape(const QString &s)
|
||||
{
|
||||
QString r;
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
const QChar ch = s.at(i);
|
||||
if (ch == QLatin1Char('\n'))
|
||||
r += QLatin1String("\\n");
|
||||
else if (ch == QLatin1Char('\r'))
|
||||
r += QLatin1String("\\r");
|
||||
else if (ch == QLatin1Char('\\'))
|
||||
r += QLatin1String("\\\\");
|
||||
else if (ch == QLatin1Char('"'))
|
||||
r += QLatin1String("\\\"");
|
||||
else if (ch == QLatin1Char('\''))
|
||||
r += QLatin1String("\\'");
|
||||
else
|
||||
r += ch;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void Name::init(Type type, const QString *id, quint32 line, quint32 column)
|
||||
{
|
||||
this->type = type;
|
||||
this->id = id;
|
||||
this->line = line;
|
||||
this->column = column;
|
||||
}
|
||||
|
||||
void Name::dump(QTextStream &out)
|
||||
{
|
||||
out << *id;
|
||||
}
|
||||
|
||||
void Temp::dump(QTextStream &out)
|
||||
{
|
||||
out << '%' << index;
|
||||
}
|
||||
|
||||
void Closure::dump(QTextStream &out)
|
||||
{
|
||||
QString name = value->name ? *value->name : QString();
|
||||
if (name.isEmpty())
|
||||
name.sprintf("%p", value);
|
||||
out << "closure(" << name << ')';
|
||||
}
|
||||
|
||||
void Unop::dump(QTextStream &out)
|
||||
{
|
||||
out << opname(op);
|
||||
expr->dump(out);
|
||||
}
|
||||
|
||||
Type Unop::typeForOp(AluOp op, Expr *expr)
|
||||
{
|
||||
switch (op) {
|
||||
case OpIfTrue: return BoolType;
|
||||
case OpNot: return BoolType;
|
||||
|
||||
case OpUMinus:
|
||||
case OpUPlus:
|
||||
case OpCompl:
|
||||
return maxType(expr->type, NumberType);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return InvalidType;
|
||||
}
|
||||
|
||||
void Binop::dump(QTextStream &out)
|
||||
{
|
||||
left->dump(out);
|
||||
out << ' ' << opname(op) << ' ';
|
||||
right->dump(out);
|
||||
}
|
||||
|
||||
Type Binop::typeForOp(AluOp op, Expr *left, Expr *right)
|
||||
{
|
||||
if (! (left && right))
|
||||
return InvalidType;
|
||||
|
||||
switch (op) {
|
||||
case OpInvalid:
|
||||
return InvalidType;
|
||||
|
||||
// unary operators
|
||||
case OpIfTrue:
|
||||
case OpNot:
|
||||
case OpUMinus:
|
||||
case OpUPlus:
|
||||
case OpCompl:
|
||||
return InvalidType;
|
||||
|
||||
// bit fields
|
||||
case OpBitAnd:
|
||||
case OpBitOr:
|
||||
case OpBitXor:
|
||||
return IntType;
|
||||
|
||||
case OpAdd:
|
||||
if (left->type == StringType)
|
||||
return StringType;
|
||||
return NumberType;
|
||||
|
||||
case OpSub:
|
||||
case OpMul:
|
||||
case OpDiv:
|
||||
case OpMod:
|
||||
return NumberType;
|
||||
|
||||
case OpLShift:
|
||||
case OpRShift:
|
||||
case OpURShift:
|
||||
return IntType;
|
||||
|
||||
case OpAnd:
|
||||
case OpOr:
|
||||
return BoolType;
|
||||
|
||||
case OpGt:
|
||||
case OpLt:
|
||||
case OpGe:
|
||||
case OpLe:
|
||||
case OpEqual:
|
||||
case OpNotEqual:
|
||||
case OpStrictEqual:
|
||||
case OpStrictNotEqual:
|
||||
return BoolType;
|
||||
} // switch
|
||||
|
||||
return InvalidType;
|
||||
}
|
||||
|
||||
void Call::dump(QTextStream &out)
|
||||
{
|
||||
base->dump(out);
|
||||
out << '(';
|
||||
for (ExprList *it = args; it; it = it->next) {
|
||||
if (it != args)
|
||||
out << ", ";
|
||||
it->expr->dump(out);
|
||||
}
|
||||
out << ')';
|
||||
}
|
||||
|
||||
Type Call::typeForFunction(Expr *)
|
||||
{
|
||||
return InvalidType;
|
||||
}
|
||||
|
||||
void New::dump(QTextStream &out)
|
||||
{
|
||||
out << "new ";
|
||||
base->dump(out);
|
||||
out << '(';
|
||||
for (ExprList *it = args; it; it = it->next) {
|
||||
if (it != args)
|
||||
out << ", ";
|
||||
it->expr->dump(out);
|
||||
}
|
||||
out << ')';
|
||||
}
|
||||
|
||||
Type New::typeForFunction(Expr *)
|
||||
{
|
||||
return InvalidType;
|
||||
}
|
||||
|
||||
void Subscript::dump(QTextStream &out)
|
||||
{
|
||||
base->dump(out);
|
||||
out << '[';
|
||||
index->dump(out);
|
||||
out << ']';
|
||||
}
|
||||
|
||||
void Member::dump(QTextStream &out)
|
||||
{
|
||||
base->dump(out);
|
||||
out << '.' << *name;
|
||||
}
|
||||
|
||||
void Exp::dump(QTextStream &out, Mode)
|
||||
{
|
||||
out << "(void) ";
|
||||
expr->dump(out);
|
||||
out << ';';
|
||||
}
|
||||
|
||||
void Enter::dump(QTextStream &out, Mode)
|
||||
{
|
||||
out << "%enter(";
|
||||
expr->dump(out);
|
||||
out << ");";
|
||||
}
|
||||
|
||||
void Leave::dump(QTextStream &out, Mode)
|
||||
{
|
||||
out << "%leave";
|
||||
out << ';';
|
||||
}
|
||||
|
||||
void Move::dump(QTextStream &out, Mode)
|
||||
{
|
||||
target->dump(out);
|
||||
out << ' ';
|
||||
if (op != OpInvalid)
|
||||
out << opname(op);
|
||||
out << "= ";
|
||||
// if (source->type != target->type)
|
||||
// out << typeName(source->type) << "_to_" << typeName(target->type) << '(';
|
||||
source->dump(out);
|
||||
// if (source->type != target->type)
|
||||
// out << ')';
|
||||
out << ';';
|
||||
}
|
||||
|
||||
void Jump::dump(QTextStream &out, Mode mode)
|
||||
{
|
||||
Q_UNUSED(mode);
|
||||
out << "goto " << 'L' << target << ';';
|
||||
}
|
||||
|
||||
void CJump::dump(QTextStream &out, Mode mode)
|
||||
{
|
||||
Q_UNUSED(mode);
|
||||
out << "if (";
|
||||
cond->dump(out);
|
||||
if (mode == HIR)
|
||||
out << ") goto " << 'L' << iftrue << "; else goto " << 'L' << iffalse << ';';
|
||||
else
|
||||
out << ") goto " << 'L' << iftrue << ";";
|
||||
}
|
||||
|
||||
void Ret::dump(QTextStream &out, Mode)
|
||||
{
|
||||
out << "return";
|
||||
if (expr) {
|
||||
out << ' ';
|
||||
expr->dump(out);
|
||||
}
|
||||
out << ';';
|
||||
}
|
||||
|
||||
Function *Module::newFunction(const QString &name)
|
||||
{
|
||||
Function *f = new Function(this, name);
|
||||
functions.append(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
Function::~Function()
|
||||
{
|
||||
qDeleteAll(basicBlocks);
|
||||
}
|
||||
|
||||
const QString *Function::newString(const QString &text)
|
||||
{
|
||||
return &*strings.insert(text);
|
||||
}
|
||||
|
||||
BasicBlock *Function::newBasicBlock()
|
||||
{
|
||||
return i(new BasicBlock(this));
|
||||
}
|
||||
|
||||
void Function::dump(QTextStream &out, Stmt::Mode mode)
|
||||
{
|
||||
QString n = name ? *name : QString();
|
||||
if (n.isEmpty())
|
||||
n.sprintf("%p", this);
|
||||
out << "function " << n << "() {" << endl;
|
||||
foreach (const QString *formal, formals)
|
||||
out << "\treceive " << *formal << ';' << endl;
|
||||
foreach (const QString *local, locals)
|
||||
out << "\tlocal " << *local << ';' << endl;
|
||||
foreach (BasicBlock *bb, basicBlocks)
|
||||
bb->dump(out, mode);
|
||||
out << '}' << endl;
|
||||
}
|
||||
|
||||
unsigned BasicBlock::newTemp()
|
||||
{
|
||||
return function->tempCount++;
|
||||
}
|
||||
|
||||
Temp *BasicBlock::TEMP(unsigned index)
|
||||
{
|
||||
Temp *e = function->New<Temp>();
|
||||
e->init(IR::InvalidType, index);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::CONST(Type type, double value)
|
||||
{
|
||||
Const *e = function->New<Const>();
|
||||
e->init(type, value);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::STRING(const QString *value)
|
||||
{
|
||||
String *e = function->New<String>();
|
||||
e->init(value);
|
||||
return e;
|
||||
}
|
||||
|
||||
Name *BasicBlock::NAME(const QString &id, quint32 line, quint32 column)
|
||||
{
|
||||
Name *e = function->New<Name>();
|
||||
e->init(InvalidType, function->newString(id), line, column);
|
||||
return e;
|
||||
}
|
||||
|
||||
Closure *BasicBlock::CLOSURE(Function *function)
|
||||
{
|
||||
Closure *clos = function->New<Closure>();
|
||||
clos->init(IR::InvalidType, function);
|
||||
return clos;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::UNOP(AluOp op, Expr *expr)
|
||||
{
|
||||
Unop *e = function->New<Unop>();
|
||||
e->init(op, expr);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right)
|
||||
{
|
||||
if (left && right) {
|
||||
if (Const *c1 = left->asConst()) {
|
||||
if (Const *c2 = right->asConst()) {
|
||||
const IR::Type ty = Binop::typeForOp(op, left, right);
|
||||
|
||||
switch (op) {
|
||||
case OpAdd: return CONST(ty, c1->value + c2->value);
|
||||
case OpAnd: return CONST(ty, c1->value ? c2->value : 0);
|
||||
case OpBitAnd: return CONST(ty, int(c1->value) & int(c2->value));
|
||||
case OpBitOr: return CONST(ty, int(c1->value) | int(c2->value));
|
||||
case OpBitXor: return CONST(ty, int(c1->value) ^ int(c2->value));
|
||||
case OpDiv: return CONST(ty, c1->value / c2->value);
|
||||
case OpEqual: return CONST(ty, c1->value == c2->value);
|
||||
case OpGe: return CONST(ty, c1->value >= c2->value);
|
||||
case OpGt: return CONST(ty, c1->value > c2->value);
|
||||
case OpLe: return CONST(ty, c1->value <= c2->value);
|
||||
case OpLShift: return CONST(ty, int(c1->value) << int(c2->value));
|
||||
case OpLt: return CONST(ty, c1->value < c2->value);
|
||||
case OpMod: return CONST(ty, ::fmod(c1->value, c2->value));
|
||||
case OpMul: return CONST(ty, c1->value * c2->value);
|
||||
case OpNotEqual: return CONST(ty, c1->value != c2->value);
|
||||
case OpOr: return CONST(ty, c1->value ? c1->value : c2->value);
|
||||
case OpRShift: return CONST(ty, int(c1->value) >> int(c2->value));
|
||||
case OpStrictEqual: return CONST(ty, c1->value == c2->value);
|
||||
case OpStrictNotEqual: return CONST(ty, c1->value != c2->value);
|
||||
case OpSub: return CONST(ty, c1->value - c2->value);
|
||||
case OpURShift: return CONST(ty, unsigned(c1->value) >> int(c2->value));
|
||||
|
||||
case OpIfTrue: // unary ops
|
||||
case OpNot:
|
||||
case OpUMinus:
|
||||
case OpUPlus:
|
||||
case OpCompl:
|
||||
case OpInvalid:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (op == OpAdd) {
|
||||
if (String *s1 = left->asString()) {
|
||||
if (String *s2 = right->asString()) {
|
||||
return STRING(function->newString(*s1->value + *s2->value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binop *e = function->New<Binop>();
|
||||
e->init(op, left, right);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::CALL(Expr *base, ExprList *args)
|
||||
{
|
||||
Call *e = function->New<Call>();
|
||||
e->init(base, args);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::NEW(Expr *base, ExprList *args)
|
||||
{
|
||||
New *e = function->New<New>();
|
||||
e->init(base, args);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index)
|
||||
{
|
||||
Subscript *e = function->New<Subscript>();
|
||||
e->init(base, index);
|
||||
return e;
|
||||
}
|
||||
|
||||
Expr *BasicBlock::MEMBER(Expr *base, const QString *name)
|
||||
{
|
||||
Member*e = function->New<Member>();
|
||||
e->init(base, name);
|
||||
return e;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::EXP(Expr *expr)
|
||||
{
|
||||
Exp *s = function->New<Exp>();
|
||||
s->init(expr);
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::ENTER(Expr *expr)
|
||||
{
|
||||
Enter *s = function->New<Enter>();
|
||||
s->init(expr);
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::LEAVE()
|
||||
{
|
||||
Leave *s = function->New<Leave>();
|
||||
s->init();
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::MOVE(Expr *target, Expr *source, AluOp op)
|
||||
{
|
||||
Move *s = function->New<Move>();
|
||||
s->init(target, source, op);
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::JUMP(BasicBlock *target)
|
||||
{
|
||||
if (isTerminated())
|
||||
return 0;
|
||||
|
||||
Jump *s = function->New<Jump>();
|
||||
s->init(target);
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
|
||||
{
|
||||
if (isTerminated())
|
||||
return 0;
|
||||
|
||||
CJump *s = function->New<CJump>();
|
||||
s->init(cond, iftrue, iffalse);
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Stmt *BasicBlock::RET(Expr *expr, Type type)
|
||||
{
|
||||
if (isTerminated())
|
||||
return 0;
|
||||
|
||||
Ret *s = function->New<Ret>();
|
||||
s->init(expr, type);
|
||||
statements.append(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void BasicBlock::dump(QTextStream &out, Stmt::Mode mode)
|
||||
{
|
||||
out << 'L' << this << ':' << endl;
|
||||
foreach (Stmt *s, statements) {
|
||||
out << '\t';
|
||||
s->dump(out, mode);
|
||||
out << endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // end of namespace IR
|
||||
} // end of namespace QQmlJS
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -0,0 +1,666 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the QtQml module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** 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, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia 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.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QV4IR_P_H
|
||||
#define QV4IR_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <private/qqmljsmemorypool_p.h>
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QString>
|
||||
|
||||
QT_BEGIN_HEADER
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QTextStream;
|
||||
class QQmlType;
|
||||
|
||||
namespace QQmlJS {
|
||||
|
||||
namespace IR {
|
||||
|
||||
struct BasicBlock;
|
||||
struct Function;
|
||||
struct Module;
|
||||
|
||||
struct Stmt;
|
||||
struct Expr;
|
||||
|
||||
// expressions
|
||||
struct Const;
|
||||
struct String;
|
||||
struct Name;
|
||||
struct Temp;
|
||||
struct Closure;
|
||||
struct Unop;
|
||||
struct Binop;
|
||||
struct Call;
|
||||
struct New;
|
||||
struct Subscript;
|
||||
struct Member;
|
||||
|
||||
// statements
|
||||
struct Exp;
|
||||
struct Enter;
|
||||
struct Leave;
|
||||
struct Move;
|
||||
struct Jump;
|
||||
struct CJump;
|
||||
struct Ret;
|
||||
|
||||
enum AluOp {
|
||||
OpInvalid = 0,
|
||||
|
||||
OpIfTrue,
|
||||
OpNot,
|
||||
OpUMinus,
|
||||
OpUPlus,
|
||||
OpCompl,
|
||||
|
||||
OpBitAnd,
|
||||
OpBitOr,
|
||||
OpBitXor,
|
||||
|
||||
OpAdd,
|
||||
OpSub,
|
||||
OpMul,
|
||||
OpDiv,
|
||||
OpMod,
|
||||
|
||||
OpLShift,
|
||||
OpRShift,
|
||||
OpURShift,
|
||||
|
||||
OpGt,
|
||||
OpLt,
|
||||
OpGe,
|
||||
OpLe,
|
||||
OpEqual,
|
||||
OpNotEqual,
|
||||
OpStrictEqual,
|
||||
OpStrictNotEqual,
|
||||
|
||||
OpAnd,
|
||||
OpOr
|
||||
};
|
||||
AluOp binaryOperator(int op);
|
||||
const char *opname(IR::AluOp op);
|
||||
|
||||
enum Type {
|
||||
InvalidType,
|
||||
UndefinedType,
|
||||
NullType,
|
||||
VoidType,
|
||||
StringType,
|
||||
UrlType,
|
||||
ColorType,
|
||||
SGAnchorLineType,
|
||||
AttachType,
|
||||
ObjectType,
|
||||
VariantType,
|
||||
VarType,
|
||||
|
||||
FirstNumberType,
|
||||
BoolType = FirstNumberType,
|
||||
IntType,
|
||||
FloatType,
|
||||
NumberType
|
||||
};
|
||||
Type maxType(IR::Type left, IR::Type right);
|
||||
bool isRealType(IR::Type type);
|
||||
const char *typeName(IR::Type t);
|
||||
|
||||
struct ExprVisitor {
|
||||
virtual ~ExprVisitor() {}
|
||||
virtual void visitConst(Const *) {}
|
||||
virtual void visitString(String *) {}
|
||||
virtual void visitName(Name *) {}
|
||||
virtual void visitTemp(Temp *) {}
|
||||
virtual void visitClosure(Closure *) {}
|
||||
virtual void visitUnop(Unop *) {}
|
||||
virtual void visitBinop(Binop *) {}
|
||||
virtual void visitCall(Call *) {}
|
||||
virtual void visitNew(New *) {}
|
||||
virtual void visitSubscript(Subscript *) {}
|
||||
virtual void visitMember(Member *) {}
|
||||
};
|
||||
|
||||
struct StmtVisitor {
|
||||
virtual ~StmtVisitor() {}
|
||||
virtual void visitExp(Exp *) {}
|
||||
virtual void visitEnter(Enter *) {}
|
||||
virtual void visitLeave(Leave *) {}
|
||||
virtual void visitMove(Move *) {}
|
||||
virtual void visitJump(Jump *) {}
|
||||
virtual void visitCJump(CJump *) {}
|
||||
virtual void visitRet(Ret *) {}
|
||||
};
|
||||
|
||||
struct Expr {
|
||||
Type type;
|
||||
|
||||
Expr(): type(InvalidType) {}
|
||||
virtual ~Expr() {}
|
||||
virtual void accept(ExprVisitor *) = 0;
|
||||
virtual Const *asConst() { return 0; }
|
||||
virtual String *asString() { return 0; }
|
||||
virtual Name *asName() { return 0; }
|
||||
virtual Temp *asTemp() { return 0; }
|
||||
virtual Closure *asClosure() { return 0; }
|
||||
virtual Unop *asUnop() { return 0; }
|
||||
virtual Binop *asBinop() { return 0; }
|
||||
virtual Call *asCall() { return 0; }
|
||||
virtual New *asNew() { return 0; }
|
||||
virtual Subscript *asSubscript() { return 0; }
|
||||
virtual Member *asMember() { return 0; }
|
||||
virtual void dump(QTextStream &out) = 0;
|
||||
};
|
||||
|
||||
struct ExprList {
|
||||
Expr *expr;
|
||||
ExprList *next;
|
||||
|
||||
void init(Expr *expr, ExprList *next = 0)
|
||||
{
|
||||
this->expr = expr;
|
||||
this->next = next;
|
||||
}
|
||||
};
|
||||
|
||||
struct Const: Expr {
|
||||
double value;
|
||||
|
||||
void init(Type type, double value)
|
||||
{
|
||||
this->type = type;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitConst(this); }
|
||||
virtual Const *asConst() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
};
|
||||
|
||||
struct String: Expr {
|
||||
const QString *value;
|
||||
|
||||
void init(const QString *value)
|
||||
{
|
||||
this->type = StringType;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitString(this); }
|
||||
virtual String *asString() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
static QString escape(const QString &s);
|
||||
};
|
||||
|
||||
struct Name: Expr {
|
||||
const QString *id;
|
||||
quint32 line;
|
||||
quint32 column;
|
||||
|
||||
void init(Type type, const QString *id, quint32 line, quint32 column);
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitName(this); }
|
||||
virtual Name *asName() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
};
|
||||
|
||||
struct Temp: Expr {
|
||||
unsigned index;
|
||||
|
||||
void init(Type type, unsigned index)
|
||||
{
|
||||
this->type = type;
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitTemp(this); }
|
||||
virtual Temp *asTemp() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
};
|
||||
|
||||
struct Closure: Expr {
|
||||
Function *value;
|
||||
|
||||
void init(Type type, Function *value)
|
||||
{
|
||||
this->type = type;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitClosure(this); }
|
||||
virtual Closure *asClosure() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
};
|
||||
|
||||
struct Unop: Expr {
|
||||
AluOp op;
|
||||
Expr *expr;
|
||||
|
||||
void init(AluOp op, Expr *expr)
|
||||
{
|
||||
this->type = this->typeForOp(op, expr);
|
||||
this->op = op;
|
||||
this->expr = expr;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitUnop(this); }
|
||||
virtual Unop *asUnop() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
|
||||
private:
|
||||
static Type typeForOp(AluOp op, Expr *expr);
|
||||
};
|
||||
|
||||
struct Binop: Expr {
|
||||
AluOp op;
|
||||
Expr *left;
|
||||
Expr *right;
|
||||
|
||||
void init(AluOp op, Expr *left, Expr *right)
|
||||
{
|
||||
this->type = typeForOp(op, left, right);
|
||||
this->op = op;
|
||||
this->left = left;
|
||||
this->right = right;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitBinop(this); }
|
||||
virtual Binop *asBinop() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
|
||||
static Type typeForOp(AluOp op, Expr *left, Expr *right);
|
||||
};
|
||||
|
||||
struct Call: Expr {
|
||||
Expr *base;
|
||||
ExprList *args;
|
||||
|
||||
void init(Expr *base, ExprList *args)
|
||||
{
|
||||
this->type = typeForFunction(base);
|
||||
this->base = base;
|
||||
this->args = args;
|
||||
}
|
||||
|
||||
Expr *onlyArgument() const {
|
||||
if (args && ! args->next)
|
||||
return args->expr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitCall(this); }
|
||||
virtual Call *asCall() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
|
||||
private:
|
||||
static Type typeForFunction(Expr *base);
|
||||
};
|
||||
|
||||
struct New: Expr {
|
||||
Expr *base;
|
||||
ExprList *args;
|
||||
|
||||
void init(Expr *base, ExprList *args)
|
||||
{
|
||||
this->type = typeForFunction(base);
|
||||
this->base = base;
|
||||
this->args = args;
|
||||
}
|
||||
|
||||
Expr *onlyArgument() const {
|
||||
if (args && ! args->next)
|
||||
return args->expr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitNew(this); }
|
||||
virtual New *asNew() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
|
||||
private:
|
||||
static Type typeForFunction(Expr *base);
|
||||
};
|
||||
|
||||
struct Subscript: Expr {
|
||||
Expr *base;
|
||||
Expr *index;
|
||||
|
||||
void init(Expr *base, Expr *index)
|
||||
{
|
||||
this->type = typeForFunction(base);
|
||||
this->base = base;
|
||||
this->index = index;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitSubscript(this); }
|
||||
virtual Subscript *asSubscript() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
|
||||
private:
|
||||
static Type typeForFunction(Expr *) { return IR::InvalidType; }
|
||||
};
|
||||
|
||||
struct Member: Expr {
|
||||
Expr *base;
|
||||
const QString *name;
|
||||
|
||||
void init(Expr *base, const QString *name)
|
||||
{
|
||||
this->type = typeForFunction(base);
|
||||
this->base = base;
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
virtual void accept(ExprVisitor *v) { v->visitMember(this); }
|
||||
virtual Member *asMember() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out);
|
||||
|
||||
private:
|
||||
static Type typeForFunction(Expr *) { return IR::InvalidType; }
|
||||
};
|
||||
|
||||
struct Stmt {
|
||||
enum Mode {
|
||||
HIR,
|
||||
MIR
|
||||
};
|
||||
|
||||
QVector<unsigned> uses;
|
||||
QVector<unsigned> defs;
|
||||
QVector<unsigned> liveIn;
|
||||
QVector<unsigned> liveOut;
|
||||
|
||||
virtual ~Stmt() {}
|
||||
virtual Stmt *asTerminator() { return 0; }
|
||||
|
||||
virtual void accept(StmtVisitor *) = 0;
|
||||
virtual Exp *asExp() { return 0; }
|
||||
virtual Move *asMove() { return 0; }
|
||||
virtual Enter *asEnter() { return 0; }
|
||||
virtual Leave *asLeave() { return 0; }
|
||||
virtual Jump *asJump() { return 0; }
|
||||
virtual CJump *asCJump() { return 0; }
|
||||
virtual Ret *asRet() { return 0; }
|
||||
virtual void dump(QTextStream &out, Mode mode = HIR) = 0;
|
||||
};
|
||||
|
||||
struct Exp: Stmt {
|
||||
Expr *expr;
|
||||
|
||||
void init(Expr *expr)
|
||||
{
|
||||
this->expr = expr;
|
||||
}
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitExp(this); }
|
||||
virtual Exp *asExp() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode);
|
||||
};
|
||||
|
||||
struct Move: Stmt {
|
||||
Expr *target;
|
||||
Expr *source;
|
||||
AluOp op;
|
||||
|
||||
void init(Expr *target, Expr *source, AluOp op)
|
||||
{
|
||||
this->target = target;
|
||||
this->source = source;
|
||||
this->op = op;
|
||||
}
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitMove(this); }
|
||||
virtual Move *asMove() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode);
|
||||
};
|
||||
|
||||
struct Enter: Stmt {
|
||||
Expr *expr;
|
||||
|
||||
void init(Expr *expr)
|
||||
{
|
||||
this->expr = expr;
|
||||
}
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitEnter(this); }
|
||||
virtual Enter *asEnter() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode);
|
||||
};
|
||||
|
||||
struct Leave: Stmt {
|
||||
void init() {}
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitLeave(this); }
|
||||
virtual Leave *asLeave() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode);
|
||||
};
|
||||
|
||||
struct Jump: Stmt {
|
||||
BasicBlock *target;
|
||||
|
||||
void init(BasicBlock *target)
|
||||
{
|
||||
this->target = target;
|
||||
}
|
||||
|
||||
virtual Stmt *asTerminator() { return this; }
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitJump(this); }
|
||||
virtual Jump *asJump() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode mode);
|
||||
};
|
||||
|
||||
struct CJump: Stmt {
|
||||
Expr *cond;
|
||||
BasicBlock *iftrue;
|
||||
BasicBlock *iffalse;
|
||||
|
||||
void init(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse)
|
||||
{
|
||||
this->cond = cond;
|
||||
this->iftrue = iftrue;
|
||||
this->iffalse = iffalse;
|
||||
}
|
||||
|
||||
virtual Stmt *asTerminator() { return this; }
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitCJump(this); }
|
||||
virtual CJump *asCJump() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode mode);
|
||||
};
|
||||
|
||||
struct Ret: Stmt {
|
||||
Expr *expr;
|
||||
Type type;
|
||||
|
||||
void init(Expr *expr, Type type)
|
||||
{
|
||||
this->expr = expr;
|
||||
this->type = type;
|
||||
}
|
||||
|
||||
virtual Stmt *asTerminator() { return this; }
|
||||
|
||||
virtual void accept(StmtVisitor *v) { v->visitRet(this); }
|
||||
virtual Ret *asRet() { return this; }
|
||||
|
||||
virtual void dump(QTextStream &out, Mode);
|
||||
};
|
||||
|
||||
struct Module {
|
||||
MemoryPool pool;
|
||||
QVector<Function *> functions;
|
||||
|
||||
Function *newFunction(const QString &name);
|
||||
};
|
||||
|
||||
struct Function {
|
||||
Module *module;
|
||||
MemoryPool *pool;
|
||||
const QString *name;
|
||||
QVector<BasicBlock *> basicBlocks;
|
||||
int tempCount;
|
||||
QSet<QString> strings;
|
||||
QList<const QString *> formals;
|
||||
QList<const QString *> locals;
|
||||
|
||||
template <typename _Tp> _Tp *New() { return new (pool->allocate(sizeof(_Tp))) _Tp(); }
|
||||
|
||||
Function(Module *module, const QString &name)
|
||||
: module(module), pool(&module->pool), tempCount(0) { this->name = newString(name); }
|
||||
|
||||
~Function();
|
||||
|
||||
BasicBlock *newBasicBlock();
|
||||
const QString *newString(const QString &text);
|
||||
|
||||
void RECEIVE(const QString &name) { formals.append(newString(name)); }
|
||||
void LOCAL(const QString &name) { locals.append(newString(name)); }
|
||||
|
||||
inline BasicBlock *i(BasicBlock *block) { basicBlocks.append(block); return block; }
|
||||
|
||||
void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
|
||||
};
|
||||
|
||||
struct BasicBlock {
|
||||
Function *function;
|
||||
QVector<Stmt *> statements;
|
||||
QVector<BasicBlock *> in;
|
||||
QVector<BasicBlock *> out;
|
||||
QVector<unsigned> liveIn;
|
||||
QVector<unsigned> liveOut;
|
||||
int index;
|
||||
int offset;
|
||||
|
||||
BasicBlock(Function *function): function(function), index(-1), offset(-1) {}
|
||||
~BasicBlock() {}
|
||||
|
||||
template <typename Instr> inline Instr i(Instr i) { statements.append(i); return i; }
|
||||
|
||||
inline bool isEmpty() const {
|
||||
return statements.isEmpty();
|
||||
}
|
||||
|
||||
inline Stmt *terminator() const {
|
||||
if (! statements.isEmpty() && statements.at(statements.size() - 1)->asTerminator() != 0)
|
||||
return statements.at(statements.size() - 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool isTerminated() const {
|
||||
if (terminator() != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned newTemp();
|
||||
|
||||
Temp *TEMP(unsigned index);
|
||||
|
||||
Expr *CONST(Type type, double value);
|
||||
Expr *STRING(const QString *value);
|
||||
|
||||
Name *NAME(const QString &id, quint32 line, quint32 column);
|
||||
|
||||
Closure *CLOSURE(Function *function);
|
||||
|
||||
Expr *UNOP(AluOp op, Expr *expr);
|
||||
Expr *BINOP(AluOp op, Expr *left, Expr *right);
|
||||
Expr *CALL(Expr *base, ExprList *args = 0);
|
||||
Expr *NEW(Expr *base, ExprList *args = 0);
|
||||
Expr *SUBSCRIPT(Expr *base, Expr *index);
|
||||
Expr *MEMBER(Expr *base, const QString *name);
|
||||
|
||||
Stmt *EXP(Expr *expr);
|
||||
Stmt *ENTER(Expr *expr);
|
||||
Stmt *LEAVE();
|
||||
|
||||
Stmt *MOVE(Expr *target, Expr *source, AluOp op = IR::OpInvalid);
|
||||
|
||||
Stmt *JUMP(BasicBlock *target);
|
||||
Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse);
|
||||
Stmt *RET(Expr *expr, Type type);
|
||||
|
||||
void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR);
|
||||
};
|
||||
|
||||
} // end of namespace IR
|
||||
|
||||
} // end of namespace QQmlJS
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QT_END_HEADER
|
||||
|
||||
#endif // QV4IR_P_H
|
|
@ -0,0 +1,319 @@
|
|||
|
||||
#include "qv4isel_p.h"
|
||||
#include "qmljs_runtime.h"
|
||||
#include "qmljs_objects.h"
|
||||
|
||||
#define TARGET_AMD64
|
||||
typedef quint64 guint64;
|
||||
typedef qint64 gint64;
|
||||
typedef uchar guint8;
|
||||
typedef uint guint32;
|
||||
typedef void *gpointer;
|
||||
#include "amd64-codegen.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#ifndef NO_UDIS86
|
||||
# include <udis86.h>
|
||||
#endif
|
||||
|
||||
using namespace QQmlJS;
|
||||
|
||||
static inline bool protect(const void *addr, size_t size)
|
||||
{
|
||||
size_t pageSize = sysconf(_SC_PAGESIZE);
|
||||
size_t iaddr = reinterpret_cast<size_t>(addr);
|
||||
size_t roundAddr = iaddr & ~(pageSize - static_cast<size_t>(1));
|
||||
int mode = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
return mprotect(reinterpret_cast<void*>(roundAddr), size + (iaddr - roundAddr), mode) == 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
amd64_patch (unsigned char* code, gpointer target)
|
||||
{
|
||||
guint8 rex = 0;
|
||||
|
||||
#ifdef __native_client_codegen__
|
||||
code = amd64_skip_nops (code);
|
||||
#endif
|
||||
#if defined(__native_client_codegen__) && defined(__native_client__)
|
||||
if (nacl_is_code_address (code)) {
|
||||
/* For tail calls, code is patched after being installed */
|
||||
/* but not through the normal "patch callsite" method. */
|
||||
unsigned char buf[kNaClAlignment];
|
||||
unsigned char *aligned_code = (uintptr_t)code & ~kNaClAlignmentMask;
|
||||
int ret;
|
||||
memcpy (buf, aligned_code, kNaClAlignment);
|
||||
/* Patch a temp buffer of bundle size, */
|
||||
/* then install to actual location. */
|
||||
amd64_patch (buf + ((uintptr_t)code - (uintptr_t)aligned_code), target);
|
||||
ret = nacl_dyncode_modify (aligned_code, buf, kNaClAlignment);
|
||||
g_assert (ret == 0);
|
||||
return;
|
||||
}
|
||||
target = nacl_modify_patch_target (target);
|
||||
#endif
|
||||
|
||||
/* Skip REX */
|
||||
if ((code [0] >= 0x40) && (code [0] <= 0x4f)) {
|
||||
rex = code [0];
|
||||
code += 1;
|
||||
}
|
||||
|
||||
if ((code [0] & 0xf8) == 0xb8) {
|
||||
/* amd64_set_reg_template */
|
||||
*(guint64*)(code + 1) = (guint64)target;
|
||||
}
|
||||
else if ((code [0] == 0x8b) && rex && x86_modrm_mod (code [1]) == 0 && x86_modrm_rm (code [1]) == 5) {
|
||||
/* mov 0(%rip), %dreg */
|
||||
*(guint32*)(code + 2) = (guint32)(guint64)target - 7;
|
||||
}
|
||||
else if ((code [0] == 0xff) && (code [1] == 0x15)) {
|
||||
/* call *<OFFSET>(%rip) */
|
||||
*(guint32*)(code + 2) = ((guint32)(guint64)target) - 7;
|
||||
}
|
||||
else if (code [0] == 0xe8) {
|
||||
/* call <DISP> */
|
||||
gint64 disp = (guint8*)target - (guint8*)code;
|
||||
assert (amd64_is_imm32 (disp));
|
||||
x86_patch (code, (unsigned char*)target);
|
||||
}
|
||||
else
|
||||
x86_patch (code, (unsigned char*)target);
|
||||
}
|
||||
InstructionSelection::InstructionSelection(IR::Module *module)
|
||||
: _module(module)
|
||||
, _function(0)
|
||||
, _block(0)
|
||||
, _code(0)
|
||||
, _codePtr(0)
|
||||
{
|
||||
}
|
||||
|
||||
InstructionSelection::~InstructionSelection()
|
||||
{
|
||||
}
|
||||
|
||||
void InstructionSelection::visitFunction(IR::Function *function)
|
||||
{
|
||||
uchar *code = (uchar *) malloc(getpagesize());
|
||||
Q_ASSERT(! (size_t(code) & 15));
|
||||
|
||||
protect(code, getpagesize());
|
||||
|
||||
uchar *codePtr = code;
|
||||
|
||||
qSwap(_code, code);
|
||||
qSwap(_codePtr, codePtr);
|
||||
|
||||
int locals = function->tempCount * sizeof(Value);
|
||||
locals = (locals + 15) & ~15;
|
||||
|
||||
amd64_push_reg(_codePtr, AMD64_R14);
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8);
|
||||
amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals);
|
||||
|
||||
foreach (IR::BasicBlock *block, function->basicBlocks) {
|
||||
_block = block;
|
||||
_addrs[block] = _codePtr;
|
||||
foreach (IR::Stmt *s, block->statements) {
|
||||
s->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
QHashIterator<IR::BasicBlock *, QVector<uchar *> > it(_patches);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
uchar *target = _addrs[it.key()];
|
||||
foreach (uchar *instr, it.value()) {
|
||||
amd64_patch(instr, target);
|
||||
}
|
||||
}
|
||||
|
||||
amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals);
|
||||
amd64_pop_reg(_codePtr, AMD64_R14);
|
||||
amd64_ret(_codePtr);
|
||||
|
||||
qSwap(_codePtr, codePtr);
|
||||
qSwap(_code, code);
|
||||
|
||||
static bool showCode = !qgetenv("SHOW_CODE").isNull();
|
||||
if (showCode) {
|
||||
#ifndef NO_UDIS86
|
||||
ud_t ud_obj;
|
||||
|
||||
ud_init(&ud_obj);
|
||||
ud_set_input_buffer(&ud_obj, code, codePtr - code);
|
||||
ud_set_mode(&ud_obj, 64);
|
||||
ud_set_syntax(&ud_obj, UD_SYN_INTEL);
|
||||
|
||||
while (ud_disassemble(&ud_obj)) {
|
||||
printf("\t%s\n", ud_insn_asm(&ud_obj));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void (*f)(Context *) = (void (*)(Context *)) code;
|
||||
|
||||
Context *ctx = new (GC) Context;
|
||||
ctx->activation = Value::object(ctx, new (GC) ArgumentsObject);
|
||||
f(ctx);
|
||||
Value d;
|
||||
ctx->activation.objectValue->get(String::get(ctx, QLatin1String("d")), &d);
|
||||
__qmljs_to_string(ctx, &d, &d);
|
||||
qDebug() << qPrintable(d.stringValue->text());
|
||||
}
|
||||
|
||||
String *InstructionSelection::identifier(const QString &s)
|
||||
{
|
||||
String *&id = _identifiers[s];
|
||||
if (! id)
|
||||
id = new (GC) String(s);
|
||||
return id;
|
||||
}
|
||||
|
||||
void InstructionSelection::visitExp(IR::Exp *s)
|
||||
{
|
||||
// if (IR::Call *c = s->expr->asCall()) {
|
||||
// return;
|
||||
// }
|
||||
Q_ASSERT(!"TODO");
|
||||
}
|
||||
|
||||
void InstructionSelection::visitEnter(IR::Enter *)
|
||||
{
|
||||
Q_ASSERT(!"TODO");
|
||||
}
|
||||
|
||||
void InstructionSelection::visitLeave(IR::Leave *)
|
||||
{
|
||||
Q_ASSERT(!"TODO");
|
||||
}
|
||||
|
||||
void InstructionSelection::visitMove(IR::Move *s)
|
||||
{
|
||||
// %rdi, %rsi, %rdx, %rcx, %r8 and %r9
|
||||
if (s->op == IR::OpInvalid) {
|
||||
if (IR::Name *n = s->target->asName()) {
|
||||
String *propertyName = identifier(*n->id);
|
||||
|
||||
if (IR::Const *c = s->source->asConst()) {
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
|
||||
amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName);
|
||||
amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value);
|
||||
amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX);
|
||||
amd64_call_code(_codePtr, __qmljs_set_activation_property_number);
|
||||
return;
|
||||
} else if (IR::Temp *t = s->source->asTemp()) {
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
|
||||
amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName);
|
||||
amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 8 + sizeof(Value) * (t->index - 1));
|
||||
amd64_call_code(_codePtr, __qmljs_set_activation_property);
|
||||
return;
|
||||
}
|
||||
} else if (IR::Temp *t = s->target->asTemp()) {
|
||||
const int offset = 8 + sizeof(Value) * (t->index - 1);
|
||||
if (IR::Name *n = s->source->asName()) {
|
||||
String *propertyName = identifier(*n->id);
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
|
||||
amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset);
|
||||
amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName);
|
||||
amd64_call_code(_codePtr, __qmljs_get_activation_property);
|
||||
return;
|
||||
} else if (IR::Const *c = s->source->asConst()) {
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
|
||||
amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset);
|
||||
amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value);
|
||||
amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX);
|
||||
amd64_call_code(_codePtr, __qmljs_init_number);
|
||||
return;
|
||||
} else if (IR::Binop *b = s->source->asBinop()) {
|
||||
IR::Temp *l = b->left->asTemp();
|
||||
IR::Temp *r = b->right->asTemp();
|
||||
if (l && r) {
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
|
||||
amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, offset);
|
||||
amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 8 + sizeof(Value) * (l->index - 1));
|
||||
amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 8 + sizeof(Value) * (r->index - 1));
|
||||
|
||||
void (*op)(Context *, Value *, const Value *, const Value *) = 0;
|
||||
|
||||
switch (b->op) {
|
||||
case QQmlJS::IR::OpInvalid:
|
||||
case QQmlJS::IR::OpIfTrue:
|
||||
case QQmlJS::IR::OpNot:
|
||||
case QQmlJS::IR::OpUMinus:
|
||||
case QQmlJS::IR::OpUPlus:
|
||||
case QQmlJS::IR::OpCompl:
|
||||
Q_ASSERT(!"unreachable");
|
||||
break;
|
||||
|
||||
case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break;
|
||||
case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break;
|
||||
case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break;
|
||||
case QQmlJS::IR::OpAdd: op = __qmljs_add; break;
|
||||
case QQmlJS::IR::OpSub: op = __qmljs_sub; break;
|
||||
case QQmlJS::IR::OpMul: op = __qmljs_mul; break;
|
||||
case QQmlJS::IR::OpDiv: op = __qmljs_div; break;
|
||||
case QQmlJS::IR::OpMod: op = __qmljs_mod; break;
|
||||
case QQmlJS::IR::OpLShift: op = __qmljs_shl; break;
|
||||
case QQmlJS::IR::OpRShift: op = __qmljs_shr; break;
|
||||
case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break;
|
||||
case QQmlJS::IR::OpGt: op = __qmljs_gt; break;
|
||||
case QQmlJS::IR::OpLt: op = __qmljs_lt; break;
|
||||
case QQmlJS::IR::OpGe: op = __qmljs_ge; break;
|
||||
case QQmlJS::IR::OpLe: op = __qmljs_le; break;
|
||||
case QQmlJS::IR::OpEqual: op = __qmljs_eq; break;
|
||||
case QQmlJS::IR::OpNotEqual: op = __qmljs_ne; break;
|
||||
case QQmlJS::IR::OpStrictEqual: op = __qmljs_se; break;
|
||||
case QQmlJS::IR::OpStrictNotEqual: op = __qmljs_sne; break;
|
||||
|
||||
case QQmlJS::IR::OpAnd:
|
||||
case QQmlJS::IR::OpOr:
|
||||
Q_ASSERT(!"unreachable");
|
||||
break;
|
||||
}
|
||||
amd64_call_code(_codePtr, op);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// inplace assignment, e.g. x += 1, ++x, ...
|
||||
}
|
||||
Q_ASSERT(!"TODO");
|
||||
}
|
||||
|
||||
void InstructionSelection::visitJump(IR::Jump *s)
|
||||
{
|
||||
if (_block->index + 1 != s->target->index) {
|
||||
_patches[s->target].append(_codePtr);
|
||||
amd64_jump32(_codePtr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void InstructionSelection::visitCJump(IR::CJump *s)
|
||||
{
|
||||
if (IR::Temp *t = s->cond->asTemp()) {
|
||||
amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8);
|
||||
amd64_lea_membase(_codePtr, AMD64_RSI, AMD64_RSP, 8 + sizeof(Value) * (t->index - 1));
|
||||
amd64_call_code(_codePtr, __qmljs_to_boolean);
|
||||
amd64_alu_reg_imm_size(_codePtr, X86_CMP, X86_EAX, 0, 4);
|
||||
_patches[s->iftrue].append(_codePtr);
|
||||
amd64_branch32(_codePtr, X86_CC_NZ, 0, 1);
|
||||
|
||||
if (_block->index + 1 != s->iffalse->index) {
|
||||
_patches[s->iffalse].append(_codePtr);
|
||||
amd64_jump32(_codePtr, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(!"TODO");
|
||||
}
|
||||
|
||||
void InstructionSelection::visitRet(IR::Ret *s)
|
||||
{
|
||||
qWarning() << "TODO: RET";
|
||||
//Q_ASSERT(!"TODO");
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef QV4ISEL_P_H
|
||||
#define QV4ISEL_P_H
|
||||
|
||||
#include "qv4ir_p.h"
|
||||
#include "qmljs_objects.h"
|
||||
|
||||
#include <QtCore/QHash>
|
||||
|
||||
namespace QQmlJS {
|
||||
|
||||
class InstructionSelection: protected IR::StmtVisitor
|
||||
{
|
||||
public:
|
||||
InstructionSelection(IR::Module *module);
|
||||
~InstructionSelection();
|
||||
|
||||
void visitFunction(IR::Function *function);
|
||||
|
||||
protected:
|
||||
String *identifier(const QString &s);
|
||||
|
||||
virtual void visitExp(IR::Exp *);
|
||||
virtual void visitEnter(IR::Enter *);
|
||||
virtual void visitLeave(IR::Leave *);
|
||||
virtual void visitMove(IR::Move *);
|
||||
virtual void visitJump(IR::Jump *);
|
||||
virtual void visitCJump(IR::CJump *);
|
||||
virtual void visitRet(IR::Ret *);
|
||||
|
||||
private:
|
||||
IR::Module *_module;
|
||||
IR::Function *_function;
|
||||
IR::BasicBlock *_block;
|
||||
uchar *_code;
|
||||
uchar *_codePtr;
|
||||
QHash<IR::BasicBlock *, QVector<uchar *> > _patches;
|
||||
QHash<IR::BasicBlock *, uchar *> _addrs;
|
||||
QHash<QString, String *> _identifiers;
|
||||
};
|
||||
|
||||
} // end of namespace QQmlJS
|
||||
|
||||
#endif // QV4ISEL_P_H
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
var a = 1
|
||||
var b = 2
|
||||
var c = 10
|
||||
var d = 100
|
||||
|
||||
for (i = 0; i < 1000000; i = i + 1) {
|
||||
if (a == 1)
|
||||
d = a + b * c
|
||||
else
|
||||
d = 321
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
QT = core qmldevtools-private
|
||||
CONFIG -= app_bundle
|
||||
CONFIG += console
|
||||
|
||||
DEFINES += __default_codegen__
|
||||
|
||||
LIBS += -lgc
|
||||
|
||||
udis86:LIBS += -ludis86
|
||||
else:DEFINES += NO_UDIS86
|
||||
|
||||
SOURCES += main.cpp \
|
||||
qv4codegen.cpp \
|
||||
qv4ir.cpp \
|
||||
qmljs_runtime.cpp \
|
||||
qmljs_objects.cpp \
|
||||
qv4isel.cpp
|
||||
|
||||
HEADERS += \
|
||||
qv4codegen_p.h \
|
||||
qv4ir_p.h \
|
||||
qmljs_runtime.h \
|
||||
qmljs_objects.h \
|
||||
qv4isel_p.h \
|
||||
x86-codegen.h \
|
||||
amd64-codegen.h
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue