Initial import

This commit is contained in:
Roberto Raggi 2012-04-16 21:23:25 +02:00
commit 47062ff244
15 changed files with 9630 additions and 0 deletions

1831
amd64-codegen.h Normal file

File diff suppressed because it is too large Load Diff

50
main.cpp Normal file
View File

@ -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);
}
}
}
}

109
qmljs_objects.cpp Normal file
View File

@ -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;
}

264
qmljs_objects.h Normal file
View File

@ -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

339
qmljs_runtime.cpp Normal file
View File

@ -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"

623
qmljs_runtime.h Normal file
View File

@ -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

1780
qv4codegen.cpp Normal file

File diff suppressed because it is too large Load Diff

228
qv4codegen_p.h Normal file
View File

@ -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

697
qv4ir.cpp Normal file
View File

@ -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

666
qv4ir_p.h Normal file
View File

@ -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

319
qv4isel.cpp Normal file
View File

@ -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");
}

43
qv4isel_p.h Normal file
View File

@ -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

12
tests/simple.js Normal file
View File

@ -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
}

29
v4.pro Normal file
View File

@ -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

2640
x86-codegen.h Normal file

File diff suppressed because it is too large Load Diff