Nota do editor: Bem-vindo à terceira parte de uma série de três, que visa ajudá-lo a ter um empurrãozinho nas novidades e quebras de APIs do Node v0.12. Na primeira parte, Alex Gorbatchev extraiu as APIs que não vão quebrar. Já na segunda parte, ele separou as que irão. E na terceira, Ben Noordhuis mostra detalhes nas mudanças da API C++.
Houve inúmeras mudanças entre as versões v0.10 e v0.12 do Node. A ideia dessa série não é tentar destacar cada uma das últimas mudanças, mas sim agrupar as mudanças relacionadas, classificando-as numa ordem (indiscutivelmente de caráter bastante subjetivo) de "mais importante" para "menos importante" e explicar como atualizar melhor o código.
Boa parte deste artigo abrange a API V8 pelo fato de ser a API que mais teve mudanças significativas. Comentários e sugestões são muito bem-vindos! Sinta-se a vontade para entrar em contato com o autor pelo perfil do ben no strongloop.com.
Na versão v0.10, se escreve:
v8::Handle<v8::Value> FortyTwo(const v8::Arguments& args) {
v8::HandleScope handle_scope;
return handle_scope.Close(v8::Integer::New(42));
}
Na versão v0.12, escreve:
void FortyTwo(const v8::FunctionCallbackInfo<v8::Value>& info) {
// Don't need a HandleScope in this particular example.
info.GetReturnValue().Set(42);
}
As importantes alterações são:
- O tipo de retorno é void.
- O valor de retorno é alterado usando
v8::ReturnValue::Set()
,v8::ReturnValue::SetEmptyString()
,v8::ReturnValue::SetNull()
ouv8::ReturnValue::SetUndefined()
. - undefined é retornado se o valor de retorno não for alterado de forma explicita.
V8::ReturnValue::Set()
tem um número de sobrecargas de método que permite suportar argumento de diferentes tipos. Se o compilador tiver dificuldade em resolver algum tipo ou reclamar de ambigüidade, o argumento static_cast<>
serve para a conversão:
void FortyTwo(const v8::FunctionCallbackInfo<v8::Value>& info) {
const int64_t value = 42;
// error: call of overloaded 'Set(const int64_t&)' is ambiguous
info.GetReturnValue().Set(value);
// but this works
info.GetReturnValue().Set(static_cast<int32_t>(value));
// as does this
info.GetReturnValue().Set(static_cast<double>(value));
}
Lógico que é preciso escolher o tipo certo para a conversão. Visto que uma conversão explicita para um outro tipo que não seja amplo o suficiente para manter o valor original, pode gerar resultados inesperados.
Observe que v8::FunctionCallbackInfo <v8::Value>
é um v8::Arguments
renomeado e parametrizado. Além do nome e a adição do método GetReturnValue()
, não houve nenhuma outra mudança.
Na versão v0.10, se escreve:
v8::Local<v8::Integer> a = v8::Integer::New(42);
v8::Local<v8::Number> b = v8::Number::New(13.37);
v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(Foo);
Na versão v0.12, escreve:
v8::Isolate* isolate = /* ... */;
v8::Local<v8::Integer> a = v8::Integer::New(isolate, 42);
v8::Local<v8::Number> b = v8::Number::New(isolate, 13.37);
v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(isolate, Foo);
É possível obter um ponteiro para o v8::Isolate
corrente de várias maneiras:
void FortyTwo(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate;
isolate = info.GetIsolate();
// or:
isolate = info.GetReturnValue().GetIsolate();
// or even:
isolate = v8::Isolate::GetCurrent();
}
Note que a equipe V8 advertiu que v8::Isolate::GetCurrent()
vai ser extinto. Caso queira um código que dure até o futuro próximo, é melhor passar explicitamente o isolate.
Na versão v0.10, se escreve:
// create a UTF-8 string
v8::Local<v8::String> utf8 = v8::String::New("42");
// create a UTF-16 string from input in system endianness
static const uint16_t chars[] = { '4', '2', 0 };
v8::Local<v8::String> utf16 = v8::String::New(chars);
Na versão v0.12, escreve:
v8::Isolate* isolate = /* ... */;
// create a UTF-8 string
v8::Local<v8::String> utf8 = v8::String::NewFromUtf8(isolate, "42");
// create a UTF-16 string from input in system endianness
static const uint16_t chars[] = { '4', '2', 0 };
v8::Local<v8::String> utf16 = v8::String::NewFromTwoByte(isolate, chars);
// create a ISO-8859-1 a.k.a. Latin1 string
const uint8_t* octets = reinterpret_cast<const uint8_t*>("42");
v8::Local<v8::String> latin1 = v8::String::NewFromOneByte(isolate, octets);
v8::String::NewFromTwoByte()
é um nome tanto inadequado uma vez que não é uma codificação estrita de dois bytes e sim, algo que reconhece surrogate pair, portanto um método que consome caracteres UTF-16 e não UCS-2.
Nota: um Surrogate é um tipo de caractere, utilizado em algumas linguagens, que altera ou substitui um grupo de dois caracteres. Um Surrogate Pair com certeza tem mais de 2 bytes.
Na versão v0.10, escreve:
v8::Local<v8::String> symbol = v8::String::NewSymbol("42");
v8::Local<v8::String> hidden = v8::String::NewUndetectable("42");
Na versão v0.12, escreve:
v8::Isolate* isolate = /* ... */;
v8::Local<v8::String> symbol =
v8::String::NewFromUtf8(isolate, "42", v8::String::kInternalizedString);
v8::Local<v8::String> hidden =
v8::String::NewFromUtf8(isolate, "42", v8::String::kUndetectableString);
Na versão v0.10, escreve:
v8::Local<v8::String> string = /* ... */;
v8::String::AsciiValue s(string);
puts(s);
Na versão v0.12, escreve:
Nada! V8::String::AsciiValue
está quebrado: seu nome sugere um retorno de um dado ASCII de 7 bits, mas o que ele realmente retorna é um dado binário de 8 bits. E também quebra as seqüências de caracteres de múltiplos bytes.
Substitua-o com v8::String::Utf8Value
ou v8::String::Value
(para UTF-16). Caso queira extrair bytes brutos de uma string, faça isso:
v8::Local<v8::String> string = /* ... */;
const int length = string->Utf8Length() + 1; // Add one for trailing zero byte.
uint8_t* buffer = new uint8_t[length];
string->WriteOneByte(buffer, /* start */ 0, length);
Cuidado: v8::String::Utf8Length()
pode iterar sobre cada caractere, e por isso pode ser (muito) lenta para string bem maiores.
Na versão v0.10, escreve:
v8::Local<v8::Value> Example() {
v8::HandleScope handle_scope;
return handle_scope.Close(v8::Integer::New(42));
}
Na versão v0.12, escreve:
v8::Local<v8::Value> Example(v8::Isolate* isolate) {
v8::EscapableHandleScope handle_scope(isolate);
return handle_scope.Escape(v8::Integer::New(isolate, 42));
}
v8::Persistent<T>
não é mais uma instância de v8::Handle<T>
. Isso significa que não há acesso direto ao Handle
. A motivação para essa mudança é que o comportamento da versão 0.10 facilmente apresenta (i) perda de recursos ou (ii) há a possibilidade de usar um valor mesmo depois de ter sido descartado.
Na versão v0.10, escreve:
v8::Local<v8::Value> value = /* ... */;
v8::Persistent<v8::Value> persistent = v8::Persistent<v8::Value>::New(value);
// ...
v8::Local<v8::Value> value_again = *persistent;
// ...
persistent.Dispose();
persistent.Clear();
Na versão v0.12, escreve:
v8::Isolate* isolate = /* ... */
v8::Local<v8::Value> value = /* ... */;
v8::Persistent<v8::Value> persistent(isolate, value);
// or:
v8::Persistent<v8::Value> persistent;
persistent.Reset(isolate, value);
// ...
v8::Local<v8::Value> value_again =
v8::Local<v8::Value>::New(isolate, persistent); // rematerialize handle
// ...
persistent.Reset();
Outra mudança é que os Handles persistentes, por padrão, não são mais copiáveis. Na versão v0.10, escreve:
v8::Local<v8::Value> value = /* ... */;
v8::Persistent<v8::Value> a = v8::Persistent<v8::Value>::New(value);
v8::Persistent<v8::Value> b = a; // a and b now point to |value|
Na versão v0.12, escreve:
v8::Isolate* isolate = /* ... */
v8::Local<v8::Value> value = /* ... */;
v8::Persistent<v8::Value> a(isolate, value);
v8::Persistent<v8::Value> b(isolate, a);
// or:
v8::Persistent<v8::Value> b;
b.Reset(isolate, a);
Uma consequência de v8::Persistent<T>
não ter a semântica de cópia é que isso torna mais difícil o uso de classes container STL. Por esse motivo, V8 fornece um número de classes utilitárias no arquivo de cabeçalho v8-util.h
.
Exemplo de uso:
#include "v8-util.h"
void Example(v8:Isolate* isolate) {
v8::StdPersistentValueMap<int, v8::String> map(isolate);
v8::Local<v8::String> value = v8::String::NewFromUtf8(isolate, "fortytwo");
map.Set(42, value);
assert(map.Contains(42));
assert(map.Get(42)->StrictEquals(value));
assert(map.Get(21).IsEmpty());
assert(1 == map.Size());
map.Remove(42);
assert(0 == map.Size());
map.Clear(); // or we could just call .Clear()
}
Usando o C++ 11, é possível usar o v8::UniquePersistent<T>
em classes container padrão com semântica alterável.
Uma alternativa, é criar um v8::Persistent com traços copiáveis, mas faça isso somente quando tiver a certeza de que não há espaço para a perda de recursos ou o uso é livre de bugs:
v8::Isolate* isolate = /* ... */;
v8::CopyablePersistentTraits<v8::Value>::CopyablePersistent persistent;
persistent.Reset(isolate, /* ... */);
Um handle persistente normal é ignorado pelo garbage collector e permanece em memória até que seja descartado manualmente.
Já um handle fracamente persistente , por outro lado, é controlado pelo garbage collector e, quando está prestes a ser recolhido, o coletor notifica o programa através de um callback.
O programa então libera quaisquer recursos associado ao handle ou o revive caso o objeto deve permanecer em memória por mais tempo.
Na versão v0.10, escreve:
void WeakCallback(v8::Persistent<v8::Value> object, void* arg) {
puts(static_cast<const char*>(arg));
object.Dispose(); // or .ClearWeak() if you want to keep it around
}
void Example() {
v8::HandleScope handle_scope;
v8::Local<v8::Object> object = v8::Object::New();
v8::Persistent<v8::Object> persistent =
v8::Persistent<v8::Object>::New(object);
persistent.MakeWeak(const_cast<char*>("fortytwo"), WeakCallback);
}
Na versão v0.12, escreve:
void WeakCallback(const v8::WeakCallbackData<v8::Value, const char*>& data) {
puts(data.GetParameter());
// The persistent's storage cell is automatically disposed. Keep a reference
// to the original v8::Persistent<T> if you want to revive it with
// .ClearWeak().
}
void Example(v8::Isolate* isolate) {
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Object> object = v8::Object::New(isolate);
v8::Persistent<v8::Object> persistent(isolate, object);
persistent.SetWeak("fortytwo", WeakCallback);
}
Na versão v0.10, escreve:
v8::Local<v8::Value> exception = /* ... */;
v8::ThrowException(exception);
Na versão v0.12, escreve:
v8::Isolate* isolate = /* ... */
v8::Local<v8::Value> exception = /* ... */;
isolate->ThrowException(exception);
- O que tem no lançamento do Node v0.12? Seis novos recursos, além de APIs novas e depreciadas.
- Pronto para desenvolver APIs e deixá-las conectadas aos seus dados? Confira o framework LoopBack do Node.js. E veja como que fica mais fácil começar uma API tanto localmente quanto na nuvem, simplesmente com um npm install.
- Precisa de treinamento e certificação Node.js? Saiba mais sobre ambos com as opções de ofertas da StrongLoop.