asherkin@263: /* vim: set ts=4 sw=4 tw=99 et: asherkin@263: * asherkin@263: * Copyright (C) 2012 David Anderson asherkin@263: * asherkin@263: * This file is part of SourcePawn. asherkin@263: * asherkin@263: * SourcePawn is free software: you can redistribute it and/or modify it under asherkin@263: * the terms of the GNU General Public License as published by the Free asherkin@263: * Software Foundation, either version 3 of the License, or (at your option) asherkin@263: * any later version. asherkin@263: * asherkin@263: * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY asherkin@263: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS asherkin@263: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. asherkin@263: * asherkin@263: * You should have received a copy of the GNU General Public License along with asherkin@263: * SourcePawn. If not, see http://www.gnu.org/licenses/. asherkin@263: */ asherkin@263: #include "../Strings.h" asherkin@263: #include "../Zone.h" asherkin@263: #include "../TypeManager.h" asherkin@263: #include "NameBinding.h" asherkin@263: #include "Scopes.h" asherkin@263: #include "ConstantEvaluator.h" asherkin@263: #include "../Spaces-inl.h" asherkin@263: #include "../Heap-inl.h" asherkin@263: asherkin@263: using namespace ke; asherkin@263: asherkin@263: // Create symbol tables and symbols for every scope and declaration. For type asherkin@263: // declarations and methods, we also pre-allocate a Type object, so that asherkin@263: // circular dependencies between types will work. asherkin@263: class NamePopulator : public AstVisitor asherkin@263: { asherkin@263: private: asherkin@263: // This class is analagous to a Scope, however, it allows us to create asherkin@263: // scopes lazily. If a scope is requested, and the current environment asherkin@263: // has no scope, then a scope is created. If no scope is ever requested, asherkin@263: // a scope does not need to be created. asherkin@263: class SymbolEnv asherkin@263: { asherkin@263: public: asherkin@263: SymbolEnv(SymbolEnv **prevp, Scope *scope = NULL) asherkin@263: : envp_(prevp), asherkin@263: prev_(*prevp), asherkin@263: scope_(scope) asherkin@263: { asherkin@263: *envp_ = this; asherkin@263: } asherkin@263: ~SymbolEnv() { asherkin@263: *envp_ = prev_; asherkin@263: } asherkin@263: asherkin@263: Scope *scope() { asherkin@263: return scope_; asherkin@263: } asherkin@263: void setScope(Scope *scope) { asherkin@263: assert(!scope_); asherkin@263: scope_ = scope; asherkin@263: } asherkin@263: SymbolEnv *prev() const { asherkin@263: return prev_; asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: SymbolEnv **envp_; asherkin@263: SymbolEnv *prev_; asherkin@263: Scope *scope_; asherkin@263: }; asherkin@263: asherkin@263: enum ReturnStatus { asherkin@263: NoReturn, asherkin@263: VoidReturn, asherkin@263: ValueReturn asherkin@263: }; asherkin@263: asherkin@263: class FunctionScope asherkin@263: { asherkin@263: public: asherkin@263: FunctionScope(FunctionScope **prevp) asherkin@263: : prevp_(prevp), asherkin@263: prev_(*prevp), asherkin@263: status_(NoReturn) asherkin@263: { asherkin@263: *prevp_ = this; asherkin@263: } asherkin@263: ~FunctionScope() { asherkin@263: *prevp_ = prev_; asherkin@263: } asherkin@263: ReturnStatus returns() const { asherkin@263: return status_; asherkin@263: } asherkin@263: void returns(ReturnStatus status) { asherkin@263: if (status > status_) asherkin@263: status_ = status; asherkin@263: } asherkin@263: private: asherkin@263: FunctionScope **prevp_; asherkin@263: FunctionScope *prev_; asherkin@263: ReturnStatus status_; asherkin@263: }; asherkin@263: asherkin@263: public: asherkin@263: NamePopulator(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: : zone_(zone), asherkin@263: pool_(zone->pool()), asherkin@263: cc_(cc), asherkin@263: unit_(unit), asherkin@263: env_(NULL), asherkin@263: fun_(NULL) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: void analyze() { asherkin@263: ImportScope *importScope = ImportScope::New(pool_); asherkin@263: if (!importScope) asherkin@263: return; asherkin@263: asherkin@263: // At the import level, we declare system types. asherkin@263: if (!declareSystemTypes(importScope)) asherkin@263: return; asherkin@263: asherkin@263: GlobalScope *globalScope = GlobalScope::New(pool_, importScope); asherkin@263: if (!globalScope) asherkin@263: return; asherkin@263: asherkin@263: unit_->setImportScope(importScope); asherkin@263: unit_->setGlobalScope(globalScope); asherkin@263: asherkin@263: SymbolEnv env(&env_, globalScope); asherkin@263: asherkin@263: for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { asherkin@263: Statement *stmt = unit_->tree()->statements()->at(i); asherkin@263: stmt->accept(this); asherkin@263: } asherkin@263: asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: bool declareSystemType(ImportScope *scope, const char *name, Handle effective) { asherkin@263: Local tag(zone_, zone_->makeSymbol(name)); asherkin@263: if (!tag) asherkin@263: return false; asherkin@263: asherkin@263: Local type(zone_, zone_->types()->getType()); asherkin@263: asherkin@263: TypeSymbol *sym = new (pool_) TypeSymbol(scope, tag, type, effective); asherkin@263: return scope->addSymbol(sym); asherkin@263: } asherkin@263: asherkin@263: bool declareSystemType(ImportScope *scope, const char *name, PrimitiveType prim) { asherkin@263: Local primitive(zone_, zone_->types()->getPrimitive(prim)); asherkin@263: assert(primitive); asherkin@263: return declareSystemType(scope, name, primitive); asherkin@263: } asherkin@263: asherkin@263: bool declareSystemTypes(ImportScope *scope) { asherkin@263: if (!declareSystemType(scope, "float", PrimitiveType_Float)) asherkin@263: return false; asherkin@263: if (!declareSystemType(scope, "_", PrimitiveType_Int32)) asherkin@263: return false; asherkin@263: if (!declareSystemType(scope, "int", PrimitiveType_Int32)) asherkin@263: return false; asherkin@263: if (!declareSystemType(scope, "bool", PrimitiveType_Bool)) asherkin@263: return false; asherkin@263: if (!declareSystemType(scope, "char", PrimitiveType_Char)) asherkin@263: return false; asherkin@263: asherkin@263: Local voidType(zone_, zone_->types()->getVoid()); asherkin@263: if (!declareSystemType(scope, "void", voidType)) asherkin@263: return false; asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: void visitVariableDeclaration(VariableDeclaration *node) { asherkin@263: Scope *scope = env_->scope(); asherkin@263: if (!scope || scope->kind() != Scope::GLOBAL) { asherkin@263: if ((scope = getOrCreateBlockScope()) == NULL) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: if (scope->kind() == Scope::GLOBAL) { asherkin@263: // The symbol must be added now, since it could be an export. asherkin@263: VariableSymbol *sym = new (pool_) VariableSymbol(scope, node->name(), node->pos()); asherkin@263: node->setSymbol(sym); asherkin@263: asherkin@263: if (!registerName(scope, sym)) asherkin@263: return; asherkin@263: } else { asherkin@263: // We ensure the scope will exist at the name binding phase, but asherkin@263: // we don't actually register the variable symbol yet. Otherwise, asherkin@263: // when binding the initializer, we could encounter: asherkin@263: // new x = x; asherkin@263: // asherkin@263: // And we don't necessarily want this to bind. asherkin@263: } asherkin@263: asherkin@263: if (node->initialization()) asherkin@263: node->initialization()->accept(this); asherkin@263: } asherkin@263: asherkin@263: void visitEnumStatement(EnumStatement *node) { asherkin@263: Local name(zone_, node->name()); asherkin@263: Local type(zone_); asherkin@263: if (name) asherkin@263: type = zone_->types()->newEnum(name); asherkin@263: else asherkin@263: type = zone_->types()->getPrimitive(PrimitiveType_Int32); asherkin@263: asherkin@263: if (!type) asherkin@263: return; asherkin@263: asherkin@263: Scope *scope = getGlobalOrCreateBlockScope(); asherkin@263: if (!scope) asherkin@263: return; asherkin@263: asherkin@263: if (name) { asherkin@263: Local symType(zone_, zone_->types()->getType()); asherkin@263: TypeSymbol *sym = new (pool_) TypeSymbol(scope, name, symType, type); asherkin@263: if (!registerName(scope, sym)) asherkin@263: return; asherkin@263: asherkin@263: if (scope->kind() == Scope::GLOBAL) { asherkin@263: if (!unit_->globalScope()->exported()->append(sym)) asherkin@263: return; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: // We want each field of the expression to qualify as a constant expression. asherkin@263: if ((type = zone_->types()->qualify(type, TypeQual_ConstExpr)) == NULL) asherkin@263: return; asherkin@263: asherkin@263: int value = 0; asherkin@263: for (size_t i = 0; i < node->entries()->length(); i++) { asherkin@263: EnumStatement::Entry &entry = node->entries()->at(i); asherkin@263: BoxedPrimitive box = BoxedPrimitive::Int(value); asherkin@263: if (entry.expr) { asherkin@263: if (!EvaluateForConstant(entry.expr, &box) || box.type() != PrimitiveType_Int32) { asherkin@263: cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: assert(box.type() == PrimitiveType_Int32); asherkin@263: value = box.toInt(); asherkin@263: } asherkin@263: asherkin@263: ConstantSymbol *sym = new (pool_) ConstantSymbol(scope, entry.name, entry.pos, type, box); asherkin@263: if (!registerName(scope, sym)) asherkin@263: return; asherkin@263: if (scope->kind() == Scope::GLOBAL) { asherkin@263: if (!unit_->globalScope()->exported()->append(sym)) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: entry.sym = sym; asherkin@263: value++; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void visitFunctionStatement(FunctionStatement *node) { asherkin@263: // We create function types ahead of time, not for any particular asherkin@263: // reason. It makes things consistent with FunctionTypeStatement, asherkin@263: // which actually does produce a type. asherkin@263: Local type(zone_, FunctionType::New(zone_, node->name(), node->token())); asherkin@263: if (!type) asherkin@263: return; asherkin@263: asherkin@263: assert(env_->scope()); asherkin@263: asherkin@263: // Function statements are always named, so add the name to the outer scope. asherkin@263: FunctionSymbol *sym = new (pool_) FunctionSymbol(env_->scope(), node->name(), node->pos(), type); asherkin@263: if (!registerName(env_->scope(), sym)) asherkin@263: return; asherkin@263: asherkin@263: if (node->token() == TOK_PUBLIC || asherkin@263: node->token() == TOK_FORWARD || asherkin@263: node->token() == TOK_NATIVE) asherkin@263: { asherkin@263: // We're in the global scope, for sure. asherkin@263: assert(env_->scope()->kind() == Scope::GLOBAL); asherkin@263: asherkin@263: if (!unit_->globalScope()->exported()->append(sym)) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: node->setSymbol(sym); asherkin@263: asherkin@263: // Scope for the function's arguments. asherkin@263: SymbolEnv argEnv(&env_); asherkin@263: if (!registerArguments(node->signature())) asherkin@263: return; asherkin@263: asherkin@263: // Scope for function body. asherkin@263: SymbolEnv localEnv(&env_); asherkin@263: FunctionScope fun(&fun_); asherkin@263: if (node->body()) asherkin@263: node->body()->accept(this); asherkin@263: asherkin@263: // Function statements have quirky semantics around their return asherkin@263: // type. For reasonable compatibility with SP1, we use the following asherkin@263: // heuristics for when no explicit type is declared. asherkin@263: if (!node->signature().returnType()) { asherkin@263: Local type(zone_); asherkin@263: if (node->token() == TOK_FORWARD || node->token() == TOK_NATIVE) { asherkin@263: // If the function type is a native or forward, we always asherkin@263: // assume it returns an int32. asherkin@263: type = zone_->types()->getPrimitive(PrimitiveType_Int32); asherkin@263: } else if (fun.returns() != ValueReturn) { asherkin@263: // This function never returns a value, so mark it as asherkin@263: // returning void. asherkin@263: type = zone_->types()->getVoid(); asherkin@263: } asherkin@263: // No need to NULL check here, we'll catch OOM later. asherkin@263: node->sym()->type()->setReturnType(type); asherkin@263: } asherkin@263: asherkin@263: node->setScopes(argEnv.scope(), localEnv.scope()); asherkin@263: } asherkin@263: asherkin@263: void visitStructureStatement(StructureStatement *node) { asherkin@263: assert(false); asherkin@263: } asherkin@263: asherkin@263: void visiFunctionTypeStatementt(FunctionTypeStatement *node) { asherkin@263: assert(false); asherkin@263: } asherkin@263: asherkin@263: void visitNameProxy(NameProxy *name) { asherkin@263: } asherkin@263: void visitAssignment(Assignment *node) { asherkin@263: node->lvalue()->accept(this); asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitBinaryExpression(BinaryExpression *node) { asherkin@263: node->left()->accept(this); asherkin@263: node->right()->accept(this); asherkin@263: } asherkin@263: void visitReturnStatement(ReturnStatement *node) { asherkin@263: if (node->expression()) asherkin@263: fun_->returns(ValueReturn); asherkin@263: else asherkin@263: fun_->returns(VoidReturn); asherkin@263: } asherkin@263: void visitForStatement(ForStatement *node) { asherkin@263: SymbolEnv env(&env_); asherkin@263: asherkin@263: if (node->initialization()) asherkin@263: node->initialization()->accept(this); asherkin@263: if (node->condition()) asherkin@263: node->condition()->accept(this); asherkin@263: if (node->update()) asherkin@263: node->update()->accept(this); asherkin@263: node->body()->accept(this); asherkin@263: asherkin@263: node->setScope(env.scope()); asherkin@263: } asherkin@263: void visitBlockStatement(BlockStatement *node) { asherkin@263: if (node->type() == TOK_NEW) { asherkin@263: // Don't create a symbol environment for this block, since it asherkin@263: // was created solely to house multiple variable decls. asherkin@263: for (size_t i = 0; i < node->statements()->length(); i++) asherkin@263: node->statements()->at(i)->accept(this); asherkin@263: } else { asherkin@263: assert(node->type() == TOK_LBRACE); asherkin@263: asherkin@263: SymbolEnv env(&env_); asherkin@263: asherkin@263: for (size_t i = 0; i < node->statements()->length(); i++) asherkin@263: node->statements()->at(i)->accept(this); asherkin@263: asherkin@263: if (node->type() == TOK_NEW) { asherkin@263: assert(!env.scope()); asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: node->setScope(env.scope()); asherkin@263: } asherkin@263: } asherkin@263: void visitIntegerLiteral(IntegerLiteral *node) { asherkin@263: } asherkin@263: void visitExpressionStatement(ExpressionStatement *node) { asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitCallExpression(CallExpression *node) { asherkin@263: node->callee()->accept(this); asherkin@263: for (size_t i = 0; i < node->arguments()->length(); i++) asherkin@263: node->arguments()->at(i)->accept(this); asherkin@263: } asherkin@263: void visitIfStatement(IfStatement *node) { asherkin@263: node->ifTrue()->accept(this); asherkin@263: if (node->ifFalse()) asherkin@263: node->ifFalse()->accept(this); asherkin@263: } asherkin@263: void visitIndexExpression(IndexExpression *node) { asherkin@263: node->left()->accept(this); asherkin@263: node->right()->accept(this); asherkin@263: } asherkin@263: void visitFloatLiteral(FloatLiteral *node) { asherkin@263: } asherkin@263: void visitWhileStatement(WhileStatement *node) { asherkin@263: node->condition()->accept(this); asherkin@263: node->body()->accept(this); asherkin@263: } asherkin@263: void visitBreakStatement(BreakStatement *node) { asherkin@263: } asherkin@263: void visitContinueStatement(ContinueStatement *node) { asherkin@263: } asherkin@263: void visitStringLiteral(StringLiteral *node) { asherkin@263: } asherkin@263: void visitIncDecExpression(IncDecExpression *node) { asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitUnaryExpression(UnaryExpression *node) { asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitTernaryExpression(TernaryExpression *node) { asherkin@263: assert(false); asherkin@263: } asherkin@263: void visitBooleanLiteral(BooleanLiteral *node) { asherkin@263: } asherkin@263: void visitFieldExpression(FieldExpression *node) { asherkin@263: } asherkin@263: void visitSwitchStatement(SwitchStatement *node) { asherkin@263: if (node->defaultCase()) asherkin@263: node->defaultCase()->accept(this); asherkin@263: asherkin@263: for (size_t i = 0; i < node->cases()->length(); i++) asherkin@263: node->cases()->at(i)->statement()->accept(this); asherkin@263: } asherkin@263: void visitArrayLiteral(ArrayLiteral *node) { asherkin@263: } asherkin@263: void visitImportStatement(ImportStatement *node) { asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: Scope *getGlobalOrCreateBlockScope() { asherkin@263: if (env_->scope()) { asherkin@263: assert(env_->scope()->kind() == Scope::BLOCK || asherkin@263: env_->scope()->kind() == Scope::GLOBAL); asherkin@263: return env_->scope(); asherkin@263: } asherkin@263: return getOrCreateBlockScope(); asherkin@263: } asherkin@263: Scope *getOrCreateBlockScope() { asherkin@263: if (env_->scope()) { asherkin@263: assert(env_->scope()->kind() == Scope::BLOCK); asherkin@263: return env_->scope(); asherkin@263: } asherkin@263: asherkin@263: LocalScope *scope = LocalScope::New(pool_); asherkin@263: if (!scope) asherkin@263: return NULL; asherkin@263: asherkin@263: env_->setScope(scope); asherkin@263: return scope; asherkin@263: } asherkin@263: asherkin@263: bool registerName(Scope *scope, Symbol *sym) { asherkin@263: if (!scope) asherkin@263: return false; asherkin@263: asherkin@263: if (Symbol *other = scope->localLookup(sym->name())) { asherkin@263: // Report, but allow errors to continue. asherkin@263: cc_.reportError(sym->pos(), Message_RedeclaredName, asherkin@263: sym->name()->chars(), asherkin@263: other->pos().line, asherkin@263: other->pos().col); asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: return scope->addSymbol(sym); asherkin@263: } asherkin@263: asherkin@263: bool registerArguments(const FunctionSignature &sig) { asherkin@263: if (!sig.parameters()->length()) asherkin@263: return true; asherkin@263: asherkin@263: ArgumentScope *argScope = ArgumentScope::New(pool_); asherkin@263: if (!argScope) asherkin@263: return false; asherkin@263: env_->setScope(argScope); asherkin@263: asherkin@263: for (size_t i = 0; i < sig.parameters()->length(); i++) { asherkin@263: Parameter *param = sig.parameters()->at(i); asherkin@263: VariableSymbol *sym = new (pool_) VariableSymbol(argScope, param->name(), param->pos()); asherkin@263: if (!registerName(env_->scope(), sym)) asherkin@263: return false; asherkin@263: param->setSymbol(sym); asherkin@263: } asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: Zone *zone_; asherkin@263: PoolAllocator &pool_; asherkin@263: CompileContext &cc_; asherkin@263: TranslationUnit *unit_; asherkin@263: SymbolEnv *env_; asherkin@263: FunctionScope *fun_; asherkin@263: }; asherkin@263: asherkin@263: void asherkin@263: ke::PopulateNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: { asherkin@263: AutoEnterCompileContext enter(cc, unit); asherkin@263: NamePopulator populator(zone, cc, unit); asherkin@263: populator.analyze(); asherkin@263: } asherkin@263: asherkin@263: static const int INFER_ARRAY_SIZE = 0; asherkin@263: static const int EVAL_ARRAY_SIZE = -1; asherkin@263: static const int DYNAMIC_ARRAY_SIZE = -2; asherkin@263: asherkin@263: class NameBinderBase : public AstVisitor asherkin@263: { asherkin@263: public: asherkin@263: NameBinderBase(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: : zone_(zone), asherkin@263: pool_(zone->pool()), asherkin@263: cc_(cc), asherkin@263: unit_(unit) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: protected: asherkin@263: Type *buildVariableType(VariableDeclaration *node) { asherkin@263: Local type(zone_, bindType(node->type(), node->quals())); asherkin@263: if (!type) asherkin@263: return NULL; asherkin@263: asherkin@263: // We don't allow array typedefs yet. asherkin@263: assert(!type->isArray()); asherkin@263: asherkin@263: if (node->dims()) { asherkin@263: int dims[MAX_ARRAY_DEPTH]; asherkin@263: int levels = evaluateDimensions(node->pos(), type, node->dims(), dims); asherkin@263: if (levels < 0) asherkin@263: return NULL; asherkin@263: asherkin@263: // Do some extra inference based on the initializer, if present. asherkin@263: Expression *init = node->initialization(); asherkin@263: if (init) { asherkin@263: if (init->isArrayLiteral() && init->toArrayLiteral()->isFixed()) { asherkin@263: Local contained(zone_, type->isArray() asherkin@263: ? ArrayType::cast(type)->contained() asherkin@263: : type); asherkin@263: if (!inferFixedArrayDimensions(init->toArrayLiteral(), contained, dims, levels)) asherkin@263: return NULL; asherkin@263: } else if (init->isStringLiteral()) { asherkin@263: dims[levels - 1] = init->toStringLiteral()->string()->length() + 1; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: // If we got extra dimensions, we need to build a new type. asherkin@263: if (node->dims()) { asherkin@263: if ((type = buildArrayType(type, node->quals(), dims, node->dims()->length())) == NULL) asherkin@263: return NULL; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: return type; asherkin@263: } asherkin@263: asherkin@263: int evaluateDimensions(const SourcePosition &pos, Handle typeArg, ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH]) { asherkin@263: unsigned level = 0; asherkin@263: Local type(zone_, typeArg); asherkin@263: while (type->isArray()) { asherkin@263: ArrayType *atype = ArrayType::cast(type); asherkin@263: dims[level] = atype->isFixedLength() asherkin@263: ? atype->fixedLength() asherkin@263: : DYNAMIC_ARRAY_SIZE; asherkin@263: type = atype->contained(); asherkin@263: level++; asherkin@263: } asherkin@263: asherkin@263: if (!exprs) asherkin@263: return level; asherkin@263: asherkin@263: if (level + exprs->length() > MAX_ARRAY_DEPTH) { asherkin@263: cc_.reportError(pos, Message_MaximumArrayDepth); asherkin@263: return -1; asherkin@263: } asherkin@263: asherkin@263: bool hadEmptySize = false; asherkin@263: for (size_t i = 0; i < exprs->length(); i++) { asherkin@263: Expression *expr = exprs->at(i); asherkin@263: if (!expr) { asherkin@263: dims[i] = INFER_ARRAY_SIZE; asherkin@263: hadEmptySize = true; asherkin@263: continue; asherkin@263: } asherkin@263: asherkin@263: BoxedPrimitive box; asherkin@263: if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { asherkin@263: int size = box.toInt(); asherkin@263: if (size <= 0) { asherkin@263: cc_.reportError(expr->pos(), Message_BadArraySize); asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: dims[i] = size; asherkin@263: continue; asherkin@263: } asherkin@263: asherkin@263: if (hadEmptySize) { asherkin@263: cc_.reportError(expr->pos(), Message_BadDynamicInitializer); asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: dims[i] = EVAL_ARRAY_SIZE; asherkin@263: } asherkin@263: asherkin@263: return level + exprs->length(); asherkin@263: } asherkin@263: asherkin@263: // This function computes missing dimension sizes based on a literal asherkin@263: // initializer. We don't really handle any size rules here; we just asherkin@263: // do a really dumb inference. asherkin@263: bool inferFixedArrayDimensions(ArrayLiteral *lit, Handle contained, int dims[MAX_ARRAY_DEPTH], int levels) { asherkin@263: for (int i = 0; i < levels; i++) { asherkin@263: if (dims[i] == EVAL_ARRAY_SIZE || dims[i] == DYNAMIC_ARRAY_SIZE) { asherkin@263: // The following are invalid: asherkin@263: // new x[] = {...} asherkin@263: // new Type:x = {...} // where Type is a non-fixed array type asherkin@263: cc_.reportError(lit->pos(), Message_CannotInitializeEvalArray); asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: if (dims[i] == INFER_ARRAY_SIZE) { asherkin@263: // Character arrays are always dynamic when inferred from string literals. asherkin@263: if (i == levels - 1 && contained->isChar()) asherkin@263: dims[i] = DYNAMIC_ARRAY_SIZE; asherkin@263: else asherkin@263: dims[i] = lit->expressions()->length(); asherkin@263: } asherkin@263: asherkin@263: if (i != levels - 1) { asherkin@263: // Fixed array literals are guaranteed to have at least one expression. asherkin@263: Expression *next = lit->expressions()->at(0); asherkin@263: if (!next->isArrayLiteral()) { asherkin@263: // Whelp... this array is just malformed. Not the best error message asherkin@263: // but whatever. asherkin@263: cc_.reportError(next->pos(), Message_ArraySizeCannotBeDetermined); asherkin@263: return false; asherkin@263: } asherkin@263: lit = next->toArrayLiteral(); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: ArrayType *buildArrayType(Handle contained, TypeQualifiers quals, int dims[MAX_ARRAY_DEPTH], size_t postlevels) { asherkin@263: Local type(zone_, contained); asherkin@263: asherkin@263: for (size_t i = postlevels - 1; i < postlevels; i--) { asherkin@263: int size = dims[i] > 0 asherkin@263: ? dims[i] asherkin@263: : ArrayType::DYNAMIC_ARRAY_SIZE; asherkin@263: asherkin@263: if ((type = zone_->types()->newArray(contained, size)) == NULL) asherkin@263: return NULL; asherkin@263: if ((type = zone_->types()->qualify(type, quals)) == NULL) asherkin@263: return NULL; asherkin@263: } asherkin@263: return ArrayType::cast(type); asherkin@263: } asherkin@263: asherkin@263: bool fillFunctionType(Handle fun, const FunctionSignature &sig) { asherkin@263: Local type(zone_); asherkin@263: if (!fun->returnType()) { asherkin@263: if ((type = bindType(sig.returnType(), TypeQual_None)) == NULL) asherkin@263: return false; asherkin@263: fun->setReturnType(type); asherkin@263: } asherkin@263: asherkin@263: if (sig.variadic()) asherkin@263: fun->setVariadic(); asherkin@263: asherkin@263: Local params(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old)); asherkin@263: if (!params) asherkin@263: return false; asherkin@263: asherkin@263: for (size_t i = 0; i < sig.parameters()->length(); i++) { asherkin@263: Parameter *param = sig.parameters()->at(i); asherkin@263: if ((type = bindType(param->type(), param->quals())) == NULL) asherkin@263: return false; asherkin@263: asherkin@263: assert(!type->isArray()); asherkin@263: asherkin@263: if (param->dims()) { asherkin@263: int dims[MAX_ARRAY_DEPTH]; asherkin@263: int levels = evaluateDimensions(param->pos(), type, param->dims(), dims); asherkin@263: if (levels < 0) asherkin@263: return false; asherkin@263: asherkin@263: // If we got extra dimensions, we need to build a new type. asherkin@263: // Unlike variables, we do not infer anything about array asherkin@263: // sizes from its default initializer. asherkin@263: if (param->dims()) { asherkin@263: if ((type = buildArrayType(type, param->quals(), dims, param->dims()->length())) == NULL) asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: if (ArrayType::cast(type)->innermost()->isVoid()) { asherkin@263: cc_.reportError(param->type()->pos(), Message_CannotDeclareVoid); asherkin@263: return false; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: if (param->reference()) { asherkin@263: if (!type->isPod()) { asherkin@263: cc_.reportError(param->pos(), Message_ArrayCantBeReference); asherkin@263: return false; asherkin@263: } asherkin@263: if (param->quals() & TypeQual_Const) { asherkin@263: cc_.reportError(param->pos(), Message_CannotConstReference); asherkin@263: return false; asherkin@263: } asherkin@263: if ((type = zone_->types()->getReference(type, false)) == NULL) asherkin@263: return false; asherkin@263: } asherkin@263: if (param->quals() & TypeQual_Const) { asherkin@263: if (!type->isArray() && !type->isStruct()) { asherkin@263: cc_.reportError(param->pos(), Message_InvalidConstOnFormal); asherkin@263: return false; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: if (type->isVoid()) { asherkin@263: cc_.reportError(param->type()->pos(), Message_CannotDeclareVoid); asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: params->set(zone_, i, type); asherkin@263: param->sym()->setType(type); asherkin@263: } asherkin@263: asherkin@263: fun->setParameterTypes(params); asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: Type *bindType(Expression *expr, TypeQualifiers quals) { asherkin@263: Local type(zone_); asherkin@263: asherkin@263: if (!expr) { asherkin@263: // If no type was declared, we return int32. asherkin@263: type = zone_->types()->getPrimitive(PrimitiveType_Int32); asherkin@263: } else { asherkin@263: expr->accept(this); asherkin@263: asherkin@263: NameProxy *proxy = expr->asNameProxy(); asherkin@263: assert(proxy); asherkin@263: asherkin@263: Symbol *sym = proxy->sym(); asherkin@263: if (!sym) asherkin@263: return NULL; asherkin@263: asherkin@263: if (!sym->isType()) { asherkin@263: cc_.reportError(expr->pos(), Message_IdentifierIsNotAType, sym->name()->chars()); asherkin@263: return NULL; asherkin@263: } asherkin@263: asherkin@263: type = sym->toType()->effective(); asherkin@263: } asherkin@263: asherkin@263: return zone_->types()->qualify(type, quals); asherkin@263: } asherkin@263: asherkin@263: protected: asherkin@263: Zone *zone_; asherkin@263: PoolAllocator &pool_; asherkin@263: CompileContext &cc_; asherkin@263: TranslationUnit *unit_; asherkin@263: }; asherkin@263: asherkin@263: class GlobalBuilder : public NameBinderBase asherkin@263: { asherkin@263: typedef ScopedRoot ScopedDescriptor; asherkin@263: struct PrimitiveFill { asherkin@263: ScopedDescriptor *desc; asherkin@263: BoxedPrimitive prim; asherkin@263: asherkin@263: PrimitiveFill(ScopedDescriptor *desc, BoxedPrimitive prim) asherkin@263: : desc(desc), asherkin@263: prim(prim) asherkin@263: { asherkin@263: } asherkin@263: }; asherkin@263: asherkin@263: public: asherkin@263: GlobalBuilder(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: : NameBinderBase(zone, cc, unit), asherkin@263: globalScope_(unit->globalScope()) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: Struct *build() { asherkin@263: for (size_t i = 0; i < unit_->tree()->imports()->length(); i++) asherkin@263: unit_->tree()->imports()->at(i)->accept(this); asherkin@263: asherkin@263: // Next, enter the global scope and walk all statements. asherkin@263: for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { asherkin@263: Statement *stmt = unit_->tree()->statements()->at(i); asherkin@263: stmt->accept(this); asherkin@263: } asherkin@263: asherkin@263: Local globals(zone_, createGlobals()); asherkin@263: if (!globals) asherkin@263: return NULL; asherkin@263: asherkin@263: populate(globals); asherkin@263: asherkin@263: return globals; asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: Struct *createGlobals() { asherkin@263: Local descs(zone_, FixedArray::New(zone_, descriptors_.length(), Heap::Tenure_Old)); asherkin@263: if (!descs) asherkin@263: return NULL; asherkin@263: asherkin@263: for (unsigned i = 0; i < descriptors_.length(); i++) asherkin@263: descs->set(zone_, i, *descriptors_[i]); asherkin@263: asherkin@263: Local name(zone_, unit_->module()->name()); asherkin@263: Local type(zone_, StructType::New(zone_, name, descs)); asherkin@263: if (!type) asherkin@263: return NULL; asherkin@263: asherkin@263: Local map(zone_, type->newMap()); asherkin@263: return Struct::New(zone_, map, Heap::Tenure_Old); asherkin@263: } asherkin@263: asherkin@263: void populate(Handle globals) { asherkin@263: // This is a huge hack, but it's probably better than a full extra asherkin@263: // pass. The problem is that until we have all the descriptors, we asherkin@263: // can't start setting constexprs in the struct, and we need to set asherkin@263: // them now so they're available during full semantic analysis. To asherkin@263: // work around this, we build up the list of values we need to set asherkin@263: // as we build up descriptors, and then populate the global struct asherkin@263: // with this set. asherkin@263: // asherkin@263: // Note, this code is also kind of dead until we support linking. We asherkin@263: // handle constexprs explicitly in importFrom(), by sniffing out the asherkin@263: // symbol type. That code will go away later. asherkin@263: for (size_t i = 0; i < primitiveFills_.length(); i++) { asherkin@263: const PrimitiveFill &fill = primitiveFills_[i]; asherkin@263: assert(fill.prim.type() == PrimitiveType_Int32); asherkin@263: globals->set(*fill.desc, fill.prim.toInt()); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: public: asherkin@263: void visitEnumStatement(EnumStatement *stmt) { asherkin@263: if (stmt->visibility() != TOK_PUBLIC || !stmt->name()) asherkin@263: return; asherkin@263: asherkin@263: Local type(zone_, zone_->types()->getType()); asherkin@263: Descriptor *desc = Descriptor::New(zone_, stmt->name(), type, Descriptor::Public); asherkin@263: if (!desc) asherkin@263: return; asherkin@263: if (!descriptors_.append(new (pool_) ScopedRoot(desc))) asherkin@263: return; asherkin@263: asherkin@263: for (size_t i = 0; i < stmt->entries()->length(); i++) { asherkin@263: EnumStatement::Entry &e = stmt->entries()->at(i); asherkin@263: ScopedDescriptor *desc = new (pool_) ScopedDescriptor(Descriptor::New(zone_, e.name, e.sym->type(), Descriptor::Public)); asherkin@263: if (!desc || !descriptors_.append(desc)) asherkin@263: return; asherkin@263: asherkin@263: // Save the value so we can export it immediately. asherkin@263: if (!primitiveFills_.append(PrimitiveFill(desc, e.sym->value()))) asherkin@263: return; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void visitNameProxy(NameProxy *proxy) { asherkin@263: Scope *scope = unit_->globalScope(); asherkin@263: Symbol *sym = scope->lookup(proxy->name()); asherkin@263: if (!sym) asherkin@263: cc_.reportError(proxy->pos(), Message_IdentifierNotFound, proxy->name()->chars()); asherkin@263: proxy->bind(sym); asherkin@263: } asherkin@263: asherkin@263: void visitFunctionStatement(FunctionStatement *node) { asherkin@263: Local type(zone_, node->sym()->type()); asherkin@263: if (!fillFunctionType(type, node->signature())) asherkin@263: return; asherkin@263: asherkin@263: unsigned offset; asherkin@263: if (!builder_.add(type, &offset)) asherkin@263: return; asherkin@263: asherkin@263: Descriptor::Visibility visibility = (node->token() == TOK_NATIVE || node->token() == TOK_FORWARD) asherkin@263: ? Descriptor::Public asherkin@263: : Descriptor::Private; asherkin@263: asherkin@263: Descriptor *desc = Descriptor::New(zone_, node->name(), type, visibility); asherkin@263: if (!desc) asherkin@263: return; asherkin@263: if (!descriptors_.append(new (pool_) ScopedRoot(desc))) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: void visitVariableDeclaration(VariableDeclaration *node) { asherkin@263: Local type(zone_, buildVariableType(node)); asherkin@263: if (!type) asherkin@263: return; asherkin@263: asherkin@263: // If we're in global scope, and we received a |const| qualifier, then asherkin@263: // we extend this to array references as well. This is to sort of mimic asherkin@263: // SP1 functionality, but also means that you cannot have changeable asherkin@263: // const pointers in global scope. asherkin@263: if ((node->quals() & TypeQual_Const) && type->isArray()) { asherkin@263: if ((type = zone_->types()->qualify(type, TypeQual_Const)) == NULL) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: node->sym()->setType(type); asherkin@263: asherkin@263: unsigned offset; asherkin@263: if (!builder_.add(type, &offset)) asherkin@263: return; asherkin@263: asherkin@263: Descriptor *desc = Descriptor::New(zone_, node->name(), type, Descriptor::Private); asherkin@263: if (!desc) asherkin@263: return; asherkin@263: if (!descriptors_.append(new (pool_) ScopedRoot(desc))) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: void visitImportStatement(ImportStatement *node) { asherkin@263: if (!node->names()) { asherkin@263: PathComponent *path = node->path(); asherkin@263: while (path->next) asherkin@263: path = path->next; asherkin@263: asherkin@263: // Imports automatically go into the importScope. For now, we asherkin@263: // error if there are any duplicate symbols entered into the asherkin@263: // importScope. asherkin@263: if (unit_->importScope()->localLookup(path->name)) { asherkin@263: cc_.reportError(node->pos(), Message_DuplicateImport, path->name->chars()); asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: Local type(zone_, zone_->types()->getImportable()); asherkin@263: ImportSymbol *sym = new (pool_) ImportSymbol(unit_->importScope(), path->name, node->pos(), type, node->importIndex()); asherkin@263: if (!unit_->importScope()->addSymbol(sym)) asherkin@263: return; asherkin@263: } else if (node->importAllNames()) { asherkin@263: Local importable(zone_, unit_->module()->getImportAt(node->importIndex())); asherkin@263: if (importable->isPackage()) { asherkin@263: assert(false); asherkin@263: } else { asherkin@263: Local module(zone_, Module::cast(importable)); asherkin@263: if (!module->globals()) asherkin@263: importAllFromTranslationUnit(node, module->translationUnit()); asherkin@263: else asherkin@263: assert(false); asherkin@263: } asherkin@263: } else { asherkin@263: Local importable(zone_, unit_->module()->getImportAt(node->importIndex())); asherkin@263: if (importable->isPackage()) { asherkin@263: assert(false); asherkin@263: } else { asherkin@263: Local module(zone_, Module::cast(importable)); asherkin@263: asherkin@263: // Always grab from the translation unit if we can, for now. asherkin@263: if (module->translationUnit()) asherkin@263: importFromTranslationUnit(node, module->translationUnit()); asherkin@263: else asherkin@263: assert(false); asherkin@263: } asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: bool importFrom(ImportStatement *node, Symbol *import) { asherkin@263: assert(import->type()); asherkin@263: asherkin@263: // Check for duplicate import names. asherkin@263: if (unit_->importScope()->localLookup(import->name())) { asherkin@263: cc_.reportError(node->pos(), Message_DuplicateImport, import->name()->chars()); asherkin@263: return false; asherkin@263: } asherkin@263: asherkin@263: Symbol *sym; asherkin@263: if (import->isType()) { asherkin@263: // Fuck it, make a TypeSymbol instead of an ImplicitSymbol. This asherkin@263: // makes bindType() much simpler. We may run into problems asherkin@263: // implementing linking, but it seems unlikely, as long as we are asherkin@263: // exporting Types in module structs. asherkin@263: Local type(zone_, zone_->types()->getType()); asherkin@263: sym = new (pool_) TypeSymbol(unit_->importScope(), asherkin@263: import->name(), asherkin@263: type, asherkin@263: import->toType()->effective()); asherkin@263: } else if (import->isConstant()) { asherkin@263: // Same deal. No reason to lookup in the module globals if we asherkin@263: // already have a symbol. asherkin@263: sym = new (pool_) ConstantSymbol(unit_->importScope(), asherkin@263: import->name(), asherkin@263: node->pos(), asherkin@263: import->type(), asherkin@263: import->toConstant()->value()); asherkin@263: } else { asherkin@263: sym = new (pool_) ImplicitSymbol(unit_->importScope(), asherkin@263: import->name(), asherkin@263: node->pos(), asherkin@263: import->type(), asherkin@263: node->importIndex()); asherkin@263: } asherkin@263: asherkin@263: if (!unit_->importScope()->addSymbol(sym)) asherkin@263: return false; asherkin@263: asherkin@263: return true; asherkin@263: } asherkin@263: asherkin@263: void importFromTranslationUnit(ImportStatement *node, TranslationUnit *other) { asherkin@263: assert(!other->failed()); asherkin@263: asherkin@263: for (size_t i = 0; i < node->names()->length(); i++) { asherkin@263: Handle name = *node->names()->at(i); asherkin@263: asherkin@263: // See if the name exists in the other translation unit. asherkin@263: Symbol *import = other->globalScope()->findExported(name); asherkin@263: if (!import) { asherkin@263: FixedArray *path = unit_->module()->importPathAt(node->importIndex()); asherkin@263: cc_.reportError(node->pos(), Message_NotFoundInImportable, asherkin@263: name->chars(), asherkin@263: String::cast(path->at(path->length() - 1))->chars()); asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: if (!importFrom(node, import)) asherkin@263: return; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void importAllFromTranslationUnit(ImportStatement *node, TranslationUnit *other) { asherkin@263: assert(!other->failed()); asherkin@263: asherkin@263: for (size_t i = 0; i < other->globalScope()->exported()->length(); i++) { asherkin@263: Symbol *import = other->globalScope()->exported()->at(i); asherkin@263: if (!importFrom(node, import)) asherkin@263: return; asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: StructureBufferBuilder builder_; asherkin@263: Vector *> descriptors_; asherkin@263: GlobalScope *globalScope_; asherkin@263: Vector primitiveFills_; asherkin@263: }; asherkin@263: asherkin@263: class NameBinder : public NameBinderBase asherkin@263: { asherkin@263: public: asherkin@263: class AutoLinkScope asherkin@263: { asherkin@263: public: asherkin@263: AutoLinkScope(AutoLinkScope **prevp, Scope *scope) asherkin@263: : prevp_(prevp), asherkin@263: prev_(*prevp), asherkin@263: scope_(scope) asherkin@263: { asherkin@263: if (scope_) { asherkin@263: *prevp_ = this; asherkin@263: if (prev_) asherkin@263: scope_->setParent(prev_->scope()); asherkin@263: } asherkin@263: } asherkin@263: ~AutoLinkScope() { asherkin@263: if (scope_) { asherkin@263: assert(*prevp_ == this); asherkin@263: *prevp_ = prev_; asherkin@263: } asherkin@263: } asherkin@263: Scope *scope() const { asherkin@263: return scope_; asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: AutoLinkScope **prevp_; asherkin@263: AutoLinkScope *prev_; asherkin@263: Scope *scope_; asherkin@263: }; asherkin@263: asherkin@263: public: asherkin@263: NameBinder(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: : NameBinderBase(zone, cc, unit), asherkin@263: link_(NULL) asherkin@263: { asherkin@263: } asherkin@263: asherkin@263: void bind() { asherkin@263: // Start with the import scope, and collect all imported names. asherkin@263: AutoLinkScope importScope(&link_, unit_->importScope()); asherkin@263: asherkin@263: for (size_t i = 0; i < unit_->tree()->imports()->length(); i++) asherkin@263: unit_->tree()->imports()->at(i)->accept(this); asherkin@263: asherkin@263: // Next, enter the global scope and walk all statements. asherkin@263: AutoLinkScope globalScope(&link_, unit_->globalScope()); asherkin@263: for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { asherkin@263: Statement *stmt = unit_->tree()->statements()->at(i); asherkin@263: stmt->accept(this); asherkin@263: } asherkin@263: } asherkin@263: asherkin@263: void visitVariableDeclaration(VariableDeclaration *node) { asherkin@263: // Bind the initializer before registering the declaration, so that we asherkin@263: // can error on bogus initializers (new x = x). asherkin@263: if (node->initialization()) asherkin@263: node->initialization()->accept(this); asherkin@263: asherkin@263: // If the node already has a symbol (meaning it was a global), then asherkin@263: // we don't have to do anything more. asherkin@263: if (node->sym()) asherkin@263: return; asherkin@263: asherkin@263: Local type(zone_, buildVariableType(node)); asherkin@263: if (!type) asherkin@263: return; asherkin@263: asherkin@263: VariableSymbol *sym = new (pool_) VariableSymbol(link_->scope(), node->name(), node->pos(), type); asherkin@263: node->setSymbol(sym); asherkin@263: asherkin@263: if (Symbol *other = link_->scope()->localLookup(sym->name())) { asherkin@263: // Report, but allow errors to continue. asherkin@263: cc_.reportError(sym->pos(), Message_RedeclaredName, asherkin@263: sym->name()->chars(), asherkin@263: other->pos().line, asherkin@263: other->pos().col); asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: link_->scope()->addSymbol(sym); asherkin@263: } asherkin@263: void visitEnumStatement(EnumStatement *node) { asherkin@263: } asherkin@263: void visitFunctionStatement(FunctionStatement *node) { asherkin@263: AutoLinkScope argScope(&link_, node->argScope()); asherkin@263: AutoLinkScope varScope(&link_, node->varScope()); asherkin@263: asherkin@263: // If the function is in the global scope, we've already computed its asherkin@263: // type. asherkin@263: if (node->sym()->scope()->kind() != Scope::GLOBAL) { asherkin@263: Local type(zone_, node->sym()->type()); asherkin@263: if (!fillFunctionType(type, node->signature())) asherkin@263: return; asherkin@263: } asherkin@263: asherkin@263: if (node->body()) asherkin@263: node->body()->accept(this); asherkin@263: } asherkin@263: void visitStructureStatement(StructureStatement *node) { asherkin@263: assert(false); asherkin@263: } asherkin@263: void visitFunctionTypeStatement(FunctionTypeStatement *node) { asherkin@263: assert(false); asherkin@263: } asherkin@263: asherkin@263: void visitNameProxy(NameProxy *proxy) { asherkin@263: // We could have already bound globals. asherkin@263: if (proxy->sym()) asherkin@263: return; asherkin@263: asherkin@263: Scope *scope = link_->scope(); asherkin@263: Symbol *sym = scope->lookup(proxy->name()); asherkin@263: if (!sym) asherkin@263: cc_.reportError(proxy->pos(), Message_IdentifierNotFound, proxy->name()->chars()); asherkin@263: proxy->bind(sym); asherkin@263: } asherkin@263: void visitAssignment(Assignment *node) { asherkin@263: node->lvalue()->accept(this); asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitBinaryExpression(BinaryExpression *node) { asherkin@263: node->left()->accept(this); asherkin@263: node->right()->accept(this); asherkin@263: } asherkin@263: void visitReturnStatement(ReturnStatement *node) { asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitForStatement(ForStatement *node) { asherkin@263: AutoLinkScope scope(&link_, node->scope()); asherkin@263: asherkin@263: if (node->initialization()) asherkin@263: node->initialization()->accept(this); asherkin@263: if (node->condition()) asherkin@263: node->condition()->accept(this); asherkin@263: if (node->update()) asherkin@263: node->update()->accept(this); asherkin@263: node->body()->accept(this); asherkin@263: } asherkin@263: void visitBlockStatement(BlockStatement *node) { asherkin@263: AutoLinkScope scope(&link_, node->scope()); asherkin@263: for (size_t i = 0; i < node->statements()->length(); i++) asherkin@263: node->statements()->at(i)->accept(this); asherkin@263: } asherkin@263: void visitIntegerLiteral(IntegerLiteral *node) { asherkin@263: } asherkin@263: void visitExpressionStatement(ExpressionStatement *node) { asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitCallExpression(CallExpression *node) { asherkin@263: node->callee()->accept(this); asherkin@263: for (size_t i = 0; i < node->arguments()->length(); i++) asherkin@263: node->arguments()->at(i)->accept(this); asherkin@263: } asherkin@263: void visitIfStatement(IfStatement *node) { asherkin@263: node->condition()->accept(this); asherkin@263: node->ifTrue()->accept(this); asherkin@263: if (node->ifFalse()) asherkin@263: node->ifFalse()->accept(this); asherkin@263: } asherkin@263: void visitIndexExpression(IndexExpression *node) { asherkin@263: node->left()->accept(this); asherkin@263: node->right()->accept(this); asherkin@263: } asherkin@263: void visitFloatLiteral(FloatLiteral *node) { asherkin@263: } asherkin@263: void visitWhileStatement(WhileStatement *node) { asherkin@263: node->condition()->accept(this); asherkin@263: node->body()->accept(this); asherkin@263: } asherkin@263: void visitBreakStatement(BreakStatement *node) { asherkin@263: } asherkin@263: void visitContinueStatement(ContinueStatement *node) { asherkin@263: } asherkin@263: void visitStringLiteral(StringLiteral *node) { asherkin@263: } asherkin@263: void visitIncDecExpression(IncDecExpression *node) { asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitUnaryExpression(UnaryExpression *node) { asherkin@263: if (node->tag()) asherkin@263: node->tag()->accept(this); asherkin@263: node->expression()->accept(this); asherkin@263: } asherkin@263: void visitTernaryExpression(TernaryExpression *node) { asherkin@263: node->condition()->accept(this); asherkin@263: node->left()->accept(this); asherkin@263: node->right()->accept(this); asherkin@263: } asherkin@263: void visitBooleanLiteral(BooleanLiteral *node) { asherkin@263: } asherkin@263: void visitFieldExpression(FieldExpression *node) { asherkin@263: node->left()->accept(this); asherkin@263: } asherkin@263: void visitSwitchStatement(SwitchStatement *node) { asherkin@263: node->expression()->accept(this); asherkin@263: node->defaultCase()->accept(this); asherkin@263: for (size_t i = 0; i < node->cases()->length(); i++) { asherkin@263: // We don't test case expressions because they are literals. asherkin@263: Case *c = node->cases()->at(i); asherkin@263: c->statement()->accept(this); asherkin@263: } asherkin@263: } asherkin@263: void visitArrayLiteral(ArrayLiteral *node) { asherkin@263: for (size_t i = 0; i < node->expressions()->length(); i++) asherkin@263: node->expressions()->at(i)->accept(this); asherkin@263: } asherkin@263: void visitImportStatement(ImportStatement *node) { asherkin@263: } asherkin@263: asherkin@263: private: asherkin@263: AutoLinkScope *link_; asherkin@263: }; asherkin@263: asherkin@263: void asherkin@263: ke::BindGlobals(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: { asherkin@263: AutoEnterCompileContext enter(cc, unit); asherkin@263: asherkin@263: GlobalBuilder builder(zone, cc, unit); asherkin@263: Local globals(zone, builder.build()); asherkin@263: if (!globals || unit->failed()) asherkin@263: return; asherkin@263: asherkin@263: unit->module()->setGlobals(globals); asherkin@263: } asherkin@263: asherkin@263: void asherkin@263: ke::BindNamesAndTypes(Zone *zone, CompileContext &cc, TranslationUnit *unit) asherkin@263: { asherkin@263: AutoEnterCompileContext enter(cc, unit); asherkin@263: NameBinder binder(zone, cc, unit); asherkin@263: binder.bind(); asherkin@263: }