src/API.cpp
author David Anderson <dvander@alliedmods.net>
Sat Dec 22 18:22:14 2012 -0800 (2012-12-22)
changeset 230 fb3cd08bc7fb
parent 222 0cde1379b29f
child 232 2b8512340180
permissions -rw-r--r--
Re-add LIFO stack optimizations.
     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() && ReferenceType::cast(type)->contained()->isFloat();
   293       case Ke::Parameter::Int:
   294         return type->isInt32() || type->isEnum();
   295       case Ke::Parameter::IntRef:
   296        return type->isReference() &&
   297               (ReferenceType::cast(type)->contained()->isInt32() ||
   298                ReferenceType::cast(type)->contained()->isEnum());
   299       default:
   300         return false;
   301     }
   302 }
   303 
   304 static bool
   305 CheckInvokeTypes(Ke::Handle<Ke::Function> argFun, const Ke::Parameter *params, unsigned nparams)
   306 {
   307     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   308 
   309     if (fun->nargs() != nparams)
   310         return false;
   311 
   312     for (unsigned i = 0; i < nparams; i++) {
   313         if (!CheckInvokeType(params[i], fun->argType(i))) {
   314             ke::ZONE()->reportError(Message_TypeMismatchDuringInvoke,
   315                                     GetInvokeTypeName(params[i].type()),
   316                                     GetTypeName(fun->argType(i)));
   317             return false;
   318         }
   319     }
   320 
   321     // Functions can only return tagged cells, for ISPE1-compatible Invoke().
   322     ke::Local<ke::Type> returnType(ke::ZONE(), fun->type()->returnType());
   323     if (!returnType->isPrimitive() &&
   324         !returnType->isEnum() &&
   325         !returnType->isVoid())
   326     {
   327         ke::ZONE()->reportError(Message_CannotInvokeReturnType, GetTypeName(returnType));
   328         return false;
   329     }
   330 
   331     return true;
   332 }
   333 
   334 bool
   335 Ke::Keima::Invoke(Handle<Function> argFun, const Parameter *params, unsigned nparams,
   336                   bool checkTypes, int *retval)
   337 {
   338     assert(CheckInvokeTypes(argFun, params, nparams));
   339 
   340     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   341 
   342     if (fun->nargs() != nparams) {
   343         // :TODO: report error
   344         assert(false);
   345         return false;
   346     }
   347 
   348     ke::InvokeArgs args;
   349     if (!ke::ZONE()->stack().pushInvokeFrame(fun, args))
   350         return false;
   351 
   352     if (checkTypes && !CheckInvokeTypes(argFun, params, nparams))
   353         return false;
   354 
   355     for (unsigned i = 0; i < nparams; i++) {
   356         ke::LocalSlot &slot = args.arg(i);
   357         switch (params[i].type()) {
   358           case Ke::Parameter::Int:
   359             slot.i32 = params[i].i32();
   360             break;
   361 
   362           case Ke::Parameter::Float:
   363             slot.f = params[i].f();
   364             break;
   365 
   366           case Ke::Parameter::IntRef:
   367             if (params[i].writeback()) {
   368                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack);
   369             } else {
   370                 LocalSlot other;
   371                 other.i32 = *params[i].i32p();
   372                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   373             }
   374             if (!slot.ref)
   375                 return false;
   376             break;
   377 
   378           case Ke::Parameter::FloatRef:
   379             if (params[i].writeback()) {
   380                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack);
   381             } else {
   382                 LocalSlot other;
   383                 other.f = *params[i].fp();
   384                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   385             }
   386             if (!slot.ref)
   387                 return false;
   388             break;
   389 
   390           case Ke::Parameter::Array:
   391           {
   392             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->externalGenericArrayMap());
   393             if (params[i].writeback()) {
   394                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].buffer(),
   395                                               params[i].length(), Heap::Tenure_Stack);
   396                 if (!slot.obj)
   397                     return false;
   398             } else {
   399                 Array *array = Array::New1D(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   400                 if (!slot.obj)
   401                     return false;
   402                 if (params[i].length())
   403                     memcpy(array->bytes(), params[i].buffer(), params[i].length() * sizeof(int));
   404             }
   405             break;
   406           }
   407 
   408           case Ke::Parameter::CharArray:
   409           {
   410             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->externalCharArrayMap());
   411             if (params[i].writeback()) {
   412                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].buffer(),
   413                                               params[i].length(), Heap::Tenure_Stack);
   414                 if (!slot.obj)
   415                     return false;
   416             } else {
   417                 Array *array = Array::New1D(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   418                 if (!slot.obj)
   419                     return false;
   420                 if (params[i].length())
   421                     memcpy(array->bytes(), params[i].buffer(), params[i].length() * sizeof(char));
   422             }
   423             break;
   424           }
   425         }
   426     }
   427 
   428     LocalSlot result;
   429     if (!Interpret(ke::ZONE(), NULL, &result))
   430         return false;
   431 
   432     if (fun->type()->returnType()->isVoid())
   433         result.i32 = 0;
   434 
   435     *retval = result.i32;
   436     return true;
   437 }
   438 
   439 bool
   440 Ke::Keima::BindNative(const char *name, void *address)
   441 {
   442     return ke::ZONE()->nativeTable()->bind(name, address);
   443 }
   444 
   445 void
   446 Ke::Keima::SetPublicFunctionListener(IPublicFunctionListener *listener)
   447 {
   448     ke::ZONE()->setPublicFunctionListener(listener);
   449 }
   450 
   451 void
   452 Zone::registerPublic(ke::Handle<Function> fun)
   453 {
   454     if (!publicFunctionListener_)
   455         return;
   456 
   457     Ke::Local<Ke::Function> local = ToLocal<ke::Function, Ke::Function>(fun);
   458     publicFunctionListener_->registerPublic(local);
   459 }