Esempio n. 1
0
Handle<Object> GetTableCall::buildDBIndex(const NdbDictionary::Index *idx) {
  EscapableHandleScope scope(isolate);

  Local<Object> obj = NdbDictIndexEnv.newWrapper();
  wrapPointerInObject(idx, NdbDictIndexEnv, obj);

  obj->ForceSet(SYMBOL(isolate, "name"), String::NewFromUtf8(isolate, idx->getName()));
  obj->ForceSet(SYMBOL(isolate, "isPrimaryKey"), Boolean::New(isolate, false), ReadOnly);
  obj->ForceSet(SYMBOL(isolate, "isUnique"),
                Boolean::New(isolate, idx->getType() == NdbDictionary::Index::UniqueHashIndex),
                ReadOnly);
  obj->ForceSet(SYMBOL(isolate, "isOrdered"),
                Boolean::New(isolate, idx->getType() == NdbDictionary::Index::OrderedIndex),
                ReadOnly);
  
  /* Loop over the columns of the key. 
     Build the "columns" array and the "record" object, then set both.
  */  
  int ncol = idx->getNoOfColumns();
  Local<Array> idx_columns = Array::New(isolate, ncol);
  DEBUG_PRINT("Creating Index Record (%s)", idx->getName());
  Record * idx_record = new Record(dict, ncol);
  for(int i = 0 ; i < ncol ; i++) {
    const char *colName = idx->getColumn(i)->getName();
    const NdbDictionary::Column *col = ndb_table->getColumn(colName);
    idx_columns->Set(i, v8::Int32::New(isolate, col->getColumnNo()));
    idx_record->addColumn(col);
  }
  idx_record->completeIndexRecord(idx);
  obj->ForceSet(SYMBOL(isolate, "record"), Record_Wrapper(idx_record), ReadOnly);
  obj->Set(SYMBOL(isolate, "columnNumbers"), idx_columns);
  
  return scope.Escape(obj);
}
Esempio n. 2
0
/* 
DBIndex = {
  name             : ""    ,  // Index name
  isPrimaryKey     : true  ,  // true for PK; otherwise undefined
  isUnique         : true  ,  // true or false
  isOrdered        : true  ,  // true or false; can scan if true
  columnNumbers    : []    ,  // an ordered array of column numbers
};
*/
Handle<Object> GetTableCall::buildDBIndex_PK() {
  EscapableHandleScope scope(isolate);
  
  Local<Object> obj = Object::New(isolate);

  obj->ForceSet(SYMBOL(isolate, "name"), String::NewFromUtf8(isolate, "PRIMARY_KEY"));
  obj->ForceSet(SYMBOL(isolate, "isPrimaryKey"), Boolean::New(isolate, true), ReadOnly);
  obj->ForceSet(SYMBOL(isolate, "isUnique"),     Boolean::New(isolate, true), ReadOnly);
  obj->ForceSet(SYMBOL(isolate, "isOrdered"),    Boolean::New(isolate, false), ReadOnly);

  /* Loop over the columns of the key. 
     Build the "columnNumbers" array and the "record" object, then set both.
  */  
  int ncol = ndb_table->getNoOfPrimaryKeys();
  DEBUG_PRINT("Creating Primary Key Record");
  Record * pk_record = new Record(dict, ncol, true);
  Local<Array> idx_columns = Array::New(isolate, ncol);
  for(int i = 0 ; i < ncol ; i++) {
    const char * col_name = ndb_table->getPrimaryKey(i);
    const NdbDictionary::Column * col = ndb_table->getColumn(col_name);
    pk_record->addColumn(col);
    idx_columns->Set(i, v8::Int32::New(isolate, col->getColumnNo()));
  }
  pk_record->completeTableRecord(ndb_table);

  obj->Set(SYMBOL(isolate, "columnNumbers"), idx_columns);
  obj->ForceSet(SYMBOL(isolate, "record"), Record_Wrapper(pk_record), ReadOnly);
 
  return scope.Escape(obj);
}
Esempio n. 3
0
void* JsVlcPlayer::onFrameSetup( const I420VideoFrame& videoFrame )
{
    using namespace v8;

    if( 0 == videoFrame.width() || 0 == videoFrame.height() ||
        0 == videoFrame.uPlaneOffset() || 0 == videoFrame.vPlaneOffset() ||
        0 == videoFrame.size() )
    {
        assert( false );
        return nullptr;
    }

    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope( isolate );

    Local<Object> global = isolate->GetCurrentContext()->Global();

    Local<Value> abv =
        global->Get(
            String::NewFromUtf8( isolate,
                                 "Uint8Array",
                                 v8::String::kInternalizedString ) );
    Local<Value> argv[] =
        { Integer::NewFromUnsigned( isolate, videoFrame.size() ) };
    Local<Uint8Array> jsArray =
        Handle<Uint8Array>::Cast( Handle<Function>::Cast( abv )->NewInstance( 1, argv ) );

    Local<Integer> jsWidth = Integer::New( isolate, videoFrame.width() );
    Local<Integer> jsHeight = Integer::New( isolate, videoFrame.height() );
    Local<Integer> jsPixelFormat = Integer::New( isolate, static_cast<int>( PixelFormat::I420 ) );

    jsArray->ForceSet( String::NewFromUtf8( isolate, "width", v8::String::kInternalizedString ),
                       jsWidth,
                       static_cast<v8::PropertyAttribute>( ReadOnly | DontDelete ) );
    jsArray->ForceSet( String::NewFromUtf8( isolate, "height", v8::String::kInternalizedString ),
                       jsHeight,
                       static_cast<v8::PropertyAttribute>( ReadOnly | DontDelete ) );
    jsArray->ForceSet( String::NewFromUtf8( isolate, "pixelFormat", v8::String::kInternalizedString ),
                       jsPixelFormat,
                       static_cast<v8::PropertyAttribute>( ReadOnly | DontDelete ) );
    jsArray->ForceSet( String::NewFromUtf8( isolate, "uOffset", v8::String::kInternalizedString ),
                       Integer::New( isolate, videoFrame.uPlaneOffset() ),
                       static_cast<v8::PropertyAttribute>( ReadOnly | DontDelete ) );
    jsArray->ForceSet( String::NewFromUtf8( isolate, "vOffset", v8::String::kInternalizedString ),
                       Integer::New( isolate, videoFrame.vPlaneOffset() ),
                       static_cast<v8::PropertyAttribute>( ReadOnly | DontDelete ) );

    _jsFrameBuffer.Reset( isolate, jsArray );

    callCallback( CB_FrameSetup, { jsWidth, jsHeight, jsPixelFormat, jsArray } );

#ifdef USE_ARRAY_BUFFER
    return jsArray->Buffer()->GetContents().Data();
#else
    return jsArray->GetIndexedPropertiesExternalArrayData();
#endif
}
Esempio n. 4
0
static gboolean
gum_v8_module_handle_export_match (const GumExportDetails * details,
                                   gpointer user_data)
{
  GumV8ExportsContext * ctx =
      static_cast<GumV8ExportsContext *> (user_data);
  Isolate * isolate = ctx->isolate;
  Local<Context> jc = isolate->GetCurrentContext ();
  PropertyAttribute attrs =
      static_cast<PropertyAttribute> (ReadOnly | DontDelete);

  Local<Object> exp (ctx->exp->Clone ());

  if (details->type != GUM_EXPORT_FUNCTION)
  {
    Maybe<bool> success = exp->ForceSet (jc, ctx->type, ctx->variable, attrs);
    g_assert (success.IsJust ());
  }

  Maybe<bool> success = exp->ForceSet (jc,
      ctx->name,
      String::NewFromOneByte (isolate,
          reinterpret_cast<const uint8_t *> (details->name)),
      attrs);
  g_assert (success.IsJust ());

  success = exp->ForceSet (jc,
      ctx->address,
      _gum_v8_native_pointer_new (GSIZE_TO_POINTER (details->address),
          ctx->self->core),
      attrs);
  g_assert (success.IsJust ());

  Handle<Value> argv[] = {
    exp
  };
  Local<Value> result = ctx->on_match->Call (ctx->receiver, 1, argv);

  gboolean proceed = TRUE;
  if (!result.IsEmpty () && result->IsString ())
  {
    String::Utf8Value str (result);
    proceed = (strcmp (*str, "stop") != 0);
  }

  return proceed;
}
void CanvasRenderingContext2D::Create(const v8::FunctionCallbackInfo<v8::Value>& args)
{
	auto isolate = args.GetIsolate();
	HandleScope scope(isolate);

	Local<ObjectTemplate> contextTemplate = ObjectTemplate::New(isolate);
	contextTemplate->SetInternalFieldCount(1);

	contextTemplate->Set(ConvertToV8String("__draw"), FunctionTemplate::New(isolate, &Draw));
	contextTemplate->Set(ConvertToV8String("__sizeChanged"), FunctionTemplate::New(isolate, &SizeChanged));

	contextTemplate->SetAccessor(ConvertToV8String("fillStyle"), &GetFillStyle, &SetFillStyle);
	contextTemplate->SetAccessor(ConvertToV8String("strokeStyle"), &GetStrokeStyle, &SetStrokeStyle);

	contextTemplate->Set(ConvertToV8String("arc"), FunctionTemplate::New(isolate, &Arc));
	contextTemplate->Set(ConvertToV8String("beginPath"), FunctionTemplate::New(isolate, &BeginPath));
	contextTemplate->Set(ConvertToV8String("bezierCurveTo"), FunctionTemplate::New(isolate, &BezierCurveTo));
	contextTemplate->Set(ConvertToV8String("clearRect"), FunctionTemplate::New(isolate, &ClearRect));
	contextTemplate->Set(ConvertToV8String("closePath"), FunctionTemplate::New(isolate, &ClosePath));
	contextTemplate->Set(ConvertToV8String("drawImage"), FunctionTemplate::New(isolate, &DrawImage));
	contextTemplate->Set(ConvertToV8String("fill"), FunctionTemplate::New(isolate, &Fill));
	contextTemplate->Set(ConvertToV8String("fillRect"), FunctionTemplate::New(isolate, &FillRect));
	contextTemplate->Set(ConvertToV8String("fillText"), FunctionTemplate::New(isolate, &FillText));
	contextTemplate->Set(ConvertToV8String("getImageData"), FunctionTemplate::New(isolate, &GetImageData));
	contextTemplate->Set(ConvertToV8String("lineTo"), FunctionTemplate::New(isolate, &LineTo));
	contextTemplate->Set(ConvertToV8String("measureText"), FunctionTemplate::New(isolate, &MeasureText));
	contextTemplate->Set(ConvertToV8String("moveTo"), FunctionTemplate::New(isolate, &MoveTo));
	contextTemplate->Set(ConvertToV8String("quadraticCurveTo"), FunctionTemplate::New(isolate, &QuadraticCurveTo));
	contextTemplate->Set(ConvertToV8String("restore"), FunctionTemplate::New(isolate, &Restore));
	contextTemplate->Set(ConvertToV8String("rotate"), FunctionTemplate::New(isolate, &Rotate));
	contextTemplate->Set(ConvertToV8String("save"), FunctionTemplate::New(isolate, &Save));
	contextTemplate->Set(ConvertToV8String("stroke"), FunctionTemplate::New(isolate, &Stroke));
	contextTemplate->Set(ConvertToV8String("translate"), FunctionTemplate::New(isolate, &Translate));

	Local<Object> newContext = contextTemplate->NewInstance();
	newContext->ForceSet(ConvertToV8String("canvas"), args[0]);
	newContext->ForceSet(ConvertToV8String("__kind"), ConvertToV8String("2d"));

	auto nativeContext = new CanvasRenderingContext2D();
	newContext->SetInternalField(0, External::New(isolate, nativeContext));

	Persistent<Object> persistentHandle(isolate, newContext);
	persistentHandle.SetWeak(nativeContext, &Deallocate);

	args.GetReturnValue().Set(newContext);
}
Esempio n. 6
0
void
_gum_v8_module_realize (GumV8Module * self)
{
  static gsize gonce_value = 0;

  if (g_once_init_enter (&gonce_value))
  {
    Isolate * isolate = self->core->isolate;
    Local<Context> context = isolate->GetCurrentContext ();

    Local<String> type (String::NewFromUtf8 (isolate, "type"));
    Local<String> name (String::NewFromUtf8 (isolate, "name"));
    Local<String> module (String::NewFromUtf8 (isolate, "module"));
    Local<String> address (String::NewFromUtf8 (isolate, "address"));

    Local<String> function (String::NewFromUtf8 (isolate, "function"));
    Local<String> variable (String::NewFromUtf8 (isolate, "variable"));

    Local<String> empty_string = String::NewFromUtf8 (isolate, "");

    Local<Object> imp (Object::New (isolate));
    Maybe<bool> result = imp->ForceSet (context, type, function);
    g_assert (result.IsJust ());
    result = imp->ForceSet (context, name, empty_string, DontDelete);
    g_assert (result.IsJust ());
    result = imp->ForceSet (context, module, empty_string);
    g_assert (result.IsJust ());
    result = imp->ForceSet (context, address, _gum_v8_native_pointer_new (
        GSIZE_TO_POINTER (NULL), self->core));
    g_assert (result.IsJust ());

    Local<Object> exp (Object::New (isolate));
    result = exp->ForceSet (context, type, function, DontDelete);
    g_assert (result.IsJust ());
    result = exp->ForceSet (context, name, empty_string, DontDelete);
    g_assert (result.IsJust ());
    result = exp->ForceSet (context, address, _gum_v8_native_pointer_new (
        GSIZE_TO_POINTER (NULL), self->core), DontDelete);
    g_assert (result.IsJust ());

    eternal_imp.Set (isolate, imp);
    eternal_exp.Set (isolate, exp);

    eternal_type.Set (isolate, type);
    eternal_name.Set (isolate, name);
    eternal_module.Set (isolate, module);
    eternal_address.Set (isolate, address);
    eternal_variable.Set (isolate, variable);

    g_once_init_leave (&gonce_value, 1);
  }
}
Esempio n. 7
0
static gboolean
gum_v8_module_handle_import_match (const GumImportDetails * details,
                                   gpointer user_data)
{
  GumV8ImportsContext * ctx =
      static_cast<GumV8ImportsContext *> (user_data);
  Isolate * isolate = ctx->isolate;
  Local<Context> jc = isolate->GetCurrentContext ();
  PropertyAttribute attrs =
      static_cast<PropertyAttribute> (ReadOnly | DontDelete);

  Local<Object> imp (ctx->imp->Clone ());

  switch (details->type)
  {
    case GUM_IMPORT_FUNCTION:
    {
      /* the default value in our template */
      break;
    }
    case GUM_IMPORT_VARIABLE:
    {
      Maybe<bool> success = imp->ForceSet (jc, ctx->type, ctx->variable, attrs);
      g_assert (success.IsJust ());
      break;
    }
    case GUM_IMPORT_UNKNOWN:
    {
      Maybe<bool> success = imp->Delete (jc, ctx->type);
      g_assert (success.IsJust ());
      break;
    }
    default:
    {
      g_assert_not_reached ();
      break;
    }
  }

  Maybe<bool> success = imp->ForceSet (jc,
      ctx->name,
      String::NewFromOneByte (isolate,
          reinterpret_cast<const uint8_t *> (details->name)),
      attrs);
  g_assert (success.IsJust ());

  if (details->module != NULL)
  {
    success = imp->ForceSet (jc,
        ctx->module,
        String::NewFromOneByte (isolate,
            reinterpret_cast<const uint8_t *> (details->module)),
        attrs);
    g_assert (success.IsJust ());
  }
  else
  {
    success = imp->Delete (jc, ctx->module);
    g_assert (success.IsJust ());
  }

  if (details->address != 0)
  {
    success = imp->ForceSet (jc,
        ctx->address,
        _gum_v8_native_pointer_new (GSIZE_TO_POINTER (details->address),
            ctx->self->core),
        attrs);
    g_assert (success.IsJust ());
  }
  else
  {
    success = imp->Delete (jc, ctx->address);
    g_assert (success.IsJust ());
  }

  Handle<Value> argv[] = {
    imp
  };
  Local<Value> result = ctx->on_match->Call (ctx->receiver, 1, argv);

  gboolean proceed = TRUE;
  if (!result.IsEmpty () && result->IsString ())
  {
    String::Utf8Value str (result);
    proceed = (strcmp (*str, "stop") != 0);
  }

  return proceed;
}
Esempio n. 8
0
Handle<Object> GetTableCall::buildDBColumn(const NdbDictionary::Column *col) {
  EscapableHandleScope scope(isolate);
  
  Local<Object> obj = NdbDictColumnEnv.wrap(col)->ToObject();
  
  NdbDictionary::Column::Type col_type = col->getType();
  bool is_int = (col_type <= NDB_TYPE_BIGUNSIGNED);
  bool is_dec = ((col_type == NDB_TYPE_DECIMAL) || (col_type == NDB_TYPE_DECIMALUNSIGNED));
  bool is_binary = ((col_type == NDB_TYPE_BLOB) || (col_type == NDB_TYPE_BINARY) 
                   || (col_type == NDB_TYPE_VARBINARY) || (col_type == NDB_TYPE_LONGVARBINARY));
  bool is_char = ((col_type == NDB_TYPE_CHAR) || (col_type == NDB_TYPE_TEXT)
                   || (col_type == NDB_TYPE_VARCHAR) || (col_type == NDB_TYPE_LONGVARCHAR));
  bool is_lob =  ((col_type == NDB_TYPE_BLOB) || (col_type == NDB_TYPE_TEXT));

  /* Required Properties */

  obj->ForceSet(SYMBOL(isolate, "name"),
           String::NewFromUtf8(isolate, col->getName()),
           ReadOnly);
  
  obj->ForceSet(SYMBOL(isolate, "columnNumber"),
           v8::Int32::New(isolate, col->getColumnNo()),
           ReadOnly);
  
  obj->ForceSet(SYMBOL(isolate, "columnType"),
           String::NewFromUtf8(isolate, getColumnType(col)),
           ReadOnly);

  obj->ForceSet(SYMBOL(isolate, "isIntegral"),
           Boolean::New(isolate, is_int),
           ReadOnly);

  obj->ForceSet(SYMBOL(isolate, "isNullable"),
           Boolean::New(isolate, col->getNullable()),
           ReadOnly);

  obj->ForceSet(SYMBOL(isolate, "isInPrimaryKey"),
           Boolean::New(isolate, col->getPrimaryKey()),
           ReadOnly);

  obj->ForceSet(SYMBOL(isolate, "columnSpace"),
           v8::Int32::New(isolate, col->getSizeInBytes()),
           ReadOnly);           

  /* Implementation-specific properties */
  
  obj->ForceSet(SYMBOL(isolate, "ndbTypeId"),
           v8::Int32::New(isolate, static_cast<int>(col->getType())),
           ReadOnly);

  obj->ForceSet(SYMBOL(isolate, "ndbRawDefaultValue"),
           getDefaultValue(isolate, col),
           ReadOnly);
  
  /* Optional Properties, depending on columnType */
  /* Group A: Numeric */
  if(is_int || is_dec) {
    obj->ForceSet(SYMBOL(isolate, "isUnsigned"),
             Boolean::New(isolate, getIntColumnUnsigned(col)),
             ReadOnly);
  }
  
  if(is_int) {    
    obj->ForceSet(SYMBOL(isolate, "intSize"),
             v8::Int32::New(isolate, col->getSizeInBytes()),
             ReadOnly);
  }
  
  if(is_dec) {
    obj->ForceSet(SYMBOL(isolate, "scale"),
             v8::Int32::New(isolate, col->getScale()),
             ReadOnly);
    
    obj->ForceSet(SYMBOL(isolate, "precision"),
             v8::Int32::New(isolate, col->getPrecision()),
             ReadOnly);
  }

  obj->ForceSet(SYMBOL(isolate, "isAutoincrement"),
           Boolean::New(isolate, col->getAutoIncrement()),
           ReadOnly);
   
  /* Group B: Non-numeric */
  if(is_binary || is_char) {
    obj->ForceSet(SYMBOL(isolate, "isBinary"),
             Boolean::New(isolate, is_binary),
             ReadOnly);
  
    obj->ForceSet(SYMBOL(isolate, "isLob"),
             Boolean::New(isolate, is_lob),
             ReadOnly);

    if(is_binary) {
      obj->ForceSet(SYMBOL(isolate, "length"),
               v8::Int32::New(isolate, col->getLength()),
               ReadOnly);
    }

    if(is_char) {
      const EncoderCharset * csinfo = getEncoderCharsetForColumn(col);

      obj->ForceSet(SYMBOL(isolate, "length"),
               v8::Int32::New(isolate, col->getLength() / csinfo->maxlen),
               ReadOnly);

      obj->ForceSet(SYMBOL(isolate, "charsetName"),
               String::NewFromUtf8(isolate, csinfo->name),
               ReadOnly);

      obj->ForceSet(SYMBOL(isolate, "collationName"),
               String::NewFromUtf8(isolate, csinfo->collationName),
               ReadOnly);
    }
  }
    
  return scope.Escape(obj);
} 
Esempio n. 9
0
/* doAsyncCallback() runs in the main thread.  We don't want it to block.
   TODO: verify whether any IO is done 
         by checking WaitMetaRequestCount at the start and end.
*/    
void GetTableCall::doAsyncCallback(Local<Object> ctx) {
  const char *tableName;
  EscapableHandleScope scope(isolate);
  DEBUG_PRINT("GetTableCall::doAsyncCallback: return_val %d", return_val);

  /* User callback arguments */
  Handle<Value> cb_args[2];
  cb_args[0] = Null(isolate);
  cb_args[1] = Null(isolate);
  
  /* TableMetadata = {
      database         : ""    ,  // Database name
      name             : ""    ,  // Table Name
      columns          : []    ,  // ordered array of DBColumn objects
      indexes          : []    ,  // array of DBIndex objects 
      partitionKey     : []    ,  // ordered array of column numbers in the partition key
      sparseContainer  : null     // default column for sparse fields
    };
  */    
  if(ndb_table && ! return_val) {
    Local<Object> table = NdbDictTableEnv.wrap(ndb_table)->ToObject();

    // database
    table->Set(SYMBOL(isolate, "database"), String::NewFromUtf8(isolate, arg1));
    
    // name
    tableName = ndb_table->getName();
    table->Set(SYMBOL(isolate, "name"), String::NewFromUtf8(isolate, tableName));

    // partitionKey
    int nPartitionKeys = 0;
    Handle<Array> partitionKeys = Array::New(isolate);
    table->Set(SYMBOL(isolate, "partitionKey"), partitionKeys);

    // sparseContainer
    table->Set(SYMBOL(isolate,"sparseContainer"), Null(isolate));

    // columns
    Local<Array> columns = Array::New(isolate, ndb_table->getNoOfColumns());
    for(int i = 0 ; i < ndb_table->getNoOfColumns() ; i++) {
      const NdbDictionary::Column *ndb_col = ndb_table->getColumn(i);
      Handle<Object> col = buildDBColumn(ndb_col);
      columns->Set(i, col);
      if(ndb_col->getPartitionKey()) { /* partition key */
        partitionKeys->Set(nPartitionKeys++, String::NewFromUtf8(isolate, ndb_col->getName()));
      }
      if(     ! strcmp(ndb_col->getName(), "SPARSE_FIELDS")
          && ( (! strncmp(getColumnType(ndb_col), "VARCHAR", 7)
                  && (getEncoderCharsetForColumn(ndb_col)->isUnicode))
              || ! strncmp(getColumnType(ndb_col), "VARBINARY", 9)))
      {
        table->Set(SYMBOL(isolate,"sparseContainer"),
                   String::NewFromUtf8(isolate, ndb_col->getName()));
      }
    }
    table->Set(SYMBOL(isolate, "columns"), columns);

    // indexes (primary key & secondary) 
    Local<Array> js_indexes = Array::New(isolate, idx_list.count + 1);
    js_indexes->Set(0, buildDBIndex_PK());                   // primary key
    for(unsigned int i = 0 ; i < idx_list.count ; i++) {   // secondary indexes
      const NdbDictionary::Index * idx =
        dict->getIndex(idx_list.elements[i].name, arg2);
      js_indexes->Set(i+1, buildDBIndex(idx));
    }    
    table->ForceSet(SYMBOL(isolate, "indexes"), js_indexes, ReadOnly);

    // foreign keys (only foreign keys for which this table is the child)
    // now create the javascript foreign key metadata objects for dictionary objects cached earlier
    Local<Array> js_fks = Array::New(isolate, fk_count);

    int fk_number = 0;
    for(unsigned int i = 0 ; i < fk_list.count ; i++) {
      NdbDictionary::ForeignKey fk;
      if (fk_list.elements[i].type == NdbDictionary::Object::ForeignKey) {
        const char * fk_name = fk_list.elements[i].name;
        int fkGetCode = dict->getForeignKey(fk, fk_name);
        DEBUG_PRINT("getForeignKey for %s returned %i", fk_name, fkGetCode);
        // see if the foreign key child table is this table
        if(splitNameMatchesDbAndTable(fk.getChildTable())) {
          // the foreign key child table is this table; build the fk object
          DEBUG_PRINT("Adding foreign key for %s at %i", fk.getName(), fk_number);
          js_fks->Set(fk_number++, buildDBForeignKey(&fk));
        }
      }
    }
    table->ForceSet(SYMBOL(isolate, "foreignKeys"), js_fks, ReadOnly);

    // Autoincrement Cache Impl (also not part of spec)
    if(per_table_ndb) {
      table->Set(SYMBOL(isolate, "per_table_ndb"), Ndb_Wrapper(per_table_ndb));
    }
    
    // User Callback
    cb_args[1] = table;
  }
  else {
    cb_args[0] = NdbError_Wrapper(* ndbError);
  }
  
  ToLocal(& callback)->Call(ctx, 2, cb_args);
}