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@195: #include "compiler/CompileContext.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" dvander@195: #include "NativeTable.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@232: bool dvander@162: InternalCompile(const char *path) dvander@61: { dvander@195: PoolScope poolScope(ZONE()->pool()); dvander@61: RootScope roots(ZONE()); dvander@69: CompileContext cc(ZONE(), path); dvander@61: dvander@195: TranslationUnit *unit = new (ZONE()->pool()) TranslationUnit(); dvander@195: unit->path_ = path; dvander@195: dvander@195: if (!cc.addTranslationUnit(unit)) dvander@232: return false; dvander@61: dvander@195: if (!cc.compile()) dvander@232: return false; dvander@195: dvander@232: return true; 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@232: bool dvander@162: Ke::Compile(const char *path) dvander@162: { dvander@232: return InternalCompile(path); 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@195: Ke::Internals::NewHeapRoot() dvander@61: { dvander@195: ke::HeapRoot *root = ke::ZONE()->globalRoots()->create(); 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@195: Ke::Internals::Destroy(ke::HeapRoot *root) dvander@61: { dvander@195: ZONE()->globalRoots()->destroy(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@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@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@209: { dvander@209: if (!type->isArray()) dvander@209: return false; dvander@209: dvander@209: ArrayType *atype = ArrayType::cast(type); dvander@209: return atype->levels() == 1 && dvander@209: (atype->contained()->isPrimitive() || atype->contained()->isEnum()) && dvander@209: (atype->contained()->primitive() != PrimitiveType_Char); dvander@209: } dvander@65: case Ke::Parameter::CharArray: dvander@209: return type->isArray() && ArrayType::cast(type)->isCharArray(); dvander@65: case Ke::Parameter::Float: dvander@65: return type->isFloat(); dvander@65: case Ke::Parameter::FloatRef: dvander@222: return type->isReference() && ReferenceType::cast(type)->contained()->isFloat(); dvander@65: case Ke::Parameter::Int: dvander@145: return type->isInt32() || type->isEnum(); dvander@65: case Ke::Parameter::IntRef: dvander@222: return type->isReference() && dvander@222: (ReferenceType::cast(type)->contained()->isInt32() || dvander@222: ReferenceType::cast(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: } dvander@195: dvander@195: bool dvander@195: Ke::Keima::BindNative(const char *name, void *address) dvander@195: { dvander@195: return ke::ZONE()->nativeTable()->bind(name, address); dvander@195: } dvander@195: dvander@195: void dvander@195: Ke::Keima::SetPublicFunctionListener(IPublicFunctionListener *listener) dvander@195: { dvander@195: ke::ZONE()->setPublicFunctionListener(listener); dvander@195: } dvander@195: dvander@195: void dvander@195: Zone::registerPublic(ke::Handle fun) dvander@195: { dvander@195: if (!publicFunctionListener_) dvander@195: return; dvander@195: dvander@195: Ke::Local local = ToLocal(fun); dvander@195: publicFunctionListener_->registerPublic(local); dvander@195: }