src/API.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 05 01:56:10 2012 -0700 (2012-08-05)
changeset 162 c9fa93ce2f1f
parent 145 e9d804821c48
child 187 9308d52205a0
permissions -rw-r--r--
Fixed rooting bug in Ke::Compile.
     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::Module *
   130 InternalCompile(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 NULL;
   142         }
   143 
   144         if (fseek(fp, 0, SEEK_END)) {
   145             cc.reportError(SourcePosition(), Message_ErrorSeekingFile);
   146             fclose(fp);
   147             return NULL;
   148         }
   149         long size = ftell(fp);
   150         if (size < 0) {
   151             cc.reportError(SourcePosition(), Message_ErrorSeekingFile);
   152             fclose(fp);
   153             return NULL;
   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 NULL;
   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 NULL;
   171 
   172     ke::Local<ke::String> pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old));
   173     if (!pathstr)
   174         return NULL;
   175 
   176     module->setPath(pathstr);
   177     return module;
   178 }
   179 
   180 // This is split out from InternalCompile so the RootScope doesn't conflict
   181 // with making a return handle.
   182 Ke::Local<Ke::Module>
   183 Ke::Compile(const char *path)
   184 {
   185     ke::Local<ke::Module> module(ZONE(), InternalCompile(path));
   186     return ToLocal<ke::Module, Ke::Module>(module);
   187 }
   188 
   189 void
   190 Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter)
   191 {
   192     ZONE()->installErrorReporter(reporter);
   193 }
   194 
   195 class StackFrameIterator : public Ke::IStackFrameIterator
   196 {
   197     StackIter iter_;
   198 
   199   public:
   200     StackFrameIterator()
   201       : iter_(ZONE())
   202     {
   203     }
   204 
   205     virtual bool done() {
   206         return iter_.done();
   207     }
   208 
   209     const char *function() {
   210         return iter_.function();
   211     }
   212 
   213     const char *source() {
   214         return iter_.source();
   215     }
   216 
   217     unsigned lineNumber() {
   218         return iter_.lineNumber();
   219     }
   220 
   221     void prev() {
   222         iter_.prev();
   223     }
   224 };
   225 
   226 Ke::IStackFrameIterator *
   227 Ke::Keima::NewStackFrameIterator()
   228 {
   229     return new StackFrameIterator();
   230 }
   231 
   232 static inline ke::HeapRoot *
   233 FromAPI(ke::HeapRoot *from)
   234 {
   235     ke::Address address = reinterpret_cast<ke::Address>(from);
   236     address -= offsetof(ke::HeapRoot, obj);
   237     return reinterpret_cast<ke::HeapRoot *>(address);
   238 }
   239 
   240 ke::HeapRoot *
   241 Ke::Internals::NewHeapRoot(ke::Object **ptr)
   242 {
   243     ke::Local<ke::Object> obj(ZONE(), *ptr);
   244     ke::HeapRoot *root = ke::ZONE()->globalRoots()->create(obj);
   245     if (!root)
   246         return NULL;
   247 
   248     // Compute an interior pointer, so it looks like a Handle to an object.
   249     return reinterpret_cast<ke::HeapRoot *>(&root->obj);
   250 }
   251 
   252 void
   253 Ke::Internals::IncRef(ke::HeapRoot *root)
   254 {
   255     ZONE()->globalRoots()->incref(FromAPI(root));
   256 }
   257 
   258 void
   259 Ke::Internals::DecRef(ke::HeapRoot *root)
   260 {
   261     ZONE()->globalRoots()->decref(FromAPI(root));
   262 }
   263 
   264 bool
   265 Ke::Keima::IsInCallStack(Handle<Module> argModule)
   266 {
   267     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(argModule));
   268     return ZONE()->stack().isInCallStack(module);
   269 }
   270 
   271 void
   272 Ke::Keima::CollectGarbage()
   273 {
   274     ke::ZONE()->heap().reclaim();
   275 }
   276 
   277 void
   278 Ke::Keima::ClearPendingException()
   279 {
   280     ke::ZONE()->clearPendingException();
   281 }
   282 
   283 unsigned
   284 Ke::Module::numPublics()
   285 {
   286     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   287     if (!module->publics())
   288         return 0;
   289     return module->publics()->length();
   290 }
   291 
   292 Ke::Local<Ke::Function>
   293 Ke::Module::getPublic(unsigned index)
   294 {
   295     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   296     ke::Local<ke::Function> fun(ke::ZONE(), ke::Function::cast(module->publics()->at(index)));
   297     return ToLocal<ke::Function, Ke::Function>(fun);
   298 }
   299 
   300 unsigned
   301 Ke::Module::numNatives()
   302 {
   303     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   304     if (!module->natives())
   305         return 0;
   306     return module->natives()->length();
   307 }
   308 
   309 Ke::Local<Ke::Native>
   310 Ke::Module::getNative(unsigned index)
   311 {
   312     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   313     ke::Local<ke::Native> fun(ke::ZONE(), ke::Native::cast(module->natives()->at(index)));
   314     return ToLocal<ke::Native, Ke::Native>(fun);
   315 }
   316 
   317 void
   318 Ke::Module::setNativeIdentity(void *identity)
   319 {
   320     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   321     module->setNativeIdentity(identity);
   322 }
   323 
   324 unsigned
   325 Ke::String::length()
   326 {
   327     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   328     return obj->length();
   329 }
   330 
   331 const char *
   332 Ke::String::chars()
   333 {
   334     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   335     return obj->chars();
   336 }
   337 
   338 Ke::Local<Ke::String>
   339 Ke::Function::name()
   340 {
   341     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(this));
   342     return ToLocal<ke::String, Ke::String>(fun->name());
   343 }
   344 
   345 Ke::Local<Ke::String>
   346 Ke::Native::name()
   347 {
   348     ke::Local<ke::Native> native(ke::ZONE(), Open<Ke::Native, ke::Native>(this));
   349     return ToLocal<ke::String, Ke::String>(native->name());
   350 }
   351 
   352 void
   353 Ke::Native::bind(void *function)
   354 {
   355     ke::Local<ke::Native> native(ke::ZONE(), Open<Ke::Native, ke::Native>(this));
   356     native->bind(function);
   357 }
   358 
   359 static inline const char *
   360 GetInvokeTypeName(Ke::Parameter::Type type)
   361 {
   362     switch (type) {
   363       case Ke::Parameter::Array:
   364         return "array";
   365       case Ke::Parameter::CharArray:
   366         return "String";
   367       case Ke::Parameter::Float:
   368         return "Float";
   369       case Ke::Parameter::FloatRef:
   370         return "&Float";
   371       case Ke::Parameter::Int:
   372         return "int";
   373       case Ke::Parameter::IntRef:
   374         return "&int";
   375       default:
   376         return "<unknown>";
   377     }
   378 }
   379 
   380 static inline bool
   381 CheckInvokeType(const Ke::Parameter &param, Type *type)
   382 {
   383     switch (param.type()) {
   384       case Ke::Parameter::Array:
   385         return type->isArray() &&
   386                type->levels() == 1 &&
   387                (type->contained()->isPrimitive() || type->contained()->isEnum()) &&
   388                (type->contained()->primitive() != PrimitiveType_Char);
   389       case Ke::Parameter::CharArray:
   390         return type->isCharArray();
   391       case Ke::Parameter::Float:
   392         return type->isFloat();
   393       case Ke::Parameter::FloatRef:
   394         return type->isReference() && type->contained()->isFloat();
   395       case Ke::Parameter::Int:
   396         return type->isInt32() || type->isEnum();
   397       case Ke::Parameter::IntRef:
   398        return type->isReference() && (type->contained()->isInt32() || type->contained()->isEnum());
   399       default:
   400         return false;
   401     }
   402 }
   403 
   404 static bool
   405 CheckInvokeTypes(Ke::Handle<Ke::Function> argFun, const Ke::Parameter *params, unsigned nparams)
   406 {
   407     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   408 
   409     if (fun->nargs() != nparams)
   410         return false;
   411 
   412     for (unsigned i = 0; i < nparams; i++) {
   413         if (!CheckInvokeType(params[i], fun->argType(i))) {
   414             ke::ZONE()->reportError(Message_TypeMismatchDuringInvoke,
   415                                     GetInvokeTypeName(params[i].type()),
   416                                     GetTypeName(fun->argType(i)));
   417             return false;
   418         }
   419     }
   420 
   421     // Functions can only return tagged cells, for ISPE1-compatible Invoke().
   422     ke::Local<ke::Type> returnType(ke::ZONE(), fun->type()->returnType());
   423     if (!returnType->isPrimitive() &&
   424         !returnType->isEnum() &&
   425         !returnType->isVoid())
   426     {
   427         ke::ZONE()->reportError(Message_CannotInvokeReturnType, GetTypeName(returnType));
   428         return false;
   429     }
   430 
   431     return true;
   432 }
   433 
   434 bool
   435 Ke::Keima::Invoke(Handle<Function> argFun, const Parameter *params, unsigned nparams,
   436                   bool checkTypes, int *retval)
   437 {
   438     assert(CheckInvokeTypes(argFun, params, nparams));
   439 
   440     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   441 
   442     if (fun->nargs() != nparams) {
   443         // :TODO: report error
   444         assert(false);
   445         return false;
   446     }
   447 
   448     ke::InvokeArgs args;
   449     if (!ke::ZONE()->stack().pushInvokeFrame(fun, args))
   450         return false;
   451 
   452     if (checkTypes && !CheckInvokeTypes(argFun, params, nparams))
   453         return false;
   454 
   455     for (unsigned i = 0; i < nparams; i++) {
   456         ke::LocalSlot &slot = args.arg(i);
   457         switch (params[i].type()) {
   458           case Ke::Parameter::Int:
   459             slot.i32 = params[i].i32();
   460             break;
   461 
   462           case Ke::Parameter::Float:
   463             slot.f = params[i].f();
   464             break;
   465 
   466           case Ke::Parameter::IntRef:
   467             if (params[i].writeback()) {
   468                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack);
   469             } else {
   470                 LocalSlot other;
   471                 other.i32 = *params[i].i32p();
   472                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   473             }
   474             if (!slot.ref)
   475                 return false;
   476             break;
   477 
   478           case Ke::Parameter::FloatRef:
   479             if (params[i].writeback()) {
   480                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack);
   481             } else {
   482                 LocalSlot other;
   483                 other.f = *params[i].fp();
   484                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   485             }
   486             if (!slot.ref)
   487                 return false;
   488             break;
   489 
   490           case Ke::Parameter::Array:
   491           {
   492             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->genericArrayMap());
   493             if (params[i].writeback()) {
   494                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(),
   495                                               params[i].buffer(), Heap::Tenure_Stack);
   496                 if (!slot.obj)
   497                     return false;
   498             } else {
   499                 Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   500                 if (!slot.obj)
   501                     return false;
   502                 memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(int));
   503             }
   504             break;
   505           }
   506 
   507           case Ke::Parameter::CharArray:
   508           {
   509             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->charArrayMap());
   510             if (params[i].writeback()) {
   511                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(),
   512                                               params[i].buffer(), Heap::Tenure_Stack);
   513                 if (!slot.obj)
   514                     return false;
   515             } else {
   516                 Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   517                 if (!slot.obj)
   518                     return false;
   519                 memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(char));
   520             }
   521             break;
   522           }
   523         }
   524     }
   525 
   526     LocalSlot result;
   527     if (!Interpret(ke::ZONE(), NULL, &result))
   528         return false;
   529 
   530     if (fun->type()->returnType()->isVoid())
   531         result.i32 = 0;
   532 
   533     *retval = result.i32;
   534     return true;
   535 }