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