blob: aa00e4b1bc21f7ac5d819041f546b5aa51b16443 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/shell/interpreter/src/scope.h"
#include <cstdint>
#include <string>
#include "src/developer/shell/interpreter/src/code.h"
#include "src/developer/shell/interpreter/src/interpreter.h"
#include "src/developer/shell/interpreter/src/schema.h"
#include "src/developer/shell/interpreter/src/types.h"
namespace shell {
namespace interpreter {
void Scope::Shutdown(ExecutionScope* execution_scope) {
for (const auto& variable : variables_) {
if (variable.second->index() < execution_scope->size()) {
// If the variable has been allocated, clear the variable. For reference counted objects, that
// also releases the object.
variable.second->Clear(execution_scope);
}
}
variables_.clear();
}
void StringConcatenation(Thread* thread, uint64_t count) {
FX_DCHECK(thread->stack_size() >= count);
size_t string_size = 0;
for (size_t i = 0; i < count; ++i) {
auto value = reinterpret_cast<String*>(thread->Value(i));
string_size += value->size();
}
std::string string;
string.reserve(string_size);
for (size_t i = count; i > 0;) {
--i;
auto value = reinterpret_cast<String*>(thread->Value(i));
string += value->value();
value->Release();
}
thread->Consume(count);
auto result = new String(thread->isolate()->interpreter(), std::move(string));
thread->Push(reinterpret_cast<uint64_t>(result));
}
template <typename S, typename U>
bool SAddWithExceptions(ExecutionContext* context, Thread* thread, const std::string& type_name) {
U right = static_cast<U>(thread->Pop());
U left = static_cast<U>(thread->Pop());
U result = left + right;
int shift = sizeof(U) * 8 - 1;
if ((right >> shift) == (left >> shift)) {
// The two operands have the same sign.
if ((right >> shift) != (result >> shift)) {
// The result doesn't have the same sign.
if ((right >> shift) == 0) {
// The operands are positive => overflow.
context->EmitError(type_name + " overflow when adding " +
std::to_string(static_cast<S>(left)) + " and " +
std::to_string(static_cast<S>(right)) + ".");
} else {
// The operands are negative => underflow.
context->EmitError(type_name + " underflow when adding " +
std::to_string(static_cast<S>(left)) + " and " +
std::to_string(static_cast<S>(right)) + ".");
}
return false;
}
}
thread->Push(result);
return true;
}
template <typename U>
bool UAddWithExceptions(ExecutionContext* context, Thread* thread, const std::string& type_name) {
U right = static_cast<U>(thread->Pop());
U left = static_cast<U>(thread->Pop());
U result = left + right;
if (result < right) {
context->EmitError(type_name + " overflow when adding " + std::to_string(left) + " and " +
std::to_string(right) + ".");
return false;
}
thread->Push(result);
return true;
}
void ExecutionScope::Execute(ExecutionContext* context, Thread* thread,
std::unique_ptr<code::Code> code) {
size_t pc = 0;
for (;;) {
FX_DCHECK(pc < code->code().size());
code::Opcode opcode = static_cast<code::Opcode>(code->code()[pc++]);
switch (opcode) {
case code::Opcode::kNop:
break;
case code::Opcode::kEmitResult: {
const Type* type = reinterpret_cast<const Type*>(code->code()[pc++]);
uint64_t value = thread->Pop();
type->EmitResult(context, value);
break;
}
case code::Opcode::kInt8Addition: {
uint8_t right = static_cast<uint8_t>(thread->Pop());
uint8_t left = static_cast<uint8_t>(thread->Pop());
thread->Push(left + right);
break;
}
case code::Opcode::kInt16Addition: {
uint16_t right = static_cast<uint16_t>(thread->Pop());
uint16_t left = static_cast<uint16_t>(thread->Pop());
thread->Push(left + right);
break;
}
case code::Opcode::kInt32Addition: {
uint32_t right = static_cast<uint32_t>(thread->Pop());
uint32_t left = static_cast<uint32_t>(thread->Pop());
thread->Push(left + right);
break;
}
case code::Opcode::kInt64Addition: {
uint64_t right = thread->Pop();
uint64_t left = thread->Pop();
thread->Push(left + right);
break;
}
case code::Opcode::kLiteral64: {
uint64_t value = code->code()[pc++];
thread->Push(value);
break;
}
case code::Opcode::kLoadRaw8: {
uint64_t index = code->code()[pc++];
auto source = thread->isolate()->global_execution_scope()->Data<uint8_t>(index);
thread->Push(static_cast<uint64_t>(*source));
break;
}
case code::Opcode::kLoadRaw16: {
uint64_t index = code->code()[pc++];
auto source = thread->isolate()->global_execution_scope()->Data<uint16_t>(index);
thread->Push(static_cast<uint64_t>(*source));
break;
}
case code::Opcode::kLoadRaw32: {
uint64_t index = code->code()[pc++];
auto source = thread->isolate()->global_execution_scope()->Data<uint32_t>(index);
thread->Push(static_cast<uint64_t>(*source));
break;
}
case code::Opcode::kLoadRaw64: {
uint64_t index = code->code()[pc++];
auto source = thread->isolate()->global_execution_scope()->Data<uint64_t>(index);
thread->Push(*source);
break;
}
case code::Opcode::kLoadReferenceCounted: {
uint64_t index = code->code()[pc++];
auto source =
thread->isolate()->global_execution_scope()->Data<ReferenceCountedBase*>(index);
ReferenceCountedBase* value = *source;
value->Use();
thread->Push(reinterpret_cast<uint64_t>(value));
break;
}
case code::Opcode::kObjectInit: {
Object* object = reinterpret_cast<Object*>(thread->Pop());
const ObjectSchema* schema = object->schema().get();
for (auto it = schema->fields().rbegin(); it != schema->fields().rend(); ++it) {
object->SetField((*it).get(), thread->Pop());
}
thread->Push(reinterpret_cast<uint64_t>(object));
break;
}
case code::Opcode::kObjectNew: {
uint64_t type_val = code->code()[pc++];
auto schema = reinterpret_cast<std::shared_ptr<ObjectSchema>*>(type_val);
Object* object = ObjectSchema::AllocateObject(*schema);
thread->Push(reinterpret_cast<uint64_t>(object));
break;
}
case code::Opcode::kReferenceCountedLiteral: {
uint64_t value = code->code()[pc++];
auto object = reinterpret_cast<ReferenceCountedBase*>(value);
object->Use();
thread->Push(value);
break;
}
case code::Opcode::kRet:
return;
case code::Opcode::kSint8AdditionWithExceptions:
if (!SAddWithExceptions<int8_t, uint8_t>(context, thread, "Int8")) {
return;
}
break;
case code::Opcode::kSint16AdditionWithExceptions:
if (!SAddWithExceptions<int16_t, uint16_t>(context, thread, "Int16")) {
return;
}
break;
case code::Opcode::kSint32AdditionWithExceptions:
if (!SAddWithExceptions<int32_t, uint32_t>(context, thread, "Int32")) {
return;
}
break;
case code::Opcode::kSint64AdditionWithExceptions:
if (!SAddWithExceptions<int64_t, uint64_t>(context, thread, "Int64")) {
return;
}
break;
case code::Opcode::kStoreRaw8: {
uint64_t index = code->code()[pc++];
auto destination = thread->isolate()->global_execution_scope()->Data<uint8_t>(index);
*destination = static_cast<uint8_t>(thread->Pop());
break;
}
case code::Opcode::kStoreRaw16: {
uint64_t index = code->code()[pc++];
auto destination = thread->isolate()->global_execution_scope()->Data<uint16_t>(index);
*destination = static_cast<uint16_t>(thread->Pop());
break;
}
case code::Opcode::kStoreRaw32: {
uint64_t index = code->code()[pc++];
auto destination = thread->isolate()->global_execution_scope()->Data<uint32_t>(index);
*destination = static_cast<uint32_t>(thread->Pop());
break;
}
case code::Opcode::kStoreRaw64: {
uint64_t index = code->code()[pc++];
auto destination = thread->isolate()->global_execution_scope()->Data<uint64_t>(index);
*destination = thread->Pop();
break;
}
case code::Opcode::kStoreReferenceCounted: {
uint64_t index = code->code()[pc++];
auto destination =
thread->isolate()->global_execution_scope()->Data<ReferenceCountedBase*>(index);
ReferenceCountedBase* old_value = *destination;
old_value->Release();
*destination = reinterpret_cast<ReferenceCountedBase*>(thread->Pop());
break;
}
case code::Opcode::kStringConcatenation: {
StringConcatenation(thread, code->code()[pc++]);
break;
}
case code::Opcode::kUint8AdditionWithExceptions:
if (!UAddWithExceptions<uint8_t>(context, thread, "Uint8")) {
return;
}
break;
case code::Opcode::kUint16AdditionWithExceptions:
if (!UAddWithExceptions<uint16_t>(context, thread, "Uint16")) {
return;
}
break;
case code::Opcode::kUint32AdditionWithExceptions:
if (!UAddWithExceptions<uint32_t>(context, thread, "Uint32")) {
return;
}
break;
case code::Opcode::kUint64AdditionWithExceptions:
if (!UAddWithExceptions<uint64_t>(context, thread, "Uint64")) {
return;
}
break;
}
}
}
} // namespace interpreter
} // namespace shell