src/API.cpp
author David Anderson <dvander@alliedmods.net>
Sun Jul 29 12:24:49 2012 -0700 (2012-07-29)
changeset 129 86b368a4d80d
parent 92 e3974e7360e9
child 145 e9d804821c48
permissions -rw-r--r--
Fix various error reporting bugs related to Invoke.
     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 "CompileContext.h"
    24 #include "Parser.h"
    25 #include "Scopes.h"
    26 #include "SemanticAnalysis.h"
    27 #include "BytecodeCompiler.h"
    28 #include "Modules.h"
    29 #include "GlobalRoots.h"
    30 #include "Reference.h"
    31 #include "Array.h"
    32 #include "Interpreter.h"
    33 #include "Stack.h"
    34 #include "Strings.h"
    35 #include "Spaces-inl.h"
    36 #include "Heap-inl.h"
    37 
    38 using namespace ke;
    39 
    40 bool
    41 Ke::Keima::Initialize()
    42 {
    43     SingleThreadedZone = new Zone();
    44     if (!SingleThreadedZone || !SingleThreadedZone->initialize())
    45         return false;
    46     return true;
    47 }
    48 
    49 void
    50 Ke::Keima::Shutdown()
    51 {
    52     delete SingleThreadedZone;
    53     SingleThreadedZone = NULL;
    54 }
    55 
    56 Ke::HandleScope::HandleScope()
    57   : zone_(ke::ZONE()),
    58     position_(zone_->rootScopes().position()),
    59     prev_(*zone_->rootScopes().scope())
    60 {
    61     *zone_->rootScopes().scope() = this;
    62 }
    63 
    64 Ke::HandleScope::~HandleScope()
    65 {
    66     zone_->rootScopes().unwind(position_);
    67     *zone_->rootScopes().scope() = prev_;
    68 }
    69 
    70 ke::Object **
    71 Ke::HandleScope::CreateHandle(ke::Object *obj)
    72 {
    73     if (!obj)
    74         return NULL;
    75 
    76     return ZONE()->rootScopes().make(obj);
    77 }
    78 
    79 template <typename From, typename To> static inline To *
    80 Open(Ke::Handle<From> from)
    81 {
    82     return *reinterpret_cast<To **>(*from);
    83 }
    84 
    85 template <typename From, typename To> static inline To *
    86 Open(From *from)
    87 {
    88     return *reinterpret_cast<To **>(from);
    89 }
    90 
    91 template <typename From, typename To> static inline Ke::Local<To>
    92 ToLocal(From *from)
    93 {
    94     return Ke::Local<To>(reinterpret_cast<To *>(Ke::HandleScope::CreateHandle(from)));
    95 }
    96 
    97 static ke::Module *
    98 CompileFromSource(CompileContext &cc, const char *path, const char *stream, size_t length)
    99 {
   100     Parser parser(ZONE(), cc, stream, length);
   101     ParseTree *tree = parser.parse();
   102     if (!tree)
   103         return NULL;
   104     tree->dump(stdout);
   105 
   106     if (!Scope::Bind(cc, tree))
   107         return NULL;
   108 
   109     SemanticAnalysis semantics(ZONE(), cc, tree);
   110     if (!semantics.analyze())
   111         return NULL;
   112 
   113     Scope::Allocate(tree);
   114 
   115     BytecodeCompiler compiler(ZONE(), tree, NULL);
   116     ke::Local<Module> module(ke::ZONE(), compiler.compile());
   117     if (!module)
   118         return NULL;
   119 
   120     ke::InvokeArgs args;
   121     if (!ke::ZONE()->stack().pushModuleFrame(module, args))
   122         return NULL;
   123     if (!ke::Interpret(ke::ZONE(), NULL, NULL))
   124         return NULL;
   125 
   126     return module;
   127 }
   128 
   129 Ke::Local<Ke::Module>
   130 Ke::Compile(const char *path)
   131 {
   132     RootScope roots(ZONE());
   133     CompileContext cc(ZONE(), path);
   134 
   135     char *bytes = NULL;
   136     size_t length;
   137     {
   138         FILE *fp = fopen(path, "rb");
   139         if (!fp) {
   140             cc.reportError(SourcePosition(), Message_CantOpenFile);
   141             return Ke::Local<Ke::Module>();
   142         }
   143 
   144         if (fseek(fp, 0, SEEK_END)) {
   145             cc.reportError(SourcePosition(), Message_ErrorSeekingFile);
   146             fclose(fp);
   147             return Ke::Local<Ke::Module>();;
   148         }
   149         long size = ftell(fp);
   150         if (size < 0) {
   151             cc.reportError(SourcePosition(), Message_ErrorSeekingFile);
   152             fclose(fp);
   153             return Ke::Local<Ke::Module>();;
   154         }
   155         rewind(fp);
   156         length = size_t(size);
   157         bytes = new char[length + 1];
   158         if (fread(bytes, 1, length, fp) != length) {
   159             cc.reportError(SourcePosition(), Message_ErrorReadingFile);
   160             fclose(fp);
   161             delete [] bytes;
   162             return Ke::Local<Ke::Module>();;
   163         }
   164         fclose(fp);
   165     }
   166 
   167     ke::Local<ke::Module> module(ZONE(),  CompileFromSource(cc, path, bytes, length));
   168     delete [] bytes;
   169     if (!module)
   170         return Ke::Local<Ke::Module>();
   171 
   172     ke::Local<ke::String> pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old));
   173     if (!pathstr)
   174         return Ke::Local<Ke::Module>();
   175 
   176     module->setPath(pathstr);
   177     return ToLocal<ke::Module, Ke::Module>(module);
   178 }
   179 
   180 void
   181 Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter)
   182 {
   183     ZONE()->installErrorReporter(reporter);
   184 }
   185 
   186 class StackFrameIterator : public Ke::IStackFrameIterator
   187 {
   188     StackIter iter_;
   189 
   190   public:
   191     StackFrameIterator()
   192       : iter_(ZONE())
   193     {
   194     }
   195 
   196     virtual bool done() {
   197         return iter_.done();
   198     }
   199 
   200     const char *function() {
   201         return iter_.function();
   202     }
   203 
   204     const char *source() {
   205         return iter_.source();
   206     }
   207 
   208     unsigned lineNumber() {
   209         return iter_.lineNumber();
   210     }
   211 
   212     void prev() {
   213         iter_.prev();
   214     }
   215 };
   216 
   217 Ke::IStackFrameIterator *
   218 Ke::Keima::NewStackFrameIterator()
   219 {
   220     return new StackFrameIterator();
   221 }
   222 
   223 static inline ke::HeapRoot *
   224 FromAPI(ke::HeapRoot *from)
   225 {
   226     ke::Address address = reinterpret_cast<ke::Address>(from);
   227     address -= offsetof(ke::HeapRoot, obj);
   228     return reinterpret_cast<ke::HeapRoot *>(address);
   229 }
   230 
   231 ke::HeapRoot *
   232 Ke::Internals::NewHeapRoot(ke::Object **ptr)
   233 {
   234     ke::Local<ke::Object> obj(ZONE(), *ptr);
   235     ke::HeapRoot *root = ke::ZONE()->globalRoots()->create(obj);
   236     if (!root)
   237         return NULL;
   238 
   239     // Compute an interior pointer, so it looks like a Handle to an object.
   240     return reinterpret_cast<ke::HeapRoot *>(&root->obj);
   241 }
   242 
   243 void
   244 Ke::Internals::IncRef(ke::HeapRoot *root)
   245 {
   246     ZONE()->globalRoots()->incref(FromAPI(root));
   247 }
   248 
   249 void
   250 Ke::Internals::DecRef(ke::HeapRoot *root)
   251 {
   252     ZONE()->globalRoots()->decref(FromAPI(root));
   253 }
   254 
   255 bool
   256 Ke::Keima::IsInCallStack(Handle<Module> argModule)
   257 {
   258     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(argModule));
   259     return ZONE()->stack().isInCallStack(module);
   260 }
   261 
   262 void
   263 Ke::Keima::CollectGarbage()
   264 {
   265     ke::ZONE()->heap().reclaim();
   266 }
   267 
   268 void
   269 Ke::Keima::ClearPendingException()
   270 {
   271     ke::ZONE()->clearPendingException();
   272 }
   273 
   274 unsigned
   275 Ke::Module::numPublics()
   276 {
   277     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   278     if (!module->publics())
   279         return 0;
   280     return module->publics()->length();
   281 }
   282 
   283 Ke::Local<Ke::Function>
   284 Ke::Module::getPublic(unsigned index)
   285 {
   286     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   287     ke::Local<ke::Function> fun(ke::ZONE(), ke::Function::cast(module->publics()->at(index)));
   288     return ToLocal<ke::Function, Ke::Function>(fun);
   289 }
   290 
   291 unsigned
   292 Ke::Module::numNatives()
   293 {
   294     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   295     if (!module->natives())
   296         return 0;
   297     return module->natives()->length();
   298 }
   299 
   300 Ke::Local<Ke::Native>
   301 Ke::Module::getNative(unsigned index)
   302 {
   303     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   304     ke::Local<ke::Native> fun(ke::ZONE(), ke::Native::cast(module->natives()->at(index)));
   305     return ToLocal<ke::Native, Ke::Native>(fun);
   306 }
   307 
   308 void
   309 Ke::Module::setNativeIdentity(void *identity)
   310 {
   311     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   312     module->setNativeIdentity(identity);
   313 }
   314 
   315 unsigned
   316 Ke::String::length()
   317 {
   318     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   319     return obj->length();
   320 }
   321 
   322 const char *
   323 Ke::String::chars()
   324 {
   325     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   326     return obj->chars();
   327 }
   328 
   329 Ke::Local<Ke::String>
   330 Ke::Function::name()
   331 {
   332     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(this));
   333     return ToLocal<ke::String, Ke::String>(fun->name());
   334 }
   335 
   336 Ke::Local<Ke::String>
   337 Ke::Native::name()
   338 {
   339     ke::Local<ke::Native> native(ke::ZONE(), Open<Ke::Native, ke::Native>(this));
   340     return ToLocal<ke::String, Ke::String>(native->name());
   341 }
   342 
   343 void
   344 Ke::Native::bind(void *function)
   345 {
   346     ke::Local<ke::Native> native(ke::ZONE(), Open<Ke::Native, ke::Native>(this));
   347     native->bind(function);
   348 }
   349 
   350 // :TODO: this leaks an interior pointer.
   351 static const char *
   352 GetTypeName(Type *type)
   353 {
   354     if (type->isArray())
   355         return "array";
   356     if (type->isFunction())
   357         return "function";
   358     if (type->isVoid())
   359         return "void";
   360     if (type->isReference())
   361         type = type->contained();
   362     if (type->tag())
   363         return type->tag()->chars();
   364     return "int";
   365 }
   366 
   367 static inline const char *
   368 GetInvokeTypeName(Ke::Parameter::Type type)
   369 {
   370     switch (type) {
   371       case Ke::Parameter::Array:
   372         return "array";
   373       case Ke::Parameter::CharArray:
   374         return "String";
   375       case Ke::Parameter::Float:
   376         return "Float";
   377       case Ke::Parameter::FloatRef:
   378         return "&Float";
   379       case Ke::Parameter::Int:
   380         return "int";
   381       case Ke::Parameter::IntRef:
   382         return "&int";
   383       default:
   384         return "<unknown>";
   385     }
   386 }
   387 
   388 static inline bool
   389 CheckInvokeType(const Ke::Parameter &param, Type *type)
   390 {
   391     switch (param.type()) {
   392       case Ke::Parameter::Array:
   393         return type->isArray() &&
   394                type->levels() == 1 &&
   395                type->contained()->isPrimitive() &&
   396                (type->contained()->primitive() != PrimitiveType_Char);
   397       case Ke::Parameter::CharArray:
   398         return type->isCharArray();
   399       case Ke::Parameter::Float:
   400         return type->isFloat();
   401       case Ke::Parameter::FloatRef:
   402         return type->isReference() && type->contained()->isFloat();
   403       case Ke::Parameter::Int:
   404         return type->isInt32();
   405       case Ke::Parameter::IntRef:
   406        return type->isReference() && type->contained()->isInt32();
   407       default:
   408         return false;
   409     }
   410 }
   411 
   412 static bool
   413 CheckInvokeTypes(Ke::Handle<Ke::Function> argFun, const Ke::Parameter *params, unsigned nparams)
   414 {
   415     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   416 
   417     if (fun->nargs() != nparams)
   418         return false;
   419 
   420     for (unsigned i = 0; i < nparams; i++) {
   421         if (!CheckInvokeType(params[i], fun->argType(i))) {
   422             ke::ZONE()->reportError(Message_TypeMismatchDuringInvoke,
   423                                     GetInvokeTypeName(params[i].type()),
   424                                     GetTypeName(fun->argType(i)));
   425             return false;
   426         }
   427     }
   428 
   429     // Functions can only return tagged cells, for ISPE1-compatible Invoke().
   430     ke::Local<ke::Type> returnType(ke::ZONE(), fun->type()->returnType());
   431     if (!returnType->isPrimitive() && !returnType->isVoid()) {
   432         ke::ZONE()->reportError(Message_CannotInvokeReturnType, GetTypeName(returnType));
   433         return false;
   434     }
   435 
   436     return true;
   437 }
   438 
   439 bool
   440 Ke::Keima::Invoke(Handle<Function> argFun, const Parameter *params, unsigned nparams,
   441                   bool checkTypes, int *retval)
   442 {
   443     assert(CheckInvokeTypes(argFun, params, nparams));
   444 
   445     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   446 
   447     if (fun->nargs() != nparams) {
   448         // :TODO: report error
   449         assert(false);
   450         return false;
   451     }
   452 
   453     ke::InvokeArgs args;
   454     if (!ke::ZONE()->stack().pushInvokeFrame(fun, args))
   455         return false;
   456 
   457     if (checkTypes && !CheckInvokeTypes(argFun, params, nparams))
   458         return false;
   459 
   460     for (unsigned i = 0; i < nparams; i++) {
   461         ke::LocalSlot &slot = args.arg(i);
   462         switch (params[i].type()) {
   463           case Ke::Parameter::Int:
   464             slot.i32 = params[i].i32();
   465             break;
   466 
   467           case Ke::Parameter::Float:
   468             slot.f = params[i].f();
   469             break;
   470 
   471           case Ke::Parameter::IntRef:
   472             if (params[i].writeback()) {
   473                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack);
   474             } else {
   475                 LocalSlot other;
   476                 other.i32 = *params[i].i32p();
   477                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   478             }
   479             if (!slot.ref)
   480                 return false;
   481             break;
   482 
   483           case Ke::Parameter::FloatRef:
   484             if (params[i].writeback()) {
   485                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack);
   486             } else {
   487                 LocalSlot other;
   488                 other.f = *params[i].fp();
   489                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   490             }
   491             if (!slot.ref)
   492                 return false;
   493             break;
   494 
   495           case Ke::Parameter::Array:
   496           {
   497             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->genericArrayMap());
   498             if (params[i].writeback()) {
   499                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(),
   500                                               params[i].buffer(), Heap::Tenure_Stack);
   501                 if (!slot.obj)
   502                     return false;
   503             } else {
   504                 Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   505                 if (!slot.obj)
   506                     return false;
   507                 memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(int));
   508             }
   509             break;
   510           }
   511 
   512           case Ke::Parameter::CharArray:
   513           {
   514             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->charArrayMap());
   515             if (params[i].writeback()) {
   516                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(),
   517                                               params[i].buffer(), Heap::Tenure_Stack);
   518                 if (!slot.obj)
   519                     return false;
   520             } else {
   521                 Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   522                 if (!slot.obj)
   523                     return false;
   524                 memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(char));
   525             }
   526             break;
   527           }
   528         }
   529     }
   530 
   531     LocalSlot result;
   532     if (!Interpret(ke::ZONE(), NULL, &result))
   533         return false;
   534 
   535     if (fun->type()->returnType()->isVoid())
   536         result.i32 = 0;
   537 
   538     *retval = result.i32;
   539     return true;
   540 }