blob: 4ac570b6000395e4cdfd715d4b80abb307daec40 [file] [log] [blame]
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/ast.h"
#include "vm/compiler.h"
#include "vm/dart_entry.h"
#include "vm/isolate.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
namespace dart {
#define DEFINE_VISIT_FUNCTION(type, name) \
void type::Visit(AstNodeVisitor* visitor) { \
visitor->Visit##type(this); \
}
NODE_LIST(DEFINE_VISIT_FUNCTION)
#undef DEFINE_VISIT_FUNCTION
#define DEFINE_NAME_FUNCTION(type, name) \
const char* type::ShortName() const { \
return name; \
}
NODE_LIST(DEFINE_NAME_FUNCTION)
#undef DEFINE_NAME_FUNCTION
// A visitor class to collect all the nodes (including children) into an
// array.
class AstNodeCollector : public AstNodeVisitor {
public:
explicit AstNodeCollector(GrowableArray<AstNode*>* nodes)
: nodes_(nodes) { }
#define DEFINE_VISITOR_FUNCTION(type, name) \
virtual void Visit##type(type* node) { \
nodes_->Add(node); \
node->VisitChildren(this); \
}
NODE_LIST(DEFINE_VISITOR_FUNCTION)
#undef DEFINE_VISITOR_FUNCTION
private:
GrowableArray<AstNode*>* nodes_;
DISALLOW_COPY_AND_ASSIGN(AstNodeCollector);
};
void SequenceNode::CollectAllNodes(GrowableArray<AstNode*>* nodes) {
AstNodeCollector node_collector(nodes);
this->Visit(&node_collector);
}
void SequenceNode::VisitChildren(AstNodeVisitor* visitor) const {
for (intptr_t i = 0; i < this->length(); i++) {
NodeAt(i)->Visit(visitor);
}
}
void PrimaryNode::VisitChildren(AstNodeVisitor* visitor) const {
}
void ArgumentListNode::VisitChildren(AstNodeVisitor* visitor) const {
for (intptr_t i = 0; i < this->length(); i++) {
NodeAt(i)->Visit(visitor);
}
}
void ArrayNode::VisitChildren(AstNodeVisitor* visitor) const {
for (intptr_t i = 0; i < this->length(); i++) {
ElementAt(i)->Visit(visitor);
}
}
// TODO(srdjan): Add code for logical negation.
AstNode* LiteralNode::ApplyUnaryOp(Token::Kind unary_op_kind) {
if (unary_op_kind == Token::kNEGATE) {
if (literal().IsSmi()) {
const Smi& smi = Smi::Cast(literal());
const Instance& literal =
Instance::ZoneHandle(Integer::New(-smi.Value()));
return new LiteralNode(this->token_pos(), literal);
}
if (literal().IsDouble()) {
const Double& dbl = Double::Cast(literal());
// Preserve negative zero.
double new_value = (dbl.value() == 0.0) ? -0.0 : (0.0 - dbl.value());
const Double& double_instance =
Double::ZoneHandle(Double::NewCanonical(new_value));
return new LiteralNode(this->token_pos(), double_instance);
}
}
return NULL;
}
bool ComparisonNode::IsKindValid() const {
return Token::IsRelationalOperator(kind_)
|| Token::IsEqualityOperator(kind_)
|| Token::IsTypeTestOperator(kind_)
|| Token::IsTypeCastOperator(kind_);
}
const char* ComparisonNode::Name() const {
return Token::Str(kind_);
}
const Instance* ComparisonNode::EvalConstExpr() const {
const Instance* left_val = this->left()->EvalConstExpr();
if (left_val == NULL) {
return NULL;
}
const Instance* right_val = this->right()->EvalConstExpr();
if (right_val == NULL) {
return NULL;
}
switch (kind_) {
case Token::kLT:
case Token::kGT:
case Token::kLTE:
case Token::kGTE:
if ((left_val->IsNumber() || left_val->IsNull()) &&
(right_val->IsNumber() || right_val->IsNull())) {
return &Bool::False();
}
return NULL;
case Token::kEQ:
case Token::kNE:
case Token::kEQ_STRICT:
case Token::kNE_STRICT:
// The comparison is a compile time const if both operands are either a
// number, string, or boolean value (but not necessarily the same type).
if ((left_val->IsNumber() ||
left_val->IsString() ||
left_val->IsBool() ||
left_val->IsNull()) &&
(right_val->IsNumber() ||
right_val->IsString() ||
right_val->IsBool() ||
right_val->IsNull())) {
return &Bool::False();
}
return NULL;
default:
return NULL;
}
return NULL;
}
bool BinaryOpNode::IsKindValid() const {
switch (kind_) {
case Token::kADD:
case Token::kSUB:
case Token::kMUL:
case Token::kDIV:
case Token::kTRUNCDIV:
case Token::kMOD:
case Token::kOR:
case Token::kAND:
case Token::kBIT_OR:
case Token::kBIT_XOR:
case Token::kBIT_AND:
case Token::kSHL:
case Token::kSHR:
return true;
default:
return false;
}
}
const char* BinaryOpNode::Name() const {
return Token::Str(kind_);
}
const Instance* BinaryOpNode::EvalConstExpr() const {
const Instance* left_val = this->left()->EvalConstExpr();
if (left_val == NULL) {
return NULL;
}
if (!left_val->IsNumber() && !left_val->IsBool()) {
return NULL;
}
const Instance* right_val = this->right()->EvalConstExpr();
if (right_val == NULL) {
return NULL;
}
switch (kind_) {
case Token::kADD:
case Token::kSUB:
case Token::kMUL:
case Token::kDIV:
case Token::kMOD:
case Token::kTRUNCDIV:
if (left_val->IsInteger()) {
if (right_val->IsInteger()) {
return left_val;
} else if (right_val->IsNumber()) {
return right_val;
}
} else if (left_val->IsNumber() &&
right_val->IsNumber()) {
return left_val;
}
return NULL;
case Token::kBIT_OR:
case Token::kBIT_XOR:
case Token::kBIT_AND:
case Token::kSHL:
case Token::kSHR:
if (left_val->IsInteger() &&
right_val->IsInteger()) {
return right_val;
}
return NULL;
case Token::kOR:
case Token::kAND:
if (left_val->IsBool() && right_val->IsBool()) {
return left_val;
}
return NULL;
default:
UNREACHABLE();
return NULL;
}
return NULL;
}
AstNode* UnaryOpNode::UnaryOpOrLiteral(intptr_t token_pos,
Token::Kind kind,
AstNode* operand) {
AstNode* new_operand = operand->ApplyUnaryOp(kind);
if (new_operand != NULL) {
return new_operand;
}
return new UnaryOpNode(token_pos, kind, operand);
}
bool UnaryOpNode::IsKindValid() const {
switch (kind_) {
case Token::kADD:
case Token::kNEGATE:
case Token::kNOT:
case Token::kBIT_NOT:
return true;
default:
return false;
}
}
const Instance* UnaryOpNode::EvalConstExpr() const {
const Instance* val = this->operand()->EvalConstExpr();
if (val == NULL) {
return NULL;
}
switch (kind_) {
case Token::kADD:
case Token::kNEGATE:
return val->IsNumber() ? val : NULL;
case Token::kNOT:
return val->IsBool() ? val : NULL;
case Token::kBIT_NOT:
return val->IsInteger() ? val : NULL;
default:
return NULL;
}
}
const Instance* ClosureNode::EvalConstExpr() const {
if (function().IsImplicitStaticClosureFunction()) {
// Return a value that represents an instance. Only the type is relevant.
return &Instance::Handle();
}
return NULL;
}
AstNode* ClosureNode::MakeAssignmentNode(AstNode* rhs) {
if (scope() == NULL) {
// This is an implicit closure node created because a static getter was not
// found. Change the getter into a setter. If it does not exist,
// noSuchMethod will be called.
return new StaticSetterNode(token_pos(),
receiver(),
Class::ZoneHandle(function().Owner()),
String::ZoneHandle(function().name()),
rhs);
}
return NULL;
}
const char* UnaryOpNode::Name() const {
return Token::Str(kind_);
}
const char* JumpNode::Name() const {
return Token::Str(kind_);
}
const Instance* LoadLocalNode::EvalConstExpr() const {
if (local().IsConst()) {
return local().ConstValue();
}
return NULL;
}
AstNode* LoadLocalNode::MakeAssignmentNode(AstNode* rhs) {
if (local().is_final()) {
return NULL;
}
if (HasPseudo()) {
return NULL;
}
return new StoreLocalNode(token_pos(), &local(), rhs);
}
AstNode* LoadStaticFieldNode::MakeAssignmentNode(AstNode* rhs) {
if (field().is_final()) {
return NULL;
}
return new StoreStaticFieldNode(token_pos(), field(), rhs);
}
AstNode* InstanceGetterNode::MakeAssignmentNode(AstNode* rhs) {
return new InstanceSetterNode(token_pos(), receiver(), field_name(), rhs);
}
AstNode* LoadIndexedNode::MakeAssignmentNode(AstNode* rhs) {
return new StoreIndexedNode(token_pos(), array(), index_expr(),
rhs, super_class());
}
AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) {
const String& setter_name = String::Handle(Field::SetterName(field_name()));
if (is_super_getter_) {
ASSERT(receiver() != NULL);
// If the static setter is not found in the superclass, noSuchMethod will be
// called at runtime.
return new StaticSetterNode(token_pos(),
receiver(),
cls(),
field_name(),
rhs);
}
const Function& setter =
Function::ZoneHandle(cls().LookupStaticFunction(setter_name));
if (!setter.IsNull()) {
return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs);
}
// Could not find a static setter. Look for a field.
// Access to a lazily initialized static field that has not yet been
// initialized is compiled to a static implicit getter.
// A setter may not exist for such a field.
const Field& field = Field::ZoneHandle(cls().LookupStaticField(field_name()));
if (!field.IsNull()) {
if (field.is_final()) {
// Attempting to assign to a final variable will cause a NoSuchMethodError
// to be thrown. Change static getter to non-existent static setter in
// order to trigger the throw at runtime.
return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs);
}
#if defined(DEBUG)
const String& getter_name = String::Handle(Field::GetterName(field_name()));
const Function& getter =
Function::Handle(cls().LookupStaticFunction(getter_name));
ASSERT(!getter.IsNull() &&
(getter.kind() == RawFunction::kConstImplicitGetter));
#endif
return new StoreStaticFieldNode(token_pos(), field, rhs);
}
// Didn't find a static setter or a static field.
// If this static getter is in an instance function where
// a receiver is available, we turn this static getter
// into an instance setter (and will get an error at runtime if an
// instance setter cannot be found either).
if (receiver() != NULL) {
return new InstanceSetterNode(token_pos(), receiver(), field_name(), rhs);
}
return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs);
}
AstNode* StaticCallNode::MakeAssignmentNode(AstNode* rhs) {
// Return this node if it represents a 'throw NoSuchMethodError' indicating
// that a getter was not found, otherwise return null.
const Class& cls = Class::Handle(function().Owner());
const String& cls_name = String::Handle(cls.Name());
const String& func_name = String::Handle(function().name());
if (cls_name.Equals(Symbols::NoSuchMethodError()) &&
func_name.StartsWith(Symbols::ThrowNew())) {
return this;
}
return NULL;
}
const Instance* StaticGetterNode::EvalConstExpr() const {
const String& getter_name =
String::Handle(Field::GetterName(this->field_name()));
const Function& getter_func =
Function::Handle(this->cls().LookupStaticFunction(getter_name));
if (getter_func.IsNull() || !getter_func.is_const()) {
return NULL;
}
const Object& result = Object::Handle(
DartEntry::InvokeFunction(getter_func, Object::empty_array()));
if (result.IsError() || result.IsNull()) {
// TODO(turnidge): We could get better error messages by returning
// the Error object directly to the parser. This will involve
// replumbing all of the EvalConstExpr methods.
return NULL;
}
return &Instance::ZoneHandle(Instance::Cast(result).raw());
}
} // namespace dart