我正在更新 node-mapserver,它将 mapserver 分支中的 class-refactor 库包装为更易于维护的代码结构(基于我的更改基于 node-ogr 的组织)。

到目前为止,我已经重构了主模块接口(interface)并开始了第一个要包装的类,一个名为 errorObj 的 map 服务器 C 结构,我正在用 class MSError 包装它。扩展构建但我面临的是扩展中的运行时断言错误,这是我第一次触发新的 MSError 对象来包装 errorObj。

Fatal error in ../deps/v8/src/api.h, line 297
CHECK(allow_empty_handle || that != __null) failed

在 Node 0.10 之后有一个非常长的堆栈跟踪,可以在 travis build 27.2 中看到。我在堆栈跟踪中找不到任何特别有用的东西。

扩展构建和一些测试通过(从库本身导出的符号和方法),但是当库尝试创建包装 C 对象的对象时失败。

在 javascript 中,我调用 mapserver.getError() 。在扩展中,这会调用 map 服务器方法 errorObj* err = msGetErrorObj(); 并与 return scope.Close(MSError::New(err)); 一起返回。 MSError 的 New 方法执行以下操作:
Handle<Value> MSError::New(errorObj *err) {
  HandleScope scope;
  MSError *wrapped = new MSError(err);
  Handle<Value> ext = External::New(wrapped);
  Handle<Object> obj = MSError::constructor->GetFunction()->NewInstance(1, &ext);
  return scope.Close(obj);
}

我曾尝试使用 gdb 调试它,我能用我有限的技能摆脱它的最好方法是错误发生在这个调用中:
  Handle<Object> obj = MSError::constructor->GetFunction()->NewInstance(1, &ext);

我可以从中收集到 ext 为空,这意味着 External::New(wrapped) 调用未返回有效值。我已经确认 errorObj* err 确实指向一个有效的、正确初始化的 errorObj 结构。为了澄清,没有实际错误,mapserver 总是返回一个有效的 errorObj 但如果此时没有错误,代码为 0。

希望这是相关的代码。

ms_error.hpp
#ifndef __NODE_MS_ERROR_H__
#define __NODE_MS_ERROR_H__

#include <v8.h>

#include <node.h>
#include <node_object_wrap.h>

#include <mapserver.h>

using namespace v8;
using namespace node;

class MSError: public node::ObjectWrap {
  public:
    static Persistent<FunctionTemplate> constructor;
    static void Initialize(Handle<Object> target);
    static Handle<Value> New(const Arguments &args);
    static Handle<Value> New(errorObj *err);

    MSError();
    MSError(errorObj *err);
    inline errorObj *get() { return this_; }

  private:
    ~MSError();
    errorObj *this_;
};

#endif

ms_error.cpp
#include "ms_error.hpp"

Persistent<FunctionTemplate> MSError::constructor;

void MSError::Initialize(Handle<Object> target) {
  HandleScope scope;

  constructor = Persistent<FunctionTemplate>::New(FunctionTemplate::New(MSError::New));
  constructor->InstanceTemplate()->SetInternalFieldCount(1);
  constructor->SetClassName(String::NewSymbol("MSError"));

  // constructor->InstanceTemplate()->SetNamedPropertyHandler(MSError::NamedPropertyGetter, NULL, MSError::NamedPropertyQuery, NULL, MSError::NamedPropertyEnumerator);

  target->Set(String::NewSymbol("MSError"), constructor->GetFunction());
}

MSError::MSError(errorObj *err) : ObjectWrap(), this_(err) { }

MSError::MSError() : ObjectWrap(), this_(0) { }

MSError::~MSError() { }

Handle<Value> MSError::New(const Arguments& args)
{
  HandleScope scope;

  if (!args.IsConstructCall())
    return ThrowException(String::New("Cannot call constructor as function, you need to use 'new' keyword"));

  if (args[0]->IsExternal()) {
    Local<External> ext = Local<External>::Cast(args[0]);
    void *ptr = ext->Value();
    MSError *f =  static_cast<MSError *>(ptr);
    f->Wrap(args.This());
    return args.This();
  }

  return args.This();
}

最佳答案

答案是上面的代码没问题。实际的错误是 MSError::constructor 没有初始化,因为我忽略了从主模块代码中调用它。实际的空对象是 MSError::constructor->GetFunction()。

感谢 Ben Noordhuis 为我们指明了正确的方向。

关于c++ - 包装 C 对象的运行时失败类,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17488186/

10-13 05:05