src/API.cpp
author David Anderson <dvander@alliedmods.net>
Sat Nov 17 23:11:38 2012 -0800 (2012-11-17)
changeset 209 5cbec15fadfe
parent 195 c1ba166f3fc4
child 222 0cde1379b29f
permissions -rw-r--r--
Split ArrayType from Type.
     1 /* vim: set ts=4 sw=4 tw=99 et:
     2  *
     3  * Copyright (C) 2012 David Anderson
     4  *
     5  * This file is part of JITCraft.
     6  *
     7  * JITCraft is free software: you can redistribute it and/or modify it under
     8  * the terms of the GNU General Public License as published by the Free
     9  * Software Foundation, either version 3 of the License, or (at your option)
    10  * any later version.
    11  * 
    12  * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
    13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License along with
    17  * JITCraft. If not, see http://www.gnu.org/licenses/.
    18  */
    19 #include <string.h>
    20 #include "../include/keima.h"
    21 #include "Zone.h"
    22 #include "RootScopes.h"
    23 #include "compiler/CompileContext.h"
    24 #include "Modules.h"
    25 #include "GlobalRoots.h"
    26 #include "Reference.h"
    27 #include "Array.h"
    28 #include "Interpreter.h"
    29 #include "Stack.h"
    30 #include "Strings.h"
    31 #include "NativeTable.h"
    32 #include "Spaces-inl.h"
    33 #include "Heap-inl.h"
    34 
    35 using namespace ke;
    36 
    37 bool
    38 Ke::Keima::Initialize()
    39 {
    40     SingleThreadedZone = new Zone();
    41     if (!SingleThreadedZone || !SingleThreadedZone->initialize())
    42         return false;
    43     return true;
    44 }
    45 
    46 void
    47 Ke::Keima::Shutdown()
    48 {
    49     delete SingleThreadedZone;
    50     SingleThreadedZone = NULL;
    51 }
    52 
    53 Ke::HandleScope::HandleScope()
    54   : zone_(ke::ZONE()),
    55     position_(zone_->rootScopes().position()),
    56     prev_(*zone_->rootScopes().scope())
    57 {
    58     *zone_->rootScopes().scope() = this;
    59 }
    60 
    61 Ke::HandleScope::~HandleScope()
    62 {
    63     zone_->rootScopes().unwind(position_);
    64     *zone_->rootScopes().scope() = prev_;
    65 }
    66 
    67 ke::Object **
    68 Ke::HandleScope::CreateHandle(ke::Object *obj)
    69 {
    70     if (!obj)
    71         return NULL;
    72 
    73     return ZONE()->rootScopes().make(obj);
    74 }
    75 
    76 template <typename From, typename To> static inline To *
    77 Open(Ke::Handle<From> from)
    78 {
    79     return *reinterpret_cast<To **>(*from);
    80 }
    81 
    82 template <typename From, typename To> static inline To *
    83 Open(From *from)
    84 {
    85     return *reinterpret_cast<To **>(from);
    86 }
    87 
    88 template <typename From, typename To> static inline Ke::Local<To>
    89 ToLocal(From *from)
    90 {
    91     return Ke::Local<To>(reinterpret_cast<To *>(Ke::HandleScope::CreateHandle(from)));
    92 }
    93 
    94 ke::Module *
    95 InternalCompile(const char *path)
    96 {
    97     PoolScope poolScope(ZONE()->pool());
    98     RootScope roots(ZONE());
    99     CompileContext cc(ZONE(), path);
   100 
   101     TranslationUnit *unit = new (ZONE()->pool()) TranslationUnit();
   102     unit->path_ = path;
   103 
   104     if (!cc.addTranslationUnit(unit))
   105         return NULL;
   106 
   107     if (!cc.compile())
   108         return NULL;
   109 
   110     return NULL;
   111 
   112 #if 0
   113     ke::Local<ke::Module> module(ZONE(),  CompileFromSource(cc, path, bytes, length));
   114     delete [] bytes;
   115     if (!module)
   116         return NULL;
   117 
   118     ke::Local<ke::String> pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old));
   119     if (!pathstr)
   120         return NULL;
   121 
   122     module->setPath(pathstr);
   123     return module;
   124 #endif
   125     return NULL;
   126 }
   127 
   128 // This is split out from InternalCompile so the RootScope doesn't conflict
   129 // with making a return handle.
   130 Ke::Local<Ke::Module>
   131 Ke::Compile(const char *path)
   132 {
   133     ke::Local<ke::Module> module(ZONE(), InternalCompile(path));
   134     return ToLocal<ke::Module, Ke::Module>(module);
   135 }
   136 
   137 void
   138 Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter)
   139 {
   140     ZONE()->installErrorReporter(reporter);
   141 }
   142 
   143 class StackFrameIterator : public Ke::IStackFrameIterator
   144 {
   145     StackIter iter_;
   146 
   147   public:
   148     StackFrameIterator()
   149       : iter_(ZONE())
   150     {
   151     }
   152 
   153     virtual bool done() {
   154         return iter_.done();
   155     }
   156 
   157     const char *function() {
   158         return iter_.function();
   159     }
   160 
   161     const char *source() {
   162         return iter_.source();
   163     }
   164 
   165     unsigned lineNumber() {
   166         return iter_.lineNumber();
   167     }
   168 
   169     void prev() {
   170         iter_.prev();
   171     }
   172 };
   173 
   174 Ke::IStackFrameIterator *
   175 Ke::Keima::NewStackFrameIterator()
   176 {
   177     return new StackFrameIterator();
   178 }
   179 
   180 static inline ke::HeapRoot *
   181 FromAPI(ke::HeapRoot *from)
   182 {
   183     ke::Address address = reinterpret_cast<ke::Address>(from);
   184     address -= offsetof(ke::HeapRoot, obj);
   185     return reinterpret_cast<ke::HeapRoot *>(address);
   186 }
   187 
   188 ke::HeapRoot *
   189 Ke::Internals::NewHeapRoot()
   190 {
   191     ke::HeapRoot *root = ke::ZONE()->globalRoots()->create();
   192     if (!root)
   193         return NULL;
   194 
   195     // Compute an interior pointer, so it looks like a Handle to an object.
   196     return reinterpret_cast<ke::HeapRoot *>(&root->obj);
   197 }
   198 
   199 void
   200 Ke::Internals::Destroy(ke::HeapRoot *root)
   201 {
   202     ZONE()->globalRoots()->destroy(FromAPI(root));
   203 }
   204 
   205 bool
   206 Ke::Keima::IsInCallStack(Handle<Module> argModule)
   207 {
   208     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(argModule));
   209     return ZONE()->stack().isInCallStack(module);
   210 }
   211 
   212 void
   213 Ke::Keima::CollectGarbage()
   214 {
   215     ke::ZONE()->heap().reclaim();
   216 }
   217 
   218 void
   219 Ke::Keima::ClearPendingException()
   220 {
   221     ke::ZONE()->clearPendingException();
   222 }
   223 
   224 void
   225 Ke::Module::setNativeIdentity(void *identity)
   226 {
   227     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   228     module->setNativeIdentity(identity);
   229 }
   230 
   231 unsigned
   232 Ke::String::length()
   233 {
   234     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   235     return obj->length();
   236 }
   237 
   238 const char *
   239 Ke::String::chars()
   240 {
   241     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   242     return obj->chars();
   243 }
   244 
   245 Ke::Local<Ke::String>
   246 Ke::Function::name()
   247 {
   248     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(this));
   249     return ToLocal<ke::String, Ke::String>(fun->name());
   250 }
   251 
   252 static inline const char *
   253 GetInvokeTypeName(Ke::Parameter::Type type)
   254 {
   255     switch (type) {
   256       case Ke::Parameter::Array:
   257         return "array";
   258       case Ke::Parameter::CharArray:
   259         return "String";
   260       case Ke::Parameter::Float:
   261         return "Float";
   262       case Ke::Parameter::FloatRef:
   263         return "&Float";
   264       case Ke::Parameter::Int:
   265         return "int";
   266       case Ke::Parameter::IntRef:
   267         return "&int";
   268       default:
   269         return "<unknown>";
   270     }
   271 }
   272 
   273 static inline bool
   274 CheckInvokeType(const Ke::Parameter &param, Type *type)
   275 {
   276     switch (param.type()) {
   277       case Ke::Parameter::Array:
   278       {
   279         if (!type->isArray())
   280             return false;
   281 
   282         ArrayType *atype = ArrayType::cast(type);
   283         return atype->levels() == 1 &&
   284                (atype->contained()->isPrimitive() || atype->contained()->isEnum()) &&
   285                (atype->contained()->primitive() != PrimitiveType_Char);
   286       }
   287       case Ke::Parameter::CharArray:
   288         return type->isArray() && ArrayType::cast(type)->isCharArray();
   289       case Ke::Parameter::Float:
   290         return type->isFloat();
   291       case Ke::Parameter::FloatRef:
   292         return type->isReference() && type->contained_ref()->isFloat();
   293       case Ke::Parameter::Int:
   294         return type->isInt32() || type->isEnum();
   295       case Ke::Parameter::IntRef:
   296        return type->isReference() && (type->contained_ref()->isInt32() || type->contained_ref()->isEnum());
   297       default:
   298         return false;
   299     }
   300 }
   301 
   302 static bool
   303 CheckInvokeTypes(Ke::Handle<Ke::Function> argFun, const Ke::Parameter *params, unsigned nparams)
   304 {
   305     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   306 
   307     if (fun->nargs() != nparams)
   308         return false;
   309 
   310     for (unsigned i = 0; i < nparams; i++) {
   311         if (!CheckInvokeType(params[i], fun->argType(i))) {
   312             ke::ZONE()->reportError(Message_TypeMismatchDuringInvoke,
   313                                     GetInvokeTypeName(params[i].type()),
   314                                     GetTypeName(fun->argType(i)));
   315             return false;
   316         }
   317     }
   318 
   319     // Functions can only return tagged cells, for ISPE1-compatible Invoke().
   320     ke::Local<ke::Type> returnType(ke::ZONE(), fun->type()->returnType());
   321     if (!returnType->isPrimitive() &&
   322         !returnType->isEnum() &&
   323         !returnType->isVoid())
   324     {
   325         ke::ZONE()->reportError(Message_CannotInvokeReturnType, GetTypeName(returnType));
   326         return false;
   327     }
   328 
   329     return true;
   330 }
   331 
   332 bool
   333 Ke::Keima::Invoke(Handle<Function> argFun, const Parameter *params, unsigned nparams,
   334                   bool checkTypes, int *retval)
   335 {
   336     assert(CheckInvokeTypes(argFun, params, nparams));
   337 
   338     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   339 
   340     if (fun->nargs() != nparams) {
   341         // :TODO: report error
   342         assert(false);
   343         return false;
   344     }
   345 
   346     ke::InvokeArgs args;
   347     if (!ke::ZONE()->stack().pushInvokeFrame(fun, args))
   348         return false;
   349 
   350     if (checkTypes && !CheckInvokeTypes(argFun, params, nparams))
   351         return false;
   352 
   353     for (unsigned i = 0; i < nparams; i++) {
   354         ke::LocalSlot &slot = args.arg(i);
   355         switch (params[i].type()) {
   356           case Ke::Parameter::Int:
   357             slot.i32 = params[i].i32();
   358             break;
   359 
   360           case Ke::Parameter::Float:
   361             slot.f = params[i].f();
   362             break;
   363 
   364           case Ke::Parameter::IntRef:
   365             if (params[i].writeback()) {
   366                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack);
   367             } else {
   368                 LocalSlot other;
   369                 other.i32 = *params[i].i32p();
   370                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   371             }
   372             if (!slot.ref)
   373                 return false;
   374             break;
   375 
   376           case Ke::Parameter::FloatRef:
   377             if (params[i].writeback()) {
   378                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack);
   379             } else {
   380                 LocalSlot other;
   381                 other.f = *params[i].fp();
   382                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   383             }
   384             if (!slot.ref)
   385                 return false;
   386             break;
   387 
   388           case Ke::Parameter::Array:
   389           {
   390             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->externalGenericArrayMap());
   391             if (params[i].writeback()) {
   392                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].buffer(),
   393                                               params[i].length(), Heap::Tenure_Stack);
   394                 if (!slot.obj)
   395                     return false;
   396             } else {
   397                 Array *array = Array::New1D(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   398                 if (!slot.obj)
   399                     return false;
   400                 if (params[i].length())
   401                     memcpy(array->bytes(), params[i].buffer(), params[i].length() * sizeof(int));
   402             }
   403             break;
   404           }
   405 
   406           case Ke::Parameter::CharArray:
   407           {
   408             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->externalCharArrayMap());
   409             if (params[i].writeback()) {
   410                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].buffer(),
   411                                               params[i].length(), Heap::Tenure_Stack);
   412                 if (!slot.obj)
   413                     return false;
   414             } else {
   415                 Array *array = Array::New1D(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   416                 if (!slot.obj)
   417                     return false;
   418                 if (params[i].length())
   419                     memcpy(array->bytes(), params[i].buffer(), params[i].length() * sizeof(char));
   420             }
   421             break;
   422           }
   423         }
   424     }
   425 
   426     LocalSlot result;
   427     if (!Interpret(ke::ZONE(), NULL, &result))
   428         return false;
   429 
   430     if (fun->type()->returnType()->isVoid())
   431         result.i32 = 0;
   432 
   433     *retval = result.i32;
   434     return true;
   435 }
   436 
   437 bool
   438 Ke::Keima::BindNative(const char *name, void *address)
   439 {
   440     return ke::ZONE()->nativeTable()->bind(name, address);
   441 }
   442 
   443 void
   444 Ke::Keima::SetPublicFunctionListener(IPublicFunctionListener *listener)
   445 {
   446     ke::ZONE()->setPublicFunctionListener(listener);
   447 }
   448 
   449 void
   450 Zone::registerPublic(ke::Handle<Function> fun)
   451 {
   452     if (!publicFunctionListener_)
   453         return;
   454 
   455     Ke::Local<Ke::Function> local = ToLocal<ke::Function, Ke::Function>(fun);
   456     publicFunctionListener_->registerPublic(local);
   457 }