diff -r 06a0015b6a5c -r 32ad0523b309 src/API.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/API.cpp Tue Jul 24 23:53:34 2012 -0700 @@ -0,0 +1,483 @@ +/* vim: set ts=4 sw=4 tw=99 et: + * + * Copyright (C) 2012 David Anderson + * + * This file is part of JITCraft. + * + * JITCraft is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * JITCraft. If not, see http://www.gnu.org/licenses/. + */ +#include "../include/keima.h" +#include "Zone.h" +#include "RootScopes.h" +#include "CompileContext.h" +#include "Parser.h" +#include "Scopes.h" +#include "SemanticAnalysis.h" +#include "BytecodeCompiler.h" +#include "Modules.h" +#include "GlobalRoots.h" +#include "Reference.h" +#include "Array.h" +#include "Interpreter.h" +#include "Stack.h" +#include "Strings.h" + +using namespace ke; + +bool +Ke::Keima::Initialize() +{ + SingleThreadedZone = new Zone(); + if (!SingleThreadedZone || !SingleThreadedZone->initialize()) + return false; + return true; +} + +void +Ke::Keima::Shutdown() +{ + delete SingleThreadedZone; + SingleThreadedZone = NULL; +} + +Ke::HandleScope::HandleScope() + : zone_(ke::ZONE()), + position_(zone_->rootScopes().position()), + prev_(*zone_->rootScopes().scope()) +{ + *zone_->rootScopes().scope() = this; +} + +Ke::HandleScope::~HandleScope() +{ + zone_->rootScopes().unwind(position_); + *zone_->rootScopes().scope() = prev_; +} + +ke::Object ** +Ke::HandleScope::CreateHandle(ke::Object *obj) +{ + if (!obj) + return NULL; + + return ZONE()->rootScopes().make(obj); +} + +template static inline To * +Open(Ke::Handle from) +{ + return *reinterpret_cast(*from); +} + +template static inline To * +Open(From *from) +{ + return *reinterpret_cast(from); +} + +template static inline Ke::Local +ToLocal(From *from) +{ + return Ke::Local(reinterpret_cast(Ke::HandleScope::CreateHandle(from))); +} + +static ke::Module * +CompileFromSource(CompileContext &cc, const char *path, const char *stream, size_t length) +{ + Parser parser(ZONE(), cc, stream, length); + ParseTree *tree = parser.parse(); + if (!tree) + return NULL; + tree->dump(stdout); + + if (!Scope::Bind(cc, tree)) + return NULL; + + SemanticAnalysis semantics(ZONE(), cc, tree); + if (!semantics.analyze()) + return NULL; + + Scope::Allocate(tree); + + BytecodeCompiler compiler(ZONE(), tree, NULL); + ke::Local module(ke::ZONE(), compiler.compile()); + if (!module) + return NULL; + + ke::InvokeArgs args; + if (!ke::ZONE()->stack().pushModuleFrame(module, args)) + return NULL; + if (!ke::Interpret(ke::ZONE(), NULL, NULL)) + return NULL; + + return module; +} + +Ke::Local +Ke::Compile(const char *path) +{ + RootScope roots(ZONE()); + CompileContext cc(ZONE(), path); + + char *bytes = NULL; + size_t length; + { + FILE *fp = fopen(path, "rb"); + if (!fp) { + cc.reportError(SourcePosition(), Message_CantOpenFile); + return Ke::Local(); + } + + if (fseek(fp, 0, SEEK_END)) { + cc.reportError(SourcePosition(), Message_ErrorSeekingFile); + fclose(fp); + return Ke::Local();; + } + long size = ftell(fp); + if (size < 0) { + cc.reportError(SourcePosition(), Message_ErrorSeekingFile); + fclose(fp); + return Ke::Local();; + } + rewind(fp); + length = size_t(size); + bytes = new char[length + 1]; + if (fread(bytes, 1, length, fp) != length) { + cc.reportError(SourcePosition(), Message_ErrorReadingFile); + fclose(fp); + delete [] bytes; + return Ke::Local();; + } + fclose(fp); + } + + ke::Local module(ZONE(), CompileFromSource(cc, path, bytes, length)); + delete [] bytes; + if (!module) + return Ke::Local(); + + ke::Local pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old)); + if (!pathstr) + return Ke::Local(); + + module->setPath(pathstr); + return ToLocal(module); +} + +void +Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter) +{ + ZONE()->installErrorReporter(reporter); +} + +class StackFrameIterator : public Ke::IStackFrameIterator +{ + StackIter iter_; + + public: + StackFrameIterator() + : iter_(ZONE()) + { + } + + virtual bool done() { + return iter_.done(); + } + + const char *function() { + return iter_.function(); + } + + const char *source() { + return iter_.source(); + } + + unsigned lineNumber() { + return iter_.lineNumber(); + } + + void prev() { + iter_.prev(); + } +}; + +Ke::IStackFrameIterator * +Ke::Keima::NewStackFrameIterator() +{ + return new StackFrameIterator(); +} + +static inline ke::HeapRoot * +FromAPI(ke::HeapRoot *from) +{ + ke::Address address = reinterpret_cast(from); + address -= offsetof(ke::HeapRoot, obj); + return reinterpret_cast(address); +} + +ke::HeapRoot * +Ke::Internals::NewHeapRoot(ke::Object **ptr) +{ + ke::Local obj(ZONE(), *ptr); + ke::HeapRoot *root = ke::ZONE()->globalRoots()->create(obj); + if (!root) + return NULL; + + // Compute an interior pointer, so it looks like a Handle to an object. + return reinterpret_cast(&root->obj); +} + +void +Ke::Internals::IncRef(ke::HeapRoot *root) +{ + ZONE()->globalRoots()->incref(FromAPI(root)); +} + +void +Ke::Internals::DecRef(ke::HeapRoot *root) +{ + ZONE()->globalRoots()->decref(FromAPI(root)); +} + +bool +Ke::Keima::IsInCallStack(Handle argModule) +{ + ke::Local module(ke::ZONE(), Open(argModule)); + return ZONE()->stack().isInCallStack(module); +} + +void +Ke::Keima::CollectGarbage() +{ + ke::ZONE()->heap().reclaim(); +} + +unsigned +Ke::Module::numPublics() +{ + ke::Local module(ke::ZONE(), Open(this)); + if (!module->publics()) + return 0; + return module->publics()->length(); +} + +Ke::Local +Ke::Module::getPublic(unsigned index) +{ + ke::Local module(ke::ZONE(), Open(this)); + ke::Local fun(ke::ZONE(), ke::Function::cast(module->publics()->at(index))); + return ToLocal(fun); +} + +unsigned +Ke::Module::numNatives() +{ + ke::Local module(ke::ZONE(), Open(this)); + if (!module->natives()) + return 0; + return module->natives()->length(); +} + +Ke::Local +Ke::Module::getNative(unsigned index) +{ + ke::Local module(ke::ZONE(), Open(this)); + ke::Local fun(ke::ZONE(), ke::Native::cast(module->natives()->at(index))); + return ToLocal(fun); +} + +void +Ke::Module::setNativeIdentity(void *identity) +{ + ke::Local module(ke::ZONE(), Open(this)); + module->setNativeIdentity(identity); +} + +unsigned +Ke::String::length() +{ + ke::Local obj(ke::ZONE(), Open(this)); + return obj->length(); +} + +const char * +Ke::String::chars() +{ + ke::Local obj(ke::ZONE(), Open(this)); + return obj->chars(); +} + +Ke::Local +Ke::Function::name() +{ + ke::Local fun(ke::ZONE(), Open(this)); + return ToLocal(fun->name()); +} + +Ke::Local +Ke::Native::name() +{ + ke::Local native(ke::ZONE(), Open(this)); + return ToLocal(native->name()); +} + +void +Ke::Native::bind(void *function) +{ + ke::Local native(ke::ZONE(), Open(this)); + native->bind(function); +} + +static inline bool +CheckInvokeType(const Ke::Parameter ¶m, Type *type) +{ + switch (param.type()) { + case Ke::Parameter::Array: + return type->isArray() && + type->levels() == 1 && + type->contained()->isPrimitive() && + (type->contained()->primitive() != PrimitiveType_Char); + case Ke::Parameter::CharArray: + return type->isCharArray(); + case Ke::Parameter::Float: + return type->isFloat(); + case Ke::Parameter::FloatRef: + return type->isReference() && type->contained()->isFloat(); + case Ke::Parameter::Int: + return type->isInt32(); + case Ke::Parameter::IntRef: + return type->isReference() && type->contained()->isInt32(); + default: + return false; + } +} + +bool +Ke::Keima::CheckInvokeTypes(Ke::Handle argFun, const Ke::Parameter *params, unsigned nparams) +{ + ke::Local fun(ke::ZONE(), Open(argFun)); + + if (fun->nargs() != nparams) + return false; + + for (unsigned i = 0; i < nparams; i++) { + if (!CheckInvokeType(params[i], fun->argType(i))) + return false; + } + + // Functions can only return tagged cells, for ISPE1-compatible Invoke(). + ke::Local returnType(ke::ZONE(), fun->type()->returnType()); + if ((!returnType->isPrimitive() && !returnType->isVoid()) || returnType->isTraceable()) + return false; + + return true; +} + +bool +Ke::Keima::Invoke(Handle argFun, const Parameter *params, unsigned nparams, int *retval) +{ + assert(CheckInvokeTypes(argFun, params, nparams)); + + ke::Local fun(ke::ZONE(), Open(argFun)); + + if (fun->nargs() != nparams) { + // :TODO: report error + assert(false); + return false; + } + + ke::InvokeArgs args; + if (!ke::ZONE()->stack().pushInvokeFrame(fun, args)) + return false; + + for (unsigned i = 0; i < nparams; i++) { + ke::LocalSlot &slot = args.arg(i); + switch (params[i].type()) { + case Ke::Parameter::Int: + slot.i32 = params[i].i32(); + break; + + case Ke::Parameter::Float: + slot.f = params[i].f(); + break; + + case Ke::Parameter::IntRef: + if (params[i].writeback()) { + slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack); + } else { + LocalSlot other; + other.i32 = *params[i].i32p(); + slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack); + } + if (!slot.ref) + return false; + break; + + case Ke::Parameter::FloatRef: + if (params[i].writeback()) { + slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack); + } else { + LocalSlot other; + other.f = *params[i].fp(); + slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack); + } + if (!slot.ref) + return false; + break; + + case Ke::Parameter::Array: + { + ke::Local map(ke::ZONE(), ke::ZONE()->genericArrayMap()); + if (params[i].writeback()) { + slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(), + params[i].buffer(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + } else { + Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(int)); + } + break; + } + + case Ke::Parameter::CharArray: + { + ke::Local map(ke::ZONE(), ke::ZONE()->charArrayMap()); + if (params[i].writeback()) { + slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(), + params[i].buffer(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + } else { + Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack); + if (!slot.obj) + return false; + memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(char)); + } + break; + } + } + } + + LocalSlot result; + if (!Interpret(ke::ZONE(), NULL, &result)) + return false; + + if (fun->type()->returnType()->isVoid()) + result.i32 = 0; + + *retval = result.i32; + return true; +}