src/API.cpp
author David Anderson <dvander@alliedmods.net>
Sat Dec 29 00:55:38 2012 -0800 (2012-12-29)
changeset 250 0998a43cc155
parent 241 18f3010a358c
child 251 ca99a81745fe
permissions -rw-r--r--
Collect error messages per-translation unit.
     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 bool
    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 TranslationUnit();
   102     unit->path_ = path;
   103 
   104     if (!cc.addTranslationUnit(unit))
   105         return false;
   106 
   107     if (!cc.compile())
   108         return false;
   109 
   110     return true;
   111 }
   112 
   113 // This is split out from InternalCompile so the RootScope doesn't conflict
   114 // with making a return handle.
   115 bool
   116 Ke::Compile(const char *path)
   117 {
   118     return InternalCompile(path);
   119 }
   120 
   121 void
   122 Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter)
   123 {
   124     ZONE()->installErrorReporter(reporter);
   125 }
   126 
   127 class StackFrameIterator : public Ke::IStackFrameIterator
   128 {
   129     StackIter iter_;
   130 
   131   public:
   132     StackFrameIterator()
   133       : iter_(ZONE())
   134     {
   135     }
   136 
   137     virtual bool done() {
   138         return iter_.done();
   139     }
   140 
   141     const char *function() {
   142         return iter_.function();
   143     }
   144 
   145     const char *source() {
   146         return iter_.source();
   147     }
   148 
   149     unsigned lineNumber() {
   150         return iter_.lineNumber();
   151     }
   152 
   153     void prev() {
   154         iter_.prev();
   155     }
   156 };
   157 
   158 Ke::IStackFrameIterator *
   159 Ke::Keima::NewStackFrameIterator()
   160 {
   161     return new StackFrameIterator();
   162 }
   163 
   164 static inline ke::HeapRoot *
   165 FromAPI(ke::HeapRoot *from)
   166 {
   167     ke::Address address = reinterpret_cast<ke::Address>(from);
   168     address -= offsetof(ke::HeapRoot, obj);
   169     return reinterpret_cast<ke::HeapRoot *>(address);
   170 }
   171 
   172 ke::HeapRoot *
   173 Ke::Internals::NewHeapRoot()
   174 {
   175     ke::HeapRoot *root = ke::ZONE()->globalRoots()->create();
   176     if (!root)
   177         return NULL;
   178 
   179     // Compute an interior pointer, so it looks like a Handle to an object.
   180     return reinterpret_cast<ke::HeapRoot *>(&root->obj);
   181 }
   182 
   183 void
   184 Ke::Internals::Destroy(ke::HeapRoot *root)
   185 {
   186     ZONE()->globalRoots()->destroy(FromAPI(root));
   187 }
   188 
   189 bool
   190 Ke::Keima::IsInCallStack(Handle<Module> argModule)
   191 {
   192     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(argModule));
   193     return ZONE()->stack().isInCallStack(module);
   194 }
   195 
   196 void
   197 Ke::Keima::CollectGarbage()
   198 {
   199     ke::ZONE()->heap().reclaim();
   200 }
   201 
   202 void
   203 Ke::Keima::ClearPendingException()
   204 {
   205     ke::ZONE()->clearPendingException();
   206 }
   207 
   208 void
   209 Ke::Module::setNativeIdentity(void *identity)
   210 {
   211     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   212     module->setNativeIdentity(identity);
   213 }
   214 
   215 unsigned
   216 Ke::String::length()
   217 {
   218     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   219     return obj->length();
   220 }
   221 
   222 const char *
   223 Ke::String::chars()
   224 {
   225     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   226     return obj->chars();
   227 }
   228 
   229 Ke::Local<Ke::String>
   230 Ke::Function::name()
   231 {
   232     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(this));
   233     return ToLocal<ke::String, Ke::String>(fun->name());
   234 }
   235 
   236 static inline const char *
   237 GetInvokeTypeName(Ke::Parameter::Type type)
   238 {
   239     switch (type) {
   240       case Ke::Parameter::Array:
   241         return "array";
   242       case Ke::Parameter::CharArray:
   243         return "String";
   244       case Ke::Parameter::Float:
   245         return "Float";
   246       case Ke::Parameter::FloatRef:
   247         return "&Float";
   248       case Ke::Parameter::Int:
   249         return "int";
   250       case Ke::Parameter::IntRef:
   251         return "&int";
   252       default:
   253         return "<unknown>";
   254     }
   255 }
   256 
   257 static inline bool
   258 CheckInvokeType(const Ke::Parameter &param, Type *type)
   259 {
   260     switch (param.type()) {
   261       case Ke::Parameter::Array:
   262       {
   263         if (!type->isArray())
   264             return false;
   265 
   266         ArrayType *atype = ArrayType::cast(type);
   267         return atype->levels() == 1 &&
   268                (atype->contained()->isPrimitive() || atype->contained()->isEnum()) &&
   269                (atype->contained()->primitive() != PrimitiveType_Char);
   270       }
   271       case Ke::Parameter::CharArray:
   272         return type->isArray() && ArrayType::cast(type)->isCharArray();
   273       case Ke::Parameter::Float:
   274         return type->isFloat();
   275       case Ke::Parameter::FloatRef:
   276         return type->isReference() && ReferenceType::cast(type)->contained()->isFloat();
   277       case Ke::Parameter::Int:
   278         return type->isInt32() || type->isEnum();
   279       case Ke::Parameter::IntRef:
   280        return type->isReference() &&
   281               (ReferenceType::cast(type)->contained()->isInt32() ||
   282                ReferenceType::cast(type)->contained()->isEnum());
   283       default:
   284         return false;
   285     }
   286 }
   287 
   288 static bool
   289 CheckInvokeTypes(Ke::Handle<Ke::Function> argFun, const Ke::Parameter *params, unsigned nparams)
   290 {
   291     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   292 
   293     if (fun->nargs() != nparams)
   294         return false;
   295 
   296     for (unsigned i = 0; i < nparams; i++) {
   297         if (!CheckInvokeType(params[i], fun->argType(i))) {
   298             char name[255];
   299             ke::ZONE()->reportError(Message_TypeMismatchDuringInvoke,
   300                                     GetInvokeTypeName(params[i].type()),
   301                                     GetTypeName(fun->argType(i), name, sizeof(name)));
   302             return false;
   303         }
   304     }
   305 
   306     // Functions can only return tagged cells, for ISPE1-compatible Invoke().
   307     ke::Local<ke::Type> returnType(ke::ZONE(), fun->type()->returnType());
   308     if (!returnType->isPrimitive() &&
   309         !returnType->isEnum() &&
   310         !returnType->isVoid())
   311     {
   312         char name[255];
   313         ke::ZONE()->reportError(Message_CannotInvokeReturnType,
   314                                 GetTypeName(returnType, name, sizeof(name)));
   315         return false;
   316     }
   317 
   318     return true;
   319 }
   320 
   321 bool
   322 Ke::Keima::Invoke(Handle<Function> argFun, const Parameter *params, unsigned nparams,
   323                   bool checkTypes, int *retval)
   324 {
   325     assert(CheckInvokeTypes(argFun, params, nparams));
   326 
   327     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   328 
   329     if (fun->nargs() != nparams) {
   330         // :TODO: report error
   331         assert(false);
   332         return false;
   333     }
   334 
   335     ke::InvokeArgs args;
   336     if (!ke::ZONE()->stack().pushInvokeFrame(fun, args))
   337         return false;
   338 
   339     if (checkTypes && !CheckInvokeTypes(argFun, params, nparams))
   340         return false;
   341 
   342     for (unsigned i = 0; i < nparams; i++) {
   343         ke::LocalSlot &slot = args.arg(i);
   344         switch (params[i].type()) {
   345           case Ke::Parameter::Int:
   346             slot.i32 = params[i].i32();
   347             break;
   348 
   349           case Ke::Parameter::Float:
   350             slot.f = params[i].f();
   351             break;
   352 
   353           case Ke::Parameter::IntRef:
   354             if (params[i].writeback()) {
   355                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack);
   356             } else {
   357                 LocalSlot other;
   358                 other.i32 = *params[i].i32p();
   359                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   360             }
   361             if (!slot.ref)
   362                 return false;
   363             break;
   364 
   365           case Ke::Parameter::FloatRef:
   366             if (params[i].writeback()) {
   367                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack);
   368             } else {
   369                 LocalSlot other;
   370                 other.f = *params[i].fp();
   371                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   372             }
   373             if (!slot.ref)
   374                 return false;
   375             break;
   376 
   377           case Ke::Parameter::Array:
   378           {
   379             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->externalGenericArrayMap());
   380             if (params[i].writeback()) {
   381                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].buffer(),
   382                                               params[i].length(), Heap::Tenure_Stack);
   383                 if (!slot.obj)
   384                     return false;
   385             } else {
   386                 Array *array = Array::New1D(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   387                 if (!slot.obj)
   388                     return false;
   389                 if (params[i].length())
   390                     memcpy(array->bytes(), params[i].buffer(), params[i].length() * sizeof(int));
   391             }
   392             break;
   393           }
   394 
   395           case Ke::Parameter::CharArray:
   396           {
   397             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->externalCharArrayMap());
   398             if (params[i].writeback()) {
   399                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].buffer(),
   400                                               params[i].length(), Heap::Tenure_Stack);
   401                 if (!slot.obj)
   402                     return false;
   403             } else {
   404                 Array *array = Array::New1D(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   405                 if (!slot.obj)
   406                     return false;
   407                 if (params[i].length())
   408                     memcpy(array->bytes(), params[i].buffer(), params[i].length() * sizeof(char));
   409             }
   410             break;
   411           }
   412         }
   413     }
   414 
   415     LocalSlot result;
   416     if (!Interpret(ke::ZONE(), NULL, &result))
   417         return false;
   418 
   419     if (fun->type()->returnType()->isVoid())
   420         result.i32 = 0;
   421 
   422     *retval = result.i32;
   423     return true;
   424 }
   425 
   426 bool
   427 Ke::Keima::BindNative(const char *name, void *address)
   428 {
   429     return ke::ZONE()->nativeTable()->bind(name, address);
   430 }
   431 
   432 void
   433 Ke::Keima::SetPublicFunctionListener(IPublicFunctionListener *listener)
   434 {
   435     ke::ZONE()->setPublicFunctionListener(listener);
   436 }
   437 
   438 void
   439 Zone::registerPublic(ke::Handle<Function> fun)
   440 {
   441     if (!publicFunctionListener_)
   442         return;
   443 
   444     Ke::Local<Ke::Function> local = ToLocal<ke::Function, Ke::Function>(fun);
   445     publicFunctionListener_->registerPublic(local);
   446 }