blob: 69fb0d39ef3728d3d7b59c504ca485cf0ce54993 [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/log.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
namespace dart {
#define DEFINE_VISIT_FUNCTION(BaseName) \
void BaseName##Node::Visit(AstNodeVisitor* visitor) { \
visitor->Visit##BaseName##Node(this); \
}
FOR_EACH_NODE(DEFINE_VISIT_FUNCTION)
#undef DEFINE_VISIT_FUNCTION
#define DEFINE_NAME_FUNCTION(BaseName) \
const char* BaseName##Node::Name() const { return #BaseName; }
FOR_EACH_NODE(DEFINE_NAME_FUNCTION)
#undef DEFINE_NAME_FUNCTION
const Field* AstNode::MayCloneField(const Field& value) {
if (Compiler::IsBackgroundCompilation() ||
FLAG_force_clone_compiler_objects) {
return &Field::ZoneHandle(value.CloneFromOriginal());
} else {
ASSERT(value.IsZoneHandle());
return &value;
}
}
// 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(BaseName) \
virtual void Visit##BaseName##Node(BaseName##Node* node) { \
nodes_->Add(node); \
node->VisitChildren(this); \
}
FOR_EACH_NODE(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 SequenceNode::Add(AstNode* node) {
if (node->IsReturnNode()) {
node->AsReturnNode()->set_scope(scope());
}
nodes_.Add(node);
}
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);
}
}
LetNode::LetNode(TokenPosition token_pos)
: AstNode(token_pos), vars_(1), initializers_(1), nodes_(1) {}
LocalVariable* LetNode::AddInitializer(AstNode* node) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
initializers_.Add(node);
char name[64];
OS::SNPrint(name, sizeof(name), ":lt%s_%" Pd "", token_pos().ToCString(),
vars_.length());
LocalVariable* temp_var =
new LocalVariable(TokenPosition::kNoSource, token_pos(),
String::ZoneHandle(zone, Symbols::New(thread, name)),
Object::dynamic_type());
vars_.Add(temp_var);
return temp_var;
}
void LetNode::VisitChildren(AstNodeVisitor* visitor) const {
for (intptr_t i = 0; i < num_temps(); ++i) {
initializers_[i]->Visit(visitor);
}
for (intptr_t i = 0; i < nodes_.length(); ++i) {
nodes_[i]->Visit(visitor);
}
}
bool LetNode::IsPotentiallyConst() const {
for (intptr_t i = 0; i < num_temps(); i++) {
if (!initializers_[i]->IsPotentiallyConst()) {
return false;
}
}
for (intptr_t i = 0; i < nodes_.length(); i++) {
if (!nodes_[i]->IsPotentiallyConst()) {
return false;
}
}
return true;
}
const Instance* LetNode::EvalConstExpr() const {
for (intptr_t i = 0; i < num_temps(); i++) {
if (initializers_[i]->EvalConstExpr() == NULL) {
return NULL;
}
}
const Instance* last = NULL;
for (intptr_t i = 0; i < nodes_.length(); i++) {
last = nodes_[i]->EvalConstExpr();
if (last == NULL) {
return NULL;
}
}
return last;
}
void ArrayNode::VisitChildren(AstNodeVisitor* visitor) const {
for (intptr_t i = 0; i < this->length(); i++) {
ElementAt(i)->Visit(visitor);
}
}
bool StringInterpolateNode::IsPotentiallyConst() const {
for (int i = 0; i < value_->length(); i++) {
if (!value_->ElementAt(i)->IsPotentiallyConst()) {
return false;
}
}
return true;
}
bool LiteralNode::IsPotentiallyConst() const {
return true;
}
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(), Heap::kOld));
return new LiteralNode(this->token_pos(), literal);
}
if (literal().IsMint()) {
const Mint& mint = Mint::Cast(literal());
const Instance& literal =
Instance::ZoneHandle(Integer::New(-mint.value(), Heap::kOld));
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);
}
} else if (unary_op_kind == Token::kBIT_NOT) {
if (literal().IsSmi()) {
const Smi& smi = Smi::Cast(literal());
const Instance& literal =
Instance::ZoneHandle(Integer::New(~smi.Value(), Heap::kOld));
return new LiteralNode(this->token_pos(), literal);
}
if (literal().IsMint()) {
const Mint& mint = Mint::Cast(literal());
const Instance& literal =
Instance::ZoneHandle(Integer::New(~mint.value(), Heap::kOld));
return new LiteralNode(this->token_pos(), literal);
}
} else if (unary_op_kind == Token::kNOT) {
if (literal().IsBool()) {
const Bool& boolean = Bool::Cast(literal());
return new LiteralNode(this->token_pos(), Bool::Get(!boolean.value()));
}
}
return NULL;
}
const char* TypeNode::TypeName() const {
return String::Handle(type().UserVisibleName()).ToCString();
}
bool ComparisonNode::IsKindValid() const {
return Token::IsRelationalOperator(kind_) ||
Token::IsEqualityOperator(kind_) || Token::IsTypeTestOperator(kind_) ||
Token::IsTypeCastOperator(kind_);
}
const char* ComparisonNode::TokenName() const {
return (kind_ == Token::kAS) ? "as" : Token::Str(kind_);
}
bool ComparisonNode::IsPotentiallyConst() const {
switch (kind_) {
case Token::kLT:
case Token::kGT:
case Token::kLTE:
case Token::kGTE:
case Token::kEQ:
case Token::kNE:
case Token::kEQ_STRICT:
case Token::kNE_STRICT:
return this->left()->IsPotentiallyConst() &&
this->right()->IsPotentiallyConst();
default:
return false;
}
}
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:
// 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;
case Token::kEQ_STRICT:
case Token::kNE_STRICT:
// identical(a, b) is a compile time const if both operands are
// compile time constants, regardless of their type.
return &Bool::True();
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::kIFNULL:
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::TokenName() const {
return Token::Str(kind_);
}
bool BinaryOpNode::IsPotentiallyConst() const {
switch (kind_) {
case Token::kOR:
case Token::kAND:
if (this->left()->IsLiteralNode() &&
this->left()->AsLiteralNode()->literal().IsNull()) {
return false;
}
if (this->right()->IsLiteralNode() &&
this->right()->AsLiteralNode()->literal().IsNull()) {
return false;
}
// Fall-through intentional.
case Token::kADD:
case Token::kSUB:
case Token::kMUL:
case Token::kDIV:
case Token::kMOD:
case Token::kTRUNCDIV:
case Token::kBIT_OR:
case Token::kBIT_XOR:
case Token::kBIT_AND:
case Token::kSHL:
case Token::kSHR:
case Token::kIFNULL:
return this->left()->IsPotentiallyConst() &&
this->right()->IsPotentiallyConst();
default:
UNREACHABLE();
return false;
}
}
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() && !left_val->IsString() &&
kind_ != Token::kIFNULL) {
return NULL;
}
const Instance* right_val = this->right()->EvalConstExpr();
if (right_val == NULL) {
return NULL;
}
switch (kind_) {
case Token::kADD:
if (left_val->IsString()) {
return right_val->IsString() ? left_val : NULL;
}
// Fall-through intentional.
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;
case Token::kIFNULL:
if (left_val->IsNull()) {
return right_val;
}
return left_val;
default:
UNREACHABLE();
return NULL;
}
return NULL;
}
AstNode* UnaryOpNode::UnaryOpOrLiteral(TokenPosition 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::kNEGATE:
case Token::kNOT:
case Token::kBIT_NOT:
return true;
default:
return false;
}
}
bool UnaryOpNode::IsPotentiallyConst() const {
if (this->operand()->IsLiteralNode() &&
this->operand()->AsLiteralNode()->literal().IsNull()) {
return false;
}
return this->operand()->IsPotentiallyConst();
}
const Instance* UnaryOpNode::EvalConstExpr() const {
const Instance* val = this->operand()->EvalConstExpr();
if (val == NULL) {
return NULL;
}
switch (kind_) {
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;
}
}
bool ConditionalExprNode::IsPotentiallyConst() const {
return this->condition()->IsPotentiallyConst() &&
this->true_expr()->IsPotentiallyConst() &&
this->false_expr()->IsPotentiallyConst();
}
const Instance* ConditionalExprNode::EvalConstExpr() const {
const Instance* cond = this->condition()->EvalConstExpr();
if ((cond != NULL) && cond->IsBool() &&
(this->true_expr()->EvalConstExpr() != NULL) &&
(this->false_expr()->EvalConstExpr() != NULL)) {
return cond;
}
return NULL;
}
bool ClosureNode::IsPotentiallyConst() const {
if (function().IsImplicitStaticClosureFunction()) {
return true;
}
return false;
}
const Instance* ClosureNode::EvalConstExpr() const {
if (!is_deferred_reference_ && 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::TokenName() const {
return Token::Str(kind_);
}
const char* JumpNode::TokenName() const {
return Token::Str(kind_);
}
bool LoadLocalNode::IsPotentiallyConst() const {
// Parameters of const constructors are implicitly final and can be
// used in initializer expressions.
// We can't check here whether the local variable is indeed a parameter,
// but this code is executed before any other local variables are
// added to the scope.
return local().is_final();
}
const Instance* LoadLocalNode::EvalConstExpr() const {
if (local().IsConst()) {
return local().ConstValue();
}
return NULL;
}
AstNode* LoadLocalNode::MakeAssignmentNode(AstNode* rhs) {
if (local().is_final()) {
return NULL;
}
return new StoreLocalNode(token_pos(), &local(), rhs);
}
AstNode* LoadStaticFieldNode::MakeAssignmentNode(AstNode* rhs) {
if (field().is_final()) {
return NULL;
}
if (Isolate::Current()->type_checks()) {
rhs = new AssignableNode(field().token_pos(), rhs,
AbstractType::ZoneHandle(field().type()),
String::ZoneHandle(field().name()));
}
return new StoreStaticFieldNode(token_pos(),
Field::ZoneHandle(field().Original()), rhs);
}
AstNode* InstanceGetterNode::MakeAssignmentNode(AstNode* rhs) {
return new InstanceSetterNode(token_pos(), receiver(), field_name(), rhs,
is_conditional());
}
bool InstanceGetterNode::IsPotentiallyConst() const {
return field_name().Equals(Symbols::Length()) && !is_conditional() &&
receiver()->IsPotentiallyConst();
}
const Instance* InstanceGetterNode::EvalConstExpr() const {
if (field_name().Equals(Symbols::Length()) && !is_conditional()) {
const Instance* receiver_val = receiver()->EvalConstExpr();
if ((receiver_val != NULL) && receiver_val->IsString()) {
return &Instance::ZoneHandle(Smi::New(1));
}
}
return NULL;
}
AstNode* LoadIndexedNode::MakeAssignmentNode(AstNode* rhs) {
return new StoreIndexedNode(token_pos(), array(), index_expr(), rhs,
super_class());
}
AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
Isolate* isolate = thread->isolate();
if (is_super_getter()) {
ASSERT(receiver() != NULL);
const String& setter_name =
String::ZoneHandle(zone, Field::LookupSetterSymbol(field_name_));
Function& setter = Function::ZoneHandle(zone);
if (!setter_name.IsNull()) {
setter = Resolver::ResolveDynamicAnyArgs(zone, cls(), setter_name);
}
if (setter.IsNull() || setter.is_abstract()) {
// No instance setter found in super class chain,
// noSuchMethod will be called at runtime.
return new StaticSetterNode(token_pos(), receiver(), cls(), field_name_,
rhs);
}
return new StaticSetterNode(token_pos(), receiver(), field_name_, setter,
rhs);
}
if (owner().IsLibraryPrefix()) {
const LibraryPrefix& prefix = LibraryPrefix::Cast(owner_);
// The parser has already dealt with the pathological case where a
// library imports itself. See Parser::ResolveIdentInPrefixScope()
ASSERT(field_name_.CharAt(0) != Library::kPrivateIdentifierStart);
// If the prefix is not yet loaded, the getter doesn't exist. Return a
// setter that will throw a NSME at runtime.
if (!prefix.is_loaded()) {
return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs);
}
Object& obj = Object::Handle(zone, prefix.LookupObject(field_name_));
if (obj.IsField()) {
const Field& field = Field::ZoneHandle(zone, Field::Cast(obj).raw());
if (!field.is_final()) {
if (isolate->type_checks()) {
rhs = new AssignableNode(field.token_pos(), rhs,
AbstractType::ZoneHandle(zone, field.type()),
field_name_);
}
return new StoreStaticFieldNode(token_pos(), field, rhs);
}
}
// No field found in prefix. Look for a setter function.
const String& setter_name =
String::Handle(zone, Field::LookupSetterSymbol(field_name_));
if (!setter_name.IsNull()) {
obj = prefix.LookupObject(setter_name);
if (obj.IsFunction()) {
const Function& setter =
Function::ZoneHandle(zone, Function::Cast(obj).raw());
ASSERT(setter.is_static() && setter.IsSetterFunction());
return new StaticSetterNode(token_pos(), NULL, field_name_, setter,
rhs);
}
}
// No writeable field and no setter found in the prefix. Return a
// non-existing setter that will throw an NSM error.
return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs);
}
if (owner().IsLibrary()) {
const Library& library = Library::Cast(owner());
Object& obj = Object::Handle(zone, library.ResolveName(field_name_));
if (obj.IsField()) {
const Field& field = Field::ZoneHandle(zone, Field::Cast(obj).raw());
if (!field.is_final()) {
if (isolate->type_checks()) {
rhs = new AssignableNode(field.token_pos(), rhs,
AbstractType::ZoneHandle(zone, field.type()),
field_name_);
}
return new StoreStaticFieldNode(token_pos(), field, rhs);
}
}
// No field found in library. Look for a setter function.
const String& setter_name =
String::Handle(zone, Field::LookupSetterSymbol(field_name_));
if (!setter_name.IsNull()) {
obj = library.ResolveName(setter_name);
if (obj.IsFunction()) {
const Function& setter =
Function::ZoneHandle(zone, Function::Cast(obj).raw());
ASSERT(setter.is_static() && setter.IsSetterFunction());
return new StaticSetterNode(token_pos(), NULL, field_name_, setter,
rhs);
}
}
// No writeable field and no setter found in the library. Return a
// non-existing setter that will throw an NSM error.
return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs);
}
const Function& setter =
Function::ZoneHandle(zone, cls().LookupSetterFunction(field_name_));
if (!setter.IsNull() && setter.IsStaticFunction()) {
return new StaticSetterNode(token_pos(), NULL, field_name_, setter, 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(zone, 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(zone, Field::LookupGetterSymbol(field_name_));
ASSERT(!getter_name.IsNull());
const Function& getter =
Function::Handle(zone, cls().LookupStaticFunction(getter_name));
ASSERT(!getter.IsNull() &&
(getter.kind() == RawFunction::kImplicitStaticFinalGetter));
#endif
if (isolate->type_checks()) {
rhs = new AssignableNode(field.token_pos(), rhs,
AbstractType::ZoneHandle(zone, field.type()),
String::ZoneHandle(zone, field.name()));
}
return new StoreStaticFieldNode(token_pos(), field, rhs);
}
// Didn't find a static setter or a static field. Make a call to
// the non-existent setter to trigger a NoSuchMethodError at runtime.
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;
}
bool StaticGetterNode::IsPotentiallyConst() const {
if (is_deferred_reference_) {
return false;
}
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 false;
}
return true;
}
const Instance* StaticGetterNode::EvalConstExpr() const {
if (is_deferred_reference_) {
return NULL;
}
const String& getter_name =
String::Handle(Field::LookupGetterSymbol(this->field_name()));
if (getter_name.IsNull()) {
return NULL;
}
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