jitcraft/API.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 20 00:03:10 2012 -0700 (2012-07-20)
changeset 84 eaa43f765cbd
parent 72 40746ab0a30b
child 85 06a0015b6a5c
permissions -rw-r--r--
Fixed reporting of unbound names, and restricted late binding to global functions.
     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 "../include/keima.h"
    20 #include "Zone.h"
    21 #include "RootScopes.h"
    22 #include "CompileContext.h"
    23 #include "Parser.h"
    24 #include "Scopes.h"
    25 #include "SemanticAnalysis.h"
    26 #include "BytecodeCompiler.h"
    27 #include "Modules.h"
    28 #include "GlobalRoots.h"
    29 #include "Reference.h"
    30 #include "Array.h"
    31 #include "Interpreter.h"
    32 #include "Stack.h"
    33 #include "Strings.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 static ke::Module *
    95 CompileFromSource(CompileContext &cc, const char *path, const char *stream, size_t length)
    96 {
    97     Parser parser(ZONE(), cc, stream, length);
    98     ParseTree *tree = parser.parse();
    99     if (!tree)
   100         return NULL;
   101     tree->dump(stdout);
   102 
   103     if (!Scope::Bind(cc, tree))
   104         return NULL;
   105 
   106     SemanticAnalysis semantics(ZONE(), cc, tree);
   107     if (!semantics.analyze())
   108         return NULL;
   109 
   110     Scope::Allocate(tree);
   111 
   112     BytecodeCompiler compiler(ZONE(), tree, NULL);
   113     return compiler.compile();
   114 }
   115 
   116 Ke::Local<Ke::Module>
   117 Ke::Compile(const char *path)
   118 {
   119     RootScope roots(ZONE());
   120     CompileContext cc(ZONE(), path);
   121 
   122     char *bytes = NULL;
   123     size_t length;
   124     {
   125         FILE *fp = fopen(path, "rb");
   126         if (!fp) {
   127             cc.reportError(SourcePosition(), Message_CantOpenFile);
   128             return Ke::Local<Ke::Module>();
   129         }
   130 
   131         if (fseek(fp, 0, SEEK_END)) {
   132             cc.reportError(SourcePosition(), Message_ErrorSeekingFile);
   133             fclose(fp);
   134             return Ke::Local<Ke::Module>();;
   135         }
   136         long size = ftell(fp);
   137         if (size < 0) {
   138             cc.reportError(SourcePosition(), Message_ErrorSeekingFile);
   139             fclose(fp);
   140             return Ke::Local<Ke::Module>();;
   141         }
   142         rewind(fp);
   143         length = size_t(size);
   144         bytes = new char[length + 1];
   145         if (fread(bytes, 1, length, fp) != length) {
   146             cc.reportError(SourcePosition(), Message_ErrorReadingFile);
   147             fclose(fp);
   148             delete [] bytes;
   149             return Ke::Local<Ke::Module>();;
   150         }
   151         fclose(fp);
   152     }
   153 
   154     ke::Local<ke::Module> module(ZONE(),  CompileFromSource(cc, path, bytes, length));
   155     delete [] bytes;
   156     if (!module)
   157         return Ke::Local<Ke::Module>();
   158 
   159     ke::Local<ke::String> pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old));
   160     if (!pathstr)
   161         return Ke::Local<Ke::Module>();
   162 
   163     module->setPath(pathstr);
   164     return ToLocal<ke::Module, Ke::Module>(module);
   165 }
   166 
   167 void
   168 Ke::Keima::InstallMessageReporter(Ke::IMessageReporter *reporter)
   169 {
   170     ZONE()->installErrorReporter(reporter);
   171 }
   172 
   173 class StackFrameIterator : public Ke::IStackFrameIterator
   174 {
   175     StackIter iter_;
   176 
   177   public:
   178     StackFrameIterator()
   179       : iter_(ZONE())
   180     {
   181     }
   182 
   183     virtual bool done() {
   184         return iter_.done();
   185     }
   186 
   187     const char *function() {
   188         return iter_.function();
   189     }
   190 
   191     const char *source() {
   192         return iter_.source();
   193     }
   194 
   195     unsigned lineNumber() {
   196         return iter_.lineNumber();
   197     }
   198 
   199     void prev() {
   200         iter_.prev();
   201     }
   202 };
   203 
   204 Ke::IStackFrameIterator *
   205 Ke::Keima::NewStackFrameIterator()
   206 {
   207     return new StackFrameIterator();
   208 }
   209 
   210 static inline ke::HeapRoot *
   211 FromAPI(ke::HeapRoot *from)
   212 {
   213     ke::Address address = reinterpret_cast<ke::Address>(from);
   214     address -= offsetof(ke::HeapRoot, obj);
   215     return reinterpret_cast<ke::HeapRoot *>(address);
   216 }
   217 
   218 ke::HeapRoot *
   219 Ke::Internals::NewHeapRoot(ke::Object **ptr)
   220 {
   221     ke::Local<ke::Object> obj(ZONE(), *ptr);
   222     ke::HeapRoot *root = ke::ZONE()->globalRoots()->create(obj);
   223     if (!root)
   224         return NULL;
   225 
   226     // Compute an interior pointer, so it looks like a Handle to an object.
   227     return reinterpret_cast<ke::HeapRoot *>(&root->obj);
   228 }
   229 
   230 void
   231 Ke::Internals::IncRef(ke::HeapRoot *root)
   232 {
   233     ZONE()->globalRoots()->incref(FromAPI(root));
   234 }
   235 
   236 void
   237 Ke::Internals::DecRef(ke::HeapRoot *root)
   238 {
   239     ZONE()->globalRoots()->decref(FromAPI(root));
   240 }
   241 
   242 bool
   243 Ke::Keima::IsInCallStack(Handle<Module> argModule)
   244 {
   245     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(argModule));
   246     return ZONE()->stack().isInCallStack(module);
   247 }
   248 
   249 void
   250 Ke::Keima::CollectGarbage()
   251 {
   252     ke::ZONE()->heap().reclaim();
   253 }
   254 
   255 unsigned
   256 Ke::Module::numPublics()
   257 {
   258     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   259     if (!module->publics())
   260         return 0;
   261     return module->publics()->length();
   262 }
   263 
   264 Ke::Local<Ke::Function>
   265 Ke::Module::getPublic(unsigned index)
   266 {
   267     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   268     ke::Local<ke::Function> fun(ke::ZONE(), ke::Function::cast(module->publics()->at(index)));
   269     return ToLocal<ke::Function, Ke::Function>(fun);
   270 }
   271 
   272 unsigned
   273 Ke::Module::numNatives()
   274 {
   275     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   276     if (!module->natives())
   277         return 0;
   278     return module->natives()->length();
   279 }
   280 
   281 Ke::Local<Ke::Native>
   282 Ke::Module::getNative(unsigned index)
   283 {
   284     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   285     ke::Local<ke::Native> fun(ke::ZONE(), ke::Native::cast(module->natives()->at(index)));
   286     return ToLocal<ke::Native, Ke::Native>(fun);
   287 }
   288 
   289 void
   290 Ke::Module::setNativeIdentity(void *identity)
   291 {
   292     ke::Local<ke::Module> module(ke::ZONE(), Open<Ke::Module, ke::Module>(this));
   293     module->setNativeIdentity(identity);
   294 }
   295 
   296 unsigned
   297 Ke::String::length()
   298 {
   299     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   300     return obj->length();
   301 }
   302 
   303 const char *
   304 Ke::String::chars()
   305 {
   306     ke::Local<ke::String> obj(ke::ZONE(), Open<Ke::String, ke::String>(this));
   307     return obj->chars();
   308 }
   309 
   310 Ke::Local<Ke::String>
   311 Ke::Function::name()
   312 {
   313     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(this));
   314     return ToLocal<ke::String, Ke::String>(fun->name());
   315 }
   316 
   317 Ke::Local<Ke::String>
   318 Ke::Native::name()
   319 {
   320     ke::Local<ke::Native> native(ke::ZONE(), Open<Ke::Native, ke::Native>(this));
   321     return ToLocal<ke::String, Ke::String>(native->name());
   322 }
   323 
   324 void
   325 Ke::Native::bind(void *function)
   326 {
   327     ke::Local<ke::Native> native(ke::ZONE(), Open<Ke::Native, ke::Native>(this));
   328     native->bind(function);
   329 }
   330 
   331 static inline bool
   332 CheckInvokeType(const Ke::Parameter &param, Type *type)
   333 {
   334     switch (param.type()) {
   335       case Ke::Parameter::Array:
   336         return type->isArray() &&
   337                type->levels() == 1 &&
   338                type->contained()->isPrimitive() &&
   339                (type->contained()->primitive() != PrimitiveType_Char);
   340       case Ke::Parameter::CharArray:
   341         return type->isCharArray();
   342       case Ke::Parameter::Float:
   343         return type->isFloat();
   344       case Ke::Parameter::FloatRef:
   345         return type->isReference() && type->contained()->isFloat();
   346       case Ke::Parameter::Int:
   347         return type->isInt32();
   348       case Ke::Parameter::IntRef:
   349        return type->isReference() && type->contained()->isInt32();
   350       default:
   351         return false;
   352     }
   353 }
   354 
   355 bool
   356 Ke::Keima::CheckInvokeTypes(Ke::Handle<Ke::Function> argFun, const Ke::Parameter *params, unsigned nparams)
   357 {
   358     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   359 
   360     if (fun->nargs() != nparams)
   361         return false;
   362 
   363     for (unsigned i = 0; i < nparams; i++) {
   364         if (!CheckInvokeType(params[i], fun->argType(i)))
   365             return false;
   366     }
   367 
   368     // Functions can only return tagged cells, for ISPE1-compatible Invoke().
   369     ke::Local<ke::Type> returnType(ke::ZONE(), fun->type()->returnType());
   370     if ((!returnType->isPrimitive() && !returnType->isVoid()) || returnType->isTraceable())
   371         return false;
   372 
   373     return true;
   374 }
   375 
   376 bool
   377 Ke::Keima::Invoke(Handle<Function> argFun, const Parameter *params, unsigned nparams, int *retval)
   378 {
   379     assert(CheckInvokeTypes(argFun, params, nparams));
   380 
   381     ke::Local<ke::Function> fun(ke::ZONE(), Open<Ke::Function, ke::Function>(argFun));
   382 
   383     if (fun->nargs() != nparams) {
   384         // :TODO: report error
   385         assert(false);
   386         return false;
   387     }
   388 
   389     ke::InvokeArgs args;
   390     if (!ke::ZONE()->stack().pushInvokeFrame(fun, args))
   391         return false;
   392 
   393     for (unsigned i = 0; i < nparams; i++) {
   394         ke::LocalSlot &slot = args.arg(i);
   395         switch (params[i].type()) {
   396           case Ke::Parameter::Int:
   397             slot.i32 = params[i].i32();
   398             break;
   399 
   400           case Ke::Parameter::Float:
   401             slot.f = params[i].f();
   402             break;
   403 
   404           case Ke::Parameter::IntRef:
   405             if (params[i].writeback()) {
   406                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].i32p(), Heap::Tenure_Stack);
   407             } else {
   408                 LocalSlot other;
   409                 other.i32 = *params[i].i32p();
   410                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   411             }
   412             if (!slot.ref)
   413                 return false;
   414             break;
   415 
   416           case Ke::Parameter::FloatRef:
   417             if (params[i].writeback()) {
   418                 slot.ref = Reference::NewStack(ke::ZONE(), params[i].fp(), Heap::Tenure_Stack);
   419             } else {
   420                 LocalSlot other;
   421                 other.f = *params[i].fp();
   422                 slot.ref = Reference::NewStack(ke::ZONE(), other, Heap::Tenure_Stack);
   423             }
   424             if (!slot.ref)
   425                 return false;
   426             break;
   427 
   428           case Ke::Parameter::Array:
   429           {
   430             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->genericArrayMap());
   431             if (params[i].writeback()) {
   432                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(),
   433                                               params[i].buffer(), Heap::Tenure_Stack);
   434                 if (!slot.obj)
   435                     return false;
   436             } else {
   437                 Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   438                 if (!slot.obj)
   439                     return false;
   440                 memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(int));
   441             }
   442             break;
   443           }
   444 
   445           case Ke::Parameter::CharArray:
   446           {
   447             ke::Local<ke::ArrayMap> map(ke::ZONE(), ke::ZONE()->charArrayMap());
   448             if (params[i].writeback()) {
   449                 slot.obj = Array::NewExternal(ke::ZONE(), map, params[i].length(),
   450                                               params[i].buffer(), Heap::Tenure_Stack);
   451                 if (!slot.obj)
   452                     return false;
   453             } else {
   454                 Array *array = Array::New(ke::ZONE(), map, params[i].length(), Heap::Tenure_Stack);
   455                 if (!slot.obj)
   456                     return false;
   457                 memcpy(array->vec(), params[i].buffer(), params[i].length() * sizeof(char));
   458             }
   459             break;
   460           }
   461         }
   462     }
   463 
   464     LocalSlot result;
   465     if (!Interpret(ke::ZONE(), NULL, &result))
   466         return false;
   467 
   468     if (fun->type()->returnType()->isVoid())
   469         result.i32 = 0;
   470 
   471     *retval = result.i32;
   472     return true;
   473 }