| // 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 |